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" ) const ( groupCmdReadyToFight uint32 = 7555 groupCmdReadyFightFinish uint32 = 7556 groupCmdStartFight uint32 = 7557 groupCmdUseSkill uint32 = 7558 groupCmdSkillHurt uint32 = 7559 groupCmdFightOver uint32 = 7560 groupCmdSpriteDie uint32 = 7561 groupCmdUseItem uint32 = 7562 groupCmdChangePet uint32 = 7563 groupCmdEscape uint32 = 7565 groupCmdBoutDone uint32 = 7566 groupCmdChangePetSuc uint32 = 7567 groupCmdEscapeSuc uint32 = 7568 groupCmdChat uint32 = 7569 groupCmdLoadPercent uint32 = 7571 groupCmdLoadPercentNotice uint32 = 7572 groupCmdSpriteNotice uint32 = 7573 groupCmdFightWinClose uint32 = 7574 groupCmdFightOvertime uint32 = 7585 groupCmdSkillPlayOver uint32 = 7586 groupCmdFightTimeoutExit uint32 = 7587 groupCmdFightRelation uint32 = 7588 groupModelNPC uint32 = 3 groupModelBoss uint32 = 4 groupModelPlayerSingle uint32 = 5 groupModelPlayerMulti uint32 = 6 ) type fightPacketKind uint8 const ( fightPacketReady fightPacketKind = iota fightPacketStart fightPacketSkillResult fightPacketOver fightPacketChangePetSuccess fightPacketUseItem fightPacketChat fightPacketLoadPercentNotice ) type legacyEscapeSuccessInfo struct { UserID uint32 `struc:"uint32"` Nick string `struc:"[16]byte"` Side uint8 `struc:"uint8"` ActorIndex uint8 `struc:"uint8"` } type legacyBoutDoneInfo struct { Round uint32 `struc:"uint32"` } type legacySpriteDieInfo struct { Count uint8 `struc:"uint8"` Side uint8 `struc:"uint8"` ActorIndex uint8 `struc:"uint8"` Flag uint8 `struc:"uint8"` HasBackup uint32 `struc:"uint32"` } type legacyLegacySpriteDieItem struct { Flag uint8 `struc:"uint8"` Side uint8 `struc:"uint8"` ActorIndex uint8 `struc:"uint8"` Reserve uint8 `struc:"uint8"` HasBackup uint32 `struc:"uint32"` } type legacyGroupReadyToFightInfo struct { Model uint32 `struc:"uint32"` GroupOneInfo legacyReadyToFightTeam `struc:""` GroupTwoInfo legacyReadyToFightTeam `struc:""` } type legacyReadyToFightTeam struct { InvitorID uint8 `struc:"uint8"` LeaderID uint32 `struc:"uint32"` GroupMembCnt uint8 `struc:"sizeof=GroupList"` GroupList []legacyReadyFightUser `struc:""` } type legacyReadyFightUser struct { UserID uint32 `struc:"uint32"` Nick string `struc:"[16]byte"` MonCnt uint32 `struc:"sizeof=MonList"` MonList []legacyReadyFightPet `struc:""` } type legacyReadyFightPet struct { ID uint32 `struc:"uint32"` MoveCnt uint32 `struc:"sizeof=MoveList"` MoveList []uint32 `struc:"[]uint32"` } type legacyGroupStartInfo struct { IsGank uint8 `struc:"uint8"` GroupOneN uint8 `struc:"sizeof=GroupOne"` GroupOne []legacyGroupStartPet `struc:""` GroupTwoN uint8 `struc:"sizeof=GroupTwo"` GroupTwo []legacyGroupStartPet `struc:""` } type legacyGroupStartPet struct { Side uint8 `struc:"uint8"` Pos uint8 `struc:"uint8"` UserID uint32 `struc:"uint32"` IsChange uint8 `struc:"uint8"` PetID uint32 `struc:"uint32"` CatchTime uint32 `struc:"uint32"` Hp uint32 `struc:"uint32"` MaxHp uint32 `struc:"uint32"` Level uint32 `struc:"uint32"` Reserve uint32 `struc:"uint32"` Flag uint32 `struc:"uint32"` } type legacyGroupSkillHurtPacket struct { IsGank uint8 `struc:"uint8"` Attack legacyGroupSkillAttackInfo `struc:""` Attacked legacyGroupSkillDefendInfo `struc:""` } type legacyGroupSkillAttackInfo struct { IsAttackor uint8 `struc:"uint8"` Side uint8 `struc:"uint8"` Pos uint8 `struc:"uint8"` UserID uint32 `struc:"uint32"` StatusList [20]uint8 `struc:"[20]byte"` Reserve1 uint8 `struc:"uint8"` Reserve2 uint8 `struc:"uint8"` BatLvList [6]uint8 `struc:"[6]byte"` PetID uint32 `struc:"uint32"` MoveID uint32 `struc:"uint32"` Hp uint32 `struc:"uint32"` MaxHp uint32 `struc:"uint32"` MoveCnt uint32 `struc:"sizeof=MoveMap"` MoveMap []legacyGroupSkillMoveInfo `struc:""` Flag uint32 `struc:"uint32"` IsCrit uint32 `struc:"uint32"` EffectName uint32 `struc:"uint32"` AtkTimes uint32 `struc:"uint32"` Dmg int32 `struc:"int32"` ChgHp int32 `struc:"int32"` SideEffectLen uint32 `struc:"uint32"` } type legacyGroupSkillDefendInfo struct { IsAttackor uint8 `struc:"uint8"` Side uint8 `struc:"uint8"` Pos uint8 `struc:"uint8"` UserID uint32 `struc:"uint32"` StatusList [20]uint8 `struc:"[20]byte"` Reserve1 uint8 `struc:"uint8"` Reserve2 uint8 `struc:"uint8"` BatLvList [6]uint8 `struc:"[6]byte"` PetID uint32 `struc:"uint32"` MoveID uint32 `struc:"uint32"` Hp uint32 `struc:"uint32"` MaxHp uint32 `struc:"uint32"` MoveCnt uint32 `struc:"sizeof=MoveMap"` MoveMap []legacyGroupSkillMoveInfo `struc:""` Flag uint32 `struc:"uint32"` SideEffectLen uint32 `struc:"uint32"` } type legacyGroupSkillMoveInfo struct { MoveID uint32 `struc:"uint32"` PP uint32 `struc:"uint32"` } type legacyGroupFightOverInfo struct { IsGank uint8 `struc:"uint8"` Reason uint32 `struc:"uint32"` WinnerID uint32 `struc:"uint32"` Reserve uint32 `struc:"uint32"` TwoTimes uint32 `struc:"uint32"` ThreeTimes uint32 `struc:"uint32"` AutoFightTime uint32 `struc:"uint32"` Reserve2 uint32 `struc:"uint32"` EnergyTime uint32 `struc:"uint32"` LearnTimes uint32 `struc:"uint32"` } type legacyGroupChangePetSuccessInfo struct { Side uint8 `struc:"uint8"` Pos uint8 `struc:"uint8"` UserID uint32 `struc:"uint32"` PetID uint32 `struc:"uint32"` CatchTime uint32 `struc:"uint32"` Level uint32 `struc:"uint32"` Hp uint32 `struc:"uint32"` MaxHp uint32 `struc:"uint32"` SkinID uint32 `struc:"uint32"` } func groupModelByFight(f *FightC) uint32 { if f == nil { return groupModelBoss } switch { case f.Info.Status == 0: return groupModelNPC case f.Info.Status == 1 && f.Info.Mode == 1: return groupModelPlayerSingle case f.Info.Status == 1: return groupModelPlayerMulti default: return groupModelBoss } } func (f *FightC) fightPacketCmd(kind fightPacketKind) uint32 { switch kind { case fightPacketReady: return 2503 case fightPacketStart: return 2504 case fightPacketSkillResult: return 2505 case fightPacketOver: return 2506 case fightPacketChangePetSuccess: if f != nil && f.LegacyGroupProtocol { return groupCmdChangePetSuc } return 2407 case fightPacketUseItem: if f != nil && f.LegacyGroupProtocol { return groupCmdUseItem } return 2406 case fightPacketChat: if f != nil && f.LegacyGroupProtocol { return groupCmdChat } return 50002 case fightPacketLoadPercentNotice: if f != nil && f.LegacyGroupProtocol { return groupCmdLoadPercentNotice } return 2441 default: return 0 } } func (f *FightC) sendFightPacket(player common.PlayerI, kind fightPacketKind, payload any) { if player == nil { return } cmd := f.fightPacketCmd(kind) if cmd == 0 { return } player.SendPackCmd(cmd, payload) } func (f *FightC) sendLegacyGroupReady(player common.PlayerI) { if f == nil || !f.LegacyGroupProtocol || player == nil { return } player.SendPackCmd(groupCmdReadyToFight, f.buildLegacyGroupReadyInfo()) } func (f *FightC) buildLegacyGroupReadyInfo() *legacyGroupReadyToFightInfo { return &legacyGroupReadyToFightInfo{ Model: groupModelByFight(f), GroupOneInfo: f.buildLegacyReadyTeam(f.OurPlayers, f.Our), GroupTwoInfo: f.buildLegacyReadyTeam(f.OppPlayers, f.Opp), } } func (f *FightC) buildLegacyReadyTeam(players []common.PlayerI, inputs []*input.Input) legacyReadyToFightTeam { team := legacyReadyToFightTeam{InvitorID: 1} users := make([]legacyReadyFightUser, 0, len(players)) for _, p := range players { if p == nil || p.GetInfo() == nil { continue } users = append(users, legacyReadyFightUser{ UserID: p.GetInfo().UserID, Nick: p.GetInfo().Nick, MonList: collectLegacyReadyPetsByController(inputs, p.GetInfo().UserID), }) } if len(users) == 0 { if fallback := firstNonNilInput(inputs); fallback != nil && fallback.Player != nil && fallback.Player.GetInfo() != nil { info := fallback.Player.GetInfo() users = append(users, legacyReadyFightUser{ UserID: info.UserID, Nick: info.Nick, MonList: collectLegacyReadyPetsByController(inputs, info.UserID), }) } } for idx := range users { users[idx].MonCnt = uint32(len(users[idx].MonList)) } if len(users) > 0 { team.LeaderID = users[0].UserID } team.GroupList = users return team } func collectLegacyReadyPetsByController(inputs []*input.Input, controllerID uint32) []legacyReadyFightPet { pets := make([]legacyReadyFightPet, 0, 6) for _, in := range inputs { if in == nil || !in.ControlledBy(controllerID) { continue } currentPet := in.CurrentPet() if currentPet == nil { continue } pets = append(pets, buildLegacyReadyFightPet(currentPet)) } return pets } func buildLegacyReadyFightPet(pet *info.BattlePetEntity) legacyReadyFightPet { result := legacyReadyFightPet{} if pet == nil { return result } moves := make([]uint32, 0, len(pet.Info.SkillList)) for _, skill := range pet.Info.SkillList { if skill.ID == 0 { continue } moves = append(moves, skill.ID) } result.ID = pet.Info.ID result.MoveList = moves return result } func firstNonNilInput(inputs []*input.Input) *input.Input { for _, in := range inputs { if in != nil { return in } } return nil } func (f *FightC) sendLegacyGroupStart(player common.PlayerI) { if f == nil || !f.LegacyGroupProtocol || player == nil { return } player.SendPackCmd(groupCmdStartFight, f.buildLegacyGroupStartInfo()) } func (f *FightC) buildLegacyGroupStartInfo() *legacyGroupStartInfo { return &legacyGroupStartInfo{ IsGank: 0, GroupOne: f.collectLegacyGroupStartPets(f.Our, 1), GroupTwo: f.collectLegacyGroupStartPets(f.Opp, 2), } } func (f *FightC) collectLegacyGroupStartPets(inputs []*input.Input, side uint8) []legacyGroupStartPet { ret := make([]legacyGroupStartPet, 0, len(inputs)) for pos, in := range inputs { if in == nil { continue } currentPet := in.CurrentPet() if currentPet == nil { continue } userID := uint32(0) if in.Player != nil && in.Player.GetInfo() != nil { userID = in.Player.GetInfo().UserID } ret = append(ret, legacyGroupStartPet{ Side: side, Pos: uint8(pos), UserID: userID, IsChange: 0, PetID: currentPet.Info.ID, CatchTime: currentPet.Info.CatchTime, Hp: currentPet.Info.Hp, MaxHp: currentPet.Info.MaxHp, Level: currentPet.Info.Level, Reserve: 0, Flag: 1, }) } return ret } func (f *FightC) sendLegacyGroupOver(player common.PlayerI, over *model.FightOverInfo) { if f == nil || !f.LegacyGroupProtocol || player == nil { return } player.SendPackCmd(groupCmdFightOver, f.buildLegacyGroupOverInfo(over)) } func (f *FightC) buildLegacyGroupOverInfo(over *model.FightOverInfo) *legacyGroupFightOverInfo { result := &legacyGroupFightOverInfo{} if over != nil { result.Reason = resolveLegacyGroupFightOverReason(over) result.WinnerID = over.WinnerId } if our := f.primaryOurPlayer(); our != nil && our.GetInfo() != nil { playerInfo := our.GetInfo() result.TwoTimes = uint32(playerInfo.TwoTimes) result.ThreeTimes = uint32(playerInfo.ThreeTimes) result.AutoFightTime = playerInfo.AutoFightTime result.EnergyTime = uint32(playerInfo.EnergyTime) result.LearnTimes = playerInfo.LearnTimes } return result } func mapLegacyGroupFightOverReason(reason model.EnumBattleOverReason) uint32 { switch reason { case model.BattleOverReason.PlayerOffline: return 2 case model.BattleOverReason.PlayerOVerTime: return 3 case model.BattleOverReason.NOTwind: return 4 case model.BattleOverReason.DefaultEnd: return 1 case model.BattleOverReason.PlayerEscape: return 6 default: return 5 } } func resolveLegacyGroupFightOverReason(over *model.FightOverInfo) uint32 { if over == nil { return 5 } switch over.Reason { case model.BattleOverReason.PlayerOffline: return 2 case model.BattleOverReason.PlayerOVerTime: return 3 case model.BattleOverReason.PlayerEscape: return 6 case model.BattleOverReason.NOTwind: return 4 } if over.WinnerId != 0 { return 1 } return mapLegacyGroupFightOverReason(over.Reason) } func (f *FightC) sendLegacyGroupChangePetSuccess(player common.PlayerI, in *input.Input, reason *info.ChangePetInfo) { if f == nil || !f.LegacyGroupProtocol || player == nil || in == nil || reason == nil { return } player.SendPackCmd(groupCmdChangePetSuc, f.buildLegacyGroupChangePetSuccessInfo(in, reason)) } func (f *FightC) buildLegacyGroupChangePetSuccessInfo(in *input.Input, reason *info.ChangePetInfo) *legacyGroupChangePetSuccessInfo { result := &legacyGroupChangePetSuccessInfo{} if in == nil || reason == nil { return result } if !f.isOurPlayerID(in.UserID) { result.Side = 2 } else { result.Side = 1 } result.Pos = uint8(in.TeamSlotIndex()) result.UserID = reason.UserId result.PetID = reason.ID result.CatchTime = reason.CatchTime result.Level = reason.Level result.Hp = reason.Hp result.MaxHp = reason.MaxHp if currentPet := in.CurrentPet(); currentPet != nil { result.SkinID = currentPet.Info.SkinID } return result } func (f *FightC) SendLegacyEscapeSuccess(player common.PlayerI, actorIndex int) { if f == nil || !f.LegacyGroupProtocol || player == nil { return } side := uint8(1) if !f.isOurPlayerID(player.GetInfo().UserID) { side = 2 } payload := legacyEscapeSuccessInfo{ UserID: player.GetInfo().UserID, Nick: player.GetInfo().Nick, Side: side, ActorIndex: uint8(actorIndex), } f.BroadcastPlayers(func(p common.PlayerI) { p.SendPackCmd(groupCmdEscapeSuc, &payload) }) } func (f *FightC) sendLegacyRoundBroadcast(firstAttack, secondAttack *action.SelectSkillAction) { if f == nil || !f.LegacyGroupProtocol { return } if firstAttack != nil { f.sendLegacyGroupSkillHurt(firstAttack) } if secondAttack != nil { f.sendLegacyGroupSkillHurt(secondAttack) } f.sendLegacyGroupBoutDone() } func (f *FightC) sendLegacyGroupSkillHurt(skillAction *action.SelectSkillAction) { if f == nil || !f.LegacyGroupProtocol || skillAction == nil { return } packet := f.buildLegacyGroupSkillHurtPacket(skillAction) if packet == nil { return } f.BroadcastPlayers(func(p common.PlayerI) { p.SendPackCmd(groupCmdSkillHurt, packet) }) } func (f *FightC) buildLegacyGroupSkillHurtPacket(skillAction *action.SelectSkillAction) *legacyGroupSkillHurtPacket { attacker := f.GetInputByAction(skillAction, false) defender := f.GetInputByAction(skillAction, true) if attacker == nil || defender == nil { return nil } return &legacyGroupSkillHurtPacket{ IsGank: 0, Attack: f.buildLegacyGroupSkillAttackInfo(skillAction, attacker), Attacked: f.buildLegacyGroupSkillDefendInfo(defender), } } func (f *FightC) fillLegacyGroupSkillCommonFields( self *input.Input, isAttackor uint8, statusList *[20]uint8, batLvList *[6]uint8, ) (side uint8, pos uint8, userID uint32, petID uint32, hp uint32, maxHP uint32, moveMap []legacyGroupSkillMoveInfo, flag uint32) { if self == nil { return } if !f.isOurPlayerID(self.UserID) { side = 2 } else { side = 1 } pos = uint8(self.TeamSlotIndex()) userID = self.UserID attackValue := self.AttackValue if attackValue == nil { attackValue = info.NewAttackValue(self.UserID) } for i := 0; i < len(attackValue.Status) && i < 20; i++ { statusList[i] = uint8(attackValue.Status[i]) } for i := 0; i < len(attackValue.Prop) && i < len(batLvList); i++ { batLvList[i] = uint8(attackValue.Prop[i]) } currentPet := self.CurrentPet() if currentPet != nil { petID = currentPet.Info.ID hp = currentPet.Info.Hp maxHP = currentPet.Info.MaxHp moveMap = collectLegacyGroupSkillMoves(currentPet.Info.SkillList) } else { hp = clampLegacyInt32ToUint32(attackValue.RemainHp) maxHP = attackValue.MaxHp moveMap = collectLegacyGroupSkillMoves(attackValue.SkillList) } flag = attackValue.State return } func (f *FightC) buildLegacyGroupSkillAttackInfo(skillAction *action.SelectSkillAction, self *input.Input) legacyGroupSkillAttackInfo { result := legacyGroupSkillAttackInfo{} if self == nil { return result } result.IsAttackor = 0 result.Side, result.Pos, result.UserID, result.PetID, result.Hp, result.MaxHp, result.MoveMap, result.Flag = f.fillLegacyGroupSkillCommonFields(self, result.IsAttackor, &result.StatusList, &result.BatLvList) attackValue := self.AttackValue if attackValue == nil { attackValue = info.NewAttackValue(self.UserID) } if skillAction != nil && skillAction.SkillEntity != nil { result.MoveID = uint32(skillAction.SkillEntity.XML.ID) } else { result.MoveID = attackValue.SkillID } result.IsCrit = attackValue.IsCritical result.EffectName = attackValue.State result.AtkTimes = 1 result.Dmg = int32(attackValue.LostHp) result.ChgHp = attackValue.GainHp return result } func (f *FightC) buildLegacyGroupSkillDefendInfo(self *input.Input) legacyGroupSkillDefendInfo { result := legacyGroupSkillDefendInfo{} if self == nil { return result } result.IsAttackor = 1 result.Side, result.Pos, result.UserID, result.PetID, result.Hp, result.MaxHp, result.MoveMap, result.Flag = f.fillLegacyGroupSkillCommonFields(self, result.IsAttackor, &result.StatusList, &result.BatLvList) result.MoveID = 0 return result } func collectLegacyGroupSkillMoves(skills []model.SkillInfo) []legacyGroupSkillMoveInfo { moves := make([]legacyGroupSkillMoveInfo, 0, len(skills)) for _, skill := range skills { if skill.ID == 0 { continue } moves = append(moves, legacyGroupSkillMoveInfo{ MoveID: skill.ID, PP: skill.PP, }) } return moves } func clampLegacyInt32ToUint32(v int32) uint32 { if v < 0 { return 0 } return uint32(v) } func (f *FightC) sendLegacyGroupBoutDone() { if f == nil || !f.LegacyGroupProtocol { return } payload := legacyBoutDoneInfo{Round: f.Round} f.BroadcastPlayers(func(p common.PlayerI) { p.SendPackCmd(groupCmdBoutDone, &payload) }) } func (f *FightC) sendLegacySpriteDie(in *input.Input, hasBackup bool) { if f == nil || !f.LegacyGroupProtocol || in == nil { return } side := uint8(1) if !f.isOurPlayerID(in.UserID) { side = 2 } var data uint32 if hasBackup { data = 1 } payload := legacySpriteDieInfo{ Count: 1, Side: side, ActorIndex: uint8(in.TeamSlotIndex()), Flag: 1, HasBackup: data, } f.BroadcastPlayers(func(p common.PlayerI) { p.SendPackCmd(groupCmdSpriteDie, &payload) }) }