Pet Battles: Fix weather/environment effects not showing in client UI

SetWeather() updated internal state but never emitted round effects,
so the client never knew weather was applied. Weather expiry also had
no AURA_CANCEL, leaving stale icons. InitialUpdate Enviros array was
never populated with active weather data.

- SetWeather() now emits AURA_APPLY targeting environment PBOID (6+)
- Previous weather is cancelled before new weather is applied
- Cleansing weather emits AURA_CANCEL for all active environment auras
- TickWeather() emits AURA_CANCEL when weather duration expires
- BuildPetBattleEnviros() populates InitialUpdate Enviros in all paths
- Added PBOID_ENVIRONMENT_BASE constant and TargetEnvSlot field for
  environment-targeted round effects
This commit is contained in:
luis
2026-03-12 08:20:07 -03:00
parent 9ff52e29fb
commit 1debf61e1b
4 changed files with 88 additions and 8 deletions
+54 -6
View File
@@ -1669,27 +1669,64 @@ namespace PetBattles
{
if (weatherType == PET_BATTLE_WEATHER_CLEANSING)
{
// Clear all weather
for (auto& env : _environments)
// Clear all weather ? emit AURA_CANCEL for any active environment auras
for (uint8 i = 0; i < MAX_PET_BATTLE_ENVIRONMENTS; ++i)
{
env.WeatherType = PET_BATTLE_WEATHER_NONE;
env.AbilityID = 0;
env.RemainingRounds = 0;
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;
}
return;
}
// Cancel existing weather on slot 0 if any
if (_environments[0].WeatherType != PET_BATTLE_WEATHER_NONE && _environments[0].AuraInstanceID != 0)
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
cancelEffect.TargetEnvSlot = 0;
cancelEffect.Param1 = _environments[0].AuraInstanceID;
cancelEffect.Param2 = _environments[0].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;
// Emit AURA_APPLY targeting environment PBOID so client shows weather
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.Param2 = abilityID;
applyEffect.Param3 = duration;
applyEffect.Param4 = _currentRound;
_roundEffects.push_back(applyEffect);
}
void PetBattle::TickWeather()
{
for (auto& env : _environments)
for (uint8 envSlot = 0; envSlot < MAX_PET_BATTLE_ENVIRONMENTS; ++envSlot)
{
PetBattleEnvironment& env = _environments[envSlot];
if (env.WeatherType == PET_BATTLE_WEATHER_NONE)
continue;
@@ -1726,8 +1763,19 @@ namespace PetBattles
if (env.RemainingRounds <= 0)
{
// Emit AURA_CANCEL so client removes the weather icon
if (env.AuraInstanceID != 0)
{
PetBattleRoundEffect cancelEffect;
cancelEffect.EffectType = PET_BATTLE_EFFECT_AURA_CANCEL;
cancelEffect.TargetEnvSlot = static_cast<int8>(envSlot);
cancelEffect.Param1 = env.AuraInstanceID;
cancelEffect.Param2 = env.AbilityID;
_roundEffects.push_back(cancelEffect);
}
env.WeatherType = PET_BATTLE_WEATHER_NONE;
env.AbilityID = 0;
env.AuraInstanceID = 0;
}
}
}
+3
View File
@@ -62,6 +62,8 @@ namespace PetBattles
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
};
struct PetBattlePetData
@@ -214,6 +216,7 @@ namespace PetBattles
int32 Param2 = 0;
int32 Param3 = 0; // For aura effects: RoundsRemaining (captured at creation time)
int32 Param4 = 0; // For aura effects: CurrentRound (captured at creation time)
int8 TargetEnvSlot = -1; // >= 0: target is environment slot (PBOID = PBOID_ENVIRONMENT_BASE + slot)
};
class TC_GAME_API PetBattle
@@ -19,8 +19,8 @@
#define TRINITYCORE_PET_BATTLE_DEFINES_H
#include "Define.h"
#include <array>
#include <algorithm>
#include <array>
namespace PetBattles
{
@@ -34,6 +34,7 @@ namespace PetBattles
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 PBOID_ENVIRONMENT_BASE = MAX_PET_BATTLE_PLAYERS * MAX_PET_BATTLE_TEAM_SIZE; // PBOID 6 = environment slot 0
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;
@@ -350,6 +351,7 @@ namespace PetBattles
float baseChance = 0.20f + (trapLevel - 1) * 0.05f;
// Lower HP = higher capture chance
float hpModifier = 2.0f - (healthPct / 100.0f) * 1.5f;
// Quality modifier: lower quality = easier to catch
float qualityMod = 1.0f;
switch (quality)
+28 -1
View File
@@ -255,6 +255,26 @@ static void BuildPetBattlePlayerUpdate(WorldPackets::BattlePet::PetBattlePlayerU
}
}
static void BuildPetBattleEnviros(std::array<WorldPackets::BattlePet::PetBattleEnviroInfo, 3>& enviros,
PetBattles::PetBattle const* battle)
{
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)
{
WorldPackets::BattlePet::PetBattleAuraInfo auraInfo;
auraInfo.AbilityID = env.AbilityID;
auraInfo.InstanceID = env.AuraInstanceID;
auraInfo.RoundsRemaining = env.RemainingRounds;
auraInfo.CurrentRound = env.CurrentRound;
auraInfo.CasterPBOID = env.CasterTeam * PetBattles::MAX_PET_BATTLE_TEAM_SIZE
+ battle->GetTeam(env.CasterTeam).FrontPetIndex;
enviros[i].Auras.push_back(auraInfo);
}
}
}
static void BuildPetBattleRoundPlayerData(WorldPackets::BattlePet::PetBattleRoundPlayerData& roundData,
PetBattles::PetBattleTeamData const& team, PetBattles::PetBattle const* battle, uint8 teamIdx)
{
@@ -320,7 +340,11 @@ static void BuildRoundEffects(std::vector<WorldPackets::BattlePet::PetBattleEffe
effect.StackDepth = 0;
WorldPackets::BattlePet::PetBattleEffectTargetInfo target;
target.Remaining = static_cast<int32>(roundEffect.TargetTeam * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + roundEffect.TargetPet);
// Environment targets use PBOID_ENVIRONMENT_BASE + slot; pet targets use team * TEAM_SIZE + pet
if (roundEffect.TargetEnvSlot >= 0)
target.Remaining = static_cast<int32>(PetBattles::PBOID_ENVIRONMENT_BASE + roundEffect.TargetEnvSlot);
else
target.Remaining = static_cast<int32>(roundEffect.TargetTeam * PetBattles::MAX_PET_BATTLE_TEAM_SIZE + roundEffect.TargetPet);
// 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),
@@ -493,6 +517,7 @@ void WorldSession::HandlePetBattleRequestWild(WorldPackets::BattlePet::PetBattle
BuildPetBattlePlayerUpdate(initialUpdate.Players[0], battle->GetTeam(PetBattles::PET_BATTLE_TEAM_1), false, battle, PetBattles::PET_BATTLE_TEAM_1);
BuildPetBattlePlayerUpdate(initialUpdate.Players[1], battle->GetTeam(PetBattles::PET_BATTLE_TEAM_2), true, battle, PetBattles::PET_BATTLE_TEAM_2);
BuildPetBattleEnviros(initialUpdate.Enviros, battle);
initialUpdate.CurRound = battle->GetCurrentRound();
initialUpdate.CurPetBattleState = static_cast<int8>(battle->GetBattleState());
@@ -653,6 +678,7 @@ void WorldSession::StartNPCPetBattle(Creature* trainer)
BuildPetBattlePlayerUpdate(initialUpdate.Players[0], battle->GetTeam(PetBattles::PET_BATTLE_TEAM_1), false, battle, PetBattles::PET_BATTLE_TEAM_1);
BuildPetBattlePlayerUpdate(initialUpdate.Players[1], battle->GetTeam(PetBattles::PET_BATTLE_TEAM_2), false, battle, PetBattles::PET_BATTLE_TEAM_2);
BuildPetBattleEnviros(initialUpdate.Enviros, battle);
initialUpdate.CurRound = battle->GetCurrentRound();
initialUpdate.CurPetBattleState = static_cast<int8>(battle->GetBattleState());
@@ -1056,6 +1082,7 @@ void WorldSession::HandlePetBattleRequestUpdate(WorldPackets::BattlePet::PetBatt
WorldPackets::BattlePet::PetBattleInitialUpdate initialUpdate;
BuildPetBattlePlayerUpdate(initialUpdate.Players[0], battle->GetTeam(PetBattles::PET_BATTLE_TEAM_1), false, battle, PetBattles::PET_BATTLE_TEAM_1);
BuildPetBattlePlayerUpdate(initialUpdate.Players[1], battle->GetTeam(PetBattles::PET_BATTLE_TEAM_2), false, battle, PetBattles::PET_BATTLE_TEAM_2);
BuildPetBattleEnviros(initialUpdate.Enviros, battle);
initialUpdate.CurRound = battle->GetCurrentRound();
initialUpdate.CurPetBattleState = static_cast<int8>(battle->GetBattleState());
initialUpdate.IsPVP = true;