Files
bl/logic/service/fight/action.go
2026-04-04 04:58:49 +08:00

303 lines
7.1 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/logic/service/common"
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/modules/player/model"
"github.com/jinzhu/copier"
)
// Compare 比较两个1v1战斗动作的执行优先级核心逻辑
func (*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 // 速度相同时,发起方优先
}
const maxPendingActionsPerPlayer = 2
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
}
count := 0
replaceIndex := -1
for i, pending := range f.pendingActions {
if pending == nil || pending.GetPlayerID() != act.GetPlayerID() {
continue
}
count++
replaceIndex = i
}
if count >= maxPendingActionsPerPlayer && 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
}
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
ret.TargetIndex = targetIndex
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
}
}
f.submitAction(ret)
}
// 玩家使用技能
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) 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) {
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 {
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
}
// 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()
// 向双方广播战斗开始信息
f.Broadcast(func(ff *input.Input) {
ff.Player.SendPackCmd(2504, &startInfo)
})
})
}