feat(xml): 添加进化配置映射并更新错误码提示

- 在 `common/data/xmlres/file.go` 中添加 EVOLVMAP 用于加载进化配置
- 更新多个控制器中的金币不足错误码,统一使用骄阳余额不足错误码 `ErrSunDouInsufficient10016`
- 修改战斗逻辑中 AI 动作触发机制,并优化战斗流程
- 增加对融合材料合法性的校验
- 调整战斗动作通道缓冲区大小以提升并发处理能力
- 更新 XML 配置
This commit is contained in:
2025-12-16 02:50:10 +08:00
parent 597bd7b9d5
commit 39dc79f706
17 changed files with 101 additions and 47 deletions

View File

@@ -0,0 +1,22 @@
package xmlres
import "github.com/ECUST-XX/xml"
// CondEvolves 根节点结构体
type CondEvolves struct {
XMLName xml.Name `xml:"CondEvolves"`
Evolves []Evolve `xml:"Evolve"`
}
// Evolve 进化配置结构体
type Evolve struct {
ID int `xml:"ID,attr"` // 进化ID属性
Branches []Branch `xml:"Branch"` // 进化分支列表
}
// Branch 进化分支结构体
type Branch struct {
MonTo int `xml:"MonTo,attr"` // 进化目标精灵ID
EvolvItem int `xml:"EvolvItem,attr,omitempty"` // 进化道具ID可选
EvolvItemCount int `xml:"EvolvItemCount,attr,omitempty"` // 进化道具数量(可选)
}

View File

