Pet Battles: Fix weather env slot, generic detection, multi-hit overkill

- Weather now targets environment slot 2 (PBOID 8 / PetbattleEnviros::Weather)
  instead of slot 0 (Pad0), matching client expectations
- Replace hardcoded weather ability name list with generic DB2-driven detection:
  PropsID chain walk + reverse AuraBattlePetAbilityID walk + BattlePetAbilityState
  diagnostic logging to discover remaining weather abilities
- Multi-hit abilities now stop when the target dies (prevents overkill)
- Battle positioning: offset player 5 units behind pet along facing axis
This commit is contained in:
luis
2026-03-12 08:37:35 -03:00
parent cf2fcb272a
commit 05828b9eac
4 changed files with 223 additions and 94 deletions
+61 -55
View File
@@ -898,9 +898,13 @@ namespace PetBattles
return;
}
// Process each effect in order
// Process each effect in order ? stop if defender dies (prevents multi-hit overkill)
for (BattlePetAbilityEffectEntry const* effectEntry : *effects)
{
// In retail, multi-hit abilities stop when the target dies
if (!_teams[defenderTeam].Pets[defenderPet].IsAlive())
break;
TC_LOG_DEBUG("server.loading", "PetBattle: ProcessEffect effectID={} propsID={} auraAbilityID={} visualID={} Params=[{},{},{},{},{},{}]",
effectEntry->ID, effectEntry->BattlePetEffectPropertiesID,
effectEntry->AuraBattlePetAbilityID, effectEntry->BattlePetVisualID,
@@ -1015,40 +1019,41 @@ namespace PetBattles
if (auraDuration <= 0)
auraDuration = 3; // Default duration
// Weather auras: if AuraBattlePetAbilityID is a weather ability, apply to environment
if (effect->AuraBattlePetAbilityID != 0 && sPetBattleMgr->IsWeatherAbility(effect->AuraBattlePetAbilityID))
// Weather auras: if the cast ability is a weather ability, apply aura to environment
if (sPetBattleMgr->IsWeatherAbility(abilityID))
{
TC_LOG_DEBUG("server.loading", "PetBattle WEATHER_AURA: effectID={} auraAbilityID={} duration={}",
effect->ID, effect->AuraBattlePetAbilityID, auraDuration);
uint32 weatherAuraAbilityID = effect->AuraBattlePetAbilityID ? effect->AuraBattlePetAbilityID : abilityID;
TC_LOG_DEBUG("server.loading", "PetBattle WEATHER_AURA: effectID={} castAbility={} auraAbilityID={} duration={}",
effect->ID, abilityID, weatherAuraAbilityID, auraDuration);
// Cancel existing weather on slot 0 if any
if (_environments[0].AbilityID != 0 && _environments[0].AuraInstanceID != 0)
// Cancel existing weather on weather slot (PetbattleEnviros::Weather = 2, PBOID 8)
if (_environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID != 0 && _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID != 0)
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
cancelEffect.TargetEnvSlot = 0;
cancelEffect.Param1 = _environments[0].AuraInstanceID;
cancelEffect.Param2 = _environments[0].AbilityID;
cancelEffect.TargetEnvSlot = PET_BATTLE_WEATHER_ENV_SLOT;
cancelEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
cancelEffect.Param2 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID;
_roundEffects.push_back(cancelEffect);
}
// Set up environment slot 0 with the weather aura
_environments[0].AbilityID = effect->AuraBattlePetAbilityID;
_environments[0].WeatherType = PET_BATTLE_WEATHER_NONE; // Gameplay mods come from SET_STATE effects
_environments[0].RemainingRounds = auraDuration;
_environments[0].CasterTeam = attackerTeam;
_environments[0].AuraInstanceID = _nextAuraInstanceID++;
_environments[0].CurrentRound = _currentRound;
// Set up weather environment slot with the weather aura
_environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID = weatherAuraAbilityID;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType = PET_BATTLE_WEATHER_NONE; // Gameplay mods come from SET_STATE effects
_environments[PET_BATTLE_WEATHER_ENV_SLOT].RemainingRounds = auraDuration;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].CasterTeam = attackerTeam;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID = _nextAuraInstanceID++;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].CurrentRound = _currentRound;
// Emit AURA_APPLY targeting environment PBOID 6 (env slot 0)
// Emit AURA_APPLY targeting environment PBOID 8 (PetbattleEnviros::Weather)
PetBattleRoundEffect applyEffect;
applyEffect.AbilityEffectID = effect->ID;
applyEffect.EffectType = PET_BATTLE_EFFECT_AURA_APPLY;
applyEffect.SourceTeam = attackerTeam;
applyEffect.SourcePet = attackerPet;
applyEffect.TargetEnvSlot = 0;
applyEffect.Param1 = _environments[0].AuraInstanceID;
applyEffect.Param2 = effect->AuraBattlePetAbilityID;
applyEffect.TargetEnvSlot = PET_BATTLE_WEATHER_ENV_SLOT;
applyEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
applyEffect.Param2 = weatherAuraAbilityID;
applyEffect.Param3 = auraDuration;
applyEffect.Param4 = _currentRound;
_roundEffects.push_back(applyEffect);
@@ -1131,36 +1136,37 @@ namespace PetBattles
auraDuration = 3;
// Weather periodic auras (e.g. Moonlight HoT): route to environment
if (effect->AuraBattlePetAbilityID != 0 && sPetBattleMgr->IsWeatherAbility(effect->AuraBattlePetAbilityID))
if (sPetBattleMgr->IsWeatherAbility(abilityID))
{
TC_LOG_DEBUG("server.loading", "PetBattle WEATHER_PERIODIC: effectID={} auraAbilityID={} duration={}",
effect->ID, effect->AuraBattlePetAbilityID, auraDuration);
uint32 weatherAuraAbilityID = effect->AuraBattlePetAbilityID ? effect->AuraBattlePetAbilityID : abilityID;
TC_LOG_DEBUG("server.loading", "PetBattle WEATHER_PERIODIC: effectID={} castAbility={} auraAbilityID={} duration={}",
effect->ID, abilityID, weatherAuraAbilityID, auraDuration);
if (_environments[0].AbilityID != 0 && _environments[0].AuraInstanceID != 0)
if (_environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID != 0 && _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID != 0)
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
cancelEffect.TargetEnvSlot = 0;
cancelEffect.Param1 = _environments[0].AuraInstanceID;
cancelEffect.Param2 = _environments[0].AbilityID;
cancelEffect.TargetEnvSlot = PET_BATTLE_WEATHER_ENV_SLOT;
cancelEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
cancelEffect.Param2 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID;
_roundEffects.push_back(cancelEffect);
}
_environments[0].AbilityID = effect->AuraBattlePetAbilityID;
_environments[0].WeatherType = PET_BATTLE_WEATHER_NONE;
_environments[0].RemainingRounds = auraDuration;
_environments[0].CasterTeam = attackerTeam;
_environments[0].AuraInstanceID = _nextAuraInstanceID++;
_environments[0].CurrentRound = _currentRound;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID = weatherAuraAbilityID;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType = PET_BATTLE_WEATHER_NONE;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].RemainingRounds = auraDuration;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].CasterTeam = attackerTeam;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID = _nextAuraInstanceID++;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].CurrentRound = _currentRound;
PetBattleRoundEffect applyEffect;
applyEffect.AbilityEffectID = effect->ID;
applyEffect.EffectType = PET_BATTLE_EFFECT_AURA_APPLY;
applyEffect.SourceTeam = attackerTeam;
applyEffect.SourcePet = attackerPet;
applyEffect.TargetEnvSlot = 0;
applyEffect.Param1 = _environments[0].AuraInstanceID;
applyEffect.Param2 = effect->AuraBattlePetAbilityID;
applyEffect.TargetEnvSlot = PET_BATTLE_WEATHER_ENV_SLOT;
applyEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
applyEffect.Param2 = weatherAuraAbilityID;
applyEffect.Param3 = auraDuration;
applyEffect.Param4 = _currentRound;
_roundEffects.push_back(applyEffect);
@@ -1336,7 +1342,7 @@ namespace PetBattles
roundEffect.EffectType = PET_BATTLE_EFFECT_SET_STATE;
roundEffect.SourceTeam = attackerTeam;
roundEffect.SourcePet = attackerPet;
roundEffect.TargetEnvSlot = 0; // Environment slot 0
roundEffect.TargetEnvSlot = PET_BATTLE_WEATHER_ENV_SLOT;
roundEffect.Param1 = stateID;
roundEffect.Param2 = stateValue;
_roundEffects.push_back(roundEffect);
@@ -1833,33 +1839,33 @@ namespace PetBattles
return;
}
// Cancel existing weather on slot 0 if any
if (_environments[0].WeatherType != PET_BATTLE_WEATHER_NONE && _environments[0].AuraInstanceID != 0)
// Cancel existing weather on weather slot (PetbattleEnviros::Weather = 2, PBOID 8)
if (_environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType != PET_BATTLE_WEATHER_NONE && _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID != 0)
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
cancelEffect.TargetEnvSlot = 0;
cancelEffect.Param1 = _environments[0].AuraInstanceID;
cancelEffect.Param2 = _environments[0].AbilityID;
cancelEffect.TargetEnvSlot = PET_BATTLE_WEATHER_ENV_SLOT;
cancelEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
cancelEffect.Param2 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID;
_roundEffects.push_back(cancelEffect);
}
// Slot 0 is the shared battlefield weather
_environments[0].AbilityID = abilityID;
_environments[0].WeatherType = weatherType;
_environments[0].RemainingRounds = duration;
_environments[0].CasterTeam = casterTeam;
_environments[0].AuraInstanceID = _nextAuraInstanceID++;
_environments[0].CurrentRound = _currentRound;
// Weather slot (PetbattleEnviros::Weather = 2) is the shared battlefield weather
_environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID = abilityID;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType = weatherType;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].RemainingRounds = duration;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].CasterTeam = casterTeam;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID = _nextAuraInstanceID++;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].CurrentRound = _currentRound;
// Emit AURA_APPLY targeting environment PBOID so client shows weather
// Emit AURA_APPLY targeting environment PBOID 8 (EnvWeather)
PetBattleRoundEffect applyEffect;
applyEffect.AbilityEffectID = abilityID;
applyEffect.EffectType = PET_BATTLE_EFFECT_AURA_APPLY;
applyEffect.SourceTeam = casterTeam;
applyEffect.SourcePet = _teams[casterTeam].FrontPetIndex;
applyEffect.TargetEnvSlot = 0; // Environment slot 0 = shared battlefield weather
applyEffect.Param1 = _environments[0].AuraInstanceID;
applyEffect.TargetEnvSlot = PET_BATTLE_WEATHER_ENV_SLOT;
applyEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
applyEffect.Param2 = abilityID;
applyEffect.Param3 = duration;
applyEffect.Param4 = _currentRound;
@@ -1942,7 +1948,7 @@ namespace PetBattles
float PetBattle::GetWeatherDamageModifier(PetBattlePetType abilityType) const
{
PetBattleWeatherType weather = _environments[0].WeatherType;
PetBattleWeatherType weather = _environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType;
switch (weather)
{
@@ -1964,7 +1970,7 @@ namespace PetBattles
float PetBattle::GetWeatherHealingModifier() const
{
PetBattleWeatherType weather = _environments[0].WeatherType;
PetBattleWeatherType weather = _environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType;
switch (weather)
{
@@ -33,8 +33,9 @@ namespace PetBattles
static constexpr uint32 PET_BATTLE_MAX_GAME_LENGTH = 1800; // 30 minutes total
static constexpr uint32 PET_BATTLE_TRAP_ABILITY_ID = 427; // "Trap" ability used for capture
static constexpr uint32 MAX_PET_BATTLE_AURAS = 10; // max auras on a single pet
static constexpr uint32 MAX_PET_BATTLE_ENVIRONMENTS = 3; // battlefield + per-team
static constexpr uint32 MAX_PET_BATTLE_ENVIRONMENTS = 3; // Pad0(0), Pad1(1), Weather(2)
static constexpr uint32 PBOID_ENVIRONMENT_BASE = MAX_PET_BATTLE_PLAYERS * MAX_PET_BATTLE_TEAM_SIZE; // PBOID 6 = environment slot 0
static constexpr uint32 PET_BATTLE_WEATHER_ENV_SLOT = 2; // PetbattleEnviros::Weather = 2 (PBOID 8)
static constexpr float PASSIVE_HUMANOID_HEAL_PCT = 0.04f; // 4% max HP each round
static constexpr float PASSIVE_DRAGONKIN_DAMAGE_BONUS = 0.50f;
static constexpr float PASSIVE_FLYING_SPEED_BONUS = 0.50f;
+143 -21
View File
@@ -117,40 +117,162 @@ namespace PetBattles
// Build the BattlePetEffectPropertiesID -> action type mapping
BuildEffectActionMap();
// Build weather ability ID set: find abilities whose effects use WEATHER_SET PropsIDs
// Walk: WEATHER_SET PropsIDs ? effects using those PropsIDs ? turns ? abilities
// Build weather ability ID set using DB2 data (generic approach)
// Weather abilities are identified by:
// 1. Having effects with WEATHER_SET PropsID (effects that set weatherState on environment)
// 2. Having aura effects whose sub-abilities set weather state
// 3. Having BattlePetAbilityState entries that set weather-related states
{
// Step 1: collect all PropsIDs classified as WEATHER_SET
// Step 1: Find PropsIDs classified as WEATHER_SET
std::unordered_set<uint16> weatherPropsIDs;
for (auto const& [propsID, action] : _effectActionMap)
if (action == PET_BATTLE_EFFECT_ACTION_WEATHER_SET)
weatherPropsIDs.insert(propsID);
// Step 2: find all turns containing effects with weather PropsIDs
// Step 2: Find abilities containing effects with weather PropsIDs (traces effect ? turn ? ability)
std::unordered_set<uint32> weatherTurnIDs;
for (BattlePetAbilityEffectEntry const* effect : sBattlePetAbilityEffectStore)
if (weatherPropsIDs.count(effect->BattlePetEffectPropertiesID))
weatherTurnIDs.insert(effect->BattlePetAbilityTurnID);
// Step 3: find all abilities owning those turns
for (BattlePetAbilityTurnEntry const* turn : sBattlePetAbilityTurnStore)
if (weatherTurnIDs.count(turn->ID))
_weatherAbilityIDs.insert(turn->BattlePetAbilityID);
// Step 4: also add abilities referenced by AuraBattlePetAbilityID from weather turn effects
// (the aura effects reference weather abilities via AuraBattlePetAbilityID)
// Step 3: For found weather abilities, also collect their AuraBattlePetAbilityIDs
// (sub-abilities used as weather auras)
std::unordered_set<uint32> allWeatherTurnIDs;
for (BattlePetAbilityTurnEntry const* turn : sBattlePetAbilityTurnStore)
if (_weatherAbilityIDs.count(turn->BattlePetAbilityID))
allWeatherTurnIDs.insert(turn->ID);
for (BattlePetAbilityEffectEntry const* effect : sBattlePetAbilityEffectStore)
{
if (weatherTurnIDs.count(effect->BattlePetAbilityTurnID) && effect->AuraBattlePetAbilityID != 0)
if (allWeatherTurnIDs.count(effect->BattlePetAbilityTurnID) && effect->AuraBattlePetAbilityID != 0)
_weatherAbilityIDs.insert(effect->AuraBattlePetAbilityID);
// Step 4: Reverse walk ? find abilities whose AuraBattlePetAbilityID is in the weather set
// This catches parent abilities that apply weather auras but don't have WEATHER_SET effects directly
bool foundNew = true;
while (foundNew)
{
foundNew = false;
for (BattlePetAbilityEffectEntry const* effect : sBattlePetAbilityEffectStore)
{
if (effect->AuraBattlePetAbilityID == 0)
continue;
if (!_weatherAbilityIDs.count(effect->AuraBattlePetAbilityID))
continue;
// Find the parent ability of this effect
BattlePetAbilityTurnEntry const* turn = sBattlePetAbilityTurnStore.LookupEntry(effect->BattlePetAbilityTurnID);
if (!turn)
continue;
if (_weatherAbilityIDs.insert(turn->BattlePetAbilityID).second)
foundNew = true;
}
}
TC_LOG_INFO("server.loading", ">> Found {} weather ability IDs from {} weather PropsIDs",
uint32(_weatherAbilityIDs.size()), uint32(weatherPropsIDs.size()));
for (uint32 abilityID : _weatherAbilityIDs)
// Step 5: BattlePetAbilityState-based detection
// Build a map of abilityID ? set of stateIDs from BattlePetAbilityState DB2
std::unordered_map<uint32, std::vector<std::pair<uint32, int32>>> abilityStates;
for (BattlePetAbilityStateEntry const* entry : sBattlePetAbilityStateStore)
abilityStates[entry->BattlePetAbilityID].push_back({ entry->BattlePetStateID, entry->Value });
// Diagnostic: log states for abilities already found as weather
for (uint32 abilID : _weatherAbilityIDs)
{
BattlePetAbilityEntry const* ability = sBattlePetAbilityStore.LookupEntry(abilityID);
TC_LOG_INFO("server.loading", " WeatherAbility: ID={} name={}", abilityID,
auto it = abilityStates.find(abilID);
if (it != abilityStates.end())
{
for (auto const& [stateID, value] : it->second)
TC_LOG_INFO("server.loading", " WeatherAbil {} has AbilityState: stateID={} value={}", abilID, stateID, value);
}
}
// Diagnostic: for abilities NOT found as weather, check if any share stateIDs with weather abilities
// This helps discover the weather state ID pattern
std::unordered_set<uint32> weatherStateIDs;
for (uint32 abilID : _weatherAbilityIDs)
{
auto it = abilityStates.find(abilID);
if (it != abilityStates.end())
for (auto const& [stateID, value] : it->second)
weatherStateIDs.insert(stateID);
}
if (!weatherStateIDs.empty())
{
TC_LOG_INFO("server.loading", " Weather abilities use state IDs:");
for (uint32 sid : weatherStateIDs)
TC_LOG_INFO("server.loading", " stateID={}", sid);
// Find all abilities that also set these state IDs ? potential additional weather abilities
for (auto const& [abilID, states] : abilityStates)
{
if (_weatherAbilityIDs.count(abilID))
continue;
for (auto const& [stateID, value] : states)
{
if (weatherStateIDs.count(stateID))
{
BattlePetAbilityEntry const* ability = sBattlePetAbilityStore.LookupEntry(abilID);
TC_LOG_INFO("server.loading", " Candidate weather ability via state: ID={} name={} stateID={} value={}",
abilID, ability ? ability->Name.Str[LOCALE_enUS] : "???", stateID, value);
}
}
}
}
// Diagnostic: dump all effects for a few known weather ability names to trace their effect chain
static constexpr std::string_view diagnosticNames[] = {
"Call Lightning", "Moonlight", "Sandstorm", "Acid Rain"
};
for (BattlePetAbilityEntry const* ability : sBattlePetAbilityStore)
{
if (!ability->Name.Str[LOCALE_enUS])
continue;
std::string_view name(ability->Name.Str[LOCALE_enUS]);
for (auto const& dname : diagnosticNames)
{
if (name == dname)
{
TC_LOG_INFO("server.loading", " DIAG ability '{}' ID={} flags={}", name, ability->ID, ability->Flags);
// Walk turns and effects
for (BattlePetAbilityTurnEntry const* turn : sBattlePetAbilityTurnStore)
{
if (turn->BattlePetAbilityID != ability->ID)
continue;
TC_LOG_INFO("server.loading", " Turn ID={} order={} typeEnum={} eventEnum={}",
turn->ID, turn->OrderIndex, turn->TurnTypeEnum, turn->EventTypeEnum);
for (BattlePetAbilityEffectEntry const* effect : sBattlePetAbilityEffectStore)
{
if (effect->BattlePetAbilityTurnID != turn->ID)
continue;
auto actionIt = _effectActionMap.find(effect->BattlePetEffectPropertiesID);
TC_LOG_INFO("server.loading", " Effect ID={} propsID={} action={} auraAbilID={} params=[{},{},{},{},{},{}]",
effect->ID, effect->BattlePetEffectPropertiesID,
actionIt != _effectActionMap.end() ? uint16(actionIt->second) : 999,
effect->AuraBattlePetAbilityID,
effect->Param[0], effect->Param[1], effect->Param[2], effect->Param[3], effect->Param[4], effect->Param[5]);
}
}
// Also log BattlePetAbilityState entries
auto stIt = abilityStates.find(ability->ID);
if (stIt != abilityStates.end())
for (auto const& [stateID, value] : stIt->second)
TC_LOG_INFO("server.loading", " AbilityState: stateID={} value={}", stateID, value);
else
TC_LOG_INFO("server.loading", " No AbilityState entries");
break;
}
}
}
TC_LOG_INFO("server.loading", ">> Found {} weather ability IDs", uint32(_weatherAbilityIDs.size()));
for (uint32 abilID : _weatherAbilityIDs)
{
BattlePetAbilityEntry const* ability = sBattlePetAbilityStore.LookupEntry(abilID);
TC_LOG_INFO("server.loading", " WeatherAbility: ID={} name={}", abilID,
ability ? ability->Name.Str[LOCALE_enUS] : "???");
}
}
@@ -688,7 +810,7 @@ namespace PetBattles
{
battle->Start();
static constexpr float PET_BATTLE_HALF_DISTANCE = 5.0f;
static constexpr float PET_BATTLE_PLAYER_DISTANCE = 5.0f;
WorldPackets::BattlePet::PetBattleFinalizeLocation finalizeLocation;
finalizeLocation.Location.LocationResult = PET_BATTLE_REQUEST_FAIL_OK;
Position pvpMidpoint;
@@ -698,12 +820,12 @@ namespace PetBattles
float pvpFacing = p1->GetAbsoluteAngle(p2);
finalizeLocation.Location.BattleOrigin = pvpMidpoint;
finalizeLocation.Location.BattleFacing = pvpFacing;
finalizeLocation.Location.PlayerPositions[0] = Position(pvpMidpoint.m_positionX - PET_BATTLE_HALF_DISTANCE * std::cos(pvpFacing),
pvpMidpoint.m_positionY - PET_BATTLE_HALF_DISTANCE * std::sin(pvpFacing),
pvpMidpoint.m_positionZ);
finalizeLocation.Location.PlayerPositions[1] = Position(pvpMidpoint.m_positionX + PET_BATTLE_HALF_DISTANCE * std::cos(pvpFacing),
pvpMidpoint.m_positionY + PET_BATTLE_HALF_DISTANCE * std::sin(pvpFacing),
pvpMidpoint.m_positionZ);
finalizeLocation.Location.PlayerPositions[0] = Position(pvpMidpoint.m_positionX - PET_BATTLE_PLAYER_DISTANCE * std::cos(pvpFacing),
pvpMidpoint.m_positionY - PET_BATTLE_PLAYER_DISTANCE * std::sin(pvpFacing),
p1->GetPositionZ());
finalizeLocation.Location.PlayerPositions[1] = Position(pvpMidpoint.m_positionX + PET_BATTLE_PLAYER_DISTANCE * std::cos(pvpFacing),
pvpMidpoint.m_positionY + PET_BATTLE_PLAYER_DISTANCE * std::sin(pvpFacing),
p2->GetPositionZ());
p1->SendDirectMessage(finalizeLocation.Write());
p2->SendDirectMessage(finalizeLocation.Write());
}
+17 -17
View File
@@ -512,8 +512,9 @@ void WorldSession::HandlePetBattleRequestWild(WorldPackets::BattlePet::PetBattle
player->SetFacingToObject(creature);
// Send finalize location with OK result
// BattleOrigin = midpoint, players placed 5.0 units from center along facing axis (matching retail sniff)
static constexpr float PET_BATTLE_HALF_DISTANCE = 5.0f;
// BattleOrigin = midpoint; PlayerPositions pushed 5 units from center along facing axis
// so the player character stands behind their pet (matching retail sniff)
static constexpr float PET_BATTLE_PLAYER_DISTANCE = 5.0f;
WorldPackets::BattlePet::PetBattleFinalizeLocation finalizeLocation;
finalizeLocation.Location.LocationResult = PetBattles::PET_BATTLE_REQUEST_FAIL_OK;
Position midpoint;
@@ -523,13 +524,13 @@ void WorldSession::HandlePetBattleRequestWild(WorldPackets::BattlePet::PetBattle
float facing = player->GetAbsoluteAngle(creature);
finalizeLocation.Location.BattleOrigin = midpoint;
finalizeLocation.Location.BattleFacing = facing;
// Player positions are 5.0 units from center along the facing axis
finalizeLocation.Location.PlayerPositions[0] = Position(midpoint.m_positionX - PET_BATTLE_HALF_DISTANCE * std::cos(facing),
midpoint.m_positionY - PET_BATTLE_HALF_DISTANCE * std::sin(facing),
midpoint.m_positionZ);
finalizeLocation.Location.PlayerPositions[1] = Position(midpoint.m_positionX + PET_BATTLE_HALF_DISTANCE * std::cos(facing),
midpoint.m_positionY + PET_BATTLE_HALF_DISTANCE * std::sin(facing),
midpoint.m_positionZ);
// Push players back from center so they stand behind their pets
finalizeLocation.Location.PlayerPositions[0] = Position(midpoint.m_positionX - PET_BATTLE_PLAYER_DISTANCE * std::cos(facing),
midpoint.m_positionY - PET_BATTLE_PLAYER_DISTANCE * std::sin(facing),
player->GetPositionZ());
finalizeLocation.Location.PlayerPositions[1] = Position(midpoint.m_positionX + PET_BATTLE_PLAYER_DISTANCE * std::cos(facing),
midpoint.m_positionY + PET_BATTLE_PLAYER_DISTANCE * std::sin(facing),
creature->GetPositionZ());
SendPacket(finalizeLocation.Write());
// Build and send initial update
@@ -681,8 +682,7 @@ void WorldSession::StartNPCPetBattle(Creature* trainer)
player->SetFacingToObject(trainer);
// Send finalize location with OK result
// BattleOrigin = midpoint, players placed 5.0 units from center along facing axis (matching retail sniff)
static constexpr float PET_BATTLE_HALF_DISTANCE = 5.0f;
static constexpr float PET_BATTLE_PLAYER_DISTANCE = 5.0f;
WorldPackets::BattlePet::PetBattleFinalizeLocation finalizeLocation;
finalizeLocation.Location.LocationResult = PetBattles::PET_BATTLE_REQUEST_FAIL_OK;
Position midpoint;
@@ -692,12 +692,12 @@ void WorldSession::StartNPCPetBattle(Creature* trainer)
float facing = player->GetAbsoluteAngle(trainer);
finalizeLocation.Location.BattleOrigin = midpoint;
finalizeLocation.Location.BattleFacing = facing;
finalizeLocation.Location.PlayerPositions[0] = Position(midpoint.m_positionX - PET_BATTLE_HALF_DISTANCE * std::cos(facing),
midpoint.m_positionY - PET_BATTLE_HALF_DISTANCE * std::sin(facing),
midpoint.m_positionZ);
finalizeLocation.Location.PlayerPositions[1] = Position(midpoint.m_positionX + PET_BATTLE_HALF_DISTANCE * std::cos(facing),
midpoint.m_positionY + PET_BATTLE_HALF_DISTANCE * std::sin(facing),
midpoint.m_positionZ);
finalizeLocation.Location.PlayerPositions[0] = Position(midpoint.m_positionX - PET_BATTLE_PLAYER_DISTANCE * std::cos(facing),
midpoint.m_positionY - PET_BATTLE_PLAYER_DISTANCE * std::sin(facing),
player->GetPositionZ());
finalizeLocation.Location.PlayerPositions[1] = Position(midpoint.m_positionX + PET_BATTLE_PLAYER_DISTANCE * std::cos(facing),
midpoint.m_positionY + PET_BATTLE_PLAYER_DISTANCE * std::sin(facing),
trainer->GetPositionZ());
SendPacket(finalizeLocation.Write());
// Build and send initial update