Pet Battles: Wire state-driven weather system from DB2

Replace dead hardcoded PetBattleWeatherType enum with a data-driven
weather system that reads BattlePetAbilityState entries from DB2.

The old system had SetWeather/GetWeatherDamageModifier/GetWeatherHealingModifier
functions but SetWeather was never called, so weather abilities fired
visually (aura icon appeared) but had zero gameplay effect.

Now weather modifiers (damage %, healing %, accuracy, speed) are loaded
from BattlePetAbilityState DB2 onto the environment slot when a weather
aura is applied, and read back during combat calculations. Elemental
pets remain immune to all weather effects.
This commit is contained in:
luis
2026-03-20 01:22:05 -03:00
parent 8d65207489
commit 8f9c7c3de8
7 changed files with 94 additions and 155 deletions
@@ -109,6 +109,7 @@ namespace BattlePets
STATE_STAT_POWER = 18,
STATE_STAT_STAMINA = 19,
STATE_STAT_SPEED = 20,
STATE_STAT_ACCURACY = 22,
STATE_MOD_DAMAGE_DEALT_PERCENT = 23,
STATE_MOD_DAMAGE_TAKEN_PERCENT = 24,
STATE_MOD_SPEED_PERCENT = 25,
+58 -127
View File
@@ -445,11 +445,12 @@ namespace PetBattles
_needsFrontPetSwap.fill(false);
// Recalculate effective stats for all living pets (includes passive bonuses, weather speed)
PetBattleWeatherType activeWeather = _environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType;
auto const* envStates = _environments[PET_BATTLE_WEATHER_ENV_SLOT].IsActive()
? &_environments[PET_BATTLE_WEATHER_ENV_SLOT].States : nullptr;
for (uint8 t = 0; t < MAX_PET_BATTLE_PLAYERS; ++t)
for (uint8 p = 0; p < _teams[t].PetCount; ++p)
if (_teams[t].Pets[p].IsAlive())
_teams[t].Pets[p].RecalculateEffectiveStats(activeWeather);
_teams[t].Pets[p].RecalculateEffectiveStats(envStates);
// Apply passive round-start effects (Humanoid heal, Dragonkin reset, etc.)
ApplyPassiveRoundStart();
@@ -953,6 +954,14 @@ namespace PetBattles
int16 basePower = effect->Param[0];
int16 accuracy = effect->Param[1];
// Weather accuracy modifier (Elemental passive: ignores weather)
if (accuracy > 0 && attacker.PetType != PET_TYPE_ELEMENTAL)
{
int32 envAccMod = _environments[PET_BATTLE_WEATHER_ENV_SLOT].GetState(BattlePets::STATE_STAT_ACCURACY);
if (envAccMod != 0)
accuracy = std::max(int16(0), int16(accuracy + envAccMod));
}
// Check accuracy (if specified and > 0, roll for hit)
if (accuracy > 0 && accuracy < 100)
{
@@ -1043,7 +1052,7 @@ namespace PetBattles
effect->ID, abilityID, weatherAuraAbilityID, auraDuration);
// 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)
if (_environments[PET_BATTLE_WEATHER_ENV_SLOT].IsActive())
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
@@ -1051,16 +1060,19 @@ namespace PetBattles
cancelEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
cancelEffect.Param2 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID;
_roundEffects.push_back(cancelEffect);
ClearWeatherStates();
}
// 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;
// Load weather modifier states from BattlePetAbilityState DB2
ApplyWeatherStates(weatherAuraAbilityID);
// Emit AURA_APPLY targeting environment PBOID 8 (PetbattleEnviros::Weather)
PetBattleRoundEffect applyEffect;
applyEffect.AbilityEffectID = effect->ID;
@@ -1158,7 +1170,7 @@ namespace PetBattles
TC_LOG_DEBUG("server.loading", "PetBattle WEATHER_PERIODIC: effectID={} castAbility={} auraAbilityID={} duration={}",
effect->ID, abilityID, weatherAuraAbilityID, auraDuration);
if (_environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID != 0 && _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID != 0)
if (_environments[PET_BATTLE_WEATHER_ENV_SLOT].IsActive())
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
@@ -1166,15 +1178,18 @@ namespace PetBattles
cancelEffect.Param1 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AuraInstanceID;
cancelEffect.Param2 = _environments[PET_BATTLE_WEATHER_ENV_SLOT].AbilityID;
_roundEffects.push_back(cancelEffect);
ClearWeatherStates();
}
_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;
// Load weather modifier states from BattlePetAbilityState DB2
ApplyWeatherStates(weatherAuraAbilityID);
PetBattleRoundEffect applyEffect;
applyEffect.AbilityEffectID = effect->ID;
applyEffect.EffectType = PET_BATTLE_EFFECT_AURA_APPLY;
@@ -1352,6 +1367,9 @@ namespace PetBattles
TC_LOG_DEBUG("server.loading", "PetBattle WEATHER_SET_STATE: effectID={} stateID={} stateValue={}",
effect->ID, stateID, stateValue);
// Store the state on the environment for gameplay use
_environments[PET_BATTLE_WEATHER_ENV_SLOT].States[stateID] = stateValue;
// Emit SET_STATE targeting environment PBOID (slot 0)
PetBattleRoundEffect roundEffect;
roundEffect.AbilityEffectID = effect->ID;
@@ -1554,9 +1572,19 @@ namespace PetBattles
result.TypeMod = GetTypeEffectiveness(abilityType, PetBattlePetType(defender.PetType));
rawDamage *= result.TypeMod;
// Weather damage modifier (Elemental passive: ignores all weather effects)
// Weather damage modifiers from environment states (Elemental passive: ignores weather)
if (attacker.PetType != PET_TYPE_ELEMENTAL)
rawDamage *= (1.0f + GetWeatherDamageModifier(abilityType));
{
int32 envDmgDealMod = _environments[PET_BATTLE_WEATHER_ENV_SLOT].GetState(BattlePets::STATE_MOD_DAMAGE_DEALT_PERCENT);
if (envDmgDealMod != 0)
rawDamage *= (1.0f + envDmgDealMod / 100.0f);
}
if (defender.PetType != PET_TYPE_ELEMENTAL)
{
int32 envDmgTakeMod = _environments[PET_BATTLE_WEATHER_ENV_SLOT].GetState(BattlePets::STATE_MOD_DAMAGE_TAKEN_PERCENT);
if (envDmgTakeMod != 0)
rawDamage *= (1.0f + envDmgTakeMod / 100.0f);
}
// Beast passive: +25% damage when below 50% HP
if (attacker.PetType == PET_TYPE_BEAST && attacker.Health <= attacker.MaxHealth / 2)
@@ -1601,8 +1629,13 @@ namespace PetBattles
{
float rawHealing = healPower * (attackerPower / 20.0f);
// Weather healing modifier
rawHealing *= (1.0f + GetWeatherHealingModifier());
// Weather healing modifier from environment states (Elemental passive: ignores weather)
if (healer.PetType != PET_TYPE_ELEMENTAL)
{
int32 envHealMod = _environments[PET_BATTLE_WEATHER_ENV_SLOT].GetState(BattlePets::STATE_MOD_HEALING_DEALT_PERCENT);
if (envHealMod != 0)
rawHealing *= (1.0f + envHealMod / 100.0f);
}
// State-based healing modifiers
for (auto const& [stateID, stateValue] : healer.States)
@@ -1829,64 +1862,26 @@ namespace PetBattles
}
// ============================================================================
// Environment / Weather system
// Environment / Weather system (state-driven from BattlePetAbilityState DB2)
// ============================================================================
void PetBattle::SetWeather(PetBattleWeatherType weatherType, uint32 abilityID, int8 duration, uint8 casterTeam)
void PetBattle::ApplyWeatherStates(uint32 abilityID)
{
if (weatherType == PET_BATTLE_WEATHER_CLEANSING)
// Load BattlePetAbilityState entries for this weather ability onto the environment
if (auto const* states = sPetBattleMgr->GetWeatherAbilityStates(abilityID))
{
// Clear all weather ? emit AURA_CANCEL for any active environment auras
for (uint8 i = 0; i < MAX_PET_BATTLE_ENVIRONMENTS; ++i)
for (auto const& [stateID, value] : *states)
{
if (_environments[i].WeatherType != PET_BATTLE_WEATHER_NONE && _environments[i].AuraInstanceID != 0)
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
cancelEffect.TargetEnvSlot = static_cast<int8>(i);
cancelEffect.Param1 = _environments[i].AuraInstanceID;
cancelEffect.Param2 = _environments[i].AbilityID;
_roundEffects.push_back(cancelEffect);
}
_environments[i].WeatherType = PET_BATTLE_WEATHER_NONE;
_environments[i].AbilityID = 0;
_environments[i].RemainingRounds = 0;
_environments[i].AuraInstanceID = 0;
_environments[PET_BATTLE_WEATHER_ENV_SLOT].States[stateID] = value;
TC_LOG_DEBUG("server.loading", "PetBattle ApplyWeatherStates: abilityID={} stateID={} value={}",
abilityID, stateID, value);
}
return;
}
}
// 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 = 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);
}
// 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 8 (EnvWeather)
PetBattleRoundEffect applyEffect;
applyEffect.AbilityEffectID = abilityID;
applyEffect.EffectType = PET_BATTLE_EFFECT_AURA_APPLY;
applyEffect.SourceTeam = casterTeam;
applyEffect.SourcePet = _teams[casterTeam].FrontPetIndex;
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;
_roundEffects.push_back(applyEffect);
void PetBattle::ClearWeatherStates()
{
_environments[PET_BATTLE_WEATHER_ENV_SLOT].States.clear();
}
void PetBattle::TickWeather()
@@ -1894,7 +1889,7 @@ namespace PetBattles
for (uint8 envSlot = 0; envSlot < MAX_PET_BATTLE_ENVIRONMENTS; ++envSlot)
{
PetBattleEnvironment& env = _environments[envSlot];
if (env.WeatherType == PET_BATTLE_WEATHER_NONE)
if (!env.IsActive())
continue;
// Emit AURA_CHANGE for environment aura (updates round counter in client)
@@ -1915,35 +1910,6 @@ namespace PetBattles
env.RemainingRounds--;
// Weather deals periodic damage/effects
if (env.WeatherType == PET_BATTLE_WEATHER_SANDSTORM ||
env.WeatherType == PET_BATTLE_WEATHER_BLIZZARD ||
env.WeatherType == PET_BATTLE_WEATHER_LIGHTNING)
{
for (uint8 t = 0; t < MAX_PET_BATTLE_PLAYERS; ++t)
{
PetBattlePetData& frontPet = _teams[t].Pets[_teams[t].FrontPetIndex];
if (!frontPet.IsAlive())
continue;
// Elemental passive: ignores all weather effects
if (frontPet.PetType == PET_TYPE_ELEMENTAL)
continue;
int32 weatherDamage = std::max(1, frontPet.MaxHealth / 20); // 5% max HP per tick
frontPet.Health = std::max(0, frontPet.Health - weatherDamage);
PetBattleRoundEffect roundEffect;
roundEffect.AbilityEffectID = env.AbilityID;
roundEffect.EffectType = PET_BATTLE_EFFECT_SET_HEALTH;
roundEffect.TargetTeam = t;
roundEffect.TargetPet = _teams[t].FrontPetIndex;
roundEffect.Param1 = frontPet.Health;
_roundEffects.push_back(roundEffect);
}
}
if (env.RemainingRounds <= 0)
{
// Emit AURA_CANCEL so client removes the weather icon
@@ -1956,49 +1922,14 @@ namespace PetBattles
cancelEffect.Param2 = env.AbilityID;
_roundEffects.push_back(cancelEffect);
}
env.WeatherType = PET_BATTLE_WEATHER_NONE;
env.AbilityID = 0;
env.AuraInstanceID = 0;
if (envSlot == PET_BATTLE_WEATHER_ENV_SLOT)
ClearWeatherStates();
}
}
}
float PetBattle::GetWeatherDamageModifier(PetBattlePetType abilityType) const
{
PetBattleWeatherType weather = _environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType;
switch (weather)
{
case PET_BATTLE_WEATHER_RAIN:
return (abilityType == PET_TYPE_AQUATIC) ? 0.25f : 0.0f;
case PET_BATTLE_WEATHER_ARCANE:
case PET_BATTLE_WEATHER_MOONLIGHT:
return (abilityType == PET_TYPE_MAGIC) ? 0.25f : 0.0f;
case PET_BATTLE_WEATHER_LIGHTNING:
return (abilityType == PET_TYPE_MECHANICAL) ? 0.25f : 0.0f;
case PET_BATTLE_WEATHER_SCORCHED:
return (abilityType == PET_TYPE_DRAGONKIN) ? 0.25f : 0.0f;
case PET_BATTLE_WEATHER_SANDSTORM:
return -0.10f; // Sandstorm reduces all damage slightly
default:
return 0.0f;
}
}
float PetBattle::GetWeatherHealingModifier() const
{
PetBattleWeatherType weather = _environments[PET_BATTLE_WEATHER_ENV_SLOT].WeatherType;
switch (weather)
{
case PET_BATTLE_WEATHER_SUNNY:
case PET_BATTLE_WEATHER_MOONLIGHT:
return 0.25f;
default:
return 0.0f;
}
}
// ============================================================================
// Passive family abilities
// ============================================================================
+18 -10
View File
@@ -22,6 +22,7 @@
#include "BattlePetMgr.h"
#include "ObjectGuid.h"
#include <array>
#include <unordered_map>
#include <vector>
class Creature;
@@ -59,11 +60,18 @@ namespace PetBattles
struct PetBattleEnvironment
{
uint32 AbilityID = 0;
PetBattleWeatherType WeatherType = PET_BATTLE_WEATHER_NONE;
int8 RemainingRounds = 0;
uint8 CasterTeam = 0;
uint32 AuraInstanceID = 0; // Unique ID for the environment aura (sent to client)
int32 CurrentRound = 0; // Round when weather was applied
std::unordered_map<uint32, int32> States; // BattlePetState modifiers from DB2
bool IsActive() const { return AbilityID != 0 && AuraInstanceID != 0; }
int32 GetState(uint32 stateID) const
{
auto it = States.find(stateID);
return it != States.end() ? it->second : 0;
}
};
struct PetBattlePetData
@@ -124,7 +132,7 @@ namespace PetBattles
// State values for the pet
std::vector<std::pair<uint32, int32>> States;
void RecalculateEffectiveStats(PetBattleWeatherType activeWeather = PET_BATTLE_WEATHER_NONE)
void RecalculateEffectiveStats(std::unordered_map<uint32, int32> const* envStates = nullptr)
{
EffectivePower = Power;
EffectiveSpeed = Speed;
@@ -163,11 +171,12 @@ namespace PetBattles
if (PetType == PET_TYPE_FLYING && Health > MaxHealth / 2)
EffectiveSpeed = int32(EffectiveSpeed * (1.0f + PASSIVE_FLYING_SPEED_BONUS));
// Weather speed penalties (Elemental passive: ignores all weather effects)
if (PetType != PET_TYPE_ELEMENTAL)
// Weather speed modifier from environment states (Elemental passive: ignores weather)
if (PetType != PET_TYPE_ELEMENTAL && envStates)
{
if (activeWeather == PET_BATTLE_WEATHER_BLIZZARD || activeWeather == PET_BATTLE_WEATHER_MUD)
EffectiveSpeed = int32(EffectiveSpeed * 0.75f); // -25% speed
auto it = envStates->find(BattlePets::STATE_MOD_SPEED_PERCENT);
if (it != envStates->end() && it->second != 0)
EffectiveSpeed = int32(EffectiveSpeed * (1.0f + it->second / 100.0f));
}
if (EffectivePower < 1) EffectivePower = 1;
@@ -309,11 +318,10 @@ namespace PetBattles
void RemoveAura(uint8 targetTeam, uint8 targetPet, uint32 abilityID);
void TickAuras();
// Environment/weather
void SetWeather(PetBattleWeatherType weatherType, uint32 abilityID, int8 duration, uint8 casterTeam);
// Environment/weather (state-driven from BattlePetAbilityState DB2)
void ApplyWeatherStates(uint32 abilityID);
void ClearWeatherStates();
void TickWeather();
float GetWeatherDamageModifier(PetBattlePetType abilityType) const;
float GetWeatherHealingModifier() const;
// Passive family abilities
void ApplyPassiveRoundStart();
+3 -15
View File
@@ -191,21 +191,9 @@ namespace PetBattles
PET_BATTLE_AURA_SLEEP = 6,
};
// Weather/environment effect types
enum PetBattleWeatherType : uint8
{
PET_BATTLE_WEATHER_NONE = 0,
PET_BATTLE_WEATHER_SUNNY = 1, // +25% healing
PET_BATTLE_WEATHER_MOONLIGHT = 2, // +25% healing, +10% magic damage
PET_BATTLE_WEATHER_RAIN = 3, // +25% aquatic damage
PET_BATTLE_WEATHER_SANDSTORM = 4, // Damage reduction shield, deals damage
PET_BATTLE_WEATHER_BLIZZARD = 5, // Deals frost damage, -25% speed
PET_BATTLE_WEATHER_MUD = 6, // -25% speed
PET_BATTLE_WEATHER_CLEANSING = 7, // removes all weather
PET_BATTLE_WEATHER_ARCANE = 8, // +25% magic damage
PET_BATTLE_WEATHER_LIGHTNING = 9, // +25% mechanical damage, deals damage
PET_BATTLE_WEATHER_SCORCHED = 10, // +25% dragonkin damage
};
// Weather effects are now fully state-driven from BattlePetAbilityState DB2 data.
// Each weather ability defines its own modifiers (damage %, healing %, accuracy, speed)
// via BattlePetAbilityState entries, stored on the environment slot at runtime.
// DB2 effect property IDs - these correspond to BattlePetEffectPropertiesEntry::ID
// The actual effect behavior is determined by the effect properties ID
+8 -1
View File
@@ -177,12 +177,13 @@ namespace PetBattles
for (BattlePetAbilityStateEntry const* entry : sBattlePetAbilityStateStore)
abilityStates[entry->BattlePetAbilityID].push_back({ entry->BattlePetStateID, entry->Value });
// Diagnostic: log states for abilities already found as weather
// Store BattlePetAbilityState entries for weather abilities (used at runtime for modifiers)
for (uint32 abilID : _weatherAbilityIDs)
{
auto it = abilityStates.find(abilID);
if (it != abilityStates.end())
{
_weatherAbilityStates[abilID] = it->second;
for (auto const& [stateID, value] : it->second)
TC_LOG_INFO("server.loading", " WeatherAbil {} has AbilityState: stateID={} value={}", abilID, stateID, value);
}
@@ -379,6 +380,12 @@ namespace PetBattles
return _weatherAbilityIDs.count(abilityID) > 0;
}
std::vector<std::pair<uint32, int32>> const* PetBattleMgr::GetWeatherAbilityStates(uint32 abilityID) const
{
auto it = _weatherAbilityStates.find(abilityID);
return it != _weatherAbilityStates.end() ? &it->second : nullptr;
}
void PetBattleMgr::Update(uint32 diff)
{
// Update all active battles
+5 -1
View File
@@ -102,8 +102,9 @@ namespace PetBattles
// Effect action mapping (DB2 PropsID -> abstract action type)
PetBattleAbilityEffectAction GetEffectAction(uint16 propsID) const;
// Weather ability detection
// Weather ability detection and state lookup
bool IsWeatherAbility(uint32 abilityID) const;
std::vector<std::pair<uint32, int32>> const* GetWeatherAbilityStates(uint32 abilityID) const;
// PvP Queue
void JoinQueue(ObjectGuid playerGUID);
@@ -141,6 +142,9 @@ namespace PetBattles
// Weather ability IDs (abilities whose effects set weatherState on environment)
std::unordered_set<uint32> _weatherAbilityIDs;
// Weather ability states: abilityID -> BattlePetAbilityState entries (stateID, value)
std::unordered_map<uint32, std::vector<std::pair<uint32, int32>>> _weatherAbilityStates;
// PvP Queue
std::vector<PvPQueueEntry> _pvpQueue;
std::unique_ptr<PvPMatchProposal> _pendingProposal;
@@ -205,7 +205,7 @@ static void BuildPetBattleEnviros(std::array<WorldPackets::BattlePet::PetBattleE
for (uint8 i = 0; i < PetBattles::MAX_PET_BATTLE_ENVIRONMENTS; ++i)
{
PetBattles::PetBattleEnvironment const& env = battle->GetEnvironment(i);
if (env.WeatherType != PetBattles::PET_BATTLE_WEATHER_NONE && env.AuraInstanceID != 0)
if (env.IsActive())
{
WorldPackets::BattlePet::PetBattleAuraInfo auraInfo;
auraInfo.AbilityID = env.AbilityID;