@@ -54,6 +54,7 @@ var (
SkillTypeMap map[int]SkillType SkillTypeMap map[int]SkillType
RelationsMap map[int]Relation RelationsMap map[int]Relation
GoldProductMap = make(map[int]GoldProductItem, 0) GoldProductMap = make(map[int]GoldProductItem, 0)
EVOLVMAP map[int]Evolve
) )
func Initfile() { func Initfile() {
@@ -70,7 +71,10 @@ func Initfile() {
return m.ID return m.ID
}) })
EVOLVMAP = utils.ToMap[Evolve, int](getXml[CondEvolves](path+"进化仓.xml").Evolves, func(m Evolve) int {
return m.ID
})
//TalkConfig = getXml[TalkRoot](path + "talk.xml") //TalkConfig = getXml[TalkRoot](path + "talk.xml")
MonsterMap = utils.ToMap(getXml[MonsterRoot](path+"地图配置野怪.xml").Maps, func(m TMapConfig) int { MonsterMap = utils.ToMap(getXml[MonsterRoot](path+"地图配置野怪.xml").Maps, func(m TMapConfig) int {

View File

@@ -349,8 +349,8 @@ var ErrorCodes = enum.New[struct {
ErrPokemonReleasing ErrorCode `enum:"103017"` ErrPokemonReleasing ErrorCode `enum:"103017"`
// 你今天已经领了10次礼物了,不能再领了! // 你今天已经领了10次礼物了,不能再领了!
ErrDailyGiftsLimit10 ErrorCode `enum:"15007"` ErrDailyGiftsLimit10 ErrorCode `enum:"15007"`
// 你的XIN豆余额不足 // 你的骄阳余额不足
ErrXinDouInsufficient10016 ErrorCode `enum:"10016"` ErrSunDouInsufficient10016 ErrorCode `enum:"10016"`
// 你还没有开通超能NoNo无法使用相关服务。 // 你还没有开通超能NoNo无法使用相关服务。
ErrNoSuperNoNo ErrorCode `enum:"10041"` ErrNoSuperNoNo ErrorCode `enum:"10041"`
// 没有完成任务不能领奖品 // 没有完成任务不能领奖品

View File

@@ -8,7 +8,6 @@ import (
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
) )
func (h *Controller) NonoFollowOrHome(data *nono.NonoFollowOrHomeInInfo, c *player.Player) (result *nono.NonoFollowOutInfo, err errorcode.ErrorCode) { //这个时候player应该是空的 func (h *Controller) NonoFollowOrHome(data *nono.NonoFollowOrHomeInInfo, c *player.Player) (result *nono.NonoFollowOutInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
c.Info.NONO.Flag = data.Flag c.Info.NONO.Flag = data.Flag
result = &nono.NonoFollowOutInfo{ result = &nono.NonoFollowOutInfo{
@@ -56,7 +55,7 @@ func (h *Controller) PlayerPetCure(data *nono.PetCureInboundInfo, c *player.Play
return result, errorcode.ErrorCodes.ErrChampionCannotHeal return result, errorcode.ErrorCodes.ErrChampionCannotHeal
} }
if !c.UseCoins(50) { if !c.UseCoins(50) {
return result, errorcode.ErrorCodes.ErrSystemBusy return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
} }
for i := 0; i < len(c.Info.PetList); i++ { for i := 0; i < len(c.Info.PetList); i++ {
c.Info.PetList[i].Cure() c.Info.PetList[i].Cure()

View File

@@ -0,0 +1 @@
package controller

View File

@@ -13,7 +13,7 @@ import (
func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result *pet.PetFusionInfo, err errorcode.ErrorCode) { func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result *pet.PetFusionInfo, err errorcode.ErrorCode) {
if !c.UseCoins(1000) { if !c.UseCoins(1000) {
return result, errorcode.ErrorCodes.ErrSystemBusy return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
} }
// g.Dump(c.Info.PetList) // g.Dump(c.Info.PetList)
@@ -49,10 +49,14 @@ func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result
if resid == 0 { if resid == 0 {
//todo失败降低等级 //todo失败降低等级
return &pet.PetFusionInfo{}, 0
}
effect := int(service.PetFusionMaterialServiceIns.Data(data.Item1))
if effect == 0 {
return &pet.PetFusionInfo{}, 0 return &pet.PetFusionInfo{}, 0
} }
effect := int(service.PetFusionMaterialServiceIns.Data(data.Item1))
dv1 := alpacadecimal.NewFromInt(2).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(Mcatchpetinfo.Dv))) dv1 := alpacadecimal.NewFromInt(2).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(Mcatchpetinfo.Dv)))
dv2 := alpacadecimal.NewFromInt(1).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(Auxpetinfo.Dv))) dv2 := alpacadecimal.NewFromInt(1).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(Auxpetinfo.Dv)))
dv := dv1.Add(dv2).Add(alpacadecimal.NewFromInt(1)).IntPart() dv := dv1.Add(dv2).Add(alpacadecimal.NewFromInt(1)).IntPart()

View File

@@ -12,7 +12,7 @@ import (
func (h Controller) SetPetSkill(data *pet.ChangeSkillInfo, c *player.Player) (result *pet.ChangeSkillOutInfo, err errorcode.ErrorCode) { func (h Controller) SetPetSkill(data *pet.ChangeSkillInfo, c *player.Player) (result *pet.ChangeSkillOutInfo, err errorcode.ErrorCode) {
if !c.UseCoins(100) { if !c.UseCoins(100) {
return result, errorcode.ErrorCodes.ErrSystemBusy return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
} }
c.Info.Coins -= 50 c.Info.Coins -= 50
_, onpet, ok := c.FindPet(data.CatchTime) _, onpet, ok := c.FindPet(data.CatchTime)

View File

@@ -15,7 +15,7 @@ func (h Controller) BUY_FITMENT(data *room.C2S_BUY_FITMENT, c *player.Player) (r
used := xmlres.ItemsMAP[int(data.ID)].Price * int(data.Count) used := xmlres.ItemsMAP[int(data.ID)].Price * int(data.Count)
if !c.UseCoins(uint32(used)) { if !c.UseCoins(uint32(used)) {
return nil, errorcode.ErrorCodes.ErrPurchaseFailed return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
} }
c.Service.Room.Add(data.ID, data.Count) c.Service.Room.Add(data.ID, data.Count)

View File

@@ -38,7 +38,7 @@ func (h Controller) Chat(data *user.ChatInboundInfo, c *player.Player) (result *
func (h Controller) ChangePlayerColor(data *user.ChangeColorInboundInfo, c *player.Player) (result *user.ChangeColorOutboundInfo, err errorcode.ErrorCode) { func (h Controller) ChangePlayerColor(data *user.ChangeColorInboundInfo, c *player.Player) (result *user.ChangeColorOutboundInfo, err errorcode.ErrorCode) {
if !c.UseCoins(200) { //如果花不了200,直接返回 if !c.UseCoins(200) { //如果花不了200,直接返回
return return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
} }
c.Info.Coins -= 50 c.Info.Coins -= 50
c.Info.Color = data.Color c.Info.Color = data.Color
@@ -55,7 +55,7 @@ func (h Controller) ChangePlayerColor(data *user.ChangeColorInboundInfo, c *play
} }
func (h Controller) ChangePlayerDoodle(data *user.ChangeDoodleInboundInfo, c *player.Player) (result *user.ChangeDoodleOutboundInfo, err errorcode.ErrorCode) { func (h Controller) ChangePlayerDoodle(data *user.ChangeDoodleInboundInfo, c *player.Player) (result *user.ChangeDoodleOutboundInfo, err errorcode.ErrorCode) {
if !c.UseCoins(200) { //如果花不了200,直接返回 if !c.UseCoins(200) { //如果花不了200,直接返回
return return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
} }
c.Info.Coins -= 50 c.Info.Coins -= 50
c.Info.Texture = data.Id c.Info.Texture = data.Id

View File

@@ -64,7 +64,7 @@ func (f *FightC) ChangePet(c common.PlayerI, id uint32) {
BaseAction: action.NewBaseAction(c.GetInfo().UserID), BaseAction: action.NewBaseAction(c.GetInfo().UserID),
Cid: id, Cid: id,
} }
println("切精灵", c.GetInfo().UserID, id)
f.actionChan <- ret f.actionChan <- ret
} }
@@ -77,6 +77,7 @@ func (f *FightC) UseSkill(c common.PlayerI, id uint32) {
ret := &action.SelectSkillAction{ ret := &action.SelectSkillAction{
BaseAction: action.NewBaseAction(c.GetInfo().UserID), BaseAction: action.NewBaseAction(c.GetInfo().UserID),
} }
println("使用技能", c.GetInfo().UserID, id)
if f.GetInputByPlayer(c, false).CurrentPet == nil { if f.GetInputByPlayer(c, false).CurrentPet == nil {
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"blazing/logic/service/fight/action" "blazing/logic/service/fight/action"
"blazing/logic/service/fight/info" "blazing/logic/service/fight/info"
"blazing/logic/service/fight/input" "blazing/logic/service/fight/input"
"blazing/logic/service/player"
"blazing/modules/blazing/model" "blazing/modules/blazing/model"
"fmt" "fmt"
"reflect" "reflect"
@@ -339,14 +338,6 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
f.closefight = true f.closefight = true
// break // break
} else {
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
if _, ok := defender.Player.(*player.AI_player); ok {
defender.GetAction(f.Our)
//panic("AI自动技能")
}
}
} }
//attacker.CanAction = true //attacker.CanAction = true
@@ -373,16 +364,8 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
f.closefight = true f.closefight = true
} else {
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
if _, ok := defender.Player.(*player.AI_player); ok {
defender.GetAction(f.Our)
//panic("AI自动技能")
}
}
} }
break //break
} }
} }
@@ -408,6 +391,8 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
if ff.Player.GetInfo().UserID != v.Reason.UserId { if ff.Player.GetInfo().UserID != v.Reason.UserId {
println("切精灵", v.Reason.UserId, v.Reason.ID)
ff.Player.SendPackCmd(2407, &v.Reason) ff.Player.SendPackCmd(2407, &v.Reason)
} }
@@ -419,7 +404,18 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
return return
} }
f.Broadcast(func(ff *input.Input) { f.Broadcast(func(ff *input.Input) {
ff.Player.SendPackCmd(2505, &ret) ff.Player.SendPackCmd(2505, &ret)
ff.CanChange = 0 ff.CanChange = 0
}) })
println("回合结束")
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
if f.Opp.CurrentPet.Info.Hp <= 0 {
println("回合结束开始执行NPC动作")
f.Opp.GetAction()
//panic("AI自动技能")
}
}
} }

