Pet Battles: Fix all packet wire formats to match WoW 12.0 sniff
Analyzed a 12.0.1.66017 packet sniff using WowPacketParser and compared every pet battle packet structure against our implementation. Fixed all discrepancies to match Blizzard's actual wire format: - SMSG_PET_BATTLE_FINALIZE_LOCATION: Replace single Position with full PetBattleLocation struct (LocationResult, BattleOrigin, BattleFacing, PlayerPositions[2]) - SMSG_PET_BATTLE_FINAL_ROUND: Completely restructured - no longer contains round data. Now contains Abandoned/PvpBattle flags, Winners, NpcCreatureIDs, and per-pet final results (PetBattleFinalPet) - PetBattleEffectInfo: Add SourceAuraInstanceID (u16), TurnInstanceID (u16), fix CasterPBOID to uint8, reorder fields to match wire format - PetBattleEffectTargetInfo: Type is now 3 bits (not uint8), variable- length params based on type (0-7 target types with 0-4 int32s each) - PetBattleCooldownInfo: Add AbilityIndex and Pboid fields to match PetBattleAbilityInfo format (same wire structure from 6.2.4+) - Round packets (FirstRound, RoundResult, ReplacementsMade): All three now share identical wire format via WriteRoundResult. Cooldowns moved from per-player to round-level flat array. Added PetXDied (3-bit count + PBOID entries) - SMSG_PET_BATTLE_REPLACEMENTS_MADE: Now includes Effects (was missing) - CMSG_PET_BATTLE_INPUT: MoveType changed from uint8 to int32, Round from uint8 to int32, added DebugFlags (int32) and BattleInterrupted (uint8) fields. Verified against raw hex: 19 bytes total - CMSG_PET_BATTLE_REQUEST_WILD/PVP: Replace MovementInfo with proper PetBattleLocation struct matching WPP V6 parser - SMSG_PET_BATTLE_PVP_CHALLENGE: Uses PetBattleLocation struct
This commit is contained in:
@@ -549,7 +549,11 @@ namespace PetBattles
|
||||
battle->Start();
|
||||
|
||||
WorldPackets::BattlePet::PetBattleFinalizeLocation finalizeLocation;
|
||||
finalizeLocation.Location = p1->GetPosition();
|
||||
finalizeLocation.Location.LocationResult = 0;
|
||||
finalizeLocation.Location.BattleOrigin = p1->GetPosition();
|
||||
finalizeLocation.Location.BattleFacing = p1->GetAbsoluteAngle(p2);
|
||||
finalizeLocation.Location.PlayerPositions[0] = p1->GetPosition();
|
||||
finalizeLocation.Location.PlayerPositions[1] = p2->GetPosition();
|
||||
p1->SendDirectMessage(finalizeLocation.Write());
|
||||
p2->SendDirectMessage(finalizeLocation.Write());
|
||||
}
|
||||
|
||||
@@ -211,22 +211,43 @@ static void BuildPetBattleRoundPlayerData(WorldPackets::BattlePet::PetBattleRoun
|
||||
roundData.NextInputFlags = team.InputFlags;
|
||||
roundData.NextTrapStatus = battle ? static_cast<int8>(battle->GetTrapStatus(teamIdx)) : static_cast<int8>(team.TrapStatus);
|
||||
roundData.RoundTimeSecs = PetBattles::PET_BATTLE_MAX_ROUND_TIME;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < team.PetCount; ++i)
|
||||
static void BuildRoundCooldowns(std::vector<WorldPackets::BattlePet::PetBattleCooldownInfo>& cooldowns,
|
||||
PetBattles::PetBattle const* battle)
|
||||
{
|
||||
for (uint8 t = 0; t < PetBattles::MAX_PET_BATTLE_PLAYERS; ++t)
|
||||
{
|
||||
std::vector<WorldPackets::BattlePet::PetBattleCooldownInfo> petCds;
|
||||
for (uint8 j = 0; j < PetBattles::MAX_PET_BATTLE_ABILITIES; ++j)
|
||||
PetBattles::PetBattleTeamData const& team = battle->GetTeam(t);
|
||||
for (uint8 i = 0; i < team.PetCount; ++i)
|
||||
{
|
||||
if (team.Pets[i].AbilityIDs[j] == 0)
|
||||
continue;
|
||||
for (uint8 j = 0; j < PetBattles::MAX_PET_BATTLE_ABILITIES; ++j)
|
||||
{
|
||||
if (team.Pets[i].AbilityIDs[j] == 0)
|
||||
continue;
|
||||
|
||||
WorldPackets::BattlePet::PetBattleCooldownInfo cd;
|
||||
cd.AbilityID = team.Pets[i].AbilityIDs[j];
|
||||
cd.CooldownRemaining = team.Pets[i].AbilityCooldowns[j];
|
||||
cd.LockdownRemaining = team.Pets[i].AbilityLockdowns[j];
|
||||
petCds.push_back(cd);
|
||||
WorldPackets::BattlePet::PetBattleCooldownInfo cd;
|
||||
cd.AbilityID = team.Pets[i].AbilityIDs[j];
|
||||
cd.CooldownRemaining = team.Pets[i].AbilityCooldowns[j];
|
||||
cd.LockdownRemaining = team.Pets[i].AbilityLockdowns[j];
|
||||
cd.AbilityIndex = static_cast<int8>(j);
|
||||
cd.Pboid = t * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + i;
|
||||
cooldowns.push_back(cd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void BuildPetXDied(std::vector<int8>& petXDied, PetBattles::PetBattle const* battle)
|
||||
{
|
||||
for (uint8 t = 0; t < PetBattles::MAX_PET_BATTLE_PLAYERS; ++t)
|
||||
{
|
||||
PetBattles::PetBattleTeamData const& team = battle->GetTeam(t);
|
||||
for (uint8 i = 0; i < team.PetCount; ++i)
|
||||
{
|
||||
if (battle->WasPetKilledThisRound(t, i))
|
||||
petXDied.push_back(static_cast<int8>(t * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + i));
|
||||
}
|
||||
roundData.PetCooldowns.push_back(std::move(petCds));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,16 +259,65 @@ static void BuildRoundEffects(std::vector<WorldPackets::BattlePet::PetBattleEffe
|
||||
WorldPackets::BattlePet::PetBattleEffectInfo effect;
|
||||
effect.AbilityEffectID = roundEffect.AbilityEffectID;
|
||||
effect.Flags = roundEffect.Flags;
|
||||
effect.PetBattleEffectType = roundEffect.EffectType;
|
||||
effect.CasterPBOID = static_cast<int8>(roundEffect.SourceTeam * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + roundEffect.SourcePet);
|
||||
effect.SourceAuraInstanceID = 0;
|
||||
effect.TurnInstanceID = 0;
|
||||
effect.PetBattleEffectType = static_cast<int8>(roundEffect.EffectType);
|
||||
effect.CasterPBOID = static_cast<uint8>(roundEffect.SourceTeam * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + roundEffect.SourcePet);
|
||||
effect.StackDepth = 0;
|
||||
|
||||
WorldPackets::BattlePet::PetBattleEffectTargetInfo target;
|
||||
target.Type = roundEffect.EffectType;
|
||||
target.Petx = static_cast<uint8>(roundEffect.TargetTeam * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + roundEffect.TargetPet);
|
||||
target.Param1 = roundEffect.Param1;
|
||||
target.Param2 = roundEffect.Param2;
|
||||
effect.Targets.push_back(target);
|
||||
|
||||
// Map effect type to target type and variable-length params
|
||||
// Target types: 0=none, 1=aura(4 i32), 2=state(2 i32), 3=health(1 i32),
|
||||
// 4=stat(1 i32), 5=trigger(1 i32), 6=cooldown(3 i32), 7=broadcast(1 i32)
|
||||
switch (roundEffect.EffectType)
|
||||
{
|
||||
case PetBattles::PET_BATTLE_EFFECT_SET_HEALTH:
|
||||
case PetBattles::PET_BATTLE_EFFECT_SET_MAX_HEALTH:
|
||||
target.Type = 3; // Health
|
||||
target.Params.push_back(roundEffect.Param1);
|
||||
break;
|
||||
case PetBattles::PET_BATTLE_EFFECT_AURA_APPLY:
|
||||
case PetBattles::PET_BATTLE_EFFECT_AURA_CANCEL:
|
||||
case PetBattles::PET_BATTLE_EFFECT_AURA_CHANGE:
|
||||
target.Type = 1; // Aura
|
||||
target.Params.push_back(roundEffect.Param1); // AuraInstanceID
|
||||
target.Params.push_back(roundEffect.Param2); // AuraAbilityID
|
||||
target.Params.push_back(0); // RoundsRemaining
|
||||
target.Params.push_back(0); // CurrentRound
|
||||
break;
|
||||
case PetBattles::PET_BATTLE_EFFECT_SET_STATE:
|
||||
target.Type = 2; // State
|
||||
target.Params.push_back(roundEffect.Param1); // StateID
|
||||
target.Params.push_back(roundEffect.Param2); // StateValue
|
||||
break;
|
||||
case PetBattles::PET_BATTLE_EFFECT_STATUS_CHANGE:
|
||||
case PetBattles::PET_BATTLE_EFFECT_SET_SPEED:
|
||||
case PetBattles::PET_BATTLE_EFFECT_SET_POWER:
|
||||
target.Type = 4; // NewStatValue
|
||||
target.Params.push_back(roundEffect.Param1);
|
||||
break;
|
||||
case PetBattles::PET_BATTLE_EFFECT_TRIGGER_ABILITY:
|
||||
target.Type = 5; // TriggerAbilityID
|
||||
target.Params.push_back(roundEffect.Param1);
|
||||
break;
|
||||
case PetBattles::PET_BATTLE_EFFECT_ABILITY_CHANGE:
|
||||
target.Type = 6; // Cooldown
|
||||
target.Params.push_back(roundEffect.Param1); // ChangedAbilityID
|
||||
target.Params.push_back(roundEffect.Param2); // CooldownRemaining
|
||||
target.Params.push_back(0); // LockdownRemaining
|
||||
break;
|
||||
case PetBattles::PET_BATTLE_EFFECT_NPC_EMOTE:
|
||||
target.Type = 7; // BroadcastTextID
|
||||
target.Params.push_back(roundEffect.Param1);
|
||||
break;
|
||||
default:
|
||||
target.Type = 0; // No data
|
||||
break;
|
||||
}
|
||||
|
||||
effect.Targets.push_back(target);
|
||||
effectList.push_back(std::move(effect));
|
||||
}
|
||||
}
|
||||
@@ -330,7 +400,11 @@ void WorldSession::HandlePetBattleRequestWild(WorldPackets::BattlePet::PetBattle
|
||||
|
||||
// Send finalize location
|
||||
WorldPackets::BattlePet::PetBattleFinalizeLocation finalizeLocation;
|
||||
finalizeLocation.Location = player->GetPosition();
|
||||
finalizeLocation.Location.LocationResult = 0;
|
||||
finalizeLocation.Location.BattleOrigin = player->GetPosition();
|
||||
finalizeLocation.Location.BattleFacing = player->GetAbsoluteAngle(creature);
|
||||
finalizeLocation.Location.PlayerPositions[0] = player->GetPosition();
|
||||
finalizeLocation.Location.PlayerPositions[1] = creature->GetPosition();
|
||||
SendPacket(finalizeLocation.Write());
|
||||
|
||||
// Build and send initial update
|
||||
@@ -391,18 +465,37 @@ void WorldSession::HandlePetBattleInput(WorldPackets::BattlePet::PetBattleInput&
|
||||
{
|
||||
// Send final round
|
||||
WorldPackets::BattlePet::PetBattleFinalRound finalRound;
|
||||
finalRound.CurRound = battle->GetCurrentRound();
|
||||
finalRound.NextPetBattleState = static_cast<uint8>(battle->GetBattleState());
|
||||
for (uint8 i = 0; i < PetBattles::MAX_PET_BATTLE_PLAYERS; ++i)
|
||||
BuildPetBattleRoundPlayerData(finalRound.Players[i], battle->GetTeam(i), battle, i);
|
||||
BuildRoundEffects(finalRound.Effects, battle);
|
||||
|
||||
finalRound.Abandoned = false;
|
||||
finalRound.PvpBattle = (battle->GetBattleType() == PetBattles::PET_BATTLE_TYPE_PVP || battle->GetBattleType() == PetBattles::PET_BATTLE_TYPE_LFPB);
|
||||
finalRound.Winners[battle->GetWinnerTeam()] = true;
|
||||
|
||||
// Send to player team 1
|
||||
for (uint8 t = 0; t < PetBattles::MAX_PET_BATTLE_PLAYERS; ++t)
|
||||
{
|
||||
PetBattles::PetBattleTeamData const& team = battle->GetTeam(t);
|
||||
|
||||
if (battle->GetBattleType() == PetBattles::PET_BATTLE_TYPE_NPC && t == PetBattles::PET_BATTLE_TEAM_2)
|
||||
finalRound.NpcCreatureID[t] = team.Pets[0].CreatureID;
|
||||
|
||||
for (uint8 i = 0; i < team.PetCount; ++i)
|
||||
{
|
||||
WorldPackets::BattlePet::PetBattleFinalPet pet;
|
||||
pet.Guid = team.Pets[i].BattlePetGUID;
|
||||
pet.Level = team.Pets[i].Level;
|
||||
pet.Xp = team.Pets[i].Xp;
|
||||
pet.Health = team.Pets[i].Health;
|
||||
pet.MaxHealth = team.Pets[i].MaxHealth;
|
||||
pet.InitialLevel = team.Pets[i].Level; // TODO: track initial level before XP awards
|
||||
pet.Pboid = t * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + i;
|
||||
pet.Captured = team.Pets[i].IsCaptured;
|
||||
pet.Caged = false;
|
||||
pet.SeenAction = true;
|
||||
pet.AwardedXP = battle->CanAwardXP() && (battle->GetWinnerTeam() == t);
|
||||
finalRound.Pets.push_back(pet);
|
||||
}
|
||||
}
|
||||
|
||||
if (Player* p1 = battle->GetPlayerForTeam(PetBattles::PET_BATTLE_TEAM_1))
|
||||
p1->SendDirectMessage(finalRound.Write());
|
||||
// Send to player team 2 (PvP)
|
||||
if (Player* p2 = battle->GetPlayerForTeam(PetBattles::PET_BATTLE_TEAM_2))
|
||||
p2->SendDirectMessage(finalRound.Write());
|
||||
|
||||
@@ -438,10 +531,12 @@ void WorldSession::HandlePetBattleInput(WorldPackets::BattlePet::PetBattleInput&
|
||||
{
|
||||
WorldPackets::BattlePet::PetBattleFirstRound firstRound;
|
||||
firstRound.CurRound = battle->GetCurrentRound();
|
||||
firstRound.NextPetBattleState = static_cast<uint8>(battle->GetBattleState());
|
||||
firstRound.NextPetBattleState = static_cast<int8>(battle->GetBattleState());
|
||||
for (uint8 i = 0; i < PetBattles::MAX_PET_BATTLE_PLAYERS; ++i)
|
||||
BuildPetBattleRoundPlayerData(firstRound.Players[i], battle->GetTeam(i), battle, i);
|
||||
BuildRoundEffects(firstRound.Effects, battle);
|
||||
BuildRoundCooldowns(firstRound.Cooldowns, battle);
|
||||
BuildPetXDied(firstRound.PetXDied, battle);
|
||||
|
||||
if (Player* p1 = battle->GetPlayerForTeam(PetBattles::PET_BATTLE_TEAM_1))
|
||||
p1->SendDirectMessage(firstRound.Write());
|
||||
@@ -452,10 +547,12 @@ void WorldSession::HandlePetBattleInput(WorldPackets::BattlePet::PetBattleInput&
|
||||
{
|
||||
WorldPackets::BattlePet::PetBattleRoundResult roundResult;
|
||||
roundResult.CurRound = battle->GetCurrentRound();
|
||||
roundResult.NextPetBattleState = static_cast<uint8>(battle->GetBattleState());
|
||||
roundResult.NextPetBattleState = static_cast<int8>(battle->GetBattleState());
|
||||
for (uint8 i = 0; i < PetBattles::MAX_PET_BATTLE_PLAYERS; ++i)
|
||||
BuildPetBattleRoundPlayerData(roundResult.Players[i], battle->GetTeam(i), battle, i);
|
||||
BuildRoundEffects(roundResult.Effects, battle);
|
||||
BuildRoundCooldowns(roundResult.Cooldowns, battle);
|
||||
BuildPetXDied(roundResult.PetXDied, battle);
|
||||
|
||||
if (Player* p1 = battle->GetPlayerForTeam(PetBattles::PET_BATTLE_TEAM_1))
|
||||
p1->SendDirectMessage(roundResult.Write());
|
||||
@@ -495,9 +592,12 @@ void WorldSession::HandlePetBattleReplaceFrontPet(WorldPackets::BattlePet::PetBa
|
||||
|
||||
WorldPackets::BattlePet::PetBattleReplacementsMade replacements;
|
||||
replacements.CurRound = battle->GetCurrentRound();
|
||||
replacements.NextPetBattleState = static_cast<uint8>(battle->GetBattleState());
|
||||
replacements.NextPetBattleState = static_cast<int8>(battle->GetBattleState());
|
||||
for (uint8 i = 0; i < PetBattles::MAX_PET_BATTLE_PLAYERS; ++i)
|
||||
BuildPetBattleRoundPlayerData(replacements.Players[i], battle->GetTeam(i), battle, i);
|
||||
BuildRoundEffects(replacements.Effects, battle);
|
||||
BuildRoundCooldowns(replacements.Cooldowns, battle);
|
||||
BuildPetXDied(replacements.PetXDied, battle);
|
||||
|
||||
if (Player* p1 = battle->GetPlayerForTeam(PetBattles::PET_BATTLE_TEAM_1))
|
||||
p1->SendDirectMessage(replacements.Write());
|
||||
@@ -530,12 +630,31 @@ void WorldSession::HandlePetBattleQuitNotify(WorldPackets::BattlePet::PetBattleQ
|
||||
|
||||
// Send final round to both players
|
||||
WorldPackets::BattlePet::PetBattleFinalRound finalRound;
|
||||
finalRound.CurRound = battle->GetCurrentRound();
|
||||
finalRound.NextPetBattleState = static_cast<uint8>(battle->GetBattleState());
|
||||
for (uint8 i = 0; i < PetBattles::MAX_PET_BATTLE_PLAYERS; ++i)
|
||||
BuildPetBattleRoundPlayerData(finalRound.Players[i], battle->GetTeam(i), battle, i);
|
||||
finalRound.Abandoned = true;
|
||||
finalRound.PvpBattle = (battle->GetBattleType() == PetBattles::PET_BATTLE_TYPE_PVP || battle->GetBattleType() == PetBattles::PET_BATTLE_TYPE_LFPB);
|
||||
finalRound.Winners[battle->GetWinnerTeam()] = true;
|
||||
|
||||
for (uint8 t = 0; t < PetBattles::MAX_PET_BATTLE_PLAYERS; ++t)
|
||||
{
|
||||
PetBattles::PetBattleTeamData const& team = battle->GetTeam(t);
|
||||
for (uint8 i = 0; i < team.PetCount; ++i)
|
||||
{
|
||||
WorldPackets::BattlePet::PetBattleFinalPet pet;
|
||||
pet.Guid = team.Pets[i].BattlePetGUID;
|
||||
pet.Level = team.Pets[i].Level;
|
||||
pet.Xp = team.Pets[i].Xp;
|
||||
pet.Health = team.Pets[i].Health;
|
||||
pet.MaxHealth = team.Pets[i].MaxHealth;
|
||||
pet.InitialLevel = team.Pets[i].Level;
|
||||
pet.Pboid = t * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + i;
|
||||
pet.Captured = team.Pets[i].IsCaptured;
|
||||
pet.Caged = false;
|
||||
pet.SeenAction = true;
|
||||
pet.AwardedXP = false;
|
||||
finalRound.Pets.push_back(pet);
|
||||
}
|
||||
}
|
||||
|
||||
if (Player* p1 = battle->GetPlayerForTeam(PetBattles::PET_BATTLE_TEAM_1))
|
||||
p1->SendDirectMessage(finalRound.Write());
|
||||
if (Player* p2 = battle->GetPlayerForTeam(PetBattles::PET_BATTLE_TEAM_2))
|
||||
@@ -655,7 +774,11 @@ void WorldSession::HandlePetBattleRequestPVP(WorldPackets::BattlePet::PetBattleR
|
||||
// Send PVP challenge to target
|
||||
WorldPackets::BattlePet::PetBattlePVPChallenge challenge;
|
||||
challenge.ChallengerGUID = player->GetGUID();
|
||||
challenge.BattleOrigin = player->GetPosition();
|
||||
challenge.Location.LocationResult = 0;
|
||||
challenge.Location.BattleOrigin = player->GetPosition();
|
||||
challenge.Location.BattleFacing = player->GetAbsoluteAngle(target);
|
||||
challenge.Location.PlayerPositions[0] = player->GetPosition();
|
||||
challenge.Location.PlayerPositions[1] = target->GetPosition();
|
||||
target->SendDirectMessage(challenge.Write());
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
#include "BattlePetPackets.h"
|
||||
#include "MovementPackets.h"
|
||||
#include "PacketOperators.h"
|
||||
|
||||
namespace WorldPackets::BattlePet
|
||||
@@ -327,10 +326,13 @@ namespace WorldPackets::BattlePet
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, PetBattleEffectTargetInfo const& target)
|
||||
{
|
||||
data << uint8(target.Type);
|
||||
data << Bits<3>(target.Type);
|
||||
data.FlushBits();
|
||||
|
||||
data << uint8(target.Petx);
|
||||
data << int32(target.Param1);
|
||||
data << int32(target.Param2);
|
||||
|
||||
for (int32 param : target.Params)
|
||||
data << int32(param);
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -339,10 +341,11 @@ namespace WorldPackets::BattlePet
|
||||
{
|
||||
data << uint32(effect.AbilityEffectID);
|
||||
data << uint16(effect.Flags);
|
||||
data << uint8(effect.SourceAuraPetInstance);
|
||||
data << uint16(effect.SourceAuraInstanceID);
|
||||
data << uint16(effect.TurnInstanceID);
|
||||
data << int8(effect.PetBattleEffectType);
|
||||
data << uint8(effect.CasterPBOID);
|
||||
data << uint8(effect.StackDepth);
|
||||
data << uint8(effect.PetBattleEffectType);
|
||||
data << int8(effect.CasterPBOID);
|
||||
|
||||
data << uint32(effect.Targets.size());
|
||||
for (PetBattleEffectTargetInfo const& target : effect.Targets)
|
||||
@@ -356,46 +359,76 @@ namespace WorldPackets::BattlePet
|
||||
data << int32(cd.AbilityID);
|
||||
data << int16(cd.CooldownRemaining);
|
||||
data << int16(cd.LockdownRemaining);
|
||||
data << int8(cd.AbilityIndex);
|
||||
data << uint8(cd.Pboid);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void WriteRoundPlayerData(ByteBuffer& data, PetBattleRoundPlayerData const& player)
|
||||
// Writes the common round result wire format shared by FirstRound, RoundResult, and ReplacementsMade
|
||||
static void WriteRoundResult(ByteBuffer& data, uint32 curRound, int8 nextPetBattleState,
|
||||
std::array<PetBattleRoundPlayerData, 2> const& players,
|
||||
std::vector<PetBattleEffectInfo> const& effects,
|
||||
std::vector<PetBattleCooldownInfo> const& cooldowns,
|
||||
std::vector<int8> const& petXDied)
|
||||
{
|
||||
data << uint8(player.NextInputFlags);
|
||||
data << int8(player.NextTrapStatus);
|
||||
data << uint16(player.RoundTimeSecs);
|
||||
data << uint32(curRound);
|
||||
data << int8(nextPetBattleState);
|
||||
|
||||
data << uint32(player.PetCooldowns.size());
|
||||
for (std::vector<PetBattleCooldownInfo> const& petCds : player.PetCooldowns)
|
||||
{
|
||||
data << uint32(petCds.size());
|
||||
for (PetBattleCooldownInfo const& cd : petCds)
|
||||
data << cd;
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteRoundEffects(ByteBuffer& data, std::vector<PetBattleEffectInfo> const& effects)
|
||||
{
|
||||
data << uint32(effects.size());
|
||||
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
data << uint8(players[i].NextInputFlags);
|
||||
data << int8(players[i].NextTrapStatus);
|
||||
data << uint16(players[i].RoundTimeSecs);
|
||||
}
|
||||
|
||||
data << uint32(cooldowns.size());
|
||||
|
||||
for (PetBattleEffectInfo const& effect : effects)
|
||||
data << effect;
|
||||
|
||||
for (PetBattleCooldownInfo const& cd : cooldowns)
|
||||
data << cd;
|
||||
|
||||
data << Bits<3>(petXDied.size());
|
||||
data.FlushBits();
|
||||
|
||||
for (int8 pboid : petXDied)
|
||||
data << int8(pboid);
|
||||
}
|
||||
|
||||
// ---- CMSG Read() implementations ----
|
||||
|
||||
static void ReadPetBattleLocation(ByteBuffer& data, PetBattleLocation& loc)
|
||||
{
|
||||
data >> loc.LocationResult;
|
||||
data >> loc.BattleOrigin.m_positionX;
|
||||
data >> loc.BattleOrigin.m_positionY;
|
||||
data >> loc.BattleOrigin.m_positionZ;
|
||||
data >> loc.BattleFacing;
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
data >> loc.PlayerPositions[i].m_positionX;
|
||||
data >> loc.PlayerPositions[i].m_positionY;
|
||||
data >> loc.PlayerPositions[i].m_positionZ;
|
||||
}
|
||||
}
|
||||
|
||||
void PetBattleRequestWild::Read()
|
||||
{
|
||||
_worldPacket >> TargetGUID;
|
||||
_worldPacket >> LocationInfo;
|
||||
BattleOrigin = LocationInfo.pos;
|
||||
ReadPetBattleLocation(_worldPacket, Location);
|
||||
}
|
||||
|
||||
void PetBattleInput::Read()
|
||||
{
|
||||
_worldPacket >> MoveType;
|
||||
_worldPacket >> AbilityID;
|
||||
_worldPacket >> NewFrontPetIndex;
|
||||
_worldPacket >> DebugFlags;
|
||||
_worldPacket >> BattleInterrupted;
|
||||
_worldPacket >> AbilityID;
|
||||
_worldPacket >> Round;
|
||||
_worldPacket >> Bits<1>(IgnoreAbandonPenalty);
|
||||
}
|
||||
@@ -408,8 +441,7 @@ namespace WorldPackets::BattlePet
|
||||
void PetBattleRequestPVP::Read()
|
||||
{
|
||||
_worldPacket >> TargetGUID;
|
||||
_worldPacket >> LocationInfo;
|
||||
BattleOrigin = LocationInfo.pos;
|
||||
ReadPetBattleLocation(_worldPacket, Location);
|
||||
}
|
||||
|
||||
void PetBattleQueueProposeMatchResult::Read()
|
||||
@@ -425,12 +457,24 @@ namespace WorldPackets::BattlePet
|
||||
|
||||
// ---- SMSG Write() implementations ----
|
||||
|
||||
static void WritePetBattleLocation(ByteBuffer& data, PetBattleLocation const& loc)
|
||||
{
|
||||
data << int32(loc.LocationResult);
|
||||
data << loc.BattleOrigin.m_positionX;
|
||||
data << loc.BattleOrigin.m_positionY;
|
||||
data << loc.BattleOrigin.m_positionZ;
|
||||
data << loc.BattleFacing;
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
data << loc.PlayerPositions[i].m_positionX;
|
||||
data << loc.PlayerPositions[i].m_positionY;
|
||||
data << loc.PlayerPositions[i].m_positionZ;
|
||||
}
|
||||
}
|
||||
|
||||
WorldPacket const* PetBattleFinalizeLocation::Write()
|
||||
{
|
||||
_worldPacket << Location.m_positionX;
|
||||
_worldPacket << Location.m_positionY;
|
||||
_worldPacket << Location.m_positionZ;
|
||||
_worldPacket << Location.GetOrientation();
|
||||
WritePetBattleLocation(_worldPacket, Location);
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
@@ -474,59 +518,59 @@ namespace WorldPackets::BattlePet
|
||||
|
||||
WorldPacket const* PetBattleFirstRound::Write()
|
||||
{
|
||||
_worldPacket << uint32(CurRound);
|
||||
_worldPacket << uint8(NextPetBattleState);
|
||||
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
WriteRoundPlayerData(_worldPacket, Players[i]);
|
||||
|
||||
WriteRoundEffects(_worldPacket, Effects);
|
||||
WriteRoundResult(_worldPacket, CurRound, NextPetBattleState, Players, Effects, Cooldowns, PetXDied);
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
WorldPacket const* PetBattleRoundResult::Write()
|
||||
{
|
||||
_worldPacket << uint32(CurRound);
|
||||
_worldPacket << uint8(NextPetBattleState);
|
||||
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
WriteRoundPlayerData(_worldPacket, Players[i]);
|
||||
|
||||
WriteRoundEffects(_worldPacket, Effects);
|
||||
WriteRoundResult(_worldPacket, CurRound, NextPetBattleState, Players, Effects, Cooldowns, PetXDied);
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
WorldPacket const* PetBattleReplacementsMade::Write()
|
||||
{
|
||||
_worldPacket << uint32(CurRound);
|
||||
_worldPacket << uint8(NextPetBattleState);
|
||||
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
WriteRoundPlayerData(_worldPacket, Players[i]);
|
||||
WriteRoundResult(_worldPacket, CurRound, NextPetBattleState, Players, Effects, Cooldowns, PetXDied);
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& data, PetBattleFinalPet const& pet)
|
||||
{
|
||||
data << pet.Guid;
|
||||
data << uint16(pet.Level);
|
||||
data << uint16(pet.Xp);
|
||||
data << int32(pet.Health);
|
||||
data << int32(pet.MaxHealth);
|
||||
data << uint16(pet.InitialLevel);
|
||||
data << uint8(pet.Pboid);
|
||||
|
||||
data << Bits<1>(pet.Captured);
|
||||
data << Bits<1>(pet.Caged);
|
||||
data << Bits<1>(pet.SeenAction);
|
||||
data << Bits<1>(pet.AwardedXP);
|
||||
data.FlushBits();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
WorldPacket const* PetBattleFinalRound::Write()
|
||||
{
|
||||
_worldPacket << uint32(CurRound);
|
||||
_worldPacket << uint8(NextPetBattleState);
|
||||
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
WriteRoundPlayerData(_worldPacket, Players[i]);
|
||||
|
||||
WriteRoundEffects(_worldPacket, Effects);
|
||||
|
||||
_worldPacket << Bits<1>(AbandonedPVP);
|
||||
_worldPacket << Bits<1>(PVPWinner);
|
||||
_worldPacket << Bits<1>(Abandoned);
|
||||
_worldPacket << Bits<1>(PvpBattle);
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
_worldPacket << Bits<1>(Winners[i]);
|
||||
_worldPacket.FlushBits();
|
||||
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
_worldPacket << uint32(NpcCreatureID[i]);
|
||||
_worldPacket << int32(NpcCreatureID[i]);
|
||||
|
||||
_worldPacket << uint32(Pets.size());
|
||||
|
||||
for (PetBattleFinalPet const& pet : Pets)
|
||||
_worldPacket << pet;
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
@@ -541,10 +585,7 @@ namespace WorldPackets::BattlePet
|
||||
WorldPacket const* PetBattlePVPChallenge::Write()
|
||||
{
|
||||
_worldPacket << ChallengerGUID;
|
||||
_worldPacket << BattleOrigin.m_positionX;
|
||||
_worldPacket << BattleOrigin.m_positionY;
|
||||
_worldPacket << BattleOrigin.m_positionZ;
|
||||
_worldPacket << BattleOrigin.GetOrientation();
|
||||
WritePetBattleLocation(_worldPacket, Location);
|
||||
|
||||
return &_worldPacket;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "Packet.h"
|
||||
#include "PacketUtilities.h"
|
||||
#include "MovementInfo.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Optional.h"
|
||||
#include "Position.h"
|
||||
@@ -349,20 +348,23 @@ namespace WorldPackets
|
||||
|
||||
struct PetBattleEffectTargetInfo
|
||||
{
|
||||
uint8 Type = 0;
|
||||
uint8 Type = 0; // Written as 3 bits + ResetBitReader
|
||||
uint8 Petx = 0;
|
||||
int32 Param1 = 0;
|
||||
int32 Param2 = 0;
|
||||
// Variable-length params based on Type:
|
||||
// 0: nothing, 1: 4 int32s (aura), 2: 2 int32s (state), 3: 1 int32 (health),
|
||||
// 4: 1 int32 (stat), 5: 1 int32 (trigger), 6: 3 int32s (cooldown), 7: 1 int32 (broadcast)
|
||||
std::vector<int32> Params;
|
||||
};
|
||||
|
||||
struct PetBattleEffectInfo
|
||||
{
|
||||
uint32 AbilityEffectID = 0;
|
||||
uint16 Flags = 0;
|
||||
uint8 SourceAuraPetInstance = 0;
|
||||
uint16 SourceAuraInstanceID = 0;
|
||||
uint16 TurnInstanceID = 0;
|
||||
int8 PetBattleEffectType = 0;
|
||||
uint8 CasterPBOID = 0;
|
||||
uint8 StackDepth = 0;
|
||||
uint8 PetBattleEffectType = 0;
|
||||
int8 CasterPBOID = 0;
|
||||
std::vector<PetBattleEffectTargetInfo> Targets;
|
||||
};
|
||||
|
||||
@@ -371,6 +373,8 @@ namespace WorldPackets
|
||||
int32 AbilityID = 0;
|
||||
int16 CooldownRemaining = 0;
|
||||
int16 LockdownRemaining = 0;
|
||||
int8 AbilityIndex = 0;
|
||||
uint8 Pboid = 0;
|
||||
};
|
||||
|
||||
struct PetBattleRoundPlayerData
|
||||
@@ -378,7 +382,15 @@ namespace WorldPackets
|
||||
uint8 NextInputFlags = 0;
|
||||
int8 NextTrapStatus = 0;
|
||||
uint16 RoundTimeSecs = 0;
|
||||
std::vector<std::vector<PetBattleCooldownInfo>> PetCooldowns; // per pet
|
||||
};
|
||||
|
||||
// Shared location structure used by multiple pet battle packets
|
||||
struct PetBattleLocation
|
||||
{
|
||||
int32 LocationResult = 0;
|
||||
Position BattleOrigin; // Only XYZ written
|
||||
float BattleFacing = 0.0f;
|
||||
std::array<Position, 2> PlayerPositions; // Only XYZ written
|
||||
};
|
||||
|
||||
// ---- CMSG packets ----
|
||||
@@ -391,8 +403,7 @@ namespace WorldPackets
|
||||
void Read() override;
|
||||
|
||||
ObjectGuid TargetGUID;
|
||||
MovementInfo LocationInfo;
|
||||
Position BattleOrigin;
|
||||
PetBattleLocation Location;
|
||||
};
|
||||
|
||||
class PetBattleInput final : public ClientPacket
|
||||
@@ -402,10 +413,12 @@ namespace WorldPackets
|
||||
|
||||
void Read() override;
|
||||
|
||||
uint8 MoveType = 0;
|
||||
int32 AbilityID = 0;
|
||||
int32 MoveType = 0;
|
||||
int8 NewFrontPetIndex = -1;
|
||||
uint8 Round = 0;
|
||||
int32 DebugFlags = 0;
|
||||
uint8 BattleInterrupted = 0;
|
||||
int32 AbilityID = 0;
|
||||
int32 Round = 0;
|
||||
bool IgnoreAbandonPenalty = false;
|
||||
};
|
||||
|
||||
@@ -443,8 +456,7 @@ namespace WorldPackets
|
||||
void Read() override;
|
||||
|
||||
ObjectGuid TargetGUID;
|
||||
MovementInfo LocationInfo;
|
||||
Position BattleOrigin;
|
||||
PetBattleLocation Location;
|
||||
};
|
||||
|
||||
class JoinPetBattleQueue final : public ClientPacket
|
||||
@@ -509,7 +521,7 @@ namespace WorldPackets
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
Position Location;
|
||||
PetBattleLocation Location;
|
||||
};
|
||||
|
||||
class PetBattleInitialUpdate final : public ServerPacket
|
||||
@@ -541,9 +553,11 @@ namespace WorldPackets
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
uint32 CurRound = 0;
|
||||
uint8 NextPetBattleState = 0;
|
||||
int8 NextPetBattleState = 0;
|
||||
std::array<PetBattleRoundPlayerData, 2> Players;
|
||||
std::vector<PetBattleEffectInfo> Effects;
|
||||
std::vector<PetBattleCooldownInfo> Cooldowns;
|
||||
std::vector<int8> PetXDied;
|
||||
};
|
||||
|
||||
class PetBattleRoundResult final : public ServerPacket
|
||||
@@ -554,9 +568,11 @@ namespace WorldPackets
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
uint32 CurRound = 0;
|
||||
uint8 NextPetBattleState = 0;
|
||||
int8 NextPetBattleState = 0;
|
||||
std::array<PetBattleRoundPlayerData, 2> Players;
|
||||
std::vector<PetBattleEffectInfo> Effects;
|
||||
std::vector<PetBattleCooldownInfo> Cooldowns;
|
||||
std::vector<int8> PetXDied;
|
||||
};
|
||||
|
||||
class PetBattleReplacementsMade final : public ServerPacket
|
||||
@@ -567,8 +583,26 @@ namespace WorldPackets
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
uint32 CurRound = 0;
|
||||
uint8 NextPetBattleState = 0;
|
||||
int8 NextPetBattleState = 0;
|
||||
std::array<PetBattleRoundPlayerData, 2> Players;
|
||||
std::vector<PetBattleEffectInfo> Effects;
|
||||
std::vector<PetBattleCooldownInfo> Cooldowns;
|
||||
std::vector<int8> PetXDied;
|
||||
};
|
||||
|
||||
struct PetBattleFinalPet
|
||||
{
|
||||
ObjectGuid Guid;
|
||||
uint16 Level = 0;
|
||||
uint16 Xp = 0;
|
||||
int32 Health = 0;
|
||||
int32 MaxHealth = 0;
|
||||
uint16 InitialLevel = 0;
|
||||
uint8 Pboid = 0;
|
||||
bool Captured = false;
|
||||
bool Caged = false;
|
||||
bool SeenAction = false;
|
||||
bool AwardedXP = false;
|
||||
};
|
||||
|
||||
class PetBattleFinalRound final : public ServerPacket
|
||||
@@ -578,15 +612,11 @@ namespace WorldPackets
|
||||
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
uint32 CurRound = 0;
|
||||
uint8 NextPetBattleState = 0;
|
||||
std::array<PetBattleRoundPlayerData, 2> Players;
|
||||
std::vector<PetBattleEffectInfo> Effects;
|
||||
// Final state includes flags
|
||||
bool AbandonedPVP = false;
|
||||
bool PVPWinner = false;
|
||||
bool Abandoned = false;
|
||||
bool PvpBattle = false;
|
||||
std::array<bool, 2> Winners = {};
|
||||
std::array<uint32, 2> NpcCreatureID = {};
|
||||
std::array<int32, 2> NpcCreatureID = {};
|
||||
std::vector<PetBattleFinalPet> Pets;
|
||||
};
|
||||
|
||||
class PetBattleFinished final : public ServerPacket
|
||||
@@ -615,7 +645,7 @@ namespace WorldPackets
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
ObjectGuid ChallengerGUID;
|
||||
Position BattleOrigin;
|
||||
PetBattleLocation Location;
|
||||
};
|
||||
|
||||
class PetBattleQueueStatus final : public ServerPacket
|
||||
|
||||
Reference in New Issue
Block a user