package fight import ( "blazing/common/data" "blazing/common/socket/errorcode" "blazing/common/utils" "blazing/cool" "blazing/modules/config/service" "blazing/modules/player/model" "encoding/json" "fmt" "blazing/logic/service/common" "blazing/logic/service/fight/action" "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" "blazing/logic/service/player" "blazing/logic/service/user" "math/rand" "sync" "time" "github.com/gogf/gf/v2/util/grand" "github.com/jinzhu/copier" ) type FightC struct { //准备战斗信息 ReadyInfo info.NoteReadyToFightInfo //开始战斗信息 info.FightStartOutboundInfo Info info.Fightinfo IsReady bool ownerID uint32 // 战斗发起者ID Our *input.Input //始终等于房主ID Opp *input.Input //对手ID Switch map[uint32]*action.ActiveSwitchAction startl sync.Once rand *rand.Rand StartTime time.Time actionChan chan action.BattleActionI // 所有操作统一从这里进入 quit chan struct{} over chan struct{} First *input.Input TrueFirst *input.Input Second *input.Input closefight bool overl sync.Once waittime int info.FightOverInfo //战斗结束的插装 callback func(info.FightOverInfo) } func (f *FightC) Ownerid() uint32 { return f.ownerID } func (f *FightC) GetInputByPlayer(c common.PlayerI, isOpposite bool) *input.Input { // 判断当前玩家是否为我方玩家 isOurPlayer := c.GetInfo().UserID == f.ownerID // 当isOurPlayer与isOpposite值不同时返回我方,相同时返回对方 if isOurPlayer != isOpposite { return f.Our } return f.Opp } func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *input.Input { // 判断动作所属玩家是否为我方 isOurAction := c.GetPlayerID() == f.Our.Player.GetInfo().UserID // 根据isOpposite决定是否返回相反方向的输入 if isOurAction == !isOpposite { return f.Our } return f.Opp } // 玩家使用技能 func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity { if f.Our.Player.GetInfo().UserID == c.GetInfo().UserID { return f.Our.CurrentPet } else { return f.Opp.CurrentPet } } func (f *FightC) GetOpp(c common.PlayerI) *input.Input { return f.GetInputByPlayer(c, true) } // 获取随机数 func (f *FightC) GetRand() *rand.Rand { return f.rand } // 获取随机数 func (f *FightC) IsFirst(play common.PlayerI) bool { return f.TrueFirst.Player == play } 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), }) } var meetpet = make(map[int]model.PetInfo) func (f *FightC) initplayer(c common.PlayerI) (*input.Input, errorcode.ErrorCode) { if !c.CanFight() { return nil, errorcode.ErrorCodes.ErrNoEligiblePokemon } in := input.NewInput(f, c) in.AllPet = make([]*info.BattlePetEntity, 0) in.InitAttackValue() for i := 0; i < len(c.GetInfo().PetList); i++ { in.AllPet = append(in.AllPet, info.CreateBattlePetEntity(c.GetInfo().PetList[i], f.rand)) } 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] case info.BattleMode.PET_MELEE: in.AllPet = make([]*info.BattlePetEntity, 0) // if len(meetpet) == 0 { // initmeetpet() // } r := service.NewMELEEService().Def() if len(r) == 0 { return nil, errorcode.ErrorCodes.ErrNoEligiblePokemon } for i, v := range r { if v.Lv == 0 { v.Lv = 100 } // var effect int // if len(v.Effect) > 0 { // effect = int(v.Effect[0]) // } pet := model.GenPetInfo(int(v.MonID), 24, int(v.Nature), int(v.Effect[0]), int(v.Lv), nil) var color data.GlowFilter err := json.Unmarshal([]byte(v.Color), &color) if err == nil { pet.ShinyInfo = append(pet.ShinyInfo, color) } if len(v.Prop) == 5 { pet.Prop = [5]uint32(v.Prop) } if v.Hp != 0 { pet.MaxHp = uint32(v.Hp) } if len(v.SKill) != 0 { for i := 0; i < 4; i++ { if v.SKill[i] != 0 { pet.SkillList[i].ID = v.SKill[i] } } } pet.CatchTime = c.GetInfo().UserID + uint32(i)*1000000 pet.Cure() in.AllPet = append(in.AllPet, info.CreateBattlePetEntity(*pet, f.rand)) } // for i, v := range meetpet { // v1 := v // if len(in.AllPet) > 2 { // break // } // v1.CatchTime = c.GetInfo().UserID + uint32(i)*1000000 // v1.Cure() // in.AllPet = append(in.AllPet, info.CreateBattlePetEntity(v1, f.rand)) // } //in.AllPet = in.AllPet[:3] default: } in.CurrentPet = 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) (info.FightUserInfo, []info.ReadyFightPetInfo) { t := make([]info.ReadyFightPetInfo, len(in.AllPet)) userindo := info.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 } // 创建新战斗,邀请方和被邀请方,或者玩家和野怪方 func NewFight(p1, p2 common.PlayerI, fn func(info.FightOverInfo)) (*FightC, errorcode.ErrorCode) { fmt.Println("NewFight", p1.GetInfo().UserID) f := &FightC{} f.ownerID = p1.GetInfo().UserID f.Switch = make(map[uint32]*action.ActiveSwitchAction) f.callback = fn //战斗结束的回调 f.quit = make(chan struct{}) f.over = make(chan struct{}) f.StartTime = time.Now() seed := f.StartTime.UnixNano() ^ int64(p1.GetInfo().UserID) ^ int64(p2.GetInfo().UserID) // ^ int64(f.Round) // 用异或运算混合多维度信息 f.rand = rand.New(rand.NewSource(seed)) f.Info = p1.Getfightinfo() //这里应该挪到玩家初始化执行 f.ReadyInfo.Status = f.Info.Status var err errorcode.ErrorCode f.Our, err = f.initplayer(p1) if err > 0 { return nil, err } f.Opp, err = f.initplayer(p2) if err > 0 { return nil, err } f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.Our) f.ReadyInfo.OpponentInfo, f.ReadyInfo.OpponentPetList = initfightready(f.Opp) var loadtime time.Duration = 120 * time.Second //说明是PVE f.Broadcast(func(ff *input.Input) { ff.SetOPP(f.GetInputByPlayer(ff.Player, true)) }) f.FightStartOutboundInfo = f.buildFightStartInfo() if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC { f.Opp.Finished = true //PVE 默认boss数据直接加载完成 loadtime = 60 * time.Second //f.handleNPCFightSpecial(&fightStartInfo) if f.Opp.Player.(*player.AI_player).CanCapture > 0 { f.Opp.CanCapture = f.Opp.Player.(*player.AI_player).CanCapture f.FightStartOutboundInfo.Info2.Catchable = 1 //可以捕捉就置1 } f.Opp.AttackValue.Prop = f.Opp.Player.(*player.AI_player).Prop f.FightStartOutboundInfo.Info2.Prop = f.Opp.AttackValue.Prop } f.Broadcast(func(ff *input.Input) { ff.Player.SendPackCmd(2503, &f.ReadyInfo) if p, ok := ff.Player.(*player.Player); ok { p.Service.Info.Save(*p.Info) } }) cool.Cron.AfterFunc(loadtime, func() { fmt.Println(f.Our.UserID, "战斗超时结算") if !f.Our.Finished || !f.Opp.Finished { //如果有任一没有加载完成 f.closefight = true //阻止继续添加action f.Reason = info.BattleOverReason.PlayerOffline switch { case !f.Opp.Finished: //邀请方没加载完成 先判断邀请方,如果都没加载完成,就算做房主胜利 f.WinnerId = f.Our.Player.GetInfo().UserID case !f.Our.Finished: //被邀请方没加载完成 f.WinnerId = f.Opp.Player.GetInfo().UserID } f.Broadcast(func(ff *input.Input) { //todo 将血量和技能pp传回enterturn ff.Player.SendPackCmd(2506, &f.FightOverInfo) ff.Player.QuitFight() }) } }) return f, 0 } // 被击败的ID func (f *FightC) IsWin(c *input.Input) bool { for _, v := range f.GetInputByPlayer(c.Player, true).AllPet { if !v.NotAlive { //如果存活 return false } } return true } // 广播,并是否结束回合 func (f *FightC) Broadcast(t func(ff *input.Input)) { t(f.Our) t(f.Opp) } func (f *FightC) GetOverChan() chan struct{} { return f.over } func (f *FightC) GetOverInfo() info.FightOverInfo { return f.FightOverInfo }