View File

@@ -189,7 +189,7 @@ func (our *Input) Damage(in *Input, sub *info.DamageZone) {
} }
func (our *Input) GetAction(opp *Input) { func (our *Input) GetAction() {
next := our.Exec(func(t Effect) bool { next := our.Exec(func(t Effect) bool {
@@ -205,7 +205,7 @@ func (our *Input) GetAction(opp *Input) {
for _, v := range our.AllPet { for _, v := range our.AllPet {
if v.Info.Hp > 0 { if v.Info.Hp > 0 {
our.FightC.ChangePet(our.Player, v.Info.CatchTime) our.FightC.ChangePet(our.Player, v.Info.CatchTime)
our.GetAction(our.Opp)
return return
} }
@@ -237,7 +237,7 @@ func (our *Input) GetAction(opp *Input) {
continue continue
} }
// 计算技能对对方的伤害假设CalculatePower返回伤害值或需从技能中获取 // 计算技能对对方的伤害假设CalculatePower返回伤害值或需从技能中获取
damage := our.CalculatePower(opp, s) damage := our.CalculatePower(our.Opp, s)
if !s.CanUse() { if !s.CanUse() {
continue continue
@@ -245,7 +245,7 @@ func (our *Input) GetAction(opp *Input) {
allSkills = append(allSkills, skillWithDamage{SkillEntity: s, damage: damage}) allSkills = append(allSkills, skillWithDamage{SkillEntity: s, damage: damage})
// 判断是否能秒杀(伤害 >= 对方当前生命值) // 判断是否能秒杀(伤害 >= 对方当前生命值)
if uint32(damage.IntPart()) >= opp.CurrentPet.Info.Hp { // 假设oppPet.HP为对方当前剩余生命值 if uint32(damage.IntPart()) >= our.Opp.CurrentPet.Info.Hp { // 假设oppPet.HP为对方当前剩余生命值
killableSkills = append(killableSkills, struct { killableSkills = append(killableSkills, struct {
*info.SkillEntity *info.SkillEntity
damage alpacadecimal.Decimal damage alpacadecimal.Decimal

View File

@@ -20,22 +20,25 @@ import (
func (f *FightC) battleLoop() { func (f *FightC) battleLoop() {
f.actionChan = make(chan action.BattleActionI, 2) f.actionChan = make(chan action.BattleActionI, 10)
//fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime) //fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime)
ourID := f.Our.Player.GetInfo().UserID ourID := f.Our.Player.GetInfo().UserID
oppID := f.Opp.Player.GetInfo().UserID oppID := f.Opp.Player.GetInfo().UserID
//fmt.Println("开始收集玩家动作", waitr)
for !f.closefight { for !f.closefight {
f.Round++ f.Round++
//fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round) //fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round)
// AI自动技能
actions := f.collectPlayerActions(ourID, oppID) actions := f.collectPlayerActions(ourID, oppID)
if f.closefight { if f.closefight {
break break
} }
println("进入回合")
f.resolveRound(actions[ourID], actions[oppID]) f.resolveRound(actions[ourID], actions[oppID])
} }
@@ -111,7 +114,7 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
actions := make(map[uint32]action.BattleActionI) actions := make(map[uint32]action.BattleActionI)
waitr := time.Duration(f.waittime)*time.Millisecond*10 + 60*time.Second waitr := time.Duration(f.waittime)*time.Millisecond*10 + 60*time.Second
//fmt.Println("开始收集玩家动作", waitr)
timeout := time.After(waitr) timeout := time.After(waitr)
for len(actions) < 2 { for len(actions) < 2 {
@@ -133,12 +136,10 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
if pid != ourID && pid != oppID { if pid != ourID && pid != oppID {
continue continue
} }
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && pid != 0 {
// // 避免重复提交 f.Opp.GetAction()
// if _, exists := actions[pid]; exists { println("开始执行NPC动作")
// fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", pid) }
// continue
// }
selfinput := f.GetInputByAction(paction, false) selfinput := f.GetInputByAction(paction, false)
if ret, ok := paction.(*action.ActiveSwitchAction); ok { if ret, ok := paction.(*action.ActiveSwitchAction); ok {
//正常结束可以切换,以及死切后还能再切一次 //正常结束可以切换,以及死切后还能再切一次
@@ -170,7 +171,14 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
}) })
}) })
if selfinput.CanChange == 2 { if selfinput.CanChange == 2 {
selfinput.CanChange = 0 selfinput.CanChange = 0
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && pid == 0 {
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
f.Our.Player.SendPackCmd(2407, &ret.Reason)
f.Opp.GetAction()
println("开始执行NPC死切动作")
}
continue continue
} }
} }
@@ -188,11 +196,6 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
// selfinput.CanChange-- // selfinput.CanChange--
// } // }
} }
// AI自动技能
if pid != 0 && (f.Info.Status == info.BattleMode.FIGHT_WITH_NPC) {
f.GetInputByAction(paction, true).GetAction(f.Our)
//panic("AI自动技能")
}
actions[pid] = paction actions[pid] = paction
//fmt.Println("玩家执行动作:", pid, paction.Priority()) //fmt.Println("玩家执行动作:", pid, paction.Priority())

View File

@@ -38,6 +38,10 @@ func (s *PetFusionMaterialService) Data(Material1 [4]uint32) uint32 {
fusions := service.DictInfoServiceS.GetData("fusion") fusions := service.DictInfoServiceS.GetData("fusion")
for _, v := range Material1 { for _, v := range Material1 {
if v < 10000 {
//使用过小的道具
return 0
}
_, ok := fusions[v] _, ok := fusions[v]
if !ok { if !ok {
//todo使用了非法材料 //todo使用了非法材料

View File

@@ -0,0 +1,10 @@
<Eggs>
<Egg Id="1" MaleMon="450" FemaleMon="447" OutputMons="445 448 512" Probs="35 35 30" />
<Egg Id="2" MaleMon="253" FemaleMon="256" OutputMons="252 254 519" Probs="35 35 30" />
<Egg Id="3" MaleMon="516" FemaleMon="470" OutputMons="515 469 517" Probs="35 35 30" />
<Egg Id="4" MaleMon="525" FemaleMon="295" OutputMons="523 293 533" Probs="35 35 30" />
<Egg Id="5" MaleMon="337" FemaleMon="549" OutputMons="335 547 550" Probs="35 35 30" />
<Egg Id="6" MaleMon="597" FemaleMon="599" OutputMons="596 598 600" Probs="50 45 5" />
<Egg Id="7" MaleMon="67" FemaleMon="606" OutputMons="65 604 607" Probs="30 30 40" />
<Egg Id="8" MaleMon="962" FemaleMon="965" OutputMons="960 963 977" Probs="5 5 90" />
</Eggs>

View File

@@ -443,6 +443,16 @@ eg:
<Boss Id="1" BossCatchable="1" TaskID="1122" AppearTime="19 21" BossVisible="0" <Boss Id="1" BossCatchable="1" TaskID="1122" AppearTime="19 21" BossVisible="0"
BossFinTaskWay="2"> BossFinTaskWay="2">
<BossMon MonID="807" Hp="68" Lv="25" /> <BossMon MonID="807" Hp="68" Lv="25" />
<!-- <BossMon MonID="806" Hp="68" Lv="25" />
<BossMon MonID="805" Hp="68" Lv="25" /> -->
</Boss>
<Boss Id="2" BossVisible="0"
BossFinTaskWay="2">
<BossMon MonID="1" Hp="68" Lv="25" />
<BossMon MonID="2" Hp="68" Lv="25" />
<BossMon MonID="3" Hp="68" Lv="25" />
<!-- <BossMon MonID="806" Hp="68" Lv="25" />
<BossMon MonID="805" Hp="68" Lv="25" /> -->
</Boss> </Boss>
</Bosses> </Bosses>
@@ -4449,7 +4459,7 @@ eg:
<BossMon MonID="88" Hp="4500" Lv="100" NewSeIdxs="132 71 110 58 64" /> <BossMon MonID="88" Hp="4500" Lv="100" NewSeIdxs="132 71 110 58 64" />
<BossMon MonID="113" Hp="5000" Lv="100" NewSeIdxs="132 73 110 58 62" /> <BossMon MonID="113" Hp="5000" Lv="100" NewSeIdxs="132 73 110 58 62" />
</Boss> </Boss>
<Boss TaskID="145" BossVisible="0" AppearTime="0 23" Name="玄武"> <Boss Id="1" TaskID="145" BossVisible="0" AppearTime="0 23" Name="玄武">
<BossMon MonID="501" Hp="25000" Lv="105" NewSeIdxs="2 1 104 99 58 64" /> <BossMon MonID="501" Hp="25000" Lv="105" NewSeIdxs="2 1 104 99 58 64" />
</Boss> </Boss>
</Bosses> </Bosses>