package fight import ( "blazing/common/data/xmlres" "blazing/cool" "context" "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" "github.com/alpacahq/alpacadecimal" "github.com/gogf/gf/v2/util/gconv" ) 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 !f.closefight { f.Round++ //fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round) actions := f.collectPlayerActions(ourID, oppID) if f.closefight { break } f.resolveRound(actions[ourID], actions[oppID]) } f.Broadcast(func(ff *input.Input) { //todo 将血量和技能pp传回enterturn ff.Exec(func(tt input.Effect) bool { tt.OnBattleEnd() tt.Alive(false) //将所有属性变化失效掉 return true }) if f.Info.Mode != info.BattleMode.PET_MELEE { //不是乱斗,传回血量 for i := 0; i < len(ff.AllPet); i++ { for j := 0; j < len(ff.Player.GetInfo().PetList); j++ { if ff.Player.GetInfo().PetList[j].CatchTime == ff.AllPet[i].Info.CatchTime { if ff.UserID == f.WinnerId { if ff.CurrentPet.Info.CatchTime == ff.Player.GetInfo().PetList[j].CatchTime { f.Winpet = &ff.Player.GetInfo().PetList[j] } } ff.Player.GetInfo().PetList[j].Hp = ff.AllPet[i].Info.Hp ff.Player.GetInfo().PetList[j].SkillList = ff.AllPet[i].Info.SkillList } } } } }) if f.Reason == info.BattleOverReason.Cacthok { f.WinnerId = f.ownerID f.Our.Player.(*player.Player).Service.Pet.PetAdd(&f.Opp.Player.GetInfo().PetList[0]) f.Our.Player.SendPackCmd(2409, &info.CatchMonsterOutboundInfo{ CatchTime: uint32(f.Opp.Player.GetInfo().PetList[0].CatchTime), PetId: uint32(f.Opp.CurrentPet.ID), }) //f.Reason = 0 //清空 } //f.Reason = info.BattleOverReason.PlayerCaptureSuccess //f.WinnerId = 0 //捕捉成功不算胜利 if f.callback != nil { f.callback(&f.FightOverInfo) //先执行回调,再执行返回信息,在回调内修改战斗判断 } //大乱斗,给个延迟 //<-time.After(1000) f.Broadcast(func(ff *input.Input) { ff.Player.SendPackCmd(2506, &f.FightOverInfo) ff.Player.QuitFight() //待退出玩家战斗状态 }) // close(f.actionChan) fmt.Println(f.ownerID, "战斗循环结束") close(f.over) } // 收集玩家动作(含超时判定) func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.BattleActionI { actions := make(map[uint32]action.BattleActionI) waitr := time.Duration(f.waittime)*time.Millisecond*10 + 60*time.Second //fmt.Println("开始收集玩家动作", waitr) timeout := time.After(waitr) for len(actions) < 2 { select { case <-f.quit: f.closefight = true return actions case paction, ok := <-f.actionChan: if !ok { f.closefight = true 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 // } selfinput := f.GetInputByAction(paction, false) if ret, ok := paction.(*action.ActiveSwitchAction); ok { //正常结束可以切换,以及死切后还能再切一次 if selfinput.CanChange == 0 { if selfinput.CurrentPet.Info.Hp > 0 { //非死亡切换 selfinput.CanChange = 1 } else { selfinput.CanChange = 2 } selfinput.CurrentPet, ret.Reason = selfinput.GetPet(ret.Cid) selfinput.Player.SendPackCmd(2407, &ret.Reason) InitAttackValue := *selfinput.AttackValue oldpet := selfinput.CurrentPet f.Switch[selfinput.UserID] = ret selfinput.InitAttackValue() //切换精灵消除能力提升 //这时候精灵已经切换过了,可以直接给新精灵加效果 f.Broadcast(func(ff *input.Input) { ff.Exec(func(t input.Effect) bool { t.Switch(selfinput, InitAttackValue, oldpet) return true }) }) if selfinput.CanChange == 2 { selfinput.CanChange = 0 continue } } } else { if selfinput.CurrentPet.Info.Hp <= 0 { //0血执行非切换动作 //todo 记录异常操作 continue } if selfinput.CanChange == 1 { //非被动死亡情况下,不能执行额外动作,0允许切,2是死亡,可以额外动作 continue } // if selfinput.CanChange == 2 { // 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 //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 } cool.Loger.Debug(context.Background(), "玩家超时", pid) f.Reason = info.BattleOverReason.PlayerOVerTime f.closefight = true //对方赢 if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC { f.WinnerId = f.GetInputByPlayer(f.getPlayerByID(pid), true).Player.GetInfo().UserID } } } // 根据动作类型执行一回合结算 func (f *FightC) resolveRound(p1Action, p2Action action.BattleActionI) { if p1Action == nil || p2Action == nil { cool.Loger.Debug(context.Background(), "某方未选择动作,自动跳过结算") return } // fmt.Println("开始结算回合") // 动作优先级排序 b1, b2 := f.Compare(p1Action, p2Action) switch a := b1.(type) { case *action.ActiveSwitchAction: if f.GetInputByAction(a, false).CurrentPet.Info.Hp <= 0 { f.GetInputByAction(a, false).CurrentPet.Info.Hp = 1 } if b2k, ok := b2.(*action.SelectSkillAction); ok { if b2k.SkillEntity != nil { if b2k.CD != nil { f.waittime = *b2k.CD } } f.enterturn(b2.(*action.SelectSkillAction), nil) } else { f.enterturn(nil, nil) } case *action.UseItemAction: f.handleItemAction(a) if f.GetInputByAction(a, false).CurrentPet.Info.Hp <= 0 { f.GetInputByAction(a, false).CurrentPet.Info.Hp = 1 } if b2k, ok := b2.(*action.SelectSkillAction); ok { if b2k.SkillEntity != nil { if b2k.CD != nil { f.waittime = *b2k.CD } } f.enterturn(b2.(*action.SelectSkillAction), nil) } else { if a1, ok := b2.(*action.UseItemAction); ok { f.handleItemAction(a1) } f.enterturn(nil, nil) } default: f.handleSkillActions(b1, b2) } } // 使用道具的逻辑封装 func (f *FightC) handleItemAction(a *action.UseItemAction) { item, ok := xmlres.ItemsMAP[int(a.ItemID)] if !ok { return } r := f.GetInputByAction(a, false).Player.(*player.Player).Service.Item.CheakItem(a.ItemID) if r < 1 { return } f.GetInputByAction(a, false).Player.(*player.Player).Service.Item.SubItem(a.ItemID, 1) switch { case gconv.Int(item.Bonus) != 0: if f.Opp.CanCapture > 0 { //可以捕捉 f.Opp.CurrentPet.CatchRate = f.Opp.CanCapture ok, _ := f.Our.Capture(f.Opp.CurrentPet, a.ItemID, -1) our := f.Our.Player.(*player.Player) if ok { f.Reason = info.BattleOverReason.Cacthok f.closefight = true } else { our.SendPack(common.NewTomeeHeader(2409, f.ownerID).Pack(&info.CatchMonsterOutboundInfo{})) } } case gconv.Int(item.HP) != 0: addhp := item.HP f.GetInputByAction(a, false).Exec(func(rr input.Effect) bool { rr.Heal_Pre(a, &addhp) return true }) f.GetInputByAction(a, false).Heal(f.GetInputByAction(a, false), a, alpacadecimal.NewFromInt(int64(addhp))) f.Broadcast(func(ff *input.Input) { ff.Player.SendPackCmd(2406, &info.UsePetIteminfo{ UserID: f.GetInputByAction(a, false).UserID, ChangeHp: int32(addhp), ItemID: uint32(item.ID), UserHp: uint32(f.GetInputByAction(a, false).CurrentPet.Info.Hp), }) }) case gconv.Int(item.PP) != 0: f.GetInputByAction(a, false).HealPP(item.PP) f.Broadcast(func(ff *input.Input) { ff.Player.SendPackCmd(2406, &info.UsePetIteminfo{ UserID: f.GetInputByAction(a, false).UserID, ItemID: uint32(item.ID), UserHp: uint32(f.GetInputByAction(a, false).CurrentPet.Info.Hp), }) }) default: fmt.Println(a.ItemID, "ItemID 不在指定范围内") } } // 双方都是技能时的结算逻辑 func (f *FightC) handleSkillActions(a1, a2 action.BattleActionI) { s1, _ := a1.(*action.SelectSkillAction) s2, _ := a2.(*action.SelectSkillAction) switch { case s1 == nil || s1.SkillEntity == nil: if s2.SkillEntity != nil { if s2.CD != nil { f.waittime = *s2.CD } } f.enterturn(s2, nil) // fmt.Println("1 空过 2玩家执行技能:", s2.PlayerID, s2.Info.ID) case s2 == nil || s2.SkillEntity == nil: if s1.SkillEntity != nil { if s1.CD != nil { f.waittime = *s1.CD } } f.enterturn(s1, nil) //fmt.Println("2 空过 玩家执行技能:", s1.PlayerID, s1.Info.ID) default: if s1.CD != nil { f.waittime = *s1.CD } if s2.CD != nil { f.waittime += *s2.CD } 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 }