package fight import ( "blazing/common/socket/errorcode" "blazing/common/utils" "blazing/modules/player/model" "blazing/logic/service/common" "blazing/logic/service/fight/action" "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" "blazing/logic/service/user" "sync" "sync/atomic" "time" "github.com/gogf/gf/v2/util/grand" "github.com/jinzhu/copier" ) type FightC struct { //准备战斗信息 ReadyInfo model.NoteReadyToFightInfo //开始战斗信息 info.FightStartOutboundInfo Info info.Fightinfo IsReady bool ownerID uint32 // 战斗发起者ID Our []*input.Input // 我方战斗位 Opp []*input.Input // 敌方战斗位 OurPlayers []common.PlayerI // 我方操作者 OppPlayers []common.PlayerI // 敌方操作者 Switch map[actionSlotKey]*action.ActiveSwitchAction startl sync.Once StartTime time.Time actionMu sync.Mutex actionNotify chan struct{} acceptActions bool pendingActions []action.BattleActionI // 待处理动作队列,同一战斗位最多保留一个动作 actionRound atomic.Uint32 quit chan struct{} over chan struct{} First *input.Input TrueFirst *input.Input Second *input.Input closefight bool overl sync.Once waittime int model.FightOverInfo //战斗结束的插装 callback func(model.FightOverInfo) } type actionSlotKey struct { PlayerID uint32 ActorIndex int } func newActionSlotKey(playerID uint32, actorIndex int) actionSlotKey { return actionSlotKey{ PlayerID: playerID, ActorIndex: actorIndex, } } func actionSlotKeyFromAction(act action.BattleActionI) actionSlotKey { if act == nil { return actionSlotKey{} } return newActionSlotKey(act.GetPlayerID(), act.GetActorIndex()) } func (f *FightC) primaryOur() *input.Input { if len(f.Our) == 0 { return nil } return f.Our[0] } func (f *FightC) primaryOpp() *input.Input { if len(f.Opp) == 0 { return nil } return f.Opp[0] } func (f *FightC) primaryOurPlayer() common.PlayerI { if len(f.OurPlayers) == 0 { return nil } return f.OurPlayers[0] } func (f *FightC) primaryOppPlayer() common.PlayerI { if len(f.OppPlayers) == 0 { return nil } return f.OppPlayers[0] } func (f *FightC) selectInput(inputs []*input.Input, index int) *input.Input { if len(inputs) == 0 { return nil } if index >= 0 && index < len(inputs) && inputs[index] != nil { return inputs[index] } for _, in := range inputs { if in != nil { return in } } return nil } func (f *FightC) isPlayerInSide(players []common.PlayerI, userID uint32) bool { for _, player := range players { if player != nil && player.GetInfo().UserID == userID { return true } } return false } func (f *FightC) isOurPlayerID(userID uint32) bool { if f.isPlayerInSide(f.OurPlayers, userID) { return true } if f.isPlayerInSide(f.OppPlayers, userID) { return false } return userID == f.ownerID } func (f *FightC) bindInputFightContext(inputs []*input.Input) { for _, fighter := range inputs { if fighter == nil { continue } fighter.FightC = f if fighter.Player != nil { fighter.Player.SetFightC(f) } } } func (f *FightC) linkOppInputs() { for actorIndex, fighter := range f.Our { if fighter == nil { continue } fighter.SetOPP(f.selectInput(f.Opp, actorIndex)) } for actorIndex, fighter := range f.Opp { if fighter == nil { continue } fighter.SetOPP(f.selectInput(f.Our, actorIndex)) } } func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input { isOur := f.isOurPlayerID(userID) if isOpposite { if isOur { return f.Opp } return f.Our } if isOur { return f.Our } return f.Opp } func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) { isOur := f.isOurPlayerID(userID) if isOur { if in := f.selectInput(f.Our, 0); in != nil { return in, true } return nil, true } if in := f.selectInput(f.Opp, 0); in != nil { return in, false } return nil, false } func (f *FightC) getInputByUserID(userID uint32, index int, isOpposite bool) *input.Input { return f.selectInput(f.getSideInputs(userID, isOpposite), index) } func (f *FightC) expectedActionSlots() map[actionSlotKey]struct{} { slots := make(map[actionSlotKey]struct{}, len(f.Our)+len(f.Opp)) for actorIndex, fighter := range f.Our { if fighter == nil || fighter.Player == nil { continue } slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{} } for actorIndex, fighter := range f.Opp { if fighter == nil || fighter.Player == nil { continue } slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{} } return slots } func (f *FightC) setActionAttackValue(act action.BattleActionI) { if act == nil { return } attacker := f.GetInputByAction(act, false) if attacker == nil || attacker.AttackValue == nil { return } attacker.AttackValue.ActorIndex = uint32(act.GetActorIndex()) attacker.AttackValue.TargetIndex = uint32(act.GetTargetIndex()) } func (f *FightC) Ownerid() uint32 { return f.ownerID } func (f *FightC) GetInputByPlayer(c common.PlayerI, isOpposite bool) *input.Input { if c == nil { if isOpposite { return f.primaryOpp() } return f.primaryOur() } return f.getInputByUserID(c.GetInfo().UserID, 0, isOpposite) } func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *input.Input { if c == nil { if isOpposite { return f.primaryOpp() } return f.primaryOur() } index := c.GetActorIndex() if isOpposite { index = c.GetTargetIndex() } return f.getInputByUserID(c.GetPlayerID(), index, isOpposite) } // 玩家使用技能 func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity { return f.GetCurrPETAt(c, 0) } func (f *FightC) GetCurrPETAt(c common.PlayerI, actorIndex int) *info.BattlePetEntity { if c == nil { return nil } in := f.getInputByUserID(c.GetInfo().UserID, actorIndex, false) if in == nil { return nil } return in.PrimaryCurPet() } func (f *FightC) GetOpp(c common.PlayerI) *input.Input { return f.GetInputByPlayer(c, true) } // // 获取随机数 func (f *FightC) IsFirst(play common.PlayerI) bool { return f.TrueFirst.Player == play } func (f *FightC) GetRound() uint32 { return f.Round } func (f *FightC) Chat(c common.PlayerI, msg string) { f.GetInputByPlayer(c, true).Player.SendPackCmd(50002, &user.ChatOutboundInfo{ SenderId: c.GetInfo().UserID, SenderNickname: c.GetInfo().Nick, Message: utils.RemoveLast(msg), }) } // 加载进度 func (f *FightC) LoadPercent(c common.PlayerI, percent int32) { if f.Info.Mode == info.BattleMode.PET_MELEE { return } if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC { return } f.GetInputByPlayer(c, true).Player.SendPackCmd(2441, &info.LoadPercentOutboundInfo{ Id: c.GetInfo().UserID, Percent: uint32(percent), }) } func (f *FightC) initplayer(c common.PlayerI, b []model.PetInfo) (*input.Input, errorcode.ErrorCode) { r := c.CanFight() if c.CanFight() != 0 { return nil, r } in := input.NewInput(f, c) in.AllPet = make([]*info.BattlePetEntity, 0) in.InitAttackValue() for i := 0; i < len(b); i++ { //玩家精灵重置到100等级 pet := b[i] 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 } switch f.Info.Mode { case info.BattleMode.SINGLE_MODE: in.AllPet = in.AllPet[:1] default: } in.SetCurPetAt(0, in.AllPet[0]) return in, 0 } // RandomElfIDs 从1-2000中随机抽取n个不重复的精灵ID func RandomElfIDs(n int) []int { if n <= 0 || n > 2000 { return nil } // 用map记录已抽取的ID,避免重复 used := make(map[int]struct{}, n) ids := make([]int, 0, n) for len(ids) < n { // 生成1-2000的随机数 id := grand.Intn(2000) + 1 // rand.Intn(2000)生成0-1999,+1后为1-2000 // 检查是否已抽取 if _, exists := used[id]; !exists { used[id] = struct{}{} ids = append(ids, id) } } return ids } func initfightready(in *input.Input) (model.FightUserInfo, []model.ReadyFightPetInfo) { t := make([]model.ReadyFightPetInfo, len(in.AllPet)) userindo := model.FightUserInfo{ UserID: in.UserID, Nick: in.Player.GetInfo().Nick, } for i := 0; i < len(in.AllPet); i++ { err := copier.CopyWithOption(&t[i], &in.AllPet[i].Info, copier.Option{IgnoreEmpty: true, DeepCopy: true}) if err != nil { panic(err) } } return userindo, t } // 被击败的ID func (f *FightC) IsWin(c *input.Input) bool { if c == nil || c.Player == nil { return false } for _, sideInput := range f.getSideInputs(c.Player.GetInfo().UserID, true) { if sideInput == nil { continue } for _, v := range sideInput.AllPet { if v.Alive() { return false } } } return true } // 广播,并是否结束回合 func (f *FightC) Broadcast(t func(ff *input.Input)) { for _, ff := range f.Our { if ff != nil { t(ff) } } for _, ff := range f.Opp { if ff != nil { t(ff) } } } func (f *FightC) GetOverChan() chan struct{} { return f.over } func (f *FightC) GetOverInfo() model.FightOverInfo { return f.FightOverInfo } func (f *FightC) GetAttackValue(b bool) *model.AttackValue { our := f.primaryOur() if our == nil { return nil } return f.GetInputByPlayer(our.Player, b).AttackValue }