feat(pet): 重构宠物属性计算与技能学习逻辑,优化代码结构

This commit is contained in:
1
2025-09-21 17:01:31 +00:00
parent 691cfc878b
commit afb31bd79b
6 changed files with 260 additions and 142 deletions

View File

@@ -4,8 +4,8 @@ import "github.com/ECUST-XX/xml"
// Move 表示怪物可学习的技能
type PetMoves struct {
ID int `xml:"ID,attr"`
LearningLv int `xml:"LearningLv,attr"`
ID uint32 `xml:"ID,attr"`
LearningLv uint32 `xml:"LearningLv,attr"`
}
// LearnableMoves 包含怪物可学习的技能列表
@@ -16,31 +16,33 @@ type LearnableMoves struct {
// PetInfo 表示一个怪物的信息
type PetInfo struct {
ID int `xml:"ID,attr"`
DefName string `xml:"DefName,attr"` //名字
Type int `xml:"Type,attr"` //类型
GrowthType int `xml:"GrowthType,attr"` //成长类型
HP int `xml:"HP,attr"` //血量种族值
Atk uint32 `xml:"Atk,attr"` //攻击种族值
Def uint32 `xml:"Def,attr"`
SpAtk uint32 `xml:"SpAtk,attr"`
SpDef uint32 `xml:"SpDef,attr"`
Spd uint32 `xml:"Spd,attr"`
YieldingExp int `xml:"YieldingExp,attr"`
CatchRate string `xml:"CatchRate,attr"`
YieldingEV string `xml:"YieldingEV,attr"`
EvolvesFrom int `xml:"EvolvesFrom,attr"`
EvolvesTo int `xml:"EvolvesTo,attr"`
EvolvingLv int `xml:"EvolvingLv,attr"`
FreeForbidden int `xml:"FreeForbidden,attr"`
FuseMaster int `xml:"FuseMaster,attr"`
FuseSub int `xml:"FuseSub,attr"`
Gender int `xml:"Gender,attr"`
PetClass int `xml:"PetClass,attr"`
FormParam float64 `xml:"FormParam,attr"`
CharacterAttrParam int `xml:"CharacterAttrParam,attr"`
GradeParam float64 `xml:"GradeParam,attr"`
AddSeParam int `xml:"AddSeParam,attr"`
LearnableMoves LearnableMoves `xml:"LearnableMoves"`
DefName string `xml:"DefName,attr"` // 名字
Type int `xml:"Type,attr"` // 类型
IsLarge int `xml:"IsLarge,attr"` // 是否为大型怪物
GrowthType int `xml:"GrowthType,attr"` // 成长类型
HP int `xml:"HP,attr"` // 血量种族值
Atk uint32 `xml:"Atk,attr"` // 攻击种族值
Def uint32 `xml:"Def,attr"` // 防御种族值
SpAtk uint32 `xml:"SpAtk,attr"` // 特殊攻击种族值
SpDef uint32 `xml:"SpDef,attr"` // 特殊防御种族值
Spd uint32 `xml:"Spd,attr"` // 速度种族值
YieldingExp int `xml:"YieldingExp,attr"` // 击败后获得的经验值
CatchRate int `xml:"CatchRate,attr"` // 捕捉率
YieldingEV string `xml:"YieldingEV,attr"` // 努力值奖励,格式为"HP Atk Def SpAtk SpDef Spd"
EvolvesFrom int `xml:"EvolvesFrom,attr"` // 进化前的怪物ID
EvolvesTo uint32 `xml:"EvolvesTo,attr"` // 进化后的怪物ID
EvolvingLv int `xml:"EvolvingLv,attr"` // 进化等级
FreeForbidden int `xml:"FreeForbidden,attr"` // 是否禁止放生
FuseMaster int `xml:"FuseMaster,attr"` // 是否可作为融合主素材
FuseSub int `xml:"FuseSub,attr"` // 是否可作为融合副素材
Gender int `xml:"Gender,attr"` // 性别 0-无性别 1-雄性 2-雌性
PetClass int `xml:"PetClass,attr"` // 宠物类别
FormParam float64 `xml:"FormParam,attr"` // 形态参数
CharacterAttrParam int `xml:"CharacterAttrParam,attr"` // 特性参数
GradeParam int `xml:"GradeParam,attr"` // 等级参数
AddSeParam int `xml:"AddSeParam,attr"` // 附加状态参数
Recycle int `xml:"Recycle,attr"` // 是否可回收
LearnableMoves LearnableMoves `xml:"LearnableMoves"` // 可学习的技能
}
// Monsters 表示所有怪物的集合

View File

@@ -114,34 +114,28 @@ func (h *Controller) PlayerShowPet(
}
func (h *Controller) PetOneCure(
data *pet.PetOneCureInboundInfo, c *player.Player) (result *pet.PetOneCureOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
result = &pet.PetOneCureOutboundInfo{
data.CatchTime,
}
var temp []model.PetInfo
for _, pi := range c.Info.PetList {
if pi.CatchTime == data.CatchTime {
pi.Hp = pi.MaxHp
// 恢复技能PP值
var skillList [4]model.SkillInfo
for i, skill := range pi.SkillList {
// 跳过无效技能
if skill.ID == 0 {
continue
}
// 恢复至最大PP值从配置表获取
if maxPP, ok := xmlres.SkillMap[int(skill.ID)]; ok {
skill.PP = uint32(maxPP.MaxPP)
skillList[i] = skill
}
_, onpet, ok := FindWithIndex(c.Info.PetList, func(item model.PetInfo) bool {
return item.CatchTime == data.CatchTime
})
if ok {
onpet.Hp = onpet.MaxHp
for i := 0; i < 4; i++ {
// 跳过无效技能
if onpet.SkillList[i].ID == 0 {
continue
}
// 恢复至最大PP值从配置表获取
if maxPP, ok := xmlres.SkillMap[int(onpet.SkillList[i].ID)]; ok {
onpet.SkillList[i].PP = uint32(maxPP.MaxPP)
}
pi.SkillList = skillList
}
temp = append(temp, pi)
}
c.Info.PetList = temp
return result, 0
return &pet.PetOneCureOutboundInfo{
CatchTime: data.CatchTime,
}, 0
}
@@ -165,3 +159,14 @@ func (h *Controller) PetFirst(
return result, 0
}
// FindWithIndex 遍历slice找到第一个满足条件的元素
// 返回:索引、元素指针、是否找到
func FindWithIndex[T any](slice []T, predicate func(item T) bool) (int, *T, bool) {
for i := range slice {
if predicate(slice[i]) {
return i, &slice[i], true
}
}
return -1, nil, false
}

View File

@@ -328,28 +328,25 @@ func (f *FightC) battleLoop() {
tt, ok := ff.Player.(*player.Player)
mo, ism := f.Opp.Player.(*player.AI_player)
if ok { //如果获取玩家
if ok && ism && mo.CanCapture { //如果获取玩家
if ism && mo.CanCapture { //如果获取到IA
ok, _ := f.Our.Capture(f.Opp.CurrentPet, faction.ItemID, -1)
if ok { //todo 待补充
tt.Service.PetAdd(*f.Opp.CurrentPet.Info)
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{
CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
PetId: uint32(f.Opp.CurrentPet.ID),
})
ff.Player.SendFightEndInfo(info.FightOverInfo{
ok, _ := f.Our.Capture(f.Opp.CurrentPet, faction.ItemID, -1)
if ok { //todo 待补充
tt.Service.PetAdd(*f.Opp.CurrentPet.Info)
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{
CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
PetId: uint32(f.Opp.CurrentPet.ID),
})
ff.Player.SendFightEndInfo(info.FightOverInfo{
WinnerId: f.ownerID,
})
f.closefight = true
}
} else { //说明不是可以捕捉的
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
WinnerId: f.ownerID,
})
f.closefight = true
}
} else { //说明不是可以捕捉的
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
}
})

View File

@@ -0,0 +1,99 @@
package player
import (
"blazing/common/data/xmlres"
"blazing/modules/blazing/model"
"math"
)
// calculateExperience 根据公式计算指定等级所需的经验值
func calculateExperience(level uint32) uint32 {
var x float64
// 判断等级奇偶性确定x的值
if level%2 == 1 { // 奇数
x = 0.5
} else { // 偶数
x = -0.5
}
// 应用公式计算
lvFloat := float64(level)
experience := 3.75*lvFloat*lvFloat + 3.75*lvFloat + 1 + x
// 四舍五入为整数
return uint32(math.Round(experience))
}
// 主函数实现
// 添加经验
// 超NO 加成
func (p *Player) AddPetExp(pet *model.PetInfo, addExp uint32) {
originalLevel := pet.Level
for {
needExp := calculateExperience(pet.Level)
if addExp >= needExp {
addExp -= needExp
pet.Level++
} else {
break
}
}
pet.Exp = addExp
pet.NextLvExp = calculateExperience(pet.Level)
pet.LvExp = pet.NextLvExp - pet.Exp
// 处理进化逻辑
for {
basic := xmlres.PetMAP[int(pet.ID)]
// 检查是否可以进化
if basic.EvolvesTo == 0 ||
basic.EvolvingLv == 0 ||
basic.EvolvingLv >= int(pet.Level) ||
basic.IsLarge != 0 {
break
}
// 获取进化后的资产
pet.ID = uint32(basic.EvolvesTo)
}
// 重新计算面板
pet.CalculatePetPane()
// 发送经验更新消息
//player.SendMessage(generatePetUpdateInfo(petEntity, originalExp+addExp-exp, addition))
// 处理技能学习
canLearnSkillList := model.LastFourElements(pet.GetLevelRangeCanLearningSkills(originalLevel, pet.Level)) //获取最后四个技能,如果不足,那就取全部技能
for i := 0; i < 4; i++ {
if pet.SkillList[i].ID == 0 {
if len(canLearnSkillList) != 0 {
skid := canLearnSkillList[len(canLearnSkillList)-1]
pet.SkillList[i].ID = skid
pet.SkillList[i].PP = uint32(xmlres.SkillMap[int(skid)].MaxPP)
canLearnSkillList = append([]uint32{}, canLearnSkillList[:len(canLearnSkillList)-1]...)
}
}
}
//todo 待实现
// // 发送技能更新消息
// updateSkillInfo := UpdateSkillInfo{
// PetCatchTime: petEntity.captureTime,
// ActiveSkillNum: activeSkillNum,
// UnActiveSkillNum: unActiveSkillNum,
// SkillArray: canLearnSkillList,
// }
// player.SendMessage(UpdateSkillOutboundInfo{
// InfoArray: []UpdateSkillInfo{updateSkillInfo},
// })
// return exp
}

View File

@@ -84,8 +84,6 @@ func GenPetInfo(id int, dv, natureId, abilityTypeEnum, shinyid, level int) *PetI
CatchTime: uint32(time.Now().Unix()),
Level: uint32(level)} //等级
naxml := xmlres.NatureRootMap[int(p.Nature)]
petxml := xmlres.PetMAP[int(id)]
if shinyid != -1 {
p.Shiny = uint32(shinyid)
} else {
@@ -127,89 +125,20 @@ func GenPetInfo(id int, dv, natureId, abilityTypeEnum, shinyid, level int) *PetI
}
}
tttt := make([]uint32, 0)
for _, v := range petxml.LearnableMoves.Moves {
if p.Level >= uint32(v.LearningLv) {
tttt = append(tttt, uint32(v.ID))
}
}
tttt = LastFourElements(tttt) //获取最后四个技能,如果不足,那就取全部技能
tttt := LastFourElements(p.GetLevelRangeCanLearningSkills(0, p.Level)) //获取最后四个技能,如果不足,那就取全部技能
for i := 0; i < len(tttt); i++ {
p.SkillList[i].ID = tttt[i]
p.SkillList[i].PP = uint32(xmlres.SkillMap[int(tttt[i])].MaxPP)
}
p.SkillListLen = uint32(len(tttt))
// 计算各项属性
hp := p.CalculatePetHPPanelSize(
uint32(petxml.HP),
p.Dv,
p.Level,
p.EvHp,
)
// * 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.Dv,
p.Level,
p.EvAttack,
naxml.AttackCorrect,
)
p.Prop[1] = p.CalculatePetPanelSize(
uint32(petxml.Def),
p.Dv,
p.Level,
p.EvDefence,
naxml.DefenseCorrect,
)
p.Prop[2] = p.CalculatePetPanelSize(
uint32(petxml.SpAtk),
p.Dv,
p.Level,
p.EvSpecialAttack,
naxml.SaCorrect,
)
p.Prop[3] = p.CalculatePetPanelSize(
uint32(petxml.SpDef),
p.Dv,
p.Level,
p.EvSpecialDefense,
naxml.SdCorrect,
)
p.Prop[4] = p.CalculatePetPanelSize(
uint32(petxml.Spd),
p.Dv,
p.Level,
p.EvSpeed,
naxml.SpeedCorrect,
)
// 设置计算结果
p.MaxHp = hp
p.Hp = hp
p.CalculatePetPane()
p.Hp = p.MaxHp
return p
}
// 计算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, dv, level, ev uint32, natureCorrect float64) uint32 {
base1 := float64((float64(base)*2+float64(ev)/4.0+float64(dv))*(float64(level)/100.0) + 5)
return uint32(float64(base1) * natureCorrect)
}
// PetInfo 精灵信息结构(合并后的优化版本)
type PetInfo struct {

View File

@@ -0,0 +1,86 @@
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, dv, level, ev uint32, natureCorrect float64) uint32 {
base1 := float64((float64(base)*2+float64(ev)/4.0+float64(dv))*(float64(level)/100.0) + 5)
return uint32(float64(base1) * natureCorrect)
}
// 计算生成面板
func (p *PetInfo) CalculatePetPane() {
naxml := xmlres.NatureRootMap[int(p.Nature)]
petxml := xmlres.PetMAP[int(p.ID)]
// 计算各项属性
hp := p.calculatePetHPPanelSize(
uint32(petxml.HP),
p.Dv,
p.Level,
p.EvHp,
)
// * 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.Dv,
p.Level,
p.EvAttack,
naxml.AttackCorrect,
)
p.Prop[1] = p.calculatePetPanelSize(
uint32(petxml.Def),
p.Dv,
p.Level,
p.EvDefence,
naxml.DefenseCorrect,
)
p.Prop[2] = p.calculatePetPanelSize(
uint32(petxml.SpAtk),
p.Dv,
p.Level,
p.EvSpecialAttack,
naxml.SaCorrect,
)
p.Prop[3] = p.calculatePetPanelSize(
uint32(petxml.SpDef),
p.Dv,
p.Level,
p.EvSpecialDefense,
naxml.SdCorrect,
)
p.Prop[4] = p.calculatePetPanelSize(
uint32(petxml.Spd),
p.Dv,
p.Level,
p.EvSpeed,
naxml.SpeedCorrect,
)
// 设置计算结果
p.MaxHp = hp
}