Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ bool RageWinterchillSpreadRangedInCircleAction::Execute(Event /*event*/)
return false;
}

bool RageWinterchillMoveAwayFromDeathAndDecayAction::Execute(Event /*event*/)
{
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
if (!winterchill ||
!IsBotInsideActiveWinterchillDeathAndDecay(bot, WINTERCHILL_DEATH_AND_DECAY_ACTION_RADIUS))
return false;

return MoveAway(winterchill, 5.0f);
}

// Anetheron

bool AnetheronMisdirectBossAndInfernalsToTanksAction::Execute(Event /*event*/)
Expand Down
8 changes: 8 additions & 0 deletions src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ class RageWinterchillSpreadRangedInCircleAction : public HyjalSummitMovementActi
bool Execute(Event event) override;
};

class RageWinterchillMoveAwayFromDeathAndDecayAction : public HyjalSummitAttackAction
{
public:
RageWinterchillMoveAwayFromDeathAndDecayAction(
PlayerbotAI* botAI) : HyjalSummitAttackAction(botAI, "rage winterchill move away from death and decay") {}
bool Execute(Event event) override;
};

// Anetheron

class AnetheronMisdirectBossAndInfernalsToTanksAction : public HyjalSummitAttackAction
Expand Down
21 changes: 21 additions & 0 deletions src/Ai/Raid/HyjalSummit/Multiplier/RaidHyjalSummitMultipliers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ float RageWinterchillDisableCombatFormationMoveMultiplier::GetValue(Action* acti
return 1.0f;
}

float RageWinterchillControlDeathAndDecayAvoidanceMultiplier::GetValue(Action* action)
{
if (botAI->IsTank(bot) || !AI_VALUE2(Unit*, "find target", "rage winterchill"))
return 1.0f;

if (IsBotInsideActiveWinterchillDeathAndDecay(bot, WINTERCHILL_DEATH_AND_DECAY_CONTROL_RADIUS))
{
if (dynamic_cast<AvoidAoeAction*>(action))
return 0.0f;

if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<RageWinterchillMoveAwayFromDeathAndDecayAction*>(action))
return 0.0f;

if (dynamic_cast<CastReachTargetSpellAction*>(action))
return 0.0f;
}

return 1.0f;
}

// Anetheron

float AnetheronDisableTankActionsMultiplier::GetValue(Action* action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ class RageWinterchillDisableCombatFormationMoveMultiplier : public Multiplier
virtual float GetValue(Action* action);
};

class RageWinterchillControlDeathAndDecayAvoidanceMultiplier : public Multiplier
{
public:
RageWinterchillControlDeathAndDecayAvoidanceMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "rage winterchill control death and decay avoidance multiplier") {}
virtual float GetValue(Action* action);
};

// Anetheron

class AnetheronDisableTankActionsMultiplier : public Multiplier
Expand Down
6 changes: 6 additions & 0 deletions src/Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class RaidHyjalSummitActionContext : public NamedObjectContext<Action>
creators["rage winterchill spread ranged in circle"] =
&RaidHyjalSummitActionContext::rage_winterchill_spread_ranged_in_circle;

creators["rage winterchill move away from death and decay"] =
&RaidHyjalSummitActionContext::rage_winterchill_move_away_from_death_and_decay;

// Anetheron
creators["anetheron misdirect boss and infernals to tanks"] =
&RaidHyjalSummitActionContext::anetheron_misdirect_boss_and_infernals_to_tanks;
Expand Down Expand Up @@ -126,6 +129,9 @@ class RaidHyjalSummitActionContext : public NamedObjectContext<Action>
static Action* rage_winterchill_spread_ranged_in_circle(
PlayerbotAI* botAI) { return new RageWinterchillSpreadRangedInCircleAction(botAI); }

static Action* rage_winterchill_move_away_from_death_and_decay(
PlayerbotAI* botAI) { return new RageWinterchillMoveAwayFromDeathAndDecayAction(botAI); }

