package fight import ( "blazing/logic/service/common" "blazing/logic/service/fight/action" "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" "blazing/modules/player/model" ) // Compare 比较两个战斗动作的执行优先级 func (f *FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.BattleActionI) { if a == nil { return b, a } if b == nil { return a, b } // 动作本身的优先级比较 p1 := b.Priority() - a.Priority() if p1 > 0 { // 对手优先级更高 return b, a } else if p1 < 0 { return a, b } if speedA, speedB := f.actionSpeed(a), f.actionSpeed(b); speedA.Cmp(speedB) != 0 { if speedA.Cmp(speedB) > 0 { return a, b } return b, a } if a.GetActorIndex() != b.GetActorIndex() { if a.GetActorIndex() < b.GetActorIndex() { return a, b } return b, a } return a, b // 速度相同时,发起方优先 } func (f *FightC) openActionWindow() { f.actionMu.Lock() f.acceptActions = true f.pendingActions = f.pendingActions[:0] f.actionRound.Store(uint32(f.Round)) f.actionMu.Unlock() } func (f *FightC) closeActionWindow() { f.actionMu.Lock() f.acceptActions = false f.pendingActions = f.pendingActions[:0] f.actionRound.Store(0) f.actionMu.Unlock() } func (f *FightC) submitAction(act action.BattleActionI) { if act == nil || f.closefight { return } round := f.actionRound.Load() if round == 0 { return } act.SetRound(round) f.actionMu.Lock() if !f.acceptActions || act.GetRound() != f.actionRound.Load() { f.actionMu.Unlock() return } replaceIndex := -1 for i, pending := range f.pendingActions { if pending == nil || actionSlotKeyFromAction(pending) != actionSlotKeyFromAction(act) { continue } replaceIndex = i break } if replaceIndex >= 0 { f.pendingActions[replaceIndex] = act } else { f.pendingActions = append(f.pendingActions, act) } notify := f.actionNotify f.actionMu.Unlock() if notify == nil { return } select { case notify <- struct{}{}: default: } } func (f *FightC) nextAction() action.BattleActionI { f.actionMu.Lock() if len(f.pendingActions) == 0 { f.actionMu.Unlock() return nil } act := f.pendingActions[0] copy(f.pendingActions, f.pendingActions[1:]) f.pendingActions = f.pendingActions[:len(f.pendingActions)-1] hasMore := len(f.pendingActions) > 0 notify := f.actionNotify f.actionMu.Unlock() if hasMore && notify != nil { select { case notify <- struct{}{}: default: } } return act } // 玩家逃跑/无响应/掉线 func (f *FightC) Over(c common.PlayerI, res model.EnumBattleOverReason) { if f.closefight { return } if f.Info.Status != info.BattleMode.FIGHT_WITH_NPC && res == model.BattleOverReason.PlayerEscape { 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 if f.GetInputByPlayer(c, true) != nil { f.WinnerId = f.GetInputByPlayer(c, true).UserID } f.FightOverInfo.Reason = f.Reason f.FightOverInfo.WinnerId = f.WinnerId f.closefight = true close(f.quit) }) } // 切换精灵 主动和被驱逐 func (f *FightC) ChangePet(c common.PlayerI, id uint32) { f.ChangePetAt(c, id, 0) } func (f *FightC) ChangePetAt(c common.PlayerI, id uint32, actorIndex int) { if f.closefight { return } self := f.getInputByUserID(c.GetInfo().UserID, actorIndex, false) if self == nil { return } ii, _ := self.GetPet(id) if ii == nil { //无法切换不允许切换的精灵 return } //todo 待实现无法切精灵的情况 ret := &action.ActiveSwitchAction{ BaseAction: action.NewBaseAction(c.GetInfo().UserID), Cid: id, } ret.ActorIndex = actorIndex f.submitAction(ret) } // 玩家使用技能 func (f *FightC) UseSkill(c common.PlayerI, id uint32) { f.UseSkillAt(c, id, 0, 0) } func (f *FightC) UseSkillAt(c common.PlayerI, id uint32, actorIndex, targetIndex int) { if f.closefight { return } ret := &action.SelectSkillAction{ BaseAction: action.NewBaseAction(c.GetInfo().UserID), } ret.ActorIndex = actorIndex self := f.getInputByUserID(c.GetInfo().UserID, actorIndex, false) if self == nil { return } currentPet := self.PrimaryCurPet() if currentPet == nil { return } if currentPet.Info.Hp <= 0 { return } // t, ok := f.GetInputByPlayer(c, false).CurPet[0].Skills[id] // if ok { // ret.SkillEntity = t // } for _, v := range currentPet.Skills { if v.XML.ID == int(id) { ret.SkillEntity = v break } } ret.TargetIndex = normalizeSkillTargetIndex(actorIndex, targetIndex, ret.SkillEntity) f.submitAction(ret) } func normalizeSkillTargetIndex(actorIndex, targetIndex int, skill *info.SkillEntity) int { if skill == nil { return targetIndex } // 约定:非负目标位表示敌方;负值 -(index+1) 表示同侧(自己/队友) if _, targetIsOpposite := DecodeTargetIndex(targetIndex); !targetIsOpposite { return targetIndex } // GBTL.AtkType: 0=所有人 1=仅己方 2=仅对方 3=仅自己(默认2) switch skill.XML.AtkType { case 1, 3: // 旧协议未传目标时,己方类技能默认作用自己;新协议可通过负编码显式指定队友。 return EncodeTargetIndex(actorIndex, false) default: return targetIndex } } // 玩家使用技能 func (f *FightC) Capture(c common.PlayerI, id uint32) { if f.closefight { return } f.submitAction(&action.UseItemAction{BaseAction: action.NewBaseAction(c.GetInfo().UserID), ItemID: id}) } func (f *FightC) c(c common.PlayerI, cacthid, itemid uint32) { f.UseItemAt(c, cacthid, itemid, 0, 0) } func (f *FightC) UseItem(c common.PlayerI, cacthid, itemid uint32) { f.UseItemAt(c, cacthid, itemid, 0, 0) } func (f *FightC) UseItemAt(c common.PlayerI, cacthid, itemid uint32, actorIndex, targetIndex int) { if f.closefight { return } if f.Info.Mode == info.BattleMode.PET_MELEE { go f.UseSkill(c, 0) return } actionInfo := &action.UseItemAction{BaseAction: action.NewBaseAction(c.GetInfo().UserID), ItemID: itemid, CacthTime: cacthid} actionInfo.ActorIndex = actorIndex actionInfo.TargetIndex = targetIndex f.submitAction(actionInfo) } // ReadyFight 处理玩家战斗准备逻辑,当满足条件时启动战斗循环 func (f *FightC) ReadyFight(c common.PlayerI) { if f.LegacyGroupProtocol { input := f.GetInputByPlayer(c, false) if input == nil { return } input.Finished = true if f.checkBothPlayersReady(c) { f.startLegacyGroupBattle() } return } f.Broadcast(func(ff *input.Input) { ff.Player.SendPackCmd(2404, &info.S2C_2404{UserID: c.GetInfo().UserID}) }) // 2. 标记当前玩家已准备完成 input := f.GetInputByPlayer(c, false) input.Finished = true if f.checkBothPlayersReady(c) { f.startBattle(f.FightStartOutboundInfo) } } // buildFightStartInfo 构建战斗开始时需要发送给双方的信息 func (f *FightC) buildFightStartInfo() info.FightStartOutboundInfo { startInfo := info.FightStartOutboundInfo{} ourInfos := f.collectFightPetInfos(f.Our) oppInfos := f.collectFightPetInfos(f.Opp) startInfo.Info1 = append(startInfo.Info1, ourInfos...) startInfo.Info2 = append(startInfo.Info2, oppInfos...) startInfo.Info1Len = uint32(len(startInfo.Info1)) startInfo.Info2Len = uint32(len(startInfo.Info2)) return startInfo } func (f *FightC) collectFightPetInfos(inputs []*input.Input) []info.FightPetInfo { infos := make([]info.FightPetInfo, 0, len(inputs)) for actorIndex, fighter := range inputs { if fighter == nil || fighter.Player == nil { continue } currentPet := fighter.PrimaryCurPet() if currentPet == nil { continue } fightInfo := info.FightPetInfo{ UserID: fighter.Player.GetInfo().UserID, ActorIndex: uint32(actorIndex), ControllerUserID: currentPet.ControllerUserID, ID: currentPet.Info.ID, Name: currentPet.Info.Name, CatchTime: currentPet.Info.CatchTime, Hp: currentPet.Info.Hp, MaxHp: currentPet.Info.MaxHp, Level: currentPet.Info.Level, Catchable: uint32(fighter.CanCapture), } if fighter.AttackValue != nil { fightInfo.Prop = fighter.AttackValue.Prop } infos = append(infos, fightInfo) } return infos } // checkBothPlayersReady 检查PVP战斗中双方是否都已准备完成 // 参数c为当前准备的玩家,返回true表示双方均准备完成 func (f *FightC) checkBothPlayersReady(currentPlayer common.PlayerI) bool { // 这里的第二个参数true含义需结合业务确认(推测为"检查对手"),建议用常量替代 opponentInput := f.GetInputByPlayer(currentPlayer, true) return opponentInput.Finished } // startBattle 启动战斗核心逻辑:提交战斗循环任务并通知双方 func (f *FightC) startBattle(startInfo info.FightStartOutboundInfo) { f.startl.Do(func() { // 提交战斗循环到战斗池(处理战斗池容量问题) // if err := Fightpool.Invoke(f); err != nil { // log.Panic(context.Background(), "战斗循环提交失败", "error", err) // } go f.battleLoop() // 向双方广播战斗开始信息 if f.LegacyGroupProtocol { f.Broadcast(func(ff *input.Input) { f.sendLegacyGroupStart(ff.Player) }) } else { f.Broadcast(func(ff *input.Input) { ff.Player.SendPackCmd(2504, &startInfo) }) } }) } func (f *FightC) startLegacyGroupBattle() { f.startl.Do(func() { go f.battleLoop() f.Broadcast(func(ff *input.Input) { f.sendLegacyGroupStart(ff.Player) }) }) }