将 `Clientmap` 从普通 map 改为 `sync.Map`,提升并发安全性。新增 `addClient` 和 `getClient` 方法封装存取逻辑,并在多处调用点进行了替换。 fix(fight): 修复战斗逻辑中技能ID与攻击时间字段引用错误 将 `attacker.AttackValue.SkillID` 和 `attacker.AttackValue.AttackTime` 的访问方式修正为正确的字段路径。 refactor(fight): 调整战斗结束信息处理流程 合并 `FightOverInfo` 结构到 `FightC` 中,简化广播发送逻辑,统一通过 `f.FightOverInfo` 发送战斗结果。 refactor(effect): 修改效果叠加判断逻辑并增强健壮性 更新效果节点比较方法,增加参数匹配检查以支持更精确的效果识别;同时添加 `equalInts` 工具函数用于数组内容对比。
209 lines
5.3 KiB
Go
209 lines
5.3 KiB
Go
package fight
|
|
|
|
import (
|
|
"blazing/logic/service/common"
|
|
"blazing/logic/service/fight/action"
|
|
"blazing/logic/service/fight/info"
|
|
"blazing/logic/service/fight/input"
|
|
"blazing/logic/service/player"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
func (f *FightC) battleLoop() {
|
|
f.actionChan = make(chan action.BattleActionI, 2)
|
|
fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime)
|
|
|
|
ourID := f.Our.Player.GetInfo().UserID
|
|
oppID := f.Opp.Player.GetInfo().UserID
|
|
|
|
for {
|
|
if f.closefight {
|
|
f.Broadcast(func(ff *input.Input) {
|
|
//todo 将血量和技能pp传回enterturn
|
|
|
|
ff.Player.SendFightEndInfo(f.FightOverInfo)
|
|
|
|
})
|
|
close(f.actionChan)
|
|
break
|
|
}
|
|
|
|
f.Round++
|
|
fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round)
|
|
|
|
actions := f.collectPlayerActions(ourID, oppID)
|
|
if f.closefight {
|
|
break
|
|
}
|
|
|
|
f.resolveRound(actions[ourID], actions[oppID])
|
|
}
|
|
|
|
fmt.Println("战斗循环结束")
|
|
}
|
|
|
|
// 收集玩家动作(含超时判定)
|
|
func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.BattleActionI {
|
|
actions := make(map[uint32]action.BattleActionI)
|
|
timeout := time.After(60 * time.Second)
|
|
|
|
for len(actions) < 2 {
|
|
select {
|
|
case paction, ok := <-f.actionChan:
|
|
if !ok || f.closefight {
|
|
return actions
|
|
}
|
|
if paction == nil {
|
|
continue
|
|
}
|
|
|
|
pid := paction.GetPlayerID()
|
|
if pid != ourID && pid != oppID {
|
|
continue
|
|
}
|
|
|
|
// 避免重复提交
|
|
if _, exists := actions[pid]; exists {
|
|
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", pid)
|
|
continue
|
|
}
|
|
|
|
// 被动切换处理(不计入本回合)
|
|
if as, ok := paction.(*action.ActiveSwitchAction); ok {
|
|
if f.GetInputByAction(as, false).CanChange {
|
|
f.GetInputByAction(as, false).CanChange = false
|
|
continue
|
|
}
|
|
}
|
|
|
|
// AI自动技能
|
|
if pid != 0 && (f.Info.Status == info.BattleStatus.FIGHT_WITH_BOSS ||
|
|
f.Info.Status == info.BattleStatus.FIGHT_WITH_NPC) {
|
|
f.GetInputByAction(paction, true).GetAction(f.Our)
|
|
}
|
|
|
|
actions[pid] = paction
|
|
//fmt.Println("玩家执行动作:", pid, paction.Priority())
|
|
|
|
case <-timeout:
|
|
f.handleTimeout(ourID, oppID, actions)
|
|
return actions
|
|
}
|
|
}
|
|
return actions
|
|
}
|
|
|
|
// 超时处理逻辑
|
|
func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.BattleActionI) {
|
|
for _, pid := range []uint32{ourID, oppID} {
|
|
if _, exists := actions[pid]; exists {
|
|
continue
|
|
}
|
|
fmt.Printf("玩家%d 超时\n", pid)
|
|
player := f.GetInputByPlayer(f.getPlayerByID(pid), true).Player
|
|
if !f.GetInputByPlayer(f.getPlayerByID(pid), false).Finished {
|
|
fmt.Printf("玩家%d 加载超时\n", pid)
|
|
f.UseSkill(player, 0) //卡加载,给对方也一个action
|
|
}
|
|
f.Over(f.getPlayerByID(pid), info.BattleOverReason.PlayerOVerTime)
|
|
}
|
|
}
|
|
|
|
// 根据动作类型执行一回合结算
|
|
func (f *FightC) resolveRound(p1Action, p2Action action.BattleActionI) {
|
|
if p1Action == nil || p2Action == nil {
|
|
fmt.Println("某方未选择动作,自动跳过结算")
|
|
return
|
|
}
|
|
|
|
fmt.Println("开始结算回合")
|
|
|
|
// 动作优先级排序
|
|
b1, b2 := f.Compare(p1Action, p2Action)
|
|
|
|
switch a := b1.(type) {
|
|
|
|
case *action.EscapeAction:
|
|
f.FightOverInfo.WinnerId = b2.GetPlayerID() //对方胜利
|
|
f.FightOverInfo.Reason = a.Reason
|
|
|
|
f.closefight = true
|
|
|
|
case *action.ActiveSwitchAction:
|
|
if _, ok := b2.(*action.SelectSkillAction); ok {
|
|
f.enterturn(b2.(*action.SelectSkillAction), nil)
|
|
} else {
|
|
f.enterturn(nil, nil)
|
|
}
|
|
|
|
case *action.UseItemAction:
|
|
f.handleItemAction(a, b2)
|
|
|
|
default:
|
|
f.handleSkillActions(b1, b2)
|
|
}
|
|
}
|
|
|
|
// 使用道具的逻辑封装
|
|
func (f *FightC) handleItemAction(a *action.UseItemAction, other action.BattleActionI) {
|
|
switch {
|
|
case a.ItemID >= 30001 && a.ItemID <= 300010:
|
|
our, ok1 := f.Our.Player.(*player.Player)
|
|
opp, ok2 := f.Opp.Player.(*player.AI_player)
|
|
if ok1 && ok2 && opp.CanCapture {
|
|
ok, res := f.Our.Capture(f.Opp.CurrentPet, a.ItemID, -1)
|
|
if ok {
|
|
fmt.Println(res)
|
|
our.Service.PetAdd(*f.Opp.CurrentPet.Info)
|
|
our.CatchPetInfo(info.CatchMonsterOutboundInfo{
|
|
CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
|
|
PetId: uint32(f.Opp.CurrentPet.ID),
|
|
})
|
|
//f.WinnerId = 0 //捕捉成功不算胜利
|
|
f.Reason = 6
|
|
|
|
f.closefight = true
|
|
} else {
|
|
our.CatchPetInfo(info.CatchMonsterOutboundInfo{})
|
|
}
|
|
}
|
|
case a.ItemID == 300001:
|
|
fmt.Println("ItemID 是 300001")
|
|
default:
|
|
fmt.Println("ItemID 不在指定范围内")
|
|
}
|
|
|
|
if _, ok := other.(*action.SelectSkillAction); ok {
|
|
f.enterturn(other.(*action.SelectSkillAction), nil)
|
|
} else {
|
|
f.enterturn(nil, nil)
|
|
}
|
|
}
|
|
|
|
// 双方都是技能时的结算逻辑
|
|
func (f *FightC) handleSkillActions(a1, a2 action.BattleActionI) {
|
|
s1, _ := a1.(*action.SelectSkillAction)
|
|
s2, _ := a2.(*action.SelectSkillAction)
|
|
|
|
switch {
|
|
case s1 == nil || s1.SkillEntity == nil:
|
|
f.enterturn(s2, nil)
|
|
fmt.Println("1 空过 2玩家执行技能:", s2.PlayerID, s2.Info.ID)
|
|
case s2 == nil || s2.SkillEntity == nil:
|
|
f.enterturn(s1, nil)
|
|
fmt.Println("2 空过 玩家执行技能:", s1.PlayerID, s1.Info.ID)
|
|
default:
|
|
f.enterturn(s1, s2)
|
|
fmt.Println("玩家执行技能:", s1.PlayerID, s1.Info.ID, s2.PlayerID, s2.Info.ID)
|
|
}
|
|
}
|
|
|
|
// 根据玩家ID返回对应对象
|
|
func (f *FightC) getPlayerByID(id uint32) common.PlayerI {
|
|
if id == f.Our.Player.GetInfo().UserID {
|
|
return f.Our.Player
|
|
}
|
|
return f.Opp.Player
|
|
}
|