// Anetheron
static Action* anetheron_misdirect_boss_and_infernals_to_tanks(
PlayerbotAI* botAI) { return new AnetheronMisdirectBossAndInfernalsToTanksAction(botAI); }
Expand Down
6 changes: 6 additions & 0 deletions src/Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class RaidHyjalSummitTriggerContext : public NamedObjectContext<Trigger>
creators["rage winterchill boss casts death and decay"] =
&RaidHyjalSummitTriggerContext::rage_winterchill_boss_casts_death_and_decay;

creators["rage winterchill bot is standing in death and decay"] =
&RaidHyjalSummitTriggerContext::rage_winterchill_bot_is_standing_in_death_and_decay;

// Anetheron
creators["anetheron pulling boss or infernal"] =
&RaidHyjalSummitTriggerContext::anetheron_pulling_boss_or_infernal;
Expand Down Expand Up @@ -126,6 +129,9 @@ class RaidHyjalSummitTriggerContext : public NamedObjectContext<Trigger>
static Trigger* rage_winterchill_boss_casts_death_and_decay(
PlayerbotAI* botAI) { return new RageWinterchillBossCastsDeathAndDecayTrigger(botAI); }

static Trigger* rage_winterchill_bot_is_standing_in_death_and_decay(
PlayerbotAI* botAI) { return new RageWinterchillBotIsStandingInDeathAndDecayTrigger(botAI); }

// Anetheron
static Trigger* anetheron_pulling_boss_or_infernal(
PlayerbotAI* botAI) { return new AnetheronPullingBossOrInfernalTrigger(botAI); }
Expand Down
4 changes: 4 additions & 0 deletions src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ void RaidHyjalSummitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("rage winterchill boss casts death and decay", {
NextAction("rage winterchill spread ranged in circle", ACTION_RAID + 1) }));

triggers.push_back(new TriggerNode("rage winterchill bot is standing in death and decay", {
NextAction("rage winterchill move away from death and decay", ACTION_EMERGENCY + 2) }));

// Anetheron
triggers.push_back(new TriggerNode("anetheron pulling boss or infernal", {
NextAction("anetheron misdirect boss and infernals to tanks", ACTION_RAID + 3) }));
Expand Down Expand Up @@ -113,6 +116,7 @@ void RaidHyjalSummitStrategy::InitMultipliers(std::vector<Multiplier*>& multipli
// Rage Winterchill
multipliers.push_back(new RageWinterchillDisableMainTankAvoidAoeMultiplier(botAI));
multipliers.push_back(new RageWinterchillDisableCombatFormationMoveMultiplier(botAI));
multipliers.push_back(new RageWinterchillControlDeathAndDecayAvoidanceMultiplier(botAI));

// Anetheron
multipliers.push_back(new AnetheronDisableTankActionsMultiplier(botAI));
Expand Down
11 changes: 11 additions & 0 deletions src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ bool RageWinterchillBossCastsDeathAndDecayTrigger::IsActive()
AI_VALUE2(Unit*, "find target", "rage winterchill");
}

bool RageWinterchillBotIsStandingInDeathAndDecayTrigger::IsActive()
{
if (botAI->IsTank(bot))
return false;

if (!AI_VALUE2(Unit*, "find target", "rage winterchill"))
return false;

return IsBotInsideActiveWinterchillDeathAndDecay(bot, WINTERCHILL_DEATH_AND_DECAY_TRIGGER_RADIUS);
}

// Anetheron

bool AnetheronPullingBossOrInfernalTrigger::IsActive()
Expand Down
8 changes: 8 additions & 0 deletions src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ class RageWinterchillBossCastsDeathAndDecayTrigger : public Trigger
bool IsActive() override;
};

class RageWinterchillBotIsStandingInDeathAndDecayTrigger : public Trigger
{
public:
RageWinterchillBotIsStandingInDeathAndDecayTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill bot is standing in death and decay") {}
bool IsActive() override;
};

// Anetheron

class AnetheronPullingBossOrInfernalTrigger : public Trigger
Expand Down
29 changes: 29 additions & 0 deletions src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@

