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

283 lines
8.3 KiB
Go
Raw Normal View History

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
}
2026-04-04 06:11:01 +08:00
return NewFightWithOptions(
WithFightInputs([]*input.Input{ourInput}, []*input.Input{oppInput}),
2026-04-04 06:11:01 +08:00
WithFightCallback(fn),
WithFightInfo(fightInfo),
2026-04-04 06:11:01 +08:00
)
}
// buildFight 基于已准备好的双方 Inputs 构建战斗实例。
// 约束opts.ourInputs/opts.oppInputs 必须非空。
2026-04-04 06:11:01 +08:00
func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
if opts == nil {
2026-04-04 06:11:01 +08:00
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
opts.normalizePlayers()
if opts.owner == nil || opts.opponent == nil {
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
}
2026-04-04 06:11:01 +08:00
f := &FightC{}
f.ownerID = opts.owner.GetInfo().UserID
f.OurPlayers = opts.ourPlayers
f.OppPlayers = opts.oppPlayers
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
2026-04-04 06:11:01 +08:00
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)
2026-04-04 06:11:01 +08:00
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()
2026-04-04 06:11:01 +08:00
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.primaryOur())
f.ReadyInfo.OpponentInfo, f.ReadyInfo.OpponentPetList = initfightready(f.primaryOpp())
2026-04-04 06:11:01 +08:00
loadtime := 120 * time.Second
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
2026-04-04 06:11:01 +08:00
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
}
ai.ApplyBattleProps(opp.AttackValue)
2026-04-04 06:11:01 +08:00
}
}
}
f.FightStartOutboundInfo = f.buildFightStartInfo()
f.Broadcast(func(ff *input.Input) {
ff.Player.SendPackCmd(2503, &f.ReadyInfo)
})
cool.Cron.AfterFunc(loadtime, func() {
2026-04-04 06:11:01 +08:00
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 {
2026-04-04 06:11:01 +08:00
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
}
2026-04-04 06:11:01 +08:00
// 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
2026-04-04 06:11:01 +08:00
}
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)
2026-04-04 06:11:01 +08:00
}
return inputs, 0
2026-04-04 06:11:01 +08:00
}