Files
bl/logic/service/fight/loop.go

409 lines
10 KiB
Go
Raw Normal View History

package fight
import (
"blazing/common/data/xmlres"
"blazing/common/utils"
2025-11-15 23:02:46 +00:00
"blazing/cool"
"context"
2025-11-15 22:17:43 +00:00
"blazing/logic/service/common"
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/player"
2025-11-11 05:54:24 +00:00
"fmt"
"time"
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/util/gconv"
)
func (f *FightC) battleLoop() {
f.actionChan = make(chan action.BattleActionI, 10)
2025-11-15 23:02:46 +00:00
//fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime)
ourID := f.Our.Player.GetInfo().UserID
oppID := f.Opp.Player.GetInfo().UserID
//fmt.Println("开始收集玩家动作", waitr)
for !f.closefight {
f.Round++
2025-11-15 23:02:46 +00:00
//fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round)
// AI自动技能
actions := f.collectPlayerActions(ourID, oppID)
if f.closefight {
break
}
println("进入回合")
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
})
2025-11-15 23:02:46 +00:00
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 = utils.Min(ff.Player.GetInfo().PetList[j].MaxHp, ff.AllPet[i].Info.Hp)
2025-11-15 23:02:46 +00:00
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)
2025-11-18 22:16:55 +00:00
ff.Player.QuitFight()
2025-11-18 20:52:04 +00:00
//待退出玩家战斗状态
})
// 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
``` refactor(socket): 移除未使用的网络相关导入和注释掉的 RST 攻击检测逻辑 移除了 `ServerEvent.go` 中未使用的 `net` 和 `strings` 包导入。同时, 将原有的 RST 攻击检测及防护逻辑代码注释掉,便于后续重新设计或彻底删除。 fix(logic): 调整 fight pool 初始化与释放策略 将 `fight/action.go` 中的 `Fightpool` 类型从 `*ants.MultiPool` 改为 `*ants.Pool`,并调整其初始化方式为 `NewPool(-1)` 以适应动态扩容。 此外,在 `main.go` 中将 `ReleaseTimeout` 参数由 100 调整为 0, 确保立即清理超时任务。 feat(fight): 优化战斗输入状态重置逻辑 在 `fight/action.go` 的 `ReadyFight` 方法中提前设置 `GetInputByPlayer(c, false).Finished = true`,避免重复赋值。 同时更新了状态睡眠效果的处理流程,并简化了输入模块的状态缓存机制, 移除了冗余的 `Initeffectcache` 函数调用及相关逻辑。 perf(fight): 动态计算玩家动作等待时间 在 `loop.go` 的 `collectPlayerActions` 方法中, 将固定超时时间替换为基于 `waittime` 的动态等待时间计算公式, 提高响应灵活性。同时修复通道关闭判断条件以增强稳定性。 refactor(fight): 更新技能效果解析和状态持续逻辑 修改 `input.go` 中 `GenSataus` 方法以正确初始化状态数组; 在 `Turn.go` 中重构 `Turn_End` 方法内的执行逻辑, 确保仅在我方后手时增加回合数,提升战斗流程准确性。 chore(service): 删除废弃文件 SocketHandler_Tomee.go 完全移除已弃用的 `SocketHandler_Tomee.go` 文件及其全部内容, 减少项目冗余代码。 ```
2025-11-13 05:05:05 +08:00
timeout := time.After(waitr)
for len(actions) < 2 {
``` refactor(socket): 移除未使用的网络相关导入和注释掉的 RST 攻击检测逻辑 移除了 `ServerEvent.go` 中未使用的 `net` 和 `strings` 包导入。同时, 将原有的 RST 攻击检测及防护逻辑代码注释掉,便于后续重新设计或彻底删除。 fix(logic): 调整 fight pool 初始化与释放策略 将 `fight/action.go` 中的 `Fightpool` 类型从 `*ants.MultiPool` 改为 `*ants.Pool`,并调整其初始化方式为 `NewPool(-1)` 以适应动态扩容。 此外,在 `main.go` 中将 `ReleaseTimeout` 参数由 100 调整为 0, 确保立即清理超时任务。 feat(fight): 优化战斗输入状态重置逻辑 在 `fight/action.go` 的 `ReadyFight` 方法中提前设置 `GetInputByPlayer(c, false).Finished = true`,避免重复赋值。 同时更新了状态睡眠效果的处理流程,并简化了输入模块的状态缓存机制, 移除了冗余的 `Initeffectcache` 函数调用及相关逻辑。 perf(fight): 动态计算玩家动作等待时间 在 `loop.go` 的 `collectPlayerActions` 方法中, 将固定超时时间替换为基于 `waittime` 的动态等待时间计算公式, 提高响应灵活性。同时修复通道关闭判断条件以增强稳定性。 refactor(fight): 更新技能效果解析和状态持续逻辑 修改 `input.go` 中 `GenSataus` 方法以正确初始化状态数组; 在 `Turn.go` 中重构 `Turn_End` 方法内的执行逻辑, 确保仅在我方后手时增加回合数,提升战斗流程准确性。 chore(service): 删除废弃文件 SocketHandler_Tomee.go 完全移除已弃用的 `SocketHandler_Tomee.go` 文件及其全部内容, 减少项目冗余代码。 ```
2025-11-13 05:05:05 +08:00
select {
case <-f.quit:
f.closefight = true
return actions
case paction, ok := <-f.actionChan:
``` refactor(socket): 移除未使用的网络相关导入和注释掉的 RST 攻击检测逻辑 移除了 `ServerEvent.go` 中未使用的 `net` 和 `strings` 包导入。同时, 将原有的 RST 攻击检测及防护逻辑代码注释掉,便于后续重新设计或彻底删除。 fix(logic): 调整 fight pool 初始化与释放策略 将 `fight/action.go` 中的 `Fightpool` 类型从 `*ants.MultiPool` 改为 `*ants.Pool`,并调整其初始化方式为 `NewPool(-1)` 以适应动态扩容。 此外,在 `main.go` 中将 `ReleaseTimeout` 参数由 100 调整为 0, 确保立即清理超时任务。 feat(fight): 优化战斗输入状态重置逻辑 在 `fight/action.go` 的 `ReadyFight` 方法中提前设置 `GetInputByPlayer(c, false).Finished = true`,避免重复赋值。 同时更新了状态睡眠效果的处理流程,并简化了输入模块的状态缓存机制, 移除了冗余的 `Initeffectcache` 函数调用及相关逻辑。 perf(fight): 动态计算玩家动作等待时间 在 `loop.go` 的 `collectPlayerActions` 方法中, 将固定超时时间替换为基于 `waittime` 的动态等待时间计算公式, 提高响应灵活性。同时修复通道关闭判断条件以增强稳定性。 refactor(fight): 更新技能效果解析和状态持续逻辑 修改 `input.go` 中 `GenSataus` 方法以正确初始化状态数组; 在 `Turn.go` 中重构 `Turn_End` 方法内的执行逻辑, 确保仅在我方后手时增加回合数,提升战斗流程准确性。 chore(service): 删除废弃文件 SocketHandler_Tomee.go 完全移除已弃用的 `SocketHandler_Tomee.go` 文件及其全部内容, 减少项目冗余代码。 ```
2025-11-13 05:05:05 +08:00
if !ok {
f.closefight = true
return actions
}
if paction == nil {
continue
}
pid := paction.GetPlayerID()
if pid != ourID && pid != oppID {
continue
}
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && pid != 0 {
f.Opp.GetAction()
println("开始执行NPC动作")
}
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
f.Broadcast(func(ff *input.Input) {
ff.Exec(func(t input.Effect) bool {
t.SwitchOut(selfinput)
return true
})
})
} else {
selfinput.CanChange = 2
}
// oldpet := selfinput.CurrentPet
// InitAttackValue := *selfinput.AttackValue
selfinput.CurrentPet, ret.Reason = selfinput.GetPet(ret.Cid)
selfinput.Player.SendPackCmd(2407, &ret.Reason)
f.Switch[selfinput.UserID] = ret
selfinput.InitAttackValue() //切换精灵消除能力提升
//这时候精灵已经切换过了,可以直接给新精灵加效果
f.Broadcast(func(ff *input.Input) {
ff.Exec(func(t input.Effect) bool {
t.SwitchIn(selfinput)
return true
})
})
if selfinput.CanChange == 2 {
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
}
}
} else {
if selfinput.CurrentPet.Info.Hp <= 0 { //0血执行非切换动作
//todo 记录异常操作
continue
}
if selfinput.CanChange == 1 { //非被动死亡情况下,不能执行额外动作,0允许切2是死亡可以额外动作
continue
}
// if selfinput.CanChange == 2 {
// selfinput.CanChange--
// }
}
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.Logger.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.Logger.Debug(context.Background(), "某方未选择动作,自动跳过结算")
return
}
// 动作优先级排序
b1, b2 := f.Compare(p1Action, p2Action)
switch actionType := b1.(type) {
case *action.ActiveSwitchAction:
f.handleActiveSwitchAction(actionType, b2)
case *action.UseItemAction:
f.handleUseItemAction(actionType, b2)
default:
f.handleSkillActions(b1, b2)
}
}
// handleActiveSwitchAction 处理主动切换精灵动作
func (f *FightC) handleActiveSwitchAction(switchAction *action.ActiveSwitchAction, otherAction action.BattleActionI) {
input := f.GetInputByAction(switchAction, false)
if input.CurrentPet.Info.Hp <= 0 {
input.CurrentPet.Info.Hp = 1
}
if skillAction, ok := otherAction.(*action.SelectSkillAction); ok {
if skillAction.SkillEntity != nil && skillAction.CD != nil {
f.waittime = *skillAction.CD
}
f.enterturn(skillAction, nil)
} else {
f.enterturn(nil, nil)
}
}
// handleUseItemAction 处理使用道具动作
func (f *FightC) handleUseItemAction(itemAction *action.UseItemAction, otherAction action.BattleActionI) {
f.handleItemAction(itemAction)
input := f.GetInputByAction(itemAction, false)
if input.CurrentPet.Info.Hp <= 0 {
input.CurrentPet.Info.Hp = 1
}
if skillAction, ok := otherAction.(*action.SelectSkillAction); ok {
if skillAction.SkillEntity != nil && skillAction.CD != nil {
f.waittime = *skillAction.CD
}
f.enterturn(skillAction, nil)
} else {
if otherItemAction, ok := otherAction.(*action.UseItemAction); ok {
f.handleItemAction(otherItemAction)
}
f.enterturn(nil, nil)
}
}
// 使用道具的逻辑封装
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.UPDATE(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
}