Files
bl/logic/service/fight/new.go
xinian 78a68148ce
Some checks failed
ci/woodpecker/push/my-first-workflow Pipeline failed
chore: update fight logic and effect implementations
2026-04-05 02:25:44 +08:00

283 lines
8.3 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/socket/errorcode"
"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"
"blazing/modules/player/model"
"time"
)
// NewFightSingleControllerN 创建 N 打战斗(单人控制多站位)。
// ourPetsBySlot/oppPetsBySlot 的每个元素代表一个站位携带的宠物列表。
func NewFightSingleControllerN(
ourController common.PlayerI,
oppController common.PlayerI,
ourPetsBySlot [][]model.PetInfo,
oppPetsBySlot [][]model.PetInfo,
fn func(model.FightOverInfo),
) (*FightC, errorcode.ErrorCode) {
if ourController == nil || oppController == nil {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
fightInfo := ourController.Getfightinfo()
ourInputs, err := buildSideInputsByController(ourController, ourPetsBySlot, fightInfo.Mode)
if err > 0 {
return nil, err
}
oppInputs, err := buildSideInputsByController(oppController, oppPetsBySlot, fightInfo.Mode)
if err > 0 {
return nil, err
}
return NewFightWithOptions(
WithFightInputs(ourInputs, oppInputs),
WithFightPlayersOnSide(
[]common.PlayerI{ourController},
[]common.PlayerI{oppController},
),
WithInputControllerBinding(InputControllerBindingSingle),
WithFightCallback(fn),
WithFightInfo(fightInfo),
)
}
// NewFightPerSlotControllerN 创建 N 打战斗(多人各控制一个站位)。
// ourPlayers/oppPlayers 与 ourPetsBySlot/oppPetsBySlot 按站位一一对应。
func NewFightPerSlotControllerN(
ourPlayers []common.PlayerI,
oppPlayers []common.PlayerI,
ourPetsBySlot [][]model.PetInfo,
oppPetsBySlot [][]model.PetInfo,
fn func(model.FightOverInfo),
) (*FightC, errorcode.ErrorCode) {
if len(ourPlayers) == 0 || len(oppPlayers) == 0 {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
if len(ourPlayers) != len(ourPetsBySlot) || len(oppPlayers) != len(oppPetsBySlot) {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
fightInfo := ourPlayers[0].Getfightinfo()
ourInputs, err := buildSideInputsByPlayers(ourPlayers, ourPetsBySlot, fightInfo.Mode)
if err > 0 {
return nil, err
}
oppInputs, err := buildSideInputsByPlayers(oppPlayers, oppPetsBySlot, fightInfo.Mode)
if err > 0 {
return nil, err
}
return NewFightWithOptions(
WithFightInputs(ourInputs, oppInputs),
WithFightPlayersOnSide(ourPlayers, oppPlayers),
WithInputControllerBinding(InputControllerBindingPerSlot),
WithFightCallback(fn),
WithFightInfo(fightInfo),
)
}
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.FightOverInfo)) (*FightC, errorcode.ErrorCode) {
if p1 == nil || p2 == nil {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
fightInfo := p1.Getfightinfo()
ourInput, err := buildInputFromPets(p1, b1, fightInfo.Mode)
if err > 0 {
return nil, err
}
oppInput, err := buildInputFromPets(p2, b2, fightInfo.Mode)
if err > 0 {
return nil, err
}
return NewFightWithOptions(
WithFightInputs([]*input.Input{ourInput}, []*input.Input{oppInput}),
WithFightCallback(fn),
WithFightInfo(fightInfo),
)
}
// buildFight 基于已准备好的双方 Inputs 构建战斗实例。
// 约束opts.ourInputs/opts.oppInputs 必须非空。
func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
if opts == nil {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
opts.normalizePlayers()
if opts.owner == nil || opts.opponent == nil {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
f := &FightC{}
f.ownerID = opts.owner.GetInfo().UserID
f.OurPlayers = opts.ourPlayers
f.OppPlayers = opts.oppPlayers
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
f.callback = opts.callback
f.quit = make(chan struct{})
f.over = make(chan struct{})
f.actionNotify = make(chan struct{}, 1)
f.pendingActions = make([]action.BattleActionI, 0, 4)
f.StartTime = opts.startTime
if opts.fightInfo != nil {
f.Info = *opts.fightInfo
} else {
f.Info = opts.owner.Getfightinfo()
}
f.ReadyInfo.Status = f.Info.Status
if len(opts.ourInputs) == 0 || len(opts.oppInputs) == 0 {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
f.Our = opts.ourInputs
f.Opp = opts.oppInputs
f.bindInputControllers(f.Our, f.OurPlayers, opts.controllerBinding)
f.bindInputControllers(f.Opp, f.OppPlayers, opts.controllerBinding)
f.bindInputFightContext(f.Our, f.Opp)
f.linkTeamViews()
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.primaryOur())
f.ReadyInfo.OpponentInfo, f.ReadyInfo.OpponentPetList = initfightready(f.primaryOpp())
loadtime := 120 * time.Second
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
if opp := f.primaryOpp(); opp != nil {
opp.Finished = true
loadtime = 60 * time.Second
if ai, ok := opp.Player.(*player.AI_player); ok {
if ai.CanCapture > 0 {
opp.CanCapture = ai.CanCapture
}
opp.AttackValue.Prop = ai.Prop
}
}
}
f.FightStartOutboundInfo = f.buildFightStartInfo()
f.Broadcast(func(ff *input.Input) {
ff.Player.SendPackCmd(2503, &f.ReadyInfo)
})
cool.Cron.AfterFunc(loadtime, func() {
our := f.primaryOur()
opp := f.primaryOpp()
if our == nil || opp == nil {
return
}
if !our.Finished || !opp.Finished {
f.closefight = true
f.Reason = model.BattleOverReason.PlayerOffline
switch {
case !opp.Finished:
f.WinnerId = our.Player.GetInfo().UserID
case !our.Finished:
f.WinnerId = opp.Player.GetInfo().UserID
}
f.Broadcast(func(ff *input.Input) {
ff.Player.SendPackCmd(2506, &f.FightOverInfo)
ff.Player.QuitFight()
})
}
})
return f, 0
}
// bindInputControllers 按配置模式重绑站位控制者Input.Player
// Keep: 不改Single: 全部绑定 players[0]PerSlot: 按下标绑定 players[i]。
func (f *FightC) bindInputControllers(inputs []*input.Input, players []common.PlayerI, mode int) {
if len(inputs) == 0 || len(players) == 0 {
return
}
switch mode {
case InputControllerBindingSingle:
controller := players[0]
for _, in := range inputs {
if in == nil {
continue
}
in.Player = controller
}
case InputControllerBindingPerSlot:
for idx, in := range inputs {
if in == nil {
continue
}
if idx < len(players) && players[idx] != nil {
in.Player = players[idx]
continue
}
in.Player = players[0]
}
default:
// keep existing input player binding
}
}
// buildInputFromPets 根据玩家与宠物列表构建一个站位 Input。
func buildInputFromPets(c common.PlayerI, pets []model.PetInfo, mode uint32) (*input.Input, errorcode.ErrorCode) {
if c == nil {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
if r := c.CanFight(); r != 0 {
return nil, r
}
in := input.NewInput(nil, c)
in.AllPet = make([]*info.BattlePetEntity, 0, len(pets))
in.InitAttackValue()
for _, pet := range pets {
entity := info.CreateBattlePetEntity(pet)
entity.BindController(c.GetInfo().UserID)
in.AllPet = append(in.AllPet, entity)
}
in.SortPet()
if len(in.AllPet) == 0 {
return nil, errorcode.ErrorCodes.ErrNoEligiblePokemon
}
if mode == info.BattleMode.SINGLE_MODE {
in.AllPet = in.AllPet[:1]
}
in.SetCurPetAt(0, in.AllPet[0])
return in, 0
}
// buildSideInputsByController 用同一控制者构建多个站位输入(单人多站位)。
func buildSideInputsByController(controller common.PlayerI, petsBySlot [][]model.PetInfo, mode uint32) ([]*input.Input, errorcode.ErrorCode) {
if controller == nil || len(petsBySlot) == 0 {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
inputs := make([]*input.Input, 0, len(petsBySlot))
for _, slotPets := range petsBySlot {
in, err := buildInputFromPets(controller, slotPets, mode)
if err > 0 {
return nil, err
}
inputs = append(inputs, in)
}
return inputs, 0
}
// buildSideInputsByPlayers 按站位玩家一一对应构建输入(多人分站位)。
func buildSideInputsByPlayers(players []common.PlayerI, petsBySlot [][]model.PetInfo, mode uint32) ([]*input.Input, errorcode.ErrorCode) {
if len(players) == 0 || len(players) != len(petsBySlot) {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
inputs := make([]*input.Input, 0, len(players))
for idx := range players {
in, err := buildInputFromPets(players[idx], petsBySlot[idx], mode)
if err > 0 {
return nil, err
}
inputs = append(inputs, in)
}
return inputs, 0
}