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:
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user