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:
+276
-510
@@ -88,37 +88,41 @@ enum Events
|
||||
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
|
||||
{
|
||||
public:
|
||||
npc_ancient_wisp() : CreatureScript("npc_ancient_wisp") { }
|
||||
Position const nordrassilPosition = { 5503.713f, -3523.436f, 1608.781f, 0.0f };
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
float const DOOMFIRE_OFFSET = 15.0f;
|
||||
uint8 const WISP_OFFSET = 40;
|
||||
|
||||
struct npc_ancient_wisp : public ScriptedAI
|
||||
{
|
||||
return GetHyjalAI<npc_ancient_wispAI>(creature);
|
||||
npc_ancient_wisp(Creature* creature) : ScriptedAI(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
|
||||
{
|
||||
CheckTimer = 1000;
|
||||
|
||||
ArchimondeGUID = instance->GetGuidData(DATA_ARCHIMONDE);
|
||||
|
||||
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 { }
|
||||
@@ -130,40 +134,30 @@ public:
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (CheckTimer <= diff)
|
||||
{
|
||||
if (Unit* Archimonde = ObjectAccessor::GetUnit(*me, ArchimondeGUID))
|
||||
{
|
||||
if (Archimonde->HealthBelowPct(2) || !Archimonde->IsAlive())
|
||||
DoCast(me, SPELL_DENOUEMENT_WISP);
|
||||
else
|
||||
DoCast(Archimonde, SPELL_ANCIENT_SPARK);
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
scheduler.Update(diff);
|
||||
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
}
|
||||
CheckTimer = 1000;
|
||||
}
|
||||
else CheckTimer -= diff;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
InstanceScript* _instance;
|
||||
};
|
||||
|
||||
/* This script is merely a placeholder for the Doomfire that triggers Doomfire spell. It will
|
||||
MoveChase the Doomfire Spirit always, until despawn (AttackStart is called upon it's spawn) */
|
||||
class npc_doomfire : public CreatureScript
|
||||
//TODO: move to db?
|
||||
struct npc_doomfire : public ScriptedAI
|
||||
{
|
||||
public:
|
||||
npc_doomfire() : CreatureScript("npc_doomfire") { }
|
||||
npc_doomfire(Creature* creature) : ScriptedAI(creature), _summons(me) { }
|
||||
|
||||
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 JustEngagedWith(Unit* /*who*/) override { }
|
||||
@@ -172,188 +166,205 @@ public:
|
||||
{
|
||||
damage = 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* This is the script for the Doomfire Spirit Mob. This mob simply follow players or
|
||||
travels in random directions if target cannot be found. */
|
||||
class npc_doomfire_targetting : public CreatureScript
|
||||
void JustSummoned(Creature* summoned) override
|
||||
{
|
||||
public:
|
||||
npc_doomfire_targetting() : CreatureScript("npc_doomfire_targetting") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
_summons.Summon(summoned);
|
||||
if (summoned->GetEntry() == CREATURE_DOOMFIRE_SPIRIT)
|
||||
{
|
||||
return GetHyjalAI<npc_doomfire_targettingAI>(creature);
|
||||
me->GetMotionMaster()->MoveFollow(summoned, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
private:
|
||||
SummonList _summons;
|
||||
};
|
||||
|
||||
struct npc_doomfire_targettingAI : public ScriptedAI
|
||||
struct npc_doomfire_targetting : public ScriptedAI
|
||||
{
|
||||
npc_doomfire_targettingAI(Creature* creature) : ScriptedAI(creature) { }
|
||||
|
||||
ObjectGuid TargetGUID;
|
||||
uint32 ChangeTargetTimer;
|
||||
npc_doomfire_targetting(Creature* creature) : ScriptedAI(creature) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
TargetGUID.Clear();
|
||||
ChangeTargetTimer = 5000;
|
||||
_chaseTarget = nullptr;
|
||||
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
|
||||
|
||||
{
|
||||
//will update once TargetGUID is 0. In case noone actually moves(not likely) and this is 0
|
||||
//when UpdateAI needs it, it will be forced to select randomPoint
|
||||
if (!TargetGUID && who->GetTypeId() == TYPEID_PLAYER)
|
||||
TargetGUID = who->GetGUID();
|
||||
if (who->IsPlayer())
|
||||
{
|
||||
_chaseTarget = who;
|
||||
}
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override { }
|
||||
|
||||
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
private:
|
||||
Unit* _chaseTarget;
|
||||
};
|
||||
|
||||
/* Finally, Archimonde's script. His script isn't extremely complex, most are simply spells on timers.
|
||||
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
|
||||
struct boss_archimonde : public BossAI
|
||||
{
|
||||
public:
|
||||
boss_archimonde() : CreatureScript("boss_archimonde") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
boss_archimonde(Creature* creature) : BossAI(creature, DATA_ARCHIMONDE)
|
||||
{
|
||||
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
|
||||
{
|
||||
instance->SetData(DATA_ARCHIMONDE, NOT_STARTED);
|
||||
BossAI::Reset();
|
||||
_wispCount = 0;
|
||||
_isChanneling = false;
|
||||
_enraged = false;
|
||||
_availableAuras.clear();
|
||||
_availableSpells.clear();
|
||||
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
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)
|
||||
if (Map* map = me->GetMap())
|
||||
{
|
||||
Unit* affected_unit = ObjectAccessor::GetUnit(*me, (*it)->GetGUID());
|
||||
|
||||
// Remove Immunity against Hand of death
|
||||
affected_unit->ApplySpellImmune(SPELL_HAND_OF_DEATH, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false);
|
||||
affected_unit->ApplySpellImmune(0, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false);
|
||||
map->DoForAllPlayers([&](Player* player)
|
||||
{
|
||||
player->ApplySpellImmune(SPELL_HAND_OF_DEATH, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false);
|
||||
player->ApplySpellImmune(0, IMMUNITY_ID, SPELL_HAND_OF_DEATH, false);
|
||||
});
|
||||
}
|
||||
|
||||
spellEffectTargets.clear();
|
||||
fingerOfDeathTargets.clear();
|
||||
summons.DespawnAll();
|
||||
events.ScheduleEvent(EVENT_DRAIN_WORLD_TREE, 0);
|
||||
if (instance->GetBossState(DATA_AZGALOR) != DONE)
|
||||
{
|
||||
me->SetVisible(false);
|
||||
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()
|
||||
{
|
||||
// lets get spell info
|
||||
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)
|
||||
if (Map* map = me->GetMap())
|
||||
{
|
||||
target->AddAura(SPELL_PROTECTION_OF_ELUNE, target);
|
||||
|
||||
// Immunity against Hand of death
|
||||
target->ApplySpellImmune(SPELL_HAND_OF_DEATH, IMMUNITY_ID, SPELL_HAND_OF_DEATH, true);
|
||||
target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_HAND_OF_DEATH, true);
|
||||
map->DoForAllPlayers([&](Player* player)
|
||||
{
|
||||
player->AddAura(SPELL_PROTECTION_OF_ELUNE, player);
|
||||
player->ApplySpellImmune(SPELL_HAND_OF_DEATH, 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);
|
||||
DoZoneInCombat();
|
||||
|
||||
instance->SetData(DATA_ARCHIMONDE, IN_PROGRESS);
|
||||
events.ScheduleEvent(EVENT_SPELL_AIR_BURST, urand(25000, 35000));
|
||||
events.ScheduleEvent(EVENT_SPELL_DOOMFIRE, urand(10000, 20000));
|
||||
events.ScheduleEvent(EVENT_SPELL_FEAR, 42000);
|
||||
events.ScheduleEvent(EVENT_SPELL_GRIP_OF_THE_LEGION, 2000);
|
||||
events.ScheduleEvent(EVENT_SPELL_FINGER_OF_DEATH, 1000);
|
||||
ScheduleTimedEvent(25s, 35s, [&]
|
||||
{
|
||||
Talk(SAY_AIR_BURST);
|
||||
DoCastRandomTarget(SPELL_AIR_BURST);
|
||||
}, 25s, 40s);
|
||||
ScheduleTimedEvent(25s, 35s, [&]
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -362,56 +373,45 @@ public:
|
||||
{
|
||||
Talk(SAY_SLAY);
|
||||
|
||||
if (victim && victim->GetTypeId() == TYPEID_PLAYER)
|
||||
if (victim->IsPlayer())
|
||||
{
|
||||
GainSoulCharge(victim->ToPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
void GainSoulCharge(Player* victim)
|
||||
void GainSoulCharge(Player* player)
|
||||
{
|
||||
switch (victim->getClass())
|
||||
switch (player->getClass())
|
||||
{
|
||||
case CLASS_PRIEST:
|
||||
case CLASS_PALADIN:
|
||||
case CLASS_WARLOCK:
|
||||
victim->CastSpell(me, SPELL_SOUL_CHARGE_RED, true);
|
||||
player->CastSpell(me, SPELL_SOUL_CHARGE_RED, true);
|
||||
break;
|
||||
case CLASS_MAGE:
|
||||
case CLASS_ROGUE:
|
||||
case CLASS_WARRIOR:
|
||||
victim->CastSpell(me, SPELL_SOUL_CHARGE_YELLOW, true);
|
||||
player->CastSpell(me, SPELL_SOUL_CHARGE_YELLOW, true);
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
case CLASS_SHAMAN:
|
||||
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;
|
||||
}
|
||||
|
||||
events.ScheduleEvent(EVENT_SPELL_UNLEASH_SOUL_CHARGES, urand(2000, 10000));
|
||||
++SoulChargeCount;
|
||||
scheduler.Schedule(2s, 10s, [this](TaskContext)
|
||||
{
|
||||
UnleashSoulCharge();
|
||||
});
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
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();
|
||||
BossAI::JustDied(killer);
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
@@ -420,290 +420,84 @@ public:
|
||||
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
|
||||
{
|
||||
summons.Summon(summoned);
|
||||
BossAI::JustSummoned(summoned);
|
||||
if (summoned->GetEntry() == CREATURE_ANCIENT_WISP)
|
||||
{
|
||||
summoned->AI()->AttackStart(me);
|
||||
}
|
||||
else
|
||||
{
|
||||
summoned->SetFaction(me->GetFaction());
|
||||
summoned->SetFaction(me->GetFaction()); //remove?
|
||||
summoned->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
|
||||
summoned->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
|
||||
if (summoned->GetEntry() == CREATURE_DOOMFIRE_SPIRIT)
|
||||
{
|
||||
DoomfireSpiritGUID = summoned->GetGUID();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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);
|
||||
Unit* temp = SelectTarget(SelectTargetMethod::Random, 1);
|
||||
if (!temp)
|
||||
temp = me->GetVictim();
|
||||
|
||||
//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)
|
||||
Position spiritPosition = { target->GetPositionX() + DOOMFIRE_OFFSET, target->GetPositionY() + DOOMFIRE_OFFSET, target->GetPositionZ(), 0.0f };
|
||||
Position doomfirePosition = { target->GetPositionX() - DOOMFIRE_OFFSET, target->GetPositionY() - DOOMFIRE_OFFSET, target->GetPositionZ(), 0.0f };
|
||||
if (Unit* doomfireSpirit = me->SummonCreature(CREATURE_DOOMFIRE_SPIRIT, spiritPosition, TEMPSUMMON_TIMED_DESPAWN, 27000))
|
||||
{
|
||||
Unit* doomfire1 = me->SummonCreature(CREATURE_DOOMFIRE_SPIRIT,
|
||||
target->GetPositionX() + 15.0f, target->GetPositionY() + 15.0f, target->GetPositionZ(), 0,
|
||||
TEMPSUMMON_TIMED_DESPAWN, 27000);
|
||||
|
||||
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);
|
||||
if (Unit* doomfire = me->SummonCreature(CREATURE_DOOMFIRE, doomfirePosition, TEMPSUMMON_TIMED_DESPAWN, 27000))
|
||||
{
|
||||
doomfireSpirit->SetVisible(false);
|
||||
doomfire->SetVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnleashSoulCharge()
|
||||
{
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
|
||||
uint32 chargeSpell = 0;
|
||||
uint32 unleashSpell = 0;
|
||||
|
||||
switch (urand(0, 2))
|
||||
//add all auras to spells
|
||||
for (uint8 n = 0; n < 3; ++n)
|
||||
{
|
||||
case 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))
|
||||
if (me->HasAura(availableChargeAurasAndSpells[n][0]))
|
||||
{
|
||||
me->RemoveAuraFromStack(chargeSpell);
|
||||
DoCastVictim(unleashSpell);
|
||||
SoulChargeCount--;
|
||||
_availableAuras.push_back(availableChargeAurasAndSpells[n][0]);
|
||||
_availableSpells.push_back(availableChargeAurasAndSpells[n][1]);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
//only unleash when we found spells and auras
|
||||
if (!_availableAuras.empty() && !_availableSpells.empty())
|
||||
{
|
||||
events.Update(diff);
|
||||
|
||||
// Event for draining the tree
|
||||
if (!me->IsInCombat())
|
||||
//coin flip to swap front and back item
|
||||
if (urand(0, 1))
|
||||
{
|
||||
// Do not let the raid skip straight to Archimonde. Visible and hostile ONLY if Azagalor is finished.
|
||||
if ((instance->GetBossState(DATA_AZGALOR) != DONE) && (me->IsVisible() || (me->GetFaction() != FACTION_FRIENDLY)))
|
||||
{
|
||||
me->SetVisible(false);
|
||||
me->SetFaction(FACTION_FRIENDLY);
|
||||
_availableAuras.insert(_availableAuras.begin(), _availableAuras.back());
|
||||
_availableAuras.pop_back();
|
||||
_availableSpells.insert(_availableSpells.begin(), _availableSpells.back());
|
||||
_availableSpells.pop_back();
|
||||
}
|
||||
|
||||
if ((instance->GetBossState(DATA_AZGALOR) == DONE) && (!me->IsVisible() || (me->GetFaction() == FACTION_FRIENDLY)))
|
||||
{
|
||||
me->SetFaction(FACTION_DRAGONKIN);
|
||||
me->SetVisible(true);
|
||||
}
|
||||
|
||||
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;
|
||||
//remove aura and cast spell
|
||||
me->RemoveAuraFromStack(_availableAuras.front());
|
||||
DoCastVictim(_availableSpells.front());
|
||||
//clear to clean vectors
|
||||
_availableAuras.clear();
|
||||
_availableSpells.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (Unit* Nordrassil = ObjectAccessor::GetUnit(*me, WorldTreeGUID))
|
||||
Nordrassil->CastSpell(me, SPELL_DRAIN_WORLD_TREE_2, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
private:
|
||||
uint8 _wispCount;
|
||||
bool _isChanneling;
|
||||
bool _enraged;
|
||||
std::vector<uint32> _availableAuras;
|
||||
std::vector<uint32> _availableSpells;
|
||||
};
|
||||
};
|
||||
|
||||
class spell_red_sky_effect : public SpellScriptLoader
|
||||
class spell_red_sky_effect : public SpellScript
|
||||
{
|
||||
public:
|
||||
spell_red_sky_effect() : SpellScriptLoader("spell_red_sky_effect") { }
|
||||
|
||||
class spell_red_sky_effect_SpellScript : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_red_sky_effect_SpellScript);
|
||||
PrepareSpellScript(spell_red_sky_effect);
|
||||
|
||||
void HandleHit(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
@@ -713,24 +507,13 @@ public:
|
||||
|
||||
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
|
||||
class spell_finger_of_death : public SpellScript
|
||||
{
|
||||
return new spell_red_sky_effect_SpellScript();
|
||||
}
|
||||
};
|
||||
|
||||
class spell_finger_of_death : public SpellScriptLoader
|
||||
{
|
||||
public:
|
||||
spell_finger_of_death() : SpellScriptLoader("spell_finger_of_death") { }
|
||||
|
||||
class spell_finger_of_death_SpellScript : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_finger_of_death_SpellScript);
|
||||
PrepareSpellScript(spell_finger_of_death);
|
||||
|
||||
void HandleHit(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
@@ -742,24 +525,13 @@ public:
|
||||
|
||||
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
|
||||
class spell_hand_of_death : public SpellScript
|
||||
{
|
||||
return new spell_finger_of_death_SpellScript();
|
||||
}
|
||||
};
|
||||
|
||||
class spell_hand_of_death : public SpellScriptLoader
|
||||
{
|
||||
public:
|
||||
spell_hand_of_death() : SpellScriptLoader("spell_hand_of_death") { }
|
||||
|
||||
class spell_hand_of_death_SpellScript : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_hand_of_death_SpellScript);
|
||||
PrepareSpellScript(spell_hand_of_death);
|
||||
|
||||
void HandleHit(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
@@ -771,24 +543,18 @@ public:
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_hand_of_death_SpellScript::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
SpellScript* GetSpellScript() const override
|
||||
{
|
||||
return new spell_hand_of_death_SpellScript();
|
||||
OnEffectHitTarget += SpellEffectFn(spell_hand_of_death::HandleHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_archimonde()
|
||||
{
|
||||
new spell_red_sky_effect();
|
||||
new spell_hand_of_death();
|
||||
new spell_finger_of_death();
|
||||
new boss_archimonde();
|
||||
new npc_doomfire();
|
||||
new npc_doomfire_targetting();
|
||||
new npc_ancient_wisp();
|
||||
RegisterSpellScript(spell_red_sky_effect);
|
||||
RegisterSpellScript(spell_hand_of_death);
|
||||
RegisterSpellScript(spell_finger_of_death);
|
||||
RegisterHyjalAI(boss_archimonde);
|
||||
RegisterHyjalAI(npc_doomfire);
|
||||
RegisterHyjalAI(npc_doomfire_targetting);
|
||||
RegisterHyjalAI(npc_ancient_wisp);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,10 @@ public:
|
||||
Talk(SAY_ONDEATH);
|
||||
// If Archimonde has not yet been initialized, this won't trigger
|
||||
if (Creature* archi = instance->GetCreature(DATA_ARCHIMONDE))
|
||||
{
|
||||
archi->AI()->DoAction(ACTION_BECOME_ACTIVE_AND_CHANNEL);
|
||||
archi->AI()->Talk(SAY_ARCHIMONDE_INTRO, 25000ms);
|
||||
}
|
||||
BossAI::JustDied(killer);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,6 +178,11 @@ enum HyjalPaths
|
||||
HORDE_BOSS_PATH = 178527
|
||||
};
|
||||
|
||||
enum BossActions
|
||||
{
|
||||
ACTION_BECOME_ACTIVE_AND_CHANNEL = 0
|
||||
};
|
||||
|
||||
template <class AI, class T>
|
||||
inline AI* GetHyjalAI(T* obj)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user