namespace HyjalSummitHelpers
{
DeathAndDecayData* GetActiveWinterchillDeathAndDecay(uint32 instanceId)
{
auto instanceIt = deathAndDecayPosition.find(instanceId);
if (instanceIt == deathAndDecayPosition.end())
return nullptr;

uint32 now = getMSTime();
if (getMSTimeDiff(instanceIt->second.spawnTime, now) >= WINTERCHILL_DEATH_AND_DECAY_DURATION)
{
deathAndDecayPosition.erase(instanceIt);
return nullptr;
}

return &instanceIt->second;
}

std::unordered_map<ObjectGuid, RainOfFireData>* GetActiveAzgalorRainOfFire(uint32 instanceId)
{
auto instanceIt = rainOfFirePosition.find(instanceId);
Expand Down Expand Up @@ -76,6 +92,19 @@ namespace HyjalSummitHelpers

const Position WINTERCHILL_TANK_POSITION = { 5031.061f, -1784.521f, 1321.626f };
std::unordered_map<ObjectGuid, bool> hasReachedWinterchillPosition;
std::unordered_map<uint32, DeathAndDecayData> deathAndDecayPosition;

bool IsBotInsideActiveWinterchillDeathAndDecay(Player* bot, float radius)
{
if (!bot || !bot->GetMap())
return false;

DeathAndDecayData* data = GetActiveWinterchillDeathAndDecay(bot->GetMap()->GetInstanceId());
if (!data)
return false;

return bot->GetExactDist2d(data->position) < radius;
}

// Anetheron

Expand Down
17 changes: 17 additions & 0 deletions src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ namespace HyjalSummitHelpers
{
enum class HyjalSummitSpells : uint32
{
// Rage Winterchill
SPELL_DEATH_AND_DECAY = 31258,

// Anetheron
SPELL_INFERNO = 31299,

Expand Down Expand Up @@ -69,6 +72,20 @@ namespace HyjalSummitHelpers
// Rage Winterchill
extern const Position WINTERCHILL_TANK_POSITION;
extern std::unordered_map<ObjectGuid, bool> hasReachedWinterchillPosition;
constexpr uint32 WINTERCHILL_DEATH_AND_DECAY_DURATION = 20000;
// Match Azgalor-style escape thresholds: detect inside the effect, keep moving
// until slightly clear, and hold competing movement a bit longer to avoid churn.
constexpr float WINTERCHILL_DEATH_AND_DECAY_TRIGGER_RADIUS = 16.0f;
constexpr float WINTERCHILL_DEATH_AND_DECAY_ACTION_RADIUS = 17.0f;
constexpr float WINTERCHILL_DEATH_AND_DECAY_CONTROL_RADIUS = 23.0f;
struct DeathAndDecayData
{
Position position;
uint32 spawnTime;
};
extern std::unordered_map<uint32, DeathAndDecayData> deathAndDecayPosition;
DeathAndDecayData* GetActiveWinterchillDeathAndDecay(uint32 instanceId);
bool IsBotInsideActiveWinterchillDeathAndDecay(Player* bot, float radius);

// Anetheron
extern const Position ANETHERON_TANK_POSITION;
Expand Down
18 changes: 18 additions & 0 deletions src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitScripts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,26 @@ class AzgalorRainOfFireScript : public DynamicObjectScript
}
};

// Records the active Death and Decay dynamic object so bots can step away from
// Winterchill using encounter-specific movement instead of generic aoe avoidance
class RageWinterchillDeathAndDecayScript : public DynamicObjectScript
{
public:
RageWinterchillDeathAndDecayScript() : DynamicObjectScript("RageWinterchillDeathAndDecayScript") {}

void OnUpdate(DynamicObject* dynobj, uint32 /*diff*/) override
{
if (dynobj->GetSpellId() != static_cast<uint32>(HyjalSummitSpells::SPELL_DEATH_AND_DECAY))
return;

deathAndDecayPosition[dynobj->GetMap()->GetInstanceId()] =
DeathAndDecayData{ dynobj->GetPosition(), getMSTime() };
}
};

void AddSC_HyjalSummitBotScripts()
{
new ArchimondeDoomfireTrailScript();
new AzgalorRainOfFireScript();
new RageWinterchillDeathAndDecayScript();
}
Loading