refactor(fight): 重构战斗准备逻辑并优化战斗启动流程 将 ReadyFight 方法拆分为多个职责清晰的子方法: - buildFightStartInfo: 构建战斗初始信息 - checkBothPlayersReady: 检查PVP双方是否就绪 - handleNPCFightSpecial: 处理NPC战斗特殊逻辑(如可捕捉标记) - startBattle: 统一启动战斗流程 同时修复部分逻辑顺序问题,增强代码可读性和扩展性。 feat(fight): 新增精灵王挑战协议支持 增加 StartPetWarInboundInfo 结构体用于接收精灵王挑战请求, 为后续实现相关功能提供基础。 fix(effect): 修正多个技能效果数值引用错误 - effect_37: 技能威力计算使用正确参数索引 - effect_50: 固定减伤比例调整为除以2 - effect_65: 正确比较技能分类类型 - effect_68: 致死保护改为锁定剩余1点生命值 - effect_77: 回复目标由敌方改为己方 - effect_93: 固定伤害值直接取参数 refactor(effect): 移除冗余效果类文件 删除 effect_133.go 和 effect_90.go 文件,其功能已被统一条件伤害和倍率系统取代; 移除 effect_74.go、effect_75.go 中重复的状态随机施加逻辑。 refactor(effect): 更新能力操作枚举命名一致性 重命名 AbilityOpType 枚举项名称,去除前缀,提升语义清晰度: - AbilityOpStealStrengthen → StealStrengthen - AbilityOpReverse → Reverse - AbilityOpBounceWeaken → BounceWeaken chore(fight): 完善 BattlePetEntity 属性初始化逻辑 在创建 BattlePetEntity 时即设置 PType,避免后续多次查询 PetMAP; 移除 Type() 方法中的冗余配置查找逻辑。 fix(skill): 确保必中技能不参与命中率计算 在 AttackTimeC 方法中添加 return 防止必中技能继续执行命中率公式计算。 refactor(fight): 调整战斗回合结束逻辑 进入新回合时允许玩家更换精灵,并提前跳出循环防止多余处理。 style(effect): 更正拼写及变量命名风格 修改 BaseSataus.Switch 方法签名中的参数命名; 更正 Effect58 中 can 字段首字母大写;
232 lines
6.6 KiB
Go
232 lines
6.6 KiB
Go
package fight
|
||
|
||
import (
|
||
"blazing/common/data/xmlres"
|
||
"blazing/cool"
|
||
"blazing/logic/service/common"
|
||
"blazing/logic/service/fight/action"
|
||
"blazing/logic/service/fight/info"
|
||
"blazing/logic/service/fight/input"
|
||
"blazing/logic/service/player"
|
||
"context"
|
||
"log"
|
||
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
"github.com/jinzhu/copier"
|
||
"github.com/panjf2000/ants/v2"
|
||
)
|
||
|
||
// Compare 比较两个1v1战斗动作的执行优先级(核心逻辑)
|
||
func (f *FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.BattleActionI) {
|
||
// 动作本身的优先级比较
|
||
p1 := b.Priority() - a.Priority()
|
||
if p1 > 0 { // 对手优先级更高
|
||
return b, a
|
||
} else if p1 < 0 {
|
||
return a, b
|
||
}
|
||
|
||
return a, b // 速度相同时,发起方优先
|
||
}
|
||
|
||
// 玩家逃跑/无响应/掉线
|
||
func (f *FightC) Over(c common.PlayerI, res info.EnumBattleOverReason) {
|
||
if f.closefight {
|
||
cool.Loger.Debug(context.Background(), " 战斗chan已关闭")
|
||
return
|
||
}
|
||
// case *action.EscapeAction:
|
||
// f.FightOverInfo.WinnerId = b2.GetPlayerID() //对方胜利
|
||
// f.FightOverInfo.Reason = a.Reason
|
||
|
||
// f.closefight = true
|
||
// ret := &action.EscapeAction{
|
||
// BaseAction: action.NewBaseAction(c.GetInfo().UserID),
|
||
// Reason: res,
|
||
// }
|
||
|
||
f.overl.Do(func() {
|
||
f.Reason = res
|
||
f.WinnerId = f.GetInputByPlayer(c, true).UserID
|
||
close(f.quit)
|
||
|
||
})
|
||
|
||
}
|
||
|
||
// 切换精灵 主动和被驱逐
|
||
func (f *FightC) ChangePet(c common.PlayerI, id uint32) {
|
||
|
||
if f.closefight {
|
||
cool.Loger.Debug(context.Background(), " 战斗chan已关闭")
|
||
return
|
||
}
|
||
|
||
selfinput := f.GetInputByPlayer(c, false)
|
||
InitAttackValue := *selfinput.AttackValue
|
||
oldpet := selfinput.CurrentPet
|
||
ret := &action.ActiveSwitchAction{
|
||
BaseAction: action.NewBaseAction(c.GetInfo().UserID),
|
||
}
|
||
selfinput.CurrentPet, ret.Reason = selfinput.GetPet(id)
|
||
|
||
f.Switch = append(f.Switch, ret)
|
||
selfinput.InitAttackValue() //切换精灵消除能力提升
|
||
//这时候精灵已经切换过了,可以直接给新精灵加效果
|
||
|
||
f.Broadcast(func(ff *input.Input) {
|
||
|
||
ff.Exec(func(t input.Effect) bool {
|
||
|
||
t.Switch(selfinput, InitAttackValue, oldpet)
|
||
|
||
return true
|
||
})
|
||
})
|
||
|
||
f.Broadcast(func(ff *input.Input) { //先给自身广播
|
||
if ff.Player.GetInfo().UserID == c.GetInfo().UserID {
|
||
ff.Player.SendChangePet(ret.Reason)
|
||
}
|
||
|
||
})
|
||
|
||
f.actionChan <- ret
|
||
}
|
||
|
||
// 玩家使用技能
|
||
func (f *FightC) UseSkill(c common.PlayerI, id int32) {
|
||
if f.closefight {
|
||
cool.Loger.Debug(context.Background(), " 战斗chan已关闭")
|
||
return
|
||
}
|
||
ret := &action.SelectSkillAction{
|
||
BaseAction: action.NewBaseAction(c.GetInfo().UserID),
|
||
}
|
||
if f.GetInputByPlayer(c, false).CurrentPet == nil {
|
||
return
|
||
}
|
||
if f.GetInputByPlayer(c, false).CurrentPet.Info.Hp <= 0 {
|
||
return
|
||
}
|
||
for _, v := range f.GetInputByPlayer(c, false).CurrentPet.Skills {
|
||
|
||
if v != nil && v.ID == int(id) {
|
||
ret.SkillEntity = v
|
||
break
|
||
}
|
||
|
||
}
|
||
f.actionChan <- ret
|
||
}
|
||
|
||
// 玩家使用技能
|
||
func (f *FightC) Capture(c common.PlayerI, id uint32) {
|
||
if f.closefight {
|
||
cool.Loger.Debug(context.Background(), " 战斗chan已关闭")
|
||
return
|
||
}
|
||
f.actionChan <- &action.UseItemAction{BaseAction: action.NewBaseAction(c.GetInfo().UserID), ItemID: id}
|
||
}
|
||
|
||
func (f *FightC) UseItem(c common.PlayerI, cacthid, itemid uint32) {
|
||
if f.closefight {
|
||
cool.Loger.Debug(context.Background(), " 战斗chan已关闭")
|
||
return
|
||
}
|
||
f.actionChan <- &action.UseItemAction{BaseAction: action.NewBaseAction(c.GetInfo().UserID), ItemID: itemid, CacthTime: cacthid}
|
||
}
|
||
|
||
// ReadyFight 处理玩家战斗准备逻辑,当满足条件时启动战斗循环
|
||
func (f *FightC) ReadyFight(c common.PlayerI) {
|
||
// 1. 构建战斗开始信息(整理双方初始宠物信息)
|
||
fightStartInfo := f.buildFightStartInfo()
|
||
|
||
// 2. 标记当前玩家已准备完成
|
||
input := f.GetInputByPlayer(c, false)
|
||
input.Finished = true
|
||
|
||
// 3. 根据战斗类型判断是否满足战斗启动条件,满足则启动
|
||
switch f.Info.Status {
|
||
case info.BattleStatus.FIGHT_WITH_PLAYER: // PVP战斗:需双方都准备完成
|
||
if f.checkBothPlayersReady(c) {
|
||
f.startBattle(fightStartInfo)
|
||
}
|
||
case info.BattleStatus.FIGHT_WITH_BOSS: // BOSS战:单方准备完成即可启动
|
||
f.startBattle(fightStartInfo)
|
||
case info.BattleStatus.FIGHT_WITH_NPC: // NPC/野怪战斗:处理捕捉相关逻辑后启动
|
||
f.handleNPCFightSpecial(&fightStartInfo)
|
||
f.startBattle(fightStartInfo)
|
||
}
|
||
}
|
||
|
||
// buildFightStartInfo 构建战斗开始时需要发送给双方的信息
|
||
func (f *FightC) buildFightStartInfo() info.FightStartOutboundInfo {
|
||
var startInfo info.FightStartOutboundInfo
|
||
|
||
// 复制双方初始宠物信息(取列表第一个宠物)
|
||
if len(f.Info.OurPetList) > 0 {
|
||
_ = copier.Copy(&startInfo.Info1, &f.Info.OurPetList[0])
|
||
startInfo.Info1.UserID = f.Info.OurInfo.UserID
|
||
}
|
||
if len(f.Info.OpponentPetList) > 0 {
|
||
_ = copier.Copy(&startInfo.Info2, &f.Info.OpponentPetList[0])
|
||
startInfo.Info2.UserID = f.Info.OpponentInfo.UserID
|
||
}
|
||
|
||
return startInfo
|
||
}
|
||
|
||
// checkBothPlayersReady 检查PVP战斗中双方是否都已准备完成
|
||
// 参数c为当前准备的玩家,返回true表示双方均准备完成
|
||
func (f *FightC) checkBothPlayersReady(currentPlayer common.PlayerI) bool {
|
||
// 这里的第二个参数true含义需结合业务确认(推测为"检查对手"),建议用常量替代
|
||
opponentInput := f.GetInputByPlayer(currentPlayer, true)
|
||
return opponentInput.Finished
|
||
}
|
||
|
||
// handleNPCFightSpecial 处理NPC战斗的特殊逻辑(如可捕捉标记)
|
||
func (f *FightC) handleNPCFightSpecial(startInfo *info.FightStartOutboundInfo) {
|
||
// 检查野怪是否可捕捉(根据宠物ID获取捕捉率)
|
||
if len(f.Info.OpponentPetList) == 0 {
|
||
return
|
||
}
|
||
npcPetID := int(f.Info.OpponentPetList[0].ID)
|
||
petCfg, ok := xmlres.PetMAP[npcPetID]
|
||
if !ok {
|
||
// log.Error(context.Background(), "NPC宠物配置不存在", "petID", npcPetID)
|
||
return
|
||
}
|
||
|
||
catchRate := gconv.Int(petCfg.CatchRate)
|
||
if catchRate > 0 {
|
||
startInfo.Info2.Catchable = 1 // 标记为可捕捉
|
||
// 标记AI对手允许被捕捉(类型断言确保安全)
|
||
if oppAI, ok := f.Opp.Player.(*player.AI_player); ok {
|
||
oppAI.CanCapture = true
|
||
}
|
||
}
|
||
}
|
||
|
||
// startBattle 启动战斗核心逻辑:提交战斗循环任务并通知双方
|
||
func (f *FightC) startBattle(startInfo info.FightStartOutboundInfo) {
|
||
// 提交战斗循环到战斗池(处理战斗池容量问题)
|
||
if err := Fightpool.Submit(f.battleLoop); err != nil {
|
||
log.Panic(context.Background(), "战斗循环提交失败", "error", err)
|
||
}
|
||
|
||
// 通知双方玩家准备完成,即将开始战斗
|
||
f.Our.Player.SendReadyToFightInfo(startInfo)
|
||
f.Opp.Player.SendReadyToFightInfo(startInfo)
|
||
|
||
// 标记战斗已启动(原注释逻辑)
|
||
// f.running = true
|
||
}
|
||
|
||
var Fightpool *ants.Pool
|
||
|
||
func init() {
|
||
Fightpool, _ = ants.NewPool(-1)
|
||
//defer p.Release()
|
||
}
|