package model import ( "blazing/common/data/xmlres" "blazing/cool" "math" "math/rand" "time" "github.com/gogf/gf/v2/util/gconv" ) const TableNamePet = "pet" // Pet mapped from table type Pet struct { *cool.Model PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"` InBag int `gorm:"not null;comment:'是否在背包中'" json:"in_bag"` //"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"` } // PetInfo 精灵信息结构(合并后的优化版本) type PetInfo struct { // 精灵编号(@UInt long → uint32) ID uint32 `fieldDesc:"精灵编号" ` // 名字:默认为全0,补齐到16字节(固定长度 → [16]byte) Name string `struc:"[16]byte" ` // 个体值(@UInt long → uint32) Dv uint32 `fieldDesc:"个体值" ` // 性格(@UInt long → uint32) Nature uint32 `fieldDesc:"性格" ` // 等级(@UInt long → uint32) Level uint32 `fieldDesc:"等级" ` // 当前等级已获得经验(@UInt long → uint32) Exp uint32 `fieldDesc:"当前等级已经获得的经验 2538" ` // 当前等级所需经验(@UInt long → uint32) LvExp uint32 `fieldDesc:"当前等级所需的经验" ` // 升到下一级的经验(@UInt long → uint32) NextLvExp uint32 `fieldDesc:"升到下一级的经验" ` // 当前生命(@UInt long → uint32) Hp uint32 `fieldDesc:"当前生命" ` // 最大生命(@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"` // 技能信息:固定4条,空则赋值0(固定长度List → [4]SkillInfo,零值即符合“赋值0”) SkillList []SkillInfo // 捕捉时间(@UInt long → 若为时间戳用uint32;若需时间类型可改为time.Time,需配合序列化处理) CatchTime uint32 //`json:"-"` // 显式忽略,不参与序列化 // 捕捉地图(@UInt long → uint32) CatchMap uint32 `fieldDesc:"捕捉地图" ` // 未知默认0(@UInt long → uint32) CatchRect uint32 `fieldDesc:"未知默认为0" ` // 捕获等级默认0(@UInt long → uint32) CatchLevel uint32 `fieldDesc:"捕获等级 默认为0" ` EffectInfoLen uint16 `struc:"sizeof=EffectInfo"` // 特性列表:长度用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=是) Shiny uint32 `fieldDesc:"是不是闪" ` // AbilityType uint32 `struc:"skip"` //特性 } 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) } } } // 传入bool则不升级 func (petinfo *PetInfo) Update(t ...bool) { basic := xmlres.PetMAP[int(petinfo.ID)] if len(t) == 0 { // 检查是否可以进化 if basic.EvolvesTo != 0 && // 有明确的进化 int(petinfo.Level) >= basic.EvolvingLv && // 有明确的进化等级 basic.IsLarge == 0 { // 非最终形态 petinfo.ID = uint32(basic.EvolvesTo) basic = xmlres.PetMAP[int(petinfo.ID)] //重新计算 } } petinfo.LvExp = petinfo.NextLvExp petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic()) } // 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 精灵特性信息结构 // // // // // // // type PetEffectInfo struct { ItemID uint32 `struc:"uint32" json:"item_id"` //如果是能量珠,就显示 Idx uint16 `struc:"skip" json:"new_se_idx"` 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"` 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{ Model: cool.NewModel(), } } // init 创建表 func init() { _ = cool.CreateTable(&Pet{}) // fmt.Println(err) } func GenPetInfo( id int, dv, natureId, abilityTypeEnum, shinyid, level int, ) *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 != -1 { p.Shiny = uint32(shinyid) } // ---- 性格 ---- if natureId == -1 { p.Nature = uint32(rng.Intn(25)) } else { p.Nature = uint32(natureId) } // ---- 个体值(DV)---- if dv == -1 { p.Dv = uint32(CalculateIndividualValue(rng)) } 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: // 随机特性 randomIndex := rng.Intn(len(xmlres.PlayerEffectMAP)) var i int for _, v := range xmlres.PlayerEffectMAP { if i == randomIndex { p.EffectInfo = append(p.EffectInfo, PetEffectInfo{ Idx: uint16(gconv.Int16(v.Idx)), Status: 1, EID: uint16(gconv.Int16(v.Eid)), Args: v.ArgsS, }) break } i++ } } // ---- 技能学习 ---- skills := LastFourElements(p.GetLevelRangeCanLearningSkills(0, p.Level)) // 最后四个技能 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() p.Update(true) return p } func LastFourElements[T any](s []T) []T { n := len(s) if n <= 4 { // 切片长度小于等于4时,返回整个切片 return s } // 切片长度大于4时,返回最后4个元素(从n-4索引到末尾) return s[n-4:] } // 除数数组(放大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(random *rand.Rand) int { // 生成0-40000的随机数,作为个体值计算的输入 a := random.Intn(40001) return CalculateIndividual(a) }