Files
bl/logic/service/fight/action.go
2025-11-18 20:52:04 +00:00

236 lines
6.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.SendPackCmd(2407, 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.ReadyInfo.OurPetList) > 0 {
_ = copier.Copy(&startInfo.Info1, &f.ReadyInfo.OurPetList[0])
startInfo.Info1.UserID = f.ReadyInfo.OurInfo.UserID
}
if len(f.ReadyInfo.OpponentPetList) > 0 {
_ = copier.Copy(&startInfo.Info2, &f.ReadyInfo.OpponentPetList[0])
startInfo.Info2.UserID = f.ReadyInfo.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.ReadyInfo.OpponentPetList) == 0 {
return
}
npcPetID := int(f.ReadyInfo.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.Broadcast(func(ff *input.Input) {
// 通知双方玩家准备完成,即将开始战斗
ff.Player.SendPackCmd(2504, startInfo)
})
// 标记战斗已启动(原注释逻辑)
// f.running = true
}
var Fightpool *ants.Pool
func init() {
Fightpool, _ = ants.NewPool(-1)
//defer p.Release()
}