```
feat(cache): 添加复合键缓存操作支持 添加了基于 uint32+string 组合键的缓存操作方法,包括 GetByCompoundKey、SetByCompoundKey、DelByCompoundKey 和 ContainsByCompoundKey 方法,用于处理用户ID和会话ID的组合缓存场景 fix(vscode): 添加 cSpell 配置支持 struc 词汇 refactor(session): 移除过时的会话管理方法 移除了基于单一字符串键的会话管理方法,因为已迁移到使用 复合键的缓存操作方式 ```
This commit is contained in:
92
modules/player/model/Barge.go
Normal file
92
modules/player/model/Barge.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// 表名常量定义:精灵捕捉击杀数量记录表
|
||||
const (
|
||||
TableNamePetCatchKillCount = "player_catch_kill_count" // 精灵捕捉击杀数量表(记录每个精灵的捕捉总数量、击杀总数量)
|
||||
)
|
||||
|
||||
// PetBargeListInfo 精灵捕捉击杀数量核心模型(简化版,直接记录数量,摒弃状态判断)
|
||||
type PetBargeListInfo struct {
|
||||
Base
|
||||
PlayerID uint64 `gorm:"not null;index:idx_milestone_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
PetId uint32 `gorm:"not null;default:0;comment:'精灵ID,关联config_pet_boss表主键'" json:"pet_id" description:"精灵ID"`
|
||||
EnCntCnt uint32 `gorm:"not null;default:0;comment:'预留未知字段,暂未使用'" json:"en_cnt_cnt" description:"未知"`
|
||||
CatchedCount uint32 `gorm:"not null;default:0;comment:'精灵捕捉总数量'" json:"catched_count" description:"捕捉数量"` // 替换原IsCatched,记录捕捉总数
|
||||
KilledCount uint32 `gorm:"not null;default:0;comment:'精灵击杀总数量'" json:"killed_count" description:"击杀数量"` // 替换原IsKilled,记录击杀总数
|
||||
}
|
||||
|
||||
// -------------------------- 核心配套方法 --------------------------
|
||||
|
||||
// TableName 指定模型对应的数据库表名(遵循项目规范)
|
||||
func (*PetBargeListInfo) TableName() string {
|
||||
return TableNamePetCatchKillCount
|
||||
}
|
||||
|
||||
// GroupName 指定表所属分组(与其他精灵表保持一致)
|
||||
func (*PetBargeListInfo) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewPetBargeListInfo 创建精灵捕捉击杀数量实例(初始化默认值)
|
||||
func NewPetBargeListInfo() *PetBargeListInfo {
|
||||
return &PetBargeListInfo{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// AddCatchedCount 增加捕捉数量(支持批量累加,默认累加1)
|
||||
// addNum:要增加的数量(需大于0)
|
||||
func (p *PetBargeListInfo) AddCatchedCount(addNum uint32) error {
|
||||
if addNum <= 0 {
|
||||
return errors.New("增加的捕捉数量必须大于0")
|
||||
}
|
||||
if p.PetId == 0 {
|
||||
return errors.New("精灵ID不能为空,无法累加捕捉数量")
|
||||
}
|
||||
p.CatchedCount += addNum
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddKilledCount 增加击杀数量(支持批量累加,默认累加1)
|
||||
// addNum:要增加的数量(需大于0)
|
||||
func (p *PetBargeListInfo) AddKilledCount(addNum uint32) error {
|
||||
if addNum <= 0 {
|
||||
return errors.New("增加的击杀数量必须大于0")
|
||||
}
|
||||
if p.PetId == 0 {
|
||||
return errors.New("精灵ID不能为空,无法累加击杀数量")
|
||||
}
|
||||
p.KilledCount += addNum
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetCatchedCount 直接设置捕捉数量(适用于批量初始化/重置)
|
||||
// count:目标捕捉数量(非负数)
|
||||
func (p *PetBargeListInfo) SetCatchedCount(count uint32) error {
|
||||
if p.PetId == 0 {
|
||||
return errors.New("精灵ID不能为空,无法设置捕捉数量")
|
||||
}
|
||||
p.CatchedCount = count
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetKilledCount 直接设置击杀数量(适用于批量初始化/重置)
|
||||
// count:目标击杀数量(非负数)
|
||||
func (p *PetBargeListInfo) SetKilledCount(count uint32) error {
|
||||
if p.PetId == 0 {
|
||||
return errors.New("精灵ID不能为空,无法设置击杀数量")
|
||||
}
|
||||
p.KilledCount = count
|
||||
return nil
|
||||
}
|
||||
|
||||
// -------------------------- 表结构自动同步 --------------------------
|
||||
func init() {
|
||||
// 程序启动时自动创建/同步精灵捕捉击杀数量表
|
||||
cool.CreateTable(&PetBargeListInfo{})
|
||||
}
|
||||
46
modules/player/model/GoldBeanConsume.go
Normal file
46
modules/player/model/GoldBeanConsume.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
// 表名常量定义:金豆消费记录表
|
||||
const (
|
||||
TableNameGoldBeanConsume = "player_gold_log" // 金豆消费记录表(记录用户金豆消耗的明细、类型、关联业务等信息)
|
||||
)
|
||||
|
||||
// 通过金豆消费时间来确认金豆物品的购买重置周期
|
||||
// GoldBeanConsume 金豆消费核心模型(与数据库表字段一一对应,存储消费明细)
|
||||
type GoldBeanConsume struct {
|
||||
Base
|
||||
|
||||
UID uint32 `gorm:"not null;default:0;index;comment:'玩家唯一ID,关联玩家表主键'" json:"uid" description:"玩家ID"`
|
||||
ConsumeNum uint32 `gorm:"not null;default:0;comment:'金豆消费数量(非负数)'" json:"consume_num" description:"消费金豆数量"`
|
||||
BizID uint32 `gorm:"not null;default:0;comment:'关联业务ID(如道具ID/扭蛋池ID,无则填0)'" json:"biz_id" description:"关联业务ID"`
|
||||
BeforeBalance uint32 `gorm:"not null;default:0;comment:'消费前金豆余额'" json:"before_balance" description:"消费前余额"`
|
||||
}
|
||||
|
||||
// -------------------------- 核心配套方法 --------------------------
|
||||
|
||||
// TableName 指定GoldBeanConsume对应的数据库表名(遵循项目规范)
|
||||
func (*GoldBeanConsume) TableName() string {
|
||||
return TableNameGoldBeanConsume
|
||||
}
|
||||
|
||||
// GroupName 指定表所属分组(与其他精灵/玩家相关表保持一致)
|
||||
func (*GoldBeanConsume) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewGoldBeanConsume 创建金豆消费记录实例(初始化通用Model及默认值)
|
||||
func NewGoldBeanConsume() *GoldBeanConsume {
|
||||
return &GoldBeanConsume{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------- 表结构自动同步 --------------------------
|
||||
func init() {
|
||||
// 程序启动时自动创建/同步金豆消费记录表
|
||||
cool.CreateTable(&GoldBeanConsume{})
|
||||
}
|
||||
34
modules/player/model/TeamInfo.go
Normal file
34
modules/player/model/TeamInfo.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package model
|
||||
|
||||
// TeamInfo 战队信息结构
|
||||
type TeamInfo struct {
|
||||
//Head common.TomeeHeader `cmd:"1001" struc:"skip"` // 命令头
|
||||
ID uint32 `struc:"uint32" default:"0"` // 默认值0
|
||||
Priv uint32 `struc:"uint32" default:"1"` // 默认值1
|
||||
SuperCore uint32 `struc:"uint32" default:"1"` // 默认值1
|
||||
IsShow uint32 `struc:"uint32" default:"1"` // 默认值1
|
||||
AllContribution uint32 `struc:"uint32" default:"1"` // 默认值1
|
||||
CanExContribution uint32 `struc:"uint32" default:"1"` // 默认值1
|
||||
}
|
||||
|
||||
// InitDefaults 初始化默认值
|
||||
func (t *TeamInfo) InitDefaults() {
|
||||
t.ID = 0
|
||||
t.Priv = 1
|
||||
t.SuperCore = 1
|
||||
t.IsShow = 1
|
||||
t.AllContribution = 1
|
||||
t.CanExContribution = 1
|
||||
}
|
||||
|
||||
// TeamPKInfo 战队PK相关信息结构
|
||||
type TeamPKInfo struct {
|
||||
GroupID uint32 `struc:"uint32" default:"1"` // 分组ID,默认值1(@UInt long)
|
||||
HomeTeamID uint32 `struc:"uint32" default:"1"` // 主队ID,默认值1(@UInt long)
|
||||
}
|
||||
|
||||
// InitDefaults 初始化默认值(确保字段默认值正确赋值)
|
||||
func (t *TeamPKInfo) InitDefaults() {
|
||||
t.GroupID = 1
|
||||
t.HomeTeamID = 1
|
||||
}
|
||||
12
modules/player/model/base.go
Normal file
12
modules/player/model/base.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package model
|
||||
|
||||
import "blazing/cool"
|
||||
|
||||
type Base struct {
|
||||
*cool.Model
|
||||
|
||||
//是否测试服ItemCnt
|
||||
IsVip uint32 `gorm:"default:0;not null;comment:'是否为VIP服务器'" json:"is_vip"`
|
||||
}
|
||||
|
||||
func NewBase() *Base { return &Base{} }
|
||||
37
modules/player/model/cdk.go
Normal file
37
modules/player/model/cdk.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
// 表名常量
|
||||
const TableNamePlayerCdkLog = "player_cdk_log"
|
||||
|
||||
// CdkLog 对应数据库表 player_cdk_log,用于记录CDK兑换日志
|
||||
type CdkLog struct {
|
||||
Base
|
||||
PlayerID uint64 `gorm:"not null;index:idx_player_cdk_log_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
CodeID uint32 `gorm:"not null;comment:'CDK编号'" json:"code_id"`
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (*CdkLog) TableName() string {
|
||||
return TableNamePlayerCdkLog
|
||||
}
|
||||
|
||||
// GroupName 返回表组名
|
||||
func (*CdkLog) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewCdkLog 创建一个新的CDK记录
|
||||
func NewCdkLog() *CdkLog {
|
||||
return &CdkLog{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 程序启动时自动创建表
|
||||
func init() {
|
||||
cool.CreateTable(&CdkLog{})
|
||||
}
|
||||
79
modules/player/model/done.go
Normal file
79
modules/player/model/done.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/tnnmigga/enum"
|
||||
)
|
||||
|
||||
type EnumMilestone int
|
||||
|
||||
var MilestoneMode = enum.New[struct {
|
||||
BOSS EnumMilestone //boss类 地图ID->BOSSID ,胜利次数 mapid bossid petid,防止换boss后数据不可用
|
||||
ITEM EnumMilestone //物品类 物品ID 使用精灵
|
||||
Fight EnumMilestone //挑战类 对战模式->对战类型->1是赢,0是总局数
|
||||
Moster EnumMilestone //野怪统计 地图ID->怪物ID
|
||||
Task EnumMilestone
|
||||
}]()
|
||||
|
||||
const TableNameMilestone = "player_milestone"
|
||||
|
||||
// Milestone 数据库存储结构体,映射milestone表
|
||||
type Milestone struct {
|
||||
Base
|
||||
PlayerID uint64 `gorm:"not null;index:idx_milestone_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
DoneType EnumMilestone `gorm:"not null;comment:'里程碑类型'" json:"done_type"`
|
||||
Args string `gorm:"type:text;not null;comment:'里程碑ID'" json:"args"`
|
||||
// 注:不单独设置"里程碑ID",通过 PlayerID + DoneType + IDs 组合唯一标识一个里程碑(更灵活)
|
||||
Results string `gorm:"type:jsonb;not null;comment:'里程碑参数'" json:"results"`
|
||||
Count uint32 `gorm:"not null;comment:'里程碑完成次数'" json:"count"`
|
||||
}
|
||||
|
||||
// MilestoneEX 里程碑扩展结构体,用于业务层解析后的数据操作
|
||||
type MilestoneEX struct {
|
||||
Milestone
|
||||
Args []uint32 // 解析后的里程碑详细数据
|
||||
Results []uint32 `json:"results"` // 解析后的里程碑详细数据
|
||||
}
|
||||
|
||||
// 检查是否触发过,成功返回触发的次数,失败返回0
|
||||
func (m *MilestoneEX) CheakNoNumber(count uint32) bool {
|
||||
// if v.DoneType == model.MilestoneMode.BOSS && IsPrefixBasicSlice(v.Args, []uint32{mapid, bossid}) && v.Count == count {
|
||||
|
||||
_, ok := lo.Find(m.Results, func(v1 uint32) bool { //寻找是否触发过
|
||||
//大于触发值就触发,然后1的返回false,因为没有奖励,这样就可以一直触发
|
||||
return v1 == count //大于等于就触发
|
||||
})
|
||||
//没找到且次数满足才能返回真
|
||||
if !ok && m.Count >= count {
|
||||
return true
|
||||
}
|
||||
|
||||
//已经触发过
|
||||
return false
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (*Milestone) TableName() string {
|
||||
return TableNameMilestone
|
||||
}
|
||||
|
||||
// GroupName 返回表组名
|
||||
func (*Milestone) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewMilestone 创建新里程碑实例
|
||||
func NewMilestone() *Milestone {
|
||||
return &Milestone{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 初始化表
|
||||
func init() {
|
||||
cool.CreateTable(&Milestone{})
|
||||
}
|
||||
51
modules/player/model/item.go
Normal file
51
modules/player/model/item.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
const TableNamePlayerBagItem = "player_item"
|
||||
|
||||
// PlayerBagItem mapped from table <player_bag_item>
|
||||
type Item struct {
|
||||
Base
|
||||
PlayerID uint64 `gorm:"not null;index:idx_player_bag_item_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
// 物品Id,
|
||||
ItemId uint32 `json:"item_id"`
|
||||
|
||||
// 物品数量,
|
||||
ItemCnt uint32 `json:"item_cnt"`
|
||||
}
|
||||
|
||||
type SingleItemInfo struct {
|
||||
// 物品Id,
|
||||
ItemId uint32 `json:"itemId"`
|
||||
// 物品数量,
|
||||
ItemCnt uint32 `json:"itemCnt"`
|
||||
// 固定值360000,
|
||||
LeftTime uint32 `json:"leftTime"`
|
||||
// 固定值0,
|
||||
ItemLevel uint32 `json:"itemLevel"`
|
||||
}
|
||||
|
||||
// TableName PlayerBagItem's table name
|
||||
func (*Item) TableName() string {
|
||||
return TableNamePlayerBagItem
|
||||
}
|
||||
|
||||
// GroupName PlayerBagItem's table group
|
||||
func (*Item) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewPlayerBagItem create a new PlayerBagItem
|
||||
func NewPlayerBag() *Item {
|
||||
return &Item{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 创建表
|
||||
func init() {
|
||||
cool.CreateTable(&Item{})
|
||||
}
|
||||
539
modules/player/model/pet.go
Normal file
539
modules/player/model/pet.go
Normal file
@@ -0,0 +1,539 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/common/data"
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/common/utils"
|
||||
"blazing/cool"
|
||||
"blazing/modules/config/service"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const TableNamePet = "player_pet"
|
||||
|
||||
// Pet mapped from table <pet>
|
||||
type Pet struct {
|
||||
Base
|
||||
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
Free int `gorm:"not null;default:0;comment:'是否放生'" json:"free"` //"0为放入仓库,1为放入背包
|
||||
CatchTime uint32 `gorm:"not null;unique;comment:'捕捉时间'" json:"catch_time"` //唯一键
|
||||
// Owner uint32 `struc:"skip"` //仅作为存储
|
||||
// FreedTime uint32 `struc:"skip"` //放生时间
|
||||
//是否可交易,这里应该定义在精灵ID里
|
||||
//是否上架
|
||||
Data string `gorm:"type:jsonb;not null;comment:'精灵全部数据'" json:"data"`
|
||||
}
|
||||
type PetEX struct {
|
||||
Pet
|
||||
Data PetInfo `orm:"data" json:"data"`
|
||||
}
|
||||
|
||||
type Attr uint32
|
||||
|
||||
func (r Attr) sub() uint32 {
|
||||
if r > 0 {
|
||||
return uint32(r) - 1
|
||||
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// PetInfo 精灵信息结构(合并后的优化版本)
|
||||
type PetInfo struct {
|
||||
|
||||
// 精灵编号(@UInt long → uint32)
|
||||
ID uint32 `fieldDesc:"精灵编号" `
|
||||
|
||||
// 名字:默认为全0,补齐到16字节(固定长度 → [16]byte)
|
||||
Name string `struc:"[16]byte" json:"Name,omitempty"`
|
||||
//generation
|
||||
Generation uint32 `fieldDesc:"世代" `
|
||||
|
||||
// 个体值(@UInt long → uint32)
|
||||
Dv uint32 `struc:"uint32" `
|
||||
|
||||
// 性格(@UInt long → uint32)
|
||||
Nature uint32 `fieldDesc:"性格" `
|
||||
|
||||
// 等级(@UInt long → uint32)
|
||||
Level uint32 `fieldDesc:"等级" `
|
||||
|
||||
// 当前等级已获得经验(@UInt long → uint32)
|
||||
Exp uint32
|
||||
|
||||
// 当前等级所需经验(@UInt long → uint32)
|
||||
LvExp uint32
|
||||
|
||||
// 升到下一级的经验(@UInt long → uint32)
|
||||
NextLvExp uint32
|
||||
|
||||
// 当前生命(@UInt long → uint32)
|
||||
Hp uint32
|
||||
|
||||
// 最大生命(@UInt long → uint32)
|
||||
MaxHp uint32 `fieldDesc:"最大生命" `
|
||||
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
|
||||
Prop [5]uint32 `fieldDesc:"属性" `
|
||||
|
||||
// * ev:生命学习力,攻击学习力,防御学习力,特攻学习力,特防学习力,速度学习力
|
||||
Ev [6]uint32 `fieldDesc:"属性" `
|
||||
|
||||
SkillListLen uint32 `struc:"sizeof=SkillList" json:"-"`
|
||||
|
||||
// 技能信息:固定4条,空则赋值0(固定长度List → [4]SkillInfo,零值即符合“赋值0”)
|
||||
SkillList []SkillInfo
|
||||
|
||||
// 捕捉时间(@UInt long → 若为时间戳用uint32;若需时间类型可改为time.Time,需配合序列化处理)
|
||||
CatchTime uint32 //显式忽略,不参与序列化
|
||||
OldCatchTime uint32 `struc:"skip" fieldDesc:"旧捕捉时间" `
|
||||
|
||||
// 捕捉地图(@UInt long → uint32)
|
||||
CatchMap uint32 `json:"CatchMap,omitempty"`
|
||||
|
||||
// 未知默认0(@UInt long → uint32)
|
||||
CatchRect uint32 `json:"CatchRect,omitempty"`
|
||||
|
||||
// 捕获等级默认0(@UInt long → uint32)
|
||||
CatchLevel uint32 `fieldDesc:"捕获等级 默认为0" `
|
||||
EffectInfoLen uint16 `struc:"sizeof=EffectInfo" json:"-"`
|
||||
// 特性列表:长度用UShort存储(变长List → []PetEffectInfo + 长度前缀规则) 第一个一定是特性
|
||||
EffectInfo []PetEffectInfo `fieldDesc:"特性列表, 长度在头部以UShort存储" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
|
||||
|
||||
// 皮肤ID默认0(@UInt long → uint32)
|
||||
SkinID uint32 `fieldDesc:"皮肤id默认为0" `
|
||||
|
||||
// 是否闪光(@UInt long → uint32,0=否,1=是)
|
||||
ShinyLen uint32 `json:"-" struc:"sizeof=ShinyInfo"`
|
||||
ShinyInfo []data.GlowFilter `json:"ShinyInfo,omitempty"`
|
||||
|
||||
//时间轮转,然后effect根据type同时只共存一个,特性是1 特质是1,柱子是两种,魂印是一个,然后异色字段,然后特训技能字段
|
||||
ExtSKill []uint32 `struc:"skip"` //特训技能
|
||||
ExtSkin []uint32 `struc:"skip"` //可用皮肤
|
||||
}
|
||||
|
||||
// 定义常量,提升可维护性(避免魔法数字)
|
||||
const (
|
||||
maxSingleEV uint32 = 255 // 单个EV最大值
|
||||
maxTotalEV uint32 = 510 // 6个EV总和最大值
|
||||
evFieldCount = 6 // EV字段数量(固定6个)
|
||||
)
|
||||
|
||||
// AddEV 优化后的EV值增加方法(符合Go命名规范:大写导出,动词开头)
|
||||
// 功能:为宠物6个EV值增加增量,保证单个≤255、总和≤510
|
||||
// 参数:evadd - 6个EV字段的增量数组(长度必须为6)
|
||||
// 返回:error - 参数非法/逻辑异常时返回错误;bool - 是否触发了超额削减(方便业务监控)
|
||||
func (pet *PetInfo) AddEV(ev_add []uint32) (bool, error) {
|
||||
// 1. 参数安全校验:避免数组越界panic
|
||||
if len(ev_add) != evFieldCount {
|
||||
return false, fmt.Errorf("evadd长度必须为%d,当前为%d", evFieldCount, len(ev_add))
|
||||
}
|
||||
if len(pet.Ev) != evFieldCount {
|
||||
return false, errors.New("pet.Ev未初始化或长度不为6")
|
||||
}
|
||||
|
||||
// 2. 第一步:直接添加并限制单项最大值(按索引顺序处理)
|
||||
var tempEV [evFieldCount]uint32
|
||||
for i := 0; i < evFieldCount; i++ {
|
||||
// 直接累加增量
|
||||
tempEV[i] = pet.Ev[i] + ev_add[i]
|
||||
// 单项不超过255
|
||||
if tempEV[i] > maxSingleEV {
|
||||
tempEV[i] = maxSingleEV
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 计算增量后的总和,检查是否超过510
|
||||
totalTemp := lo.Sum(tempEV[:])
|
||||
|
||||
// 4. 若总和超额,按索引顺序(0→5)削减(优先削减前面的字段)
|
||||
hasCut := false
|
||||
if totalTemp > maxTotalEV {
|
||||
overTotal := totalTemp - maxTotalEV // 需要削减的总量
|
||||
hasCut = true
|
||||
|
||||
// 按索引顺序遍历削减(从第0个字段开始,依次处理)
|
||||
for i := 0; i < evFieldCount && overTotal > 0; i++ {
|
||||
// 可削减的最大值:最多削减到原始值(不触碰添加前的基础EV)
|
||||
cutAble := tempEV[i] - pet.Ev[i]
|
||||
if cutAble <= 0 {
|
||||
continue // 该字段无增量可削减,跳过
|
||||
}
|
||||
|
||||
// 实际削减量:取"可削减量"和"剩余需削减量"的较小值
|
||||
cut := cutAble
|
||||
if cut > overTotal {
|
||||
cut = overTotal
|
||||
}
|
||||
|
||||
// 执行削减
|
||||
tempEV[i] -= cut
|
||||
overTotal -= cut
|
||||
}
|
||||
|
||||
// 极端情况:即使削减所有增量后仍超额(如原始EV总和已超510),继续按顺序削减原始值
|
||||
if overTotal > 0 {
|
||||
for i := 0; i < evFieldCount && overTotal > 0; i++ {
|
||||
// 此时可削减到0(根据业务需求调整,也可返回错误)
|
||||
cutAble := tempEV[i]
|
||||
if cutAble <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cut := cutAble
|
||||
if cut > overTotal {
|
||||
cut = overTotal
|
||||
}
|
||||
|
||||
tempEV[i] -= cut
|
||||
overTotal -= cut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 将处理后的结果赋值给原EV数组
|
||||
copy(pet.Ev[:], tempEV[:])
|
||||
|
||||
return hasCut, nil
|
||||
}
|
||||
func (pet *PetInfo) Cure() {
|
||||
pet.Hp = pet.MaxHp
|
||||
for i := 0; i < len(pet.SkillList); i++ {
|
||||
maxPP, ok := xmlres.SkillMap[int(pet.SkillList[i].ID)]
|
||||
// 恢复至最大PP值(从配置表获取)
|
||||
if pet.SkillList[i].ID != 0 && ok {
|
||||
pet.SkillList[i].PP = uint32(maxPP.MaxPP)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
func (pet *PetInfo) RandShiny() {
|
||||
|
||||
co := service.NewShinyService().RandShiny(pet.ID)
|
||||
if co != nil {
|
||||
pet.ShinyInfo = append(pet.ShinyInfo, *co)
|
||||
}
|
||||
//o.ShinyInfo[0].ColorMatrixFilter = GenerateRandomOffspringMatrix().Get()
|
||||
//g.Dump(ttt.ShinyInfo)
|
||||
// ttt.Shiny = 0 //待确认是否刷新异色
|
||||
}
|
||||
func (pet *PetInfo) FixShiny() {
|
||||
|
||||
co := service.NewShinyService().FixShiny(pet.ID)
|
||||
if co != nil {
|
||||
pet.ShinyInfo = append(pet.ShinyInfo, *co)
|
||||
}
|
||||
//o.ShinyInfo[0].ColorMatrixFilter = GenerateRandomOffspringMatrix().Get()
|
||||
//g.Dump(ttt.ShinyInfo)
|
||||
// ttt.Shiny = 0 //待确认是否刷新异色
|
||||
}
|
||||
func (pet *PetInfo) IsShiny() bool {
|
||||
|
||||
return len(pet.ShinyInfo) > 0
|
||||
|
||||
}
|
||||
|
||||
// 随机特性
|
||||
func (pet *PetInfo) RnadEffect() {
|
||||
|
||||
for _, v := range xmlres.PlayerEffectMAP {
|
||||
if gconv.Int(v.StarLevel) == 0 {
|
||||
ret := &PetEffectInfo{
|
||||
Idx: uint16(gconv.Int16(v.Idx)),
|
||||
Status: 1,
|
||||
EID: uint16(gconv.Int16(v.Eid)),
|
||||
Args: v.ArgsS,
|
||||
}
|
||||
_, eff, ok := pet.GetEffect(1)
|
||||
if ok {
|
||||
|
||||
copier.Copy(eff, ret)
|
||||
} else {
|
||||
pet.EffectInfo = append(pet.EffectInfo, *ret)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 1是特性特质,<!-- Stat: 精灵特效Stat: 0: 无效(默认值), 1: 永久, 2: 有`有效次数'的特效 3: 爆发特效 4: 异能精灵特质,5特训,6魂印-->
|
||||
func (pet *PetInfo) GetEffect(ptype int) (int, *PetEffectInfo, bool) {
|
||||
|
||||
return utils.FindWithIndex(pet.EffectInfo, func(item PetEffectInfo) bool {
|
||||
return gconv.Int(xmlres.EffectMAP[int(item.Idx)].Stat) == 1
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (pet *PetInfo) Downgrade(level uint32) {
|
||||
|
||||
for pet.Level > uint32(level) {
|
||||
basic, ok := xmlres.PetMAP[int(pet.ID)]
|
||||
|
||||
if ok {
|
||||
|
||||
if basic.EvolvesFrom != 0 {
|
||||
pet.ID = uint32(basic.EvolvesFrom)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
pet.Level--
|
||||
//进行降级操作
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 执行进化逻辑 ,是否进化
|
||||
func (petinfo *PetInfo) Update(isup bool) {
|
||||
|
||||
// 最大进化次数限制(防止配置表闭环导致死循环)
|
||||
maxEvolveTimes := 10
|
||||
evolveCount := 0
|
||||
|
||||
// 循环进化:直到不满足进化条件 或 达到最大进化次数
|
||||
for {
|
||||
// 防止死循环,超出次数直接退出
|
||||
if evolveCount >= maxEvolveTimes {
|
||||
break
|
||||
}
|
||||
// 进化完成后,统一更新经验(原逻辑保留)
|
||||
petinfo.LvExp = petinfo.NextLvExp
|
||||
// 获取当前宠物形态的配置
|
||||
basic, ok := xmlres.PetMAP[int(petinfo.ID)]
|
||||
// 配置不存在,直接退出循环
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic())
|
||||
if !isup {
|
||||
return
|
||||
}
|
||||
// 检查是否满足进化条件
|
||||
canEvolve := basic.EvolvesTo != 0 && // 有明确的进化目标
|
||||
int(petinfo.Level) >= basic.EvolvingLv && // 等级达到进化要求
|
||||
basic.IsLarge == 0 // 非最终形态
|
||||
|
||||
// 不满足进化条件,退出循环
|
||||
if !canEvolve {
|
||||
break
|
||||
}
|
||||
|
||||
// 执行进化:更新宠物ID为进化后形态
|
||||
petinfo.ID = uint32(basic.EvolvesTo)
|
||||
evolveCount++ // 进化次数+1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// calculateExperience 计算指定等级和种族值所需的经验值
|
||||
// level: 当前等级
|
||||
// baseValue: 种族值
|
||||
func calculateExperience(level uint32, baseValue uint32) uint32 {
|
||||
// 计算 A 部分:向上取整(3.75 * a * (a + 1))
|
||||
partA := math.Ceil(3.75 * float64(level) * float64(level+1))
|
||||
|
||||
// 计算 B 部分:向上取整(b * log(1 + a / 100))
|
||||
// 这里使用自然对数 math.Log,如果想换底数可以用换底公式
|
||||
partB := math.Log(1.0 + float64(level)/100.0)
|
||||
partB = float64(baseValue) * partB
|
||||
partB = math.Ceil(partB)
|
||||
|
||||
// 总经验是两部分之和,并向上取整
|
||||
totalExp := math.Ceil(partA + partB)
|
||||
return uint32(totalExp)
|
||||
}
|
||||
|
||||
// PetEffectInfo 精灵特性信息结构
|
||||
// <!-- NewSeIdx: 精灵特效索引 (默认0: 无效) -->
|
||||
// <!-- Type: 0 - 仅单人战斗; 1 - 仅组队战斗; 2 - both; (默认0: 仅单人) -->
|
||||
// <!-- Eid: 精灵特效eid (默认0: 无效) -->
|
||||
// <!-- Stat: 精灵特效Stat: 0: 无效(默认值), 1: 永久, 2: 有`有效次数'的特效 3: 爆发特效 4: 异能精灵特质-->
|
||||
// <!-- Times: 精灵特效可使用次数: 当type==2时有效 (默认值:0) -->
|
||||
// <!-- Args: 特效参数, 不超过8个 (注意: 每个参数不能超过 65535) -->
|
||||
// <!-- AdditionType:特效加成类型 1 种族值加成 2 技能威力加成 -->
|
||||
type PetEffectInfo struct {
|
||||
ItemID uint32 `struc:"uint32" json:"item_id"` //如果是能量珠,就显示
|
||||
Idx uint16 `struc:"skip" json:"new_se_idx"`
|
||||
Type byte `struc:"skip" json:"type"` //pvp pve特性区分,待前端修改实现
|
||||
Status byte `struc:"byte" json:"status"` //特性为1,能量珠为2
|
||||
LeftCount byte `struc:"byte" json:"left_count"` //剩余次数
|
||||
EID uint16 `struc:"uint16" json:"effect_id"` //特效ID
|
||||
ArgsLen uint32 `struc:"sizeof=Args" json:"-"`
|
||||
|
||||
Args []int ` json:"Args"` //自定义参数装载
|
||||
|
||||
}
|
||||
|
||||
// SkillInfo 精灵技能信息结构(SkillInfo)
|
||||
type SkillInfo struct {
|
||||
ID uint32
|
||||
PP uint32
|
||||
}
|
||||
|
||||
// TableName Pet's table name
|
||||
func (*Pet) TableName() string {
|
||||
return TableNamePet
|
||||
}
|
||||
|
||||
// GroupName Pet's table group
|
||||
func (*Pet) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewPet create a new Pet
|
||||
func NewPet() *Pet {
|
||||
return &Pet{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 创建表
|
||||
func init() {
|
||||
_ = cool.CreateTable(&Pet{})
|
||||
|
||||
// fmt.Println(err)
|
||||
}
|
||||
|
||||
// GenPetInfo 生成一个新的精灵实例
|
||||
// - 参数为 -1 时表示随机生成对应属性
|
||||
// * @param petTypeId 精灵类型ID
|
||||
// * @param individualValue 个体值(0-31)
|
||||
// * @param natureId 性格ID(0-24)
|
||||
// * @param abilityTypeEnum 特性类型ID(0=无, >0=指定, -1=随机)
|
||||
// * @param shinyid 闪光ID(-1=随机)
|
||||
// * @param level 等级(1-100)
|
||||
// * @return 生成的精灵实体
|
||||
func GenPetInfo(
|
||||
id int,
|
||||
dv, natureId, abilityTypeEnum, level int, shinyid []data.GlowFilter,
|
||||
) *PetInfo {
|
||||
// 创建随机源
|
||||
//rng := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// 初始化精灵
|
||||
p := &PetInfo{
|
||||
ID: uint32(id),
|
||||
CatchTime: uint32(time.Now().Unix()),
|
||||
Level: uint32(level),
|
||||
EffectInfo: make([]PetEffectInfo, 0),
|
||||
}
|
||||
|
||||
// ---- 处理闪光 ----
|
||||
if shinyid != nil {
|
||||
//todo 待实现异色字段
|
||||
p.ShinyInfo = shinyid
|
||||
// p.Shiny = uint32(shinyid)
|
||||
}
|
||||
|
||||
// ---- 性格 ----
|
||||
if natureId == -1 {
|
||||
p.Nature = uint32(grand.Intn(25))
|
||||
} else {
|
||||
p.Nature = uint32(natureId)
|
||||
}
|
||||
|
||||
// ---- 个体值(DV)----
|
||||
if dv == -1 {
|
||||
p.Dv = uint32(CalculateIndividualValue())
|
||||
} else {
|
||||
if dv < 0 {
|
||||
dv = 0
|
||||
} else if dv > 31 {
|
||||
dv = 31
|
||||
}
|
||||
p.Dv = uint32(dv)
|
||||
}
|
||||
|
||||
// ---- 特性 ----
|
||||
switch {
|
||||
case abilityTypeEnum == 0:
|
||||
// 无特性
|
||||
case abilityTypeEnum > 0:
|
||||
// 指定特性
|
||||
if v, ok := xmlres.PlayerEffectMAP[int(abilityTypeEnum)]; ok {
|
||||
p.EffectInfo = append(p.EffectInfo, PetEffectInfo{
|
||||
Idx: uint16(gconv.Int16(v.Idx)),
|
||||
Status: 1,
|
||||
EID: uint16(gconv.Int16(v.Eid)),
|
||||
Args: v.ArgsS,
|
||||
})
|
||||
}
|
||||
case abilityTypeEnum == -1:
|
||||
|
||||
for _, v := range xmlres.PlayerEffectMAP {
|
||||
if gconv.Int(v.StarLevel) == 0 {
|
||||
p.EffectInfo = append(p.EffectInfo, PetEffectInfo{
|
||||
Idx: uint16(gconv.Int16(v.Idx)),
|
||||
Status: 1,
|
||||
EID: uint16(gconv.Int16(v.Eid)),
|
||||
Args: v.ArgsS,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 技能学习 ----
|
||||
skills := utils.LastFourElements(p.GetLevelRangeCanLearningSkills(0, p.Level), 4) // 最后四个技能
|
||||
|
||||
for i := 0; i < len(skills) && i < 4; i++ {
|
||||
skillID := skills[i]
|
||||
if info, ok := xmlres.SkillMap[int(skillID)]; ok {
|
||||
p.SkillList = append(p.SkillList, SkillInfo{ID: skillID, PP: uint32(info.MaxPP)})
|
||||
|
||||
}
|
||||
}
|
||||
if len(p.SkillList) > 4 {
|
||||
p.SkillList = p.SkillList[:4]
|
||||
}
|
||||
|
||||
// ---- 属性计算 ----
|
||||
p.CalculatePetPane(true)
|
||||
|
||||
p.Update(false)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// 除数数组(放大100倍)
|
||||
// 数组按递增顺序排列,用于判断个体值等级
|
||||
var divisors = []int{
|
||||
600, 1200, 1900, 2700, 3600, 4600, 5700, 6900, 8200, 9600,
|
||||
11100, 12700, 14400, 16200, 18100, 20100, 22100, 24000,
|
||||
25800, 27500, 29100, 30600, 32000, 33300, 34500, 35600,
|
||||
36600, 37500, 38300, 39000, 39600,
|
||||
}
|
||||
|
||||
// CalculateIndividual 根据给定的a值计算个体值
|
||||
// 返回值表示a大于等于多少个除数(范围:0-31)
|
||||
func CalculateIndividual(a int) int {
|
||||
individual := 0
|
||||
for _, divisor := range divisors {
|
||||
if a >= divisor {
|
||||
individual++
|
||||
} else {
|
||||
break // 数组是递增的,可提前跳出循环
|
||||
}
|
||||
}
|
||||
return individual
|
||||
}
|
||||
|
||||
// CalculateIndividualValue 计算个体值(0-31)
|
||||
// 接收外部随机数生成器,便于控制随机性和复用
|
||||
func CalculateIndividualValue() int {
|
||||
// 生成0-40000的随机数,作为个体值计算的输入
|
||||
a := grand.Intn(40001)
|
||||
return CalculateIndividual(a)
|
||||
}
|
||||
94
modules/player/model/petinfo.go
Normal file
94
modules/player/model/petinfo.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
)
|
||||
|
||||
// 实现获取等级范围内可学习的技能
|
||||
func (p *PetInfo) GetLevelRangeCanLearningSkills(from, to uint32) []uint32 {
|
||||
|
||||
var skills []uint32
|
||||
|
||||
for _, skillIDs := range xmlres.PetMAP[int(p.ID)].LearnableMoves.Moves {
|
||||
|
||||
if skillIDs.LearningLv >= from && skillIDs.LearningLv <= to {
|
||||
skills = append(skills, skillIDs.ID)
|
||||
}
|
||||
}
|
||||
return skills
|
||||
}
|
||||
|
||||
// 计算HP面板值(无性格修正)
|
||||
func (c *PetInfo) calculatePetHPPanelSize(base, dv, level, ev uint32) uint32 {
|
||||
|
||||
return uint32((float64(base)*2+float64(ev)/4.0+float64(dv))*(float64(level)/100.0) + float64(level) + 10)
|
||||
}
|
||||
|
||||
// 计算其他属性面板值(带性格修正)
|
||||
func (c *PetInfo) calculatePetPanelSize(base, ev uint32, natureCorrect float64) uint32 {
|
||||
|
||||
base1 := float64((float64(base)*2+float64(ev)/4.0+float64(c.Dv))*(float64(c.Level)/100.0) + 5)
|
||||
return uint32(float64(base1) * natureCorrect)
|
||||
}
|
||||
|
||||
// 计算生成面板,只允许第一次生成超过100,比如boss,不允许额外超过
|
||||
func (p *PetInfo) CalculatePetPane(frist bool) {
|
||||
if !frist {
|
||||
|
||||
if p.Level > 100 {
|
||||
|
||||
oldlveel := p.Level
|
||||
p.Level = 100
|
||||
defer func() {
|
||||
p.Level = oldlveel
|
||||
}()
|
||||
}
|
||||
|
||||
}
|
||||
naxml := xmlres.NatureRootMap[int(p.Nature)]
|
||||
petxml := xmlres.PetMAP[int(p.ID)]
|
||||
// 计算各项属性
|
||||
p.MaxHp = p.calculatePetHPPanelSize(
|
||||
uint32(petxml.HP),
|
||||
p.Dv,
|
||||
p.Level,
|
||||
p.Ev[0],
|
||||
)
|
||||
p.Hp = p.MaxHp
|
||||
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
|
||||
p.Prop[0] = p.calculatePetPanelSize(
|
||||
uint32(petxml.Atk),
|
||||
|
||||
p.Ev[1],
|
||||
naxml.AttackCorrect,
|
||||
)
|
||||
|
||||
p.Prop[1] = p.calculatePetPanelSize(
|
||||
uint32(petxml.Def),
|
||||
|
||||
p.Ev[2],
|
||||
naxml.DefenseCorrect,
|
||||
)
|
||||
|
||||
p.Prop[2] = p.calculatePetPanelSize(
|
||||
uint32(petxml.SpAtk),
|
||||
|
||||
p.Ev[3],
|
||||
naxml.SaCorrect,
|
||||
)
|
||||
|
||||
p.Prop[3] = p.calculatePetPanelSize(
|
||||
uint32(petxml.SpDef),
|
||||
|
||||
p.Ev[4],
|
||||
naxml.SdCorrect,
|
||||
)
|
||||
|
||||
p.Prop[4] = p.calculatePetPanelSize(
|
||||
uint32(petxml.Spd),
|
||||
|
||||
p.Ev[5],
|
||||
naxml.SpeedCorrect,
|
||||
)
|
||||
|
||||
}
|
||||
230
modules/player/model/player.go
Normal file
230
modules/player/model/player.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/creasty/defaults"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
const TableNamePlayerInfo = "player_info"
|
||||
|
||||
type Player struct {
|
||||
*cool.Model
|
||||
PlayerID uint64 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
LastResetTime *gtime.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务
|
||||
Data string `gorm:"type:jsonb;not null;comment:'全部数据'" json:"data"`
|
||||
}
|
||||
type PlayerEX struct {
|
||||
Player
|
||||
Data PlayerInfo `orm:"data" json:"data"`
|
||||
}
|
||||
type Pos struct {
|
||||
X uint32 `struc:"uint32" default:"0"`
|
||||
Y uint32 `struc:"uint32" default:"0"`
|
||||
}
|
||||
|
||||
// 计算两个uint32的差值绝对值(转为int64避免溢出)
|
||||
func absDiff(a, b uint32) int64 {
|
||||
return int64(math.Abs(float64(int64(a) - int64(b))))
|
||||
}
|
||||
|
||||
// 判断两个Pos的X和Y差值的绝对值是否均小于50
|
||||
func bothDiffsLessThan50(pos1, pos2 Pos) bool {
|
||||
xDiff := absDiff(pos1.X, pos2.X)
|
||||
yDiff := absDiff(pos1.Y, pos2.Y)
|
||||
return xDiff < 50 && yDiff < 50
|
||||
}
|
||||
|
||||
func (p Pos) BothLessThan50(t Pos) bool {
|
||||
|
||||
return bothDiffsLessThan50(p, t)
|
||||
}
|
||||
|
||||
// PeopleItemInfo 穿戴装备信息结构(PeopleItemInfo)
|
||||
type PeopleItemInfo struct {
|
||||
ID uint32 `struc:"uint32"` // 装备id
|
||||
Level uint32 `struc:"uint32" default:"1"` // 未知字段(默认值1,@Builder.Default)
|
||||
}
|
||||
|
||||
// InitDefaults 初始化默认值(确保默认值正确赋值)
|
||||
func (p *PeopleItemInfo) InitDefaults() {
|
||||
p.Level = 1 // 未知字段默认值1,与Java的@Builder.Default保持一致
|
||||
}
|
||||
func NewPlayerInfo() PlayerInfo {
|
||||
l := PlayerInfo{
|
||||
|
||||
Clothes: make([]PeopleItemInfo, 0),
|
||||
PetList: make([]PetInfo, 0),
|
||||
}
|
||||
|
||||
// 自动填充 struct tag 里的 default 值
|
||||
if err := defaults.Set(&l); err != nil {
|
||||
panic(err) // 方便发现 default 设置错误
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// TaskStatus 任务状态(与 AS3 对应)
|
||||
type TaskStatus uint8
|
||||
|
||||
const (
|
||||
Unaccepted TaskStatus = 0 // 未接受(AS3 中 0 或 2 映射至此)
|
||||
Accepted TaskStatus = 1 // 已接受
|
||||
Completed TaskStatus = 3 // 已完成
|
||||
Reserved TaskStatus = 2 // 预留(AS3 中映射为未接受)
|
||||
)
|
||||
|
||||
// SetTask 设置第 i 个任务的状态(0 ≤ i < 2000)
|
||||
func (m *PlayerInfo) SetTask(i int, status TaskStatus) error {
|
||||
i-- //下标减1
|
||||
if i < 0 || i >= 4000 {
|
||||
return fmt.Errorf("index out of range: %d (must be 0-1999)", i)
|
||||
}
|
||||
|
||||
byteIdx := i / 4
|
||||
bitOffset := (i % 4) * 2
|
||||
|
||||
// 清除原有 2 位
|
||||
m.TaskList[byteIdx] &^= 0x3 << bitOffset
|
||||
|
||||
// 设置新状态(确保只取低 2 位)
|
||||
m.TaskList[byteIdx] |= byte(status&0x3) << bitOffset
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTask 获取第 i 个任务的状态
|
||||
func (m *PlayerInfo) GetTask(i int) TaskStatus {
|
||||
i-- //下标减1
|
||||
// if i < 0 || i >= 2000 {
|
||||
// return 0, fmt.Errorf("index out of range: %d", i)
|
||||
// }
|
||||
|
||||
byteIdx := i / 4
|
||||
bitOffset := (i % 4) * 2
|
||||
|
||||
return TaskStatus((m.TaskList[byteIdx] >> bitOffset) & 0x3)
|
||||
}
|
||||
|
||||
type PlayerInfo struct {
|
||||
ExpPool uint32 `struc:"skip" json:"exp_pool"` // 累计经验池
|
||||
|
||||
OnlineTime uint32 `struc:"skip" json:"online_time"` //在线分钟数
|
||||
// OutInfo 字段
|
||||
UserID uint32 `struc:"uint32" json:"user_id"` // 米米号 通过sid拿到
|
||||
RegisterTime uint32 `struc:"uint32" json:"register_time"` // 注册时间(秒时间戳)
|
||||
Nick string `struc:"[16]byte" default:"seer" json:"nick"` // 16字节昵称
|
||||
Title uint32 `struc:"uint32" json:"title"` // 称号
|
||||
Vip uint16 `struc:"uint16" json:"vip"` // 固定0
|
||||
Viped uint16 `struc:"uint16" default:"15" json:"viped"` // 固定15
|
||||
DSFlag uint32 `struc:"uint32" json:"ds_flag"` // 固定0
|
||||
Color uint32 `struc:"uint32" json:"color"` // 机器人颜色RGB颜色值(uint32,实际为3个uint8)
|
||||
Texture uint32 `struc:"uint32" json:"texture"` // 固定0
|
||||
Energy uint32 `struc:"uint32" default:"3000" json:"energy"` // 固定3000
|
||||
Coins uint32 `struc:"uint32" json:"coins"` // 赛尔豆
|
||||
EVPool uint32 `struc:"uint32" json:"ev_pool"` //累计学习力
|
||||
FightBadge uint32 `struc:"uint32" json:"fight_badge"` // 固定0
|
||||
MapID uint32 `struc:"uint32" default:"1" json:"map_id"` // 上线地图ID
|
||||
Pos Pos `json:"pos"` // 坐标
|
||||
TimeToday uint32 `struc:"uint32" default:"0" json:"time_today"` // 已消耗时间(秒)
|
||||
TimeLimit uint32 `struc:"uint32" default:"43200" json:"time_limit"` // 总电池限制(秒)
|
||||
IsClothHalfDay byte `struc:"byte" json:"is_cloth_half_day"` // 活动标志0/1
|
||||
IsRoomHalfDay byte `struc:"byte" json:"is_room_half_day"` // 活动标志0/1
|
||||
IFortressHalfDay byte `struc:"byte" json:"i_fortress_half_day"` // 活动标志0/1
|
||||
IsHQHalfDay byte `struc:"byte" json:"is_hq_half_day"` // 活动标志0/1
|
||||
LoginCount uint32 `struc:"uint32" json:"login_count"` // 固定0
|
||||
Inviter uint32 `struc:"uint32" json:"inviter"` // 固定0
|
||||
NewInviteeCount uint32 `struc:"uint32" json:"new_invitee_count"` // 固定0
|
||||
VipLevel uint32 `struc:"uint32" default:"8" json:"vip_level"` // 固定8
|
||||
VipValue uint32 `struc:"uint32" default:"80000" json:"vip_value"` // 固定80000
|
||||
VipStage uint32 `struc:"uint32" default:"1" json:"vip_stage"` // 超no的外形等级建议固定1
|
||||
AutoCharge uint32 `struc:"uint32" default:"1" json:"auto_charge"` // nono是否自动充电
|
||||
VipEndTime uint32 `struc:"uint32" default:"4294967295" json:"vip_end_time"` // 超no的结束时间建议尽可能大
|
||||
FreshManBonus uint32 `struc:"uint32" json:"fresh_man_bonus"` // 邀请活动建议先给固定值0
|
||||
//NonoChipList [80]byte `struc:"[80]byte" json:"-"` // 超no芯片列表
|
||||
DailyResArr [50]byte `struc:"[50]byte" default:"0" json:"daily_res_arr"` // 每日任务状态 40+是谱尼的
|
||||
Study struct {
|
||||
TeacherID uint32 `struc:"uint32" json:"teacher_id"` // 教官id
|
||||
|
||||
StudentID uint32 `struc:"uint32" json:"student_id"` // 学员id
|
||||
GraduationCount uint32 `struc:"uint32" default:"0" json:"graduation_count"` // 毕业人数
|
||||
}
|
||||
|
||||
MaxPuniLv uint32 `struc:"uint32" default:"0" json:"max_puni_lv"` // 默认0, 虚无 元素 能量 生命 轮回 永恒 圣洁 最高为8
|
||||
PetMaxLevel uint32 `struc:"uint32" json:"pet_max_level"` // 精灵最高等级
|
||||
AllPetNumber uint32 `struc:"uint32" json:"all_pet_number"` // 精灵数量
|
||||
MonKingWin uint32 `struc:"uint32" json:"mon_king_win"` // 精灵王胜场
|
||||
MessWin uint32 `struc:"skip" json:"mess_win"` // 大乱斗胜场
|
||||
CurrentStage uint32 `struc:"uint32" default:"1" json:"current_stage"` // 勇者之塔层数
|
||||
MaxStage uint32 `struc:"uint32" json:"max_stage"` // 试炼之塔最高层
|
||||
CurrentFreshStage uint32 `struc:"uint32" default:"1" json:"current_fresh_stage"` // 当前试炼层数
|
||||
MaxFreshStage uint32 `struc:"uint32" json:"max_fresh_stage"` // 最高试炼层
|
||||
MaxArenaWins uint32 `struc:"uint32" json:"max_arena_wins"` // 星际擂台连胜
|
||||
TwoTimes uint32 `struc:"uint32" default:"0" json:"two_times"` // 双倍经验加速器剩余使用次数
|
||||
ThreeTimes uint32 `struc:"uint32" default:"0" json:"three_times"` // 三倍经验加速器剩余使用次数
|
||||
AutoFight uint32 `struc:"uint32" default:"0" json:"auto_fight"` // 是否自动战斗
|
||||
AutoFightTime uint32 `struc:"uint32" default:"0" json:"auto_fight_time"` // 自动战斗剩余的场次
|
||||
EnergyTime uint32 `struc:"uint32" default:"0" json:"energy_time"` // 能量吸收仪剩余次数
|
||||
LearnTimes uint32 `struc:"uint32" default:"0" json:"learn_times"` // 学习力吸收仪剩余次数
|
||||
MonBattleMedal uint32 `struc:"uint32" default:"0" json:"mon_battle_medal"` // 默认0
|
||||
RecordCount uint32 `struc:"uint32" default:"0" json:"record_count"` // 默认0
|
||||
ObtainTm uint32 `struc:"uint32" default:"0" json:"obtain_tm"` // 默认0
|
||||
SoulBeadItemID uint32 `struc:"uint32" json:"soul_bead_item_id"` // 当前元神珠id
|
||||
ExpireTm uint32 `struc:"uint32" default:"0" json:"expire_tm"` // 默认0
|
||||
FuseTimes uint32 `struc:"uint32" default:"0" json:"fuse_times"` // 默认0
|
||||
|
||||
NONO struct {
|
||||
//fieldDescription:"1为跟随 0为收回 且如果为收回 那么后续结构不需要发送, 不序列化"
|
||||
Flag uint32 `struc:"skip" json:"nono_flag"`
|
||||
|
||||
HasNono uint32 `struc:"uint32" default:"1" json:"has_nono"` // 玩家是否有nono
|
||||
SuperNono uint32 `struc:"uint32" default:"1" json:"super_nono"` // 玩家是否有超能nono
|
||||
NonoState uint32 `struc:"uint32" default:"4294967295" json:"nono_state"` // 默认-1
|
||||
NonoColor uint32 `struc:"uint32" json:"nono_color"` // nono颜色
|
||||
Nick string `struc:"[16]byte" default:"nono" json:"nono_nick"` // nono名字(16字节)
|
||||
}
|
||||
|
||||
TeamInfo TeamInfo `struc:"struct" json:"team_info"` // 战队信息24字节
|
||||
TeamPkInfo TeamPKInfo `struc:"struct" json:"team_pk_info"` // 8字节
|
||||
Reserved byte `struc:"byte" json:"reserved"` // 1字节无内容
|
||||
Badge uint32 `struc:"uint32" default:"0" json:"badge"` // 默认0
|
||||
Reserved1 [27]byte `struc:"[27]byte" default:"3" json:"reserved1"` // 27字节默认3
|
||||
TaskList [1000]byte `struc:"[1000]byte" default:"0" json:"task_list"` // 任务状态数组500字节,默认3
|
||||
PetListCount uint32 `struc:"sizeof=PetList" json:"pet_list_count"` // 精灵列表长度
|
||||
PetList []PetInfo ` json:"pet_list"` // 精灵背包内信息
|
||||
ClothesCount uint32 `struc:"sizeof=Clothes" json:"clothes_count"` // 穿戴装备数量
|
||||
Clothes []PeopleItemInfo ` json:"clothes"` // 穿戴装备
|
||||
}
|
||||
|
||||
// trace("个人装扮是否半价:",MainManager.isClothHalfDay);
|
||||
// trace("小屋装扮是否半价:",MainManager.isRoomHalfDay);
|
||||
// trace("要塞装扮是否半价:",MainManager.iFortressHalfDay);
|
||||
// trace("总部装扮是否半价:",MainManager.isHQHalfDay);
|
||||
//
|
||||
// TableName PlayerInfo's table name
|
||||
func (*Player) TableName() string {
|
||||
return TableNamePlayerInfo
|
||||
}
|
||||
|
||||
// GroupName PlayerInfo's table group
|
||||
func (*Player) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewPlayerInfo create a new PlayerInfo
|
||||
func NewPlayer() *PlayerEX {
|
||||
return &PlayerEX{
|
||||
Player: Player{
|
||||
Model: cool.NewModel(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// init 创建表
|
||||
func init() {
|
||||
cool.CreateTable(&Player{})
|
||||
}
|
||||
69
modules/player/model/room.go
Normal file
69
modules/player/model/room.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
// 基地房型表名
|
||||
const TableNameBaseHouse = "player_room_house"
|
||||
|
||||
// NewBaseHouse 构造函数:创建基地房型实例
|
||||
func NewBaseHouse() *BaseHouse {
|
||||
return &BaseHouse{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// BaseHouse 基地房型核心结构体
|
||||
// 包含:基地展示精灵、基地拥有物品、基地摆放物品三大核心字段
|
||||
type BaseHouse struct {
|
||||
Base
|
||||
|
||||
// 基础关联字段
|
||||
PlayerID uint64 `gorm:"not null;index:idx_player_house;comment:'所属玩家ID'" json:"player_id"`
|
||||
|
||||
// 核心业务字段
|
||||
// ShowPokemon 基地展示精灵ID列表(支持展示多个精灵)
|
||||
ShowPokemon []uint32 `gorm:"type:jsonb;default:'[]';comment:'基地展示精灵ID列表'" json:"show_pokemon"`
|
||||
|
||||
UsedItems string `gorm:"type:jsonb;default:'{}';comment:'用户物品列表(物品ID:数量)'" json:"used_items"`
|
||||
|
||||
// PlacedItems 基地摆放物品(包含物品ID、摆放坐标、朝向等信息)
|
||||
PlacedItems string `gorm:"type:jsonb;default:'[]';comment:'基地摆放物品列表(含位置/朝向)'" json:"placed_items"`
|
||||
}
|
||||
|
||||
func (*BaseHouse) TableName() string {
|
||||
return TableNameBaseHouse
|
||||
}
|
||||
|
||||
type BaseHouseEx struct {
|
||||
BaseHouse
|
||||
PlacedItems []FitmentShowInfo `json:"placed_items"`
|
||||
//OwnedItems map[uint32]uint32 `json:"owned_items"`
|
||||
UsedItems map[uint32]uint32 `json:"used_items"`
|
||||
}
|
||||
|
||||
// FitmentShowInfo 表示家具展示信息
|
||||
type FitmentShowInfo struct {
|
||||
// 家具id 或 默认房型id: 500001
|
||||
Id uint32 `json:"id"`
|
||||
// x坐标
|
||||
X uint32 `json:"x"`
|
||||
// y坐标
|
||||
Y uint32 `json:"y"`
|
||||
// 默认0
|
||||
Dir uint32 `json:"dir"`
|
||||
// 默认0
|
||||
Status uint32 `json:"status"`
|
||||
}
|
||||
|
||||
// UpdateShowPokemon 更新基地展示精灵列表
|
||||
func (bh *BaseHouse) UpdateShowPokemon(pokemonIDs []uint32) {
|
||||
bh.ShowPokemon = pokemonIDs
|
||||
}
|
||||
|
||||
// --------------- 初始化创建表 ---------------
|
||||
func init() {
|
||||
// 初始化时创建基地房型表(与现有Talk表初始化逻辑一致)
|
||||
cool.CreateTable(&BaseHouse{})
|
||||
}
|
||||
44
modules/player/model/sign.go
Normal file
44
modules/player/model/sign.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
// 表名常量(遵循小写+下划线的命名规范)
|
||||
const TableNameSignInRecord = "player_sign_in_log"
|
||||
|
||||
// SignInRecord 玩家签到明细记录表
|
||||
// 记录玩家每一次的签到行为,关联签到活动表
|
||||
type SignInRecord struct {
|
||||
Base
|
||||
|
||||
// 核心关联字段
|
||||
PlayerID uint32 `gorm:"not null;index:idx_player_id;comment:'玩家ID'" json:"player_id"`
|
||||
SignInID uint32 `gorm:"not null;index:idx_sign_in_id;comment:'关联的签到活动ID(对应player_sign_in表的SignInID)'" json:"sign_in_id"`
|
||||
|
||||
IsCompleted bool `gorm:"not null;default:false;comment:'签到是否完成(0-未完成 1-已完成)'" json:"is_completed"`
|
||||
//通过bitset来实现签到的进度记录
|
||||
SignInProgress []uint32 `gorm:"type:json;not null;comment:'签到进度(状压实现,存储每日签到状态)'" json:"sign_in_progress"`
|
||||
}
|
||||
|
||||
// TableName 指定表名(遵循现有规范)
|
||||
func (*SignInRecord) TableName() string {
|
||||
return TableNameSignInRecord
|
||||
}
|
||||
|
||||
// GroupName 指定表分组(与现有表保持一致的default分组)
|
||||
func (*SignInRecord) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewSignInRecord 创建签到明细记录实例(初始化基础Model)
|
||||
func NewSignInRecord() *SignInRecord {
|
||||
return &SignInRecord{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 程序启动时自动创建表(与现有SignIn表初始化逻辑一致)
|
||||
func init() {
|
||||
cool.CreateTable(&SignInRecord{})
|
||||
}
|
||||
76
modules/player/model/talk.go
Normal file
76
modules/player/model/talk.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
// 资源采集计数表名
|
||||
const TableNameResourceCollection = "player_talk"
|
||||
|
||||
func NewTalk() *Talk {
|
||||
return &Talk{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// ResourceCollection 记录玩家每种资源的采集计数
|
||||
type Talk struct {
|
||||
Base
|
||||
PlayerID uint64 `gorm:"not null;index:idx_player_resource;comment:'所属玩家ID'" json:"player_id"`
|
||||
TalkID uint32 `gorm:"not null;comment:'资源ID'" json:"talk_id"`
|
||||
Count uint32 `gorm:"not null;comment:'采集计数'" json:"count"`
|
||||
}
|
||||
|
||||
// TableName 资源采集表名
|
||||
func (*Talk) TableName() string {
|
||||
return TableNameResourceCollection
|
||||
}
|
||||
|
||||
// GroupName 资源采集表分组
|
||||
func (*Talk) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// // 检查是否可以采集(未超过每日上限)
|
||||
// func (rc *ResourceCollection) CanCollect(maxDaily uint32) bool {
|
||||
// // 先检查是否需要重置计数
|
||||
// rc.checkAndReset()
|
||||
// return rc.CollectCnt < maxDaily
|
||||
// }
|
||||
|
||||
// // 增加采集计数,返回是否成功
|
||||
// func (rc *ResourceCollection) AddCollectCount(maxDaily uint32) bool {
|
||||
// if !rc.CanCollect(maxDaily) {
|
||||
// return false
|
||||
// }
|
||||
// rc.CollectCnt++
|
||||
// return true
|
||||
// }
|
||||
|
||||
// // 检查并重置每日计数
|
||||
// func (rc *ResourceCollection) checkAndReset() {
|
||||
// now := time.Now()
|
||||
// if now.After(rc.DailyReset) {
|
||||
// rc.CollectCnt = 0
|
||||
// // 重置为明天24点
|
||||
// rc.DailyReset = now.Truncate(24 * time.Hour).Add(24 * time.Hour)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // ResourceConfig 资源配置信息(对应XML中的配置)
|
||||
// type ResourceConfig struct {
|
||||
// Type uint32 `json:"type"` // 资源类型ID
|
||||
// MapID uint32 `json:"map_id"` // 所在地图ID
|
||||
// Name string `json:"name"` // 资源名称
|
||||
// CollectType string `json:"collect_type"` // 采集类型
|
||||
// MaxDailyCnt uint32 `json:"max_daily_cnt"` // 每日最大采集次数
|
||||
// Unit string `json:"unit"` // 单位
|
||||
// Dir uint32 `json:"dir"` // 方向
|
||||
// }
|
||||
|
||||
// 初始化创建表
|
||||
func init() {
|
||||
cool.CreateTable(&Talk{})
|
||||
// 可以在这里加载资源配置
|
||||
// LoadResourceConfigsFromXML()
|
||||
}
|
||||
55
modules/player/model/task.go
Normal file
55
modules/player/model/task.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
// todo 还需要做一个记录任务奖励的表
|
||||
const TableNameTask = "player_task"
|
||||
|
||||
// Task mapped from table <task>
|
||||
type Task struct {
|
||||
Base
|
||||
PlayerID uint64 `gorm:"not null;index:idx_task_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
TaskID uint32 `gorm:"not null;comment:'任务ID'" json:"task_id"`
|
||||
Data string `gorm:"type:jsonb;not null;comment:'全部数据'" json:"data"`
|
||||
}
|
||||
|
||||
// TaskEX 单个任务的详细信息,包含任务步骤状态和整体状态
|
||||
type TaskEX struct {
|
||||
Task
|
||||
Data []uint32 `struc:"[20]byte" orm:"data" json:"data"`
|
||||
//LastResetTime time.Time `gorm:"not null;comment:'上次重置时间(UTC)'" json:"last_reset_time"` //这里是每天重置
|
||||
// Status 任务整体状态:0-未接受,1-已接受,2-已完成未领取,3-已完成已领取
|
||||
// json标签指定JSON字段名,与业务状态说明保持一致
|
||||
//Status byte `json:"status"`
|
||||
}
|
||||
|
||||
// TableName PlayerInfo's table name
|
||||
func (*Task) TableName() string {
|
||||
return TableNameTask
|
||||
}
|
||||
|
||||
// GroupName PlayerInfo's table group
|
||||
func (*Task) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func (t *Task) GetData() string {
|
||||
return t.Data
|
||||
}
|
||||
func (t *Task) SetData(t1 string) {
|
||||
t.Data = t1
|
||||
}
|
||||
|
||||
// NewPlayerInfo create a new PlayerInfo
|
||||
func NewTask() *Task {
|
||||
return &Task{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 创建表
|
||||
func init() {
|
||||
cool.CreateTable(&Task{})
|
||||
}
|
||||
39
modules/player/model/title.go
Normal file
39
modules/player/model/title.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
)
|
||||
|
||||
// 表名常量
|
||||
const TableNamePlayerTitle = "player_title"
|
||||
|
||||
// PlayerTitle 对应数据库表 <player_title>
|
||||
type Title struct {
|
||||
Base
|
||||
PlayerID uint64 `gorm:"not null;index:idx_player_title_by_player_id;comment:'所属玩家ID'" json:"player_id"`
|
||||
//TitleID uint32 `gorm:"not null;comment:'称号ID'" json:"title_id"`
|
||||
//可用称号
|
||||
AvailableTitle []uint32 `gorm:"type:json; comment:'可用称号'" json:"available_title"`
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (*Title) TableName() string {
|
||||
return TableNamePlayerTitle
|
||||
}
|
||||
|
||||
// GroupName 返回表组名
|
||||
func (*Title) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
// NewPlayerTitle 创建一个新的称号记录
|
||||
func NewPlayerTitle() *Title {
|
||||
return &Title{
|
||||
Base: *NewBase(),
|
||||
}
|
||||
}
|
||||
|
||||
// init 程序启动时自动创建表
|
||||
func init() {
|
||||
cool.CreateTable(&Title{})
|
||||
}
|
||||
Reference in New Issue
Block a user