fix(Scripts/Hyjal): Archimonde refactored (#18594)

* wip

* wippers

* more wip

* wip314

* some work

* lesgo

* summonlist fixed

* I need glasses

* open from draft PR

* some fixes

* unused
This commit is contained in:
Dan
2024-03-27 02:37:00 +01:00
committed by GitHub
parent b1cf830009
commit 02cf369fe2
3 changed files with 402 additions and 628 deletions
@@ -88,37 +88,41 @@ enum Events
EVENT_SPELL_FINGER_OF_DEATH_PHASE_4 = 16 EVENT_SPELL_FINGER_OF_DEATH_PHASE_4 = 16
}; };
Position const NordrassilLoc = { 5503.713f, -3523.436f, 1608.781f, 0.0f }; uint32 const availableChargeAurasAndSpells[3][2] = {
{SPELL_SOUL_CHARGE_RED, SPELL_UNLEASH_SOUL_RED },
{SPELL_SOUL_CHARGE_YELLOW, SPELL_UNLEASH_SOUL_YELLOW },
{SPELL_SOUL_CHARGE_GREEN, SPELL_UNLEASH_SOUL_GREEN }
};
class npc_ancient_wisp : public CreatureScript Position const nordrassilPosition = { 5503.713f, -3523.436f, 1608.781f, 0.0f };
float const DOOMFIRE_OFFSET = 15.0f;
uint8 const WISP_OFFSET = 40;
struct npc_ancient_wisp : public ScriptedAI
{ {
public: npc_ancient_wisp(Creature* creature) : ScriptedAI(creature)
npc_ancient_wisp() : CreatureScript("npc_ancient_wisp") { }
CreatureAI* GetAI(Creature* creature) const override
{ {
return GetHyjalAI<npc_ancient_wispAI>(creature); _instance = creature->GetInstanceScript();
} }
struct npc_ancient_wispAI : public ScriptedAI
{
npc_ancient_wispAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
ArchimondeGUID.Clear();
}
InstanceScript* instance;
ObjectGuid ArchimondeGUID;
uint32 CheckTimer;
void Reset() override void Reset() override
{ {
CheckTimer = 1000;
ArchimondeGUID = instance->GetGuidData(DATA_ARCHIMONDE);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
ScheduleTimedEvent(1s, [&]
{
if (Creature* archimonde = _instance->GetCreature(DATA_ARCHIMONDE))
{
if (archimonde->HealthBelowPct(2) || !archimonde->IsAlive())
{
DoCastSelf(SPELL_DENOUEMENT_WISP);
}
else
{
DoCast(archimonde, SPELL_ANCIENT_SPARK);
}
}
}, 1s);
} }
void JustEngagedWith(Unit* /*who*/) override { } void JustEngagedWith(Unit* /*who*/) override { }
@@ -130,40 +134,30 @@ public:
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (CheckTimer <= diff) if (!UpdateVictim())
{ return;
if (Unit* Archimonde = ObjectAccessor::GetUnit(*me, ArchimondeGUID))
{ scheduler.Update(diff);
if (Archimonde->HealthBelowPct(2) || !Archimonde->IsAlive())
DoCast(me, SPELL_DENOUEMENT_WISP); if (me->HasUnitState(UNIT_STATE_CASTING))
else return;
DoCast(Archimonde, SPELL_ANCIENT_SPARK);
} }
CheckTimer = 1000;
} private:
else CheckTimer -= diff; InstanceScript* _instance;
}
};
}; };
/* This script is merely a placeholder for the Doomfire that triggers Doomfire spell. It will //TODO: move to db?
MoveChase the Doomfire Spirit always, until despawn (AttackStart is called upon it's spawn) */ struct npc_doomfire : public ScriptedAI
class npc_doomfire : public CreatureScript
{ {
public: npc_doomfire(Creature* creature) : ScriptedAI(creature), _summons(me) { }
npc_doomfire() : CreatureScript("npc_doomfire") { }
CreatureAI* GetAI(Creature* creature) const override void Reset() override
{ {
return GetHyjalAI<npc_doomfireAI>(creature); _summons.DespawnAll();
} }
struct npc_doomfireAI : public ScriptedAI
{
npc_doomfireAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override { }
void MoveInLineOfSight(Unit* /*who*/) override { } void MoveInLineOfSight(Unit* /*who*/) override { }
void JustEngagedWith(Unit* /*who*/) override { } void JustEngagedWith(Unit* /*who*/) override { }
@@ -172,188 +166,205 @@ public:
{ {
damage = 0; damage = 0;
} }
};
void JustSummoned(Creature* summoned) override
{
_summons.Summon(summoned);
if (summoned->GetEntry() == CREATURE_DOOMFIRE_SPIRIT)
{
me->GetMotionMaster()->MoveFollow(summoned, 0.0f, 0.0f);
}
}
private:
SummonList _summons;
}; };
/* This is the script for the Doomfire Spirit Mob. This mob simply follow players or struct npc_doomfire_targetting : public ScriptedAI
travels in random directions if target cannot be found. */
class npc_doomfire_targetting : public CreatureScript
{ {
public: npc_doomfire_targetting(Creature* creature) : ScriptedAI(creature) { }
npc_doomfire_targetting() : CreatureScript("npc_doomfire_targetting") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetHyjalAI<npc_doomfire_targettingAI>(creature);
}
struct npc_doomfire_targettingAI : public ScriptedAI
{
npc_doomfire_targettingAI(Creature* creature) : ScriptedAI(creature) { }
ObjectGuid TargetGUID;
uint32 ChangeTargetTimer;
void Reset() override void Reset() override
{ {
TargetGUID.Clear(); _chaseTarget = nullptr;
ChangeTargetTimer = 5000; ScheduleTimedEvent(5s, [&]
{
if (_chaseTarget)
{
me->GetMotionMaster()->MoveFollow(_chaseTarget, 0.0f, 0.0f);
}
else
{
Position randomPosition = me->GetRandomNearPosition(40.0f);
me->GetMotionMaster()->MovePoint(0, randomPosition);
}
}, 5s);
} }
void MoveInLineOfSight(Unit* who) override void MoveInLineOfSight(Unit* who) override
{ {
//will update once TargetGUID is 0. In case noone actually moves(not likely) and this is 0 if (who->IsPlayer())
//when UpdateAI needs it, it will be forced to select randomPoint {
if (!TargetGUID && who->GetTypeId() == TYPEID_PLAYER) _chaseTarget = who;
TargetGUID = who->GetGUID(); }
} }
void JustEngagedWith(Unit* /*who*/) override { }
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{ {
damage = 0; damage = 0;
} }
private:
void UpdateAI(uint32 diff) override Unit* _chaseTarget;
{
if (ChangeTargetTimer <= diff)
{
if (Unit* temp = ObjectAccessor::GetUnit(*me, TargetGUID))
{
me->GetMotionMaster()->MoveFollow(temp, 0.0f, 0.0f);
TargetGUID.Clear();
}
else
{
Position pos = me->GetRandomNearPosition(40);
me->GetMotionMaster()->MovePoint(0, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
}
ChangeTargetTimer = 5000;
}
else ChangeTargetTimer -= diff;
}
};
}; };
/* Finally, Archimonde's script. His script isn't extremely complex, most are simply spells on timers. struct boss_archimonde : public BossAI
The only complicated aspect of the battle is Finger of Death and Doomfire, with Doomfire being the
hardest bit to code. Finger of Death is simply a distance check - if no one is in melee range, then
select a random target and cast the spell on them. However, if someone IS in melee range, and this
is NOT the main tank (creature's victim), then we aggro that player and they become the new victim.
For Doomfire, we summon a mob (Doomfire Spirit) for the Doomfire mob to follow. It's spirit will
randomly select it's target to follow and then we create the random movement making it unpredictable. */
class boss_archimonde : public CreatureScript
{ {
public: boss_archimonde(Creature* creature) : BossAI(creature, DATA_ARCHIMONDE)
boss_archimonde() : CreatureScript("boss_archimonde") { }
CreatureAI* GetAI(Creature* creature) const override
{ {
return GetHyjalAI<boss_archimondeAI>(creature); scheduler.SetValidator([&]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
} }
struct boss_archimondeAI : public BossAI
{
boss_archimondeAI(Creature* creature) : BossAI(creature, BOSS_ARCHIMONDE), summons(me),
Enraged(false), BelowTenPercent(false), HasProtected(false), IsChanneling(false)
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
EventMap events;
ObjectGuid DoomfireSpiritGUID;
ObjectGuid WorldTreeGUID;
uint8 SoulChargeCount;
uint8 WispCount;
SummonList summons;
bool Enraged;
bool BelowTenPercent;
bool HasProtected;
bool IsChanneling;
std::list<Unit*> fingerOfDeathTargets;
std::list<Unit*> spellEffectTargets;
void Reset() override void Reset() override
{ {
instance->SetData(DATA_ARCHIMONDE, NOT_STARTED); BossAI::Reset();
_wispCount = 0;
_isChanneling = false;
_enraged = false;
_availableAuras.clear();
_availableSpells.clear();
me->SetReactState(REACT_AGGRESSIVE); if (Map* map = me->GetMap())
DoomfireSpiritGUID.Clear();
WorldTreeGUID.Clear();
WispCount = 0;
Enraged = false;
BelowTenPercent = false;
HasProtected = false;
IsChanneling = false;
// Reset player's immunity to Spells
for (auto it = spellEffectTargets.begin(); it != spellEffectTargets.end(); ++it)
{ {
Unit* affected_unit = ObjectAccessor::GetUnit(*me, (*it)->GetGUID()); map->DoForAllPlayers([&](Player* player)
{
// Remove Immunity against Hand of death player->ApplySpellImmune(SPELL_HAND_OF_DEATH, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false);
affected_unit->ApplySpellImmune(SPELL_HAND_OF_DEATH, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false); player->ApplySpellImmune(0, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false);
affected_unit->ApplySpellImmune(0, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false); });
} }
spellEffectTargets.clear(); if (instance->GetBossState(DATA_AZGALOR) != DONE)
fingerOfDeathTargets.clear(); {
summons.DespawnAll(); me->SetVisible(false);
events.ScheduleEvent(EVENT_DRAIN_WORLD_TREE, 0); me->SetReactState(REACT_PASSIVE);
}
else
{
DoAction(ACTION_BECOME_ACTIVE_AND_CHANNEL);
}
ScheduleHealthCheckEvent(10, [&]{
me->SetReactState(REACT_PASSIVE);
DoCastProtection();
Talk(SAY_ENRAGE);
_enraged = true;
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
ScheduleTimedEvent(1s, [&]
{
if (_wispCount >= 30)
{
Unit::DealDamage(me, me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
}
Position wispPosition = { me->GetPositionX() + float(rand() % WISP_OFFSET), me->GetPositionY() + float(rand() % WISP_OFFSET), me->GetPositionZ(), 0.0f };
if (Creature* wisp = me->SummonCreature(CREATURE_ANCIENT_WISP, wispPosition))
{
wisp->AI()->DoCast(me, SPELL_ANCIENT_SPARK);
++_wispCount;
}
}, 1500ms);
ScheduleTimedEvent(1500ms, [&]
{
DoCastVictim(SPELL_RED_SKY_EFFECT);
DoCastVictim(SPELL_HAND_OF_DEATH);
}, 3s);
ScheduleTimedEvent(2500ms, [&]
{
if (!(me->GetVictim() && me->IsWithinMeleeRange(me->GetVictim())))
{
DoCastRandomTarget(SPELL_FINGER_OF_DEATH);
}
}, 3500ms);
});
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_BECOME_ACTIVE_AND_CHANNEL:
me->SetReactState(REACT_AGGRESSIVE);
me->SetVisible(true);
if (!_isChanneling)
{
if (Creature* nordrassil = me->SummonCreature(CREATURE_CHANNEL_TARGET, nordrassilPosition, TEMPSUMMON_TIMED_DESPAWN, 1200000))
{
nordrassil->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
nordrassil->SetDisplayId(11686); //TODO: make enum
DoCast(nordrassil, SPELL_DRAIN_WORLD_TREE);
_isChanneling = true;
nordrassil->AI()->DoCast(me, SPELL_DRAIN_WORLD_TREE_2, true);
}
}
break;
}
} }
void DoCastProtection() void DoCastProtection()
{ {
// lets get spell info if (Map* map = me->GetMap())
SpellInfo const* info = sSpellMgr->GetSpellInfo(SPELL_PROTECTION_OF_ELUNE);
if (!info)
return;
// Now lets get archimode threat list
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList();
if (t_list.empty())
return;
ThreatContainer::StorageType::const_iterator itr = t_list.begin();
if (Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
if (target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER)
spellEffectTargets.push_back(target);
for (auto iter = spellEffectTargets.begin(); iter != spellEffectTargets.end(); ++iter)
if (Unit* target = *iter)
{ {
target->AddAura(SPELL_PROTECTION_OF_ELUNE, target); map->DoForAllPlayers([&](Player* player)
{
// Immunity against Hand of death player->AddAura(SPELL_PROTECTION_OF_ELUNE, player);
target->ApplySpellImmune(SPELL_HAND_OF_DEATH, IMMUNITY_ID, SPELL_HAND_OF_DEATH, true); player->ApplySpellImmune(SPELL_HAND_OF_DEATH, IMMUNITY_ID, SPELL_HAND_OF_DEATH, true);
target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_HAND_OF_DEATH, true); player->ApplySpellImmune(0, IMMUNITY_ID, SPELL_HAND_OF_DEATH, true);
});
} }
} }
void JustEngagedWith(Unit* /*who*/) override void JustEngagedWith(Unit* who) override
{ {
me->InterruptSpell(CURRENT_CHANNELED_SPELL); BossAI::JustEngagedWith(who);
me->InterruptNonMeleeSpells(false);
Talk(SAY_AGGRO); Talk(SAY_AGGRO);
DoZoneInCombat(); ScheduleTimedEvent(25s, 35s, [&]
{
instance->SetData(DATA_ARCHIMONDE, IN_PROGRESS); Talk(SAY_AIR_BURST);
events.ScheduleEvent(EVENT_SPELL_AIR_BURST, urand(25000, 35000)); DoCastRandomTarget(SPELL_AIR_BURST);
events.ScheduleEvent(EVENT_SPELL_DOOMFIRE, urand(10000, 20000)); }, 25s, 40s);
events.ScheduleEvent(EVENT_SPELL_FEAR, 42000); ScheduleTimedEvent(25s, 35s, [&]
events.ScheduleEvent(EVENT_SPELL_GRIP_OF_THE_LEGION, 2000); {
events.ScheduleEvent(EVENT_SPELL_FINGER_OF_DEATH, 1000); if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
DoCastDoomFire(target);
}
}, 20s);
ScheduleTimedEvent(25s, 35s, [&]
{
DoCastVictim(SPELL_FEAR);
}, 42s);
ScheduleTimedEvent(25s, 35s, [&]
{
DoCastRandomTarget(SPELL_GRIP_OF_THE_LEGION);
}, 5s, 25s);
ScheduleTimedEvent(5s, [&]
{
if (me->GetExactDist2d(nordrassilPosition) < 75.0f)
{
if (!_enraged)
{
_enraged = true;
Talk(SAY_ENRAGE);
ScheduleTimedEvent(1s, [&]
{
DoCastVictim(SPELL_RED_SKY_EFFECT);
DoCastVictim(SPELL_HAND_OF_DEATH);
}, 3s);
}
}
}, 5s);
instance->SetData(DATA_SPAWN_WAVES, 1); instance->SetData(DATA_SPAWN_WAVES, 1);
} }
@@ -362,56 +373,45 @@ public:
{ {
Talk(SAY_SLAY); Talk(SAY_SLAY);
if (victim && victim->GetTypeId() == TYPEID_PLAYER) if (victim->IsPlayer())
{
GainSoulCharge(victim->ToPlayer()); GainSoulCharge(victim->ToPlayer());
} }
}
void GainSoulCharge(Player* victim) void GainSoulCharge(Player* player)
{ {
switch (victim->getClass()) switch (player->getClass())
{ {
case CLASS_PRIEST: case CLASS_PRIEST:
case CLASS_PALADIN: case CLASS_PALADIN:
case CLASS_WARLOCK: case CLASS_WARLOCK:
victim->CastSpell(me, SPELL_SOUL_CHARGE_RED, true); player->CastSpell(me, SPELL_SOUL_CHARGE_RED, true);
break; break;
case CLASS_MAGE: case CLASS_MAGE:
case CLASS_ROGUE: case CLASS_ROGUE:
case CLASS_WARRIOR: case CLASS_WARRIOR:
victim->CastSpell(me, SPELL_SOUL_CHARGE_YELLOW, true); player->CastSpell(me, SPELL_SOUL_CHARGE_YELLOW, true);
break; break;
case CLASS_DRUID: case CLASS_DRUID:
case CLASS_SHAMAN: case CLASS_SHAMAN:
case CLASS_HUNTER: case CLASS_HUNTER:
victim->CastSpell(me, SPELL_SOUL_CHARGE_GREEN, true); player->CastSpell(me, SPELL_SOUL_CHARGE_GREEN, true);
break;
case CLASS_DEATH_KNIGHT:
case CLASS_NONE:
default:
break; break;
} }
scheduler.Schedule(2s, 10s, [this](TaskContext)
events.ScheduleEvent(EVENT_SPELL_UNLEASH_SOUL_CHARGES, urand(2000, 10000)); {
++SoulChargeCount; UnleashSoulCharge();
});
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* killer) override
{ {
Talk(SAY_DEATH); BossAI::JustDied(killer);
instance->SetData(DATA_ARCHIMONDE, DONE);
instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, me->GetEntry(), 1, me);
// Reset scheduled events
events.CancelEvent(EVENT_SPELL_FEAR);
events.CancelEvent(EVENT_SPELL_AIR_BURST);
events.CancelEvent(EVENT_SPELL_GRIP_OF_THE_LEGION);
events.CancelEvent(EVENT_SPELL_UNLEASH_SOUL_CHARGES);
events.CancelEvent(EVENT_SPELL_DOOMFIRE);
events.CancelEvent(EVENT_SPELL_FINGER_OF_DEATH);
events.CancelEvent(EVENT_SPELL_HAND_OF_DEATH);
events.CancelEvent(EVENT_SPELL_PROTECTION_OF_ELUNE);
events.CancelEvent(EVENT_ENRAGE);
spellEffectTargets.clear();
fingerOfDeathTargets.clear();
summons.DespawnAll();
} }
void EnterEvadeMode(EvadeReason why) override void EnterEvadeMode(EvadeReason why) override
@@ -420,290 +420,84 @@ public:
BossAI::EnterEvadeMode(why); BossAI::EnterEvadeMode(why);
} }
bool CanUseFingerOfDeath()
{
// Cast finger of death below 10% health
if (BelowTenPercent)
return true;
if (me->IsAlive())
{
/* Reset the list before checking for new targets
* else we will be using old targets that could have
* been distant but are no longer.*/
fingerOfDeathTargets.clear();
// First we check if our current victim is in melee range or not.
Unit* victim = me->GetVictim();
if (victim && me->IsWithinMeleeRange(victim))
return false;
ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().GetThreatList();
if (threatlist.empty())
return false;
auto itr = threatlist.begin();
for (; itr != threatlist.end(); ++itr)
{
Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
if (unit && unit->IsAlive() && me->IsWithinMeleeRange(unit))
fingerOfDeathTargets.push_back(unit);
}
/* Previous check searched for targets in meele range and
* added it to targets list. If there are no targets in meele
* range return true, which makes Archimonde cast Finger of Death.
*/
return fingerOfDeathTargets.empty();
}
return false;
}
void JustSummoned(Creature* summoned) override void JustSummoned(Creature* summoned) override
{ {
summons.Summon(summoned); BossAI::JustSummoned(summoned);
if (summoned->GetEntry() == CREATURE_ANCIENT_WISP) if (summoned->GetEntry() == CREATURE_ANCIENT_WISP)
{
summoned->AI()->AttackStart(me); summoned->AI()->AttackStart(me);
}
else else
{ {
summoned->SetFaction(me->GetFaction()); summoned->SetFaction(me->GetFaction()); //remove?
summoned->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); summoned->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
summoned->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); summoned->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
} }
if (summoned->GetEntry() == CREATURE_DOOMFIRE_SPIRIT)
{
DoomfireSpiritGUID = summoned->GetGUID();
}
if (summoned->GetEntry() == CREATURE_DOOMFIRE) if (summoned->GetEntry() == CREATURE_DOOMFIRE)
{ {
summoned->CastSpell(summoned, SPELL_DOOMFIRE_SPAWN, false); summoned->CastSpell(summoned, SPELL_DOOMFIRE_SPAWN);
summoned->CastSpell(summoned, SPELL_DOOMFIRE, true, 0, 0, me->GetGUID()); summoned->CastSpell(summoned, SPELL_DOOMFIRE, true, 0, 0, me->GetGUID());
if (Unit* DoomfireSpirit = ObjectAccessor::GetUnit(*me, DoomfireSpiritGUID))
{
summoned->GetMotionMaster()->MoveFollow(DoomfireSpirit, 0.0f, 0.0f);
DoomfireSpiritGUID.Clear();
}
} }
} }
void DoCastDoomfire() void DoCastDoomFire(Unit* target)
{ {
// Three doomfire can be up at the same time // hack because spell doesn't work?
Talk(SAY_DOOMFIRE); Talk(SAY_DOOMFIRE);
Unit* temp = SelectTarget(SelectTargetMethod::Random, 1); Position spiritPosition = { target->GetPositionX() + DOOMFIRE_OFFSET, target->GetPositionY() + DOOMFIRE_OFFSET, target->GetPositionZ(), 0.0f };
if (!temp) Position doomfirePosition = { target->GetPositionX() - DOOMFIRE_OFFSET, target->GetPositionY() - DOOMFIRE_OFFSET, target->GetPositionZ(), 0.0f };
temp = me->GetVictim(); if (Unit* doomfireSpirit = me->SummonCreature(CREATURE_DOOMFIRE_SPIRIT, spiritPosition, TEMPSUMMON_TIMED_DESPAWN, 27000))
//replace with spell cast 31903 once implicitTarget 73 implemented
SummonDoomfire(temp);
}
//this is code doing close to what the summoning spell would do (spell 31903)
void SummonDoomfire(Unit* target)
{ {
Unit* doomfire1 = me->SummonCreature(CREATURE_DOOMFIRE_SPIRIT, if (Unit* doomfire = me->SummonCreature(CREATURE_DOOMFIRE, doomfirePosition, TEMPSUMMON_TIMED_DESPAWN, 27000))
target->GetPositionX() + 15.0f, target->GetPositionY() + 15.0f, target->GetPositionZ(), 0, {
TEMPSUMMON_TIMED_DESPAWN, 27000); doomfireSpirit->SetVisible(false);
doomfire->SetVisible(false);
Unit* doomfire2 = me->SummonCreature(CREATURE_DOOMFIRE, }
target->GetPositionX() - 15.0f, target->GetPositionY() - 15.0f, target->GetPositionZ(), 0, }
TEMPSUMMON_TIMED_DESPAWN, 27000);
doomfire1->SetVisible(false);
doomfire2->SetVisible(false);
} }
void UnleashSoulCharge() void UnleashSoulCharge()
{ {
me->InterruptNonMeleeSpells(false); me->InterruptNonMeleeSpells(false);
//add all auras to spells
uint32 chargeSpell = 0; for (uint8 n = 0; n < 3; ++n)
uint32 unleashSpell = 0;
switch (urand(0, 2))
{ {
case 0: if (me->HasAura(availableChargeAurasAndSpells[n][0]))
chargeSpell = SPELL_SOUL_CHARGE_RED;
unleashSpell = SPELL_UNLEASH_SOUL_RED;
break;
case 1:
chargeSpell = SPELL_SOUL_CHARGE_YELLOW;
unleashSpell = SPELL_UNLEASH_SOUL_YELLOW;
break;
case 2:
chargeSpell = SPELL_SOUL_CHARGE_GREEN;
unleashSpell = SPELL_UNLEASH_SOUL_GREEN;
break;
}
if (me->HasAura(chargeSpell))
{ {
me->RemoveAuraFromStack(chargeSpell); _availableAuras.push_back(availableChargeAurasAndSpells[n][0]);
DoCastVictim(unleashSpell); _availableSpells.push_back(availableChargeAurasAndSpells[n][1]);
SoulChargeCount--;
} }
} }
//only unleash when we found spells and auras
void UpdateAI(uint32 diff) override if (!_availableAuras.empty() && !_availableSpells.empty())
{ {
events.Update(diff); //coin flip to swap front and back item
if (urand(0, 1))
// Event for draining the tree
if (!me->IsInCombat())
{ {
// Do not let the raid skip straight to Archimonde. Visible and hostile ONLY if Azagalor is finished. _availableAuras.insert(_availableAuras.begin(), _availableAuras.back());
if ((instance->GetBossState(DATA_AZGALOR) != DONE) && (me->IsVisible() || (me->GetFaction() != FACTION_FRIENDLY))) _availableAuras.pop_back();
{ _availableSpells.insert(_availableSpells.begin(), _availableSpells.back());
me->SetVisible(false); _availableSpells.pop_back();
me->SetFaction(FACTION_FRIENDLY);
} }
//remove aura and cast spell
if ((instance->GetBossState(DATA_AZGALOR) == DONE) && (!me->IsVisible() || (me->GetFaction() == FACTION_FRIENDLY))) me->RemoveAuraFromStack(_availableAuras.front());
{ DoCastVictim(_availableSpells.front());
me->SetFaction(FACTION_DRAGONKIN); //clear to clean vectors
me->SetVisible(true); _availableAuras.clear();
} _availableSpells.clear();
switch (events.ExecuteEvent())
{
case EVENT_DRAIN_WORLD_TREE:
if (!IsChanneling)
{
Creature* temp = me->SummonCreature(CREATURE_CHANNEL_TARGET, NordrassilLoc, TEMPSUMMON_TIMED_DESPAWN, 1200000);
if (temp)
WorldTreeGUID = temp->GetGUID();
if (Unit* Nordrassil = ObjectAccessor::GetUnit(*me, WorldTreeGUID))
{
Nordrassil->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
Nordrassil->SetDisplayId(11686);
DoCast(Nordrassil, SPELL_DRAIN_WORLD_TREE);
IsChanneling = true;
} }
} }
private:
if (Unit* Nordrassil = ObjectAccessor::GetUnit(*me, WorldTreeGUID)) uint8 _wispCount;
Nordrassil->CastSpell(me, SPELL_DRAIN_WORLD_TREE_2, true); bool _isChanneling;
break; bool _enraged;
} std::vector<uint32> _availableAuras;
} std::vector<uint32> _availableSpells;
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (me->HealthBelowPct(10) && !BelowTenPercent)
events.ScheduleEvent(EVENT_BELOW_10_PERCENT_HP, 0);
switch (events.ExecuteEvent())
{
case EVENT_CHECK_WORLD_TREE_DISTANCE:
{
// If Archimonde is too close to the world tree this will ENRAGE him
Creature* Check = me->SummonCreature(CREATURE_CHANNEL_TARGET, NordrassilLoc, TEMPSUMMON_TIMED_DESPAWN, 2000);
if (Check)
{
Check->SetVisible(false);
if (me->IsWithinDistInMap(Check, 75))
{
events.ScheduleEvent(EVENT_TOO_CLOSE_TO_WORLD_TREE, 0);
break;
}
}
events.RepeatEvent(5000);
break;
}
case EVENT_BELOW_10_PERCENT_HP:
me->SetReactState(REACT_PASSIVE);
DoCastProtection(); // Protection of Elune against Finger and Hand of Death
BelowTenPercent = true;
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
events.ScheduleEvent(EVENT_ENRAGE, 0);
events.ScheduleEvent(EVENT_ENRAGE_ROOT, 0);
events.ScheduleEvent(EVENT_SUMMON_WISPS, 1000);
events.ScheduleEvent(EVENT_SPELL_HAND_OF_DEATH, 1500);
events.ScheduleEvent(EVENT_SPELL_FINGER_OF_DEATH, 2500);
break;
case EVENT_SUMMON_WISPS:
// If there are more than 30 Wisps then kill Archimonde
if (WispCount >= 30)
{
Unit::DealDamage(me, me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
return; // Finish the encounter and no more event repeat
}
DoSpawnCreature(CREATURE_ANCIENT_WISP, float(rand() % 40), float(rand() % 40), 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
++WispCount;
events.ScheduleEvent(EVENT_SUMMON_WISPS, 1500);
break;
case EVENT_SPELL_HAND_OF_DEATH:
DoCastVictim(SPELL_RED_SKY_EFFECT);
DoCastVictim(SPELL_HAND_OF_DEATH);
events.ScheduleEvent(EVENT_SPELL_HAND_OF_DEATH, 3000);
break;
case EVENT_SPELL_FINGER_OF_DEATH:
if (CanUseFingerOfDeath())
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
DoCast(target, SPELL_FINGER_OF_DEATH);
DoCastVictim(SPELL_RED_SKY_EFFECT);
}
events.ScheduleEvent(EVENT_SPELL_FINGER_OF_DEATH, 3500);
break;
case EVENT_SPELL_GRIP_OF_THE_LEGION:
DoCast(SelectTarget(SelectTargetMethod::Random, 0), SPELL_GRIP_OF_THE_LEGION);
events.ScheduleEvent(EVENT_SPELL_GRIP_OF_THE_LEGION, urand(5000, 25000));
break;
case EVENT_SPELL_AIR_BURST:
Talk(SAY_AIR_BURST);
DoCast(SelectTarget(SelectTargetMethod::Random, 0), SPELL_AIR_BURST);
events.ScheduleEvent(EVENT_SPELL_AIR_BURST, urand(25000, 40000));
break;
case EVENT_SPELL_FEAR:
DoCastVictim(SPELL_FEAR);
events.ScheduleEvent(EVENT_SPELL_FEAR, 42000);
break;
case EVENT_SPELL_DOOMFIRE:
DoCastDoomfire();
events.ScheduleEvent(EVENT_SPELL_DOOMFIRE, 20000);
break;
case EVENT_SPELL_UNLEASH_SOUL_CHARGES:
UnleashSoulCharge();
break;
case EVENT_ENRAGE:
Talk(SAY_ENRAGE);
break;
case EVENT_TOO_CLOSE_TO_WORLD_TREE:
// People dragged the boss near the check and now wipe
events.ScheduleEvent(EVENT_ENRAGE, 0);
events.ScheduleEvent(EVENT_SPELL_HAND_OF_DEATH, 1000);
break;
case EVENT_ENRAGE_ROOT:
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
break;
}
DoMeleeAttackIfReady();
}
};
}; };
class spell_red_sky_effect : public SpellScript
class spell_red_sky_effect : public SpellScriptLoader
{ {
public: PrepareSpellScript(spell_red_sky_effect);
spell_red_sky_effect() : SpellScriptLoader("spell_red_sky_effect") { }
class spell_red_sky_effect_SpellScript : public SpellScript
{
PrepareSpellScript(spell_red_sky_effect_SpellScript);
void HandleHit(SpellEffIndex /*effIndex*/) void HandleHit(SpellEffIndex /*effIndex*/)
{ {
@@ -713,24 +507,13 @@ public:
void Register() override void Register() override
{ {
OnEffectHitTarget += SpellEffectFn(spell_red_sky_effect_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); OnEffectHitTarget += SpellEffectFn(spell_red_sky_effect::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_red_sky_effect_SpellScript();
} }
}; };
class spell_finger_of_death : public SpellScriptLoader class spell_finger_of_death : public SpellScript
{ {
public: PrepareSpellScript(spell_finger_of_death);
spell_finger_of_death() : SpellScriptLoader("spell_finger_of_death") { }
class spell_finger_of_death_SpellScript : public SpellScript
{
PrepareSpellScript(spell_finger_of_death_SpellScript);
void HandleHit(SpellEffIndex /*effIndex*/) void HandleHit(SpellEffIndex /*effIndex*/)
{ {
@@ -742,24 +525,13 @@ public:
void Register() override void Register() override
{ {
OnEffectHitTarget += SpellEffectFn(spell_finger_of_death_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); OnEffectHitTarget += SpellEffectFn(spell_finger_of_death::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_finger_of_death_SpellScript();
} }
}; };
class spell_hand_of_death : public SpellScriptLoader class spell_hand_of_death : public SpellScript
{ {
public: PrepareSpellScript(spell_hand_of_death);
spell_hand_of_death() : SpellScriptLoader("spell_hand_of_death") { }
class spell_hand_of_death_SpellScript : public SpellScript
{
PrepareSpellScript(spell_hand_of_death_SpellScript);
void HandleHit(SpellEffIndex /*effIndex*/) void HandleHit(SpellEffIndex /*effIndex*/)
{ {
@@ -771,24 +543,18 @@ public:
void Register() override void Register() override
{ {
OnEffectHitTarget += SpellEffectFn(spell_hand_of_death_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); OnEffectHitTarget += SpellEffectFn(spell_hand_of_death::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_hand_of_death_SpellScript();
} }
}; };
void AddSC_boss_archimonde() void AddSC_boss_archimonde()
{ {
new spell_red_sky_effect(); RegisterSpellScript(spell_red_sky_effect);
new spell_hand_of_death(); RegisterSpellScript(spell_hand_of_death);
new spell_finger_of_death(); RegisterSpellScript(spell_finger_of_death);
new boss_archimonde(); RegisterHyjalAI(boss_archimonde);
new npc_doomfire(); RegisterHyjalAI(npc_doomfire);
new npc_doomfire_targetting(); RegisterHyjalAI(npc_doomfire_targetting);
new npc_ancient_wisp(); RegisterHyjalAI(npc_ancient_wisp);
} }
@@ -105,7 +105,10 @@ public:
Talk(SAY_ONDEATH); Talk(SAY_ONDEATH);
// If Archimonde has not yet been initialized, this won't trigger // If Archimonde has not yet been initialized, this won't trigger
if (Creature* archi = instance->GetCreature(DATA_ARCHIMONDE)) if (Creature* archi = instance->GetCreature(DATA_ARCHIMONDE))
{
archi->AI()->DoAction(ACTION_BECOME_ACTIVE_AND_CHANNEL);
archi->AI()->Talk(SAY_ARCHIMONDE_INTRO, 25000ms); archi->AI()->Talk(SAY_ARCHIMONDE_INTRO, 25000ms);
}
BossAI::JustDied(killer); BossAI::JustDied(killer);
} }
@@ -178,6 +178,11 @@ enum HyjalPaths
HORDE_BOSS_PATH = 178527 HORDE_BOSS_PATH = 178527
}; };
enum BossActions
{
ACTION_BECOME_ACTIVE_AND_CHANNEL = 0
};
template <class AI, class T> template <class AI, class T>
inline AI* GetHyjalAI(T* obj) inline AI* GetHyjalAI(T* obj)
{ {