Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
boss_archimonde.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6/* ScriptData
7SDName: Boss_Archimonde
8SD%Complete: 85
9SDComment: Doomfires not completely offlike due to core limitations for random moving. Tyrande and second phase not fully implemented.
10SDCategory: Caverns of Time, Mount Hyjal
11EndScriptData */
12
13#include "ScriptMgr.h"
14#include "ScriptedCreature.h"
15#include "hyjal.h"
16#include "SpellAuras.h"
17#include "hyjal_trash.h"
18#include "Player.h"
19
30
55
63
64Position const NordrassilLoc = {5503.713f, -3523.436f, 1608.781f, 0.0f};
65
67{
68public:
69 npc_ancient_wisp() : CreatureScript("npc_ancient_wisp") { }
70
72 {
73 return new npc_ancient_wispAI(creature);
74 }
75
77 {
78 npc_ancient_wispAI(Creature* creature) : ScriptedAI(creature)
79 {
80 instance = creature->GetInstanceScript();
82 }
83
87
89 {
90 CheckTimer = 1000;
91
92 if (instance)
94
96 }
97
98 void EnterCombat(Unit* /*who*/) OVERRIDE { }
99
100 void DamageTaken(Unit* /*done_by*/, uint32 &damage) OVERRIDE
101 {
102 damage = 0;
103 }
104
106 {
107 if (CheckTimer <= diff)
108 {
109 if (Unit* Archimonde = Unit::GetUnit(*me, ArchimondeGUID))
110 {
111 if (Archimonde->HealthBelowPct(2) || !Archimonde->IsAlive())
113 else
114 DoCast(Archimonde, SPELL_ANCIENT_SPARK);
115 }
116 CheckTimer = 1000;
117 } else CheckTimer -= diff;
118 }
119 };
120};
121
122/* This script is merely a placeholder for the Doomfire that triggers Doomfire spell. It will
123 MoveChase the Doomfire Spirit always, until despawn (AttackStart is called upon it's spawn) */
125{
126public:
127 npc_doomfire() : CreatureScript("npc_doomfire") { }
128
130 {
131 return new npc_doomfireAI(creature);
132 }
133
135 {
136 npc_doomfireAI(Creature* creature) : ScriptedAI(creature) { }
137
138 void Reset() OVERRIDE { }
139
140 void MoveInLineOfSight(Unit* /*who*/) OVERRIDE { }
141
142 void EnterCombat(Unit* /*who*/) OVERRIDE { }
143
144 void DamageTaken(Unit* /*done_by*/, uint32 &damage) OVERRIDE
145 {
146 damage = 0;
147 }
148 };
149};
150
151/* This is the script for the Doomfire Spirit Mob. This mob simply follow players or
152 travels in random directions if target cannot be found. */
154{
155public:
156 npc_doomfire_targetting() : CreatureScript("npc_doomfire_targetting") { }
157
159 {
160 return new npc_doomfire_targettingAI(creature);
161 }
162
164 {
166
169
171 {
172 TargetGUID = 0;
173 ChangeTargetTimer = 5000;
174 }
175
177
178 {
179 //will update once TargetGUID is 0. In case noone actually moves(not likely) and this is 0
180 //when UpdateAI needs it, it will be forced to select randomPoint
181 if (!TargetGUID && who->GetTypeId() == TypeID::TYPEID_PLAYER)
182 TargetGUID = who->GetGUID();
183 }
184
185 void EnterCombat(Unit* /*who*/) OVERRIDE { }
186
187 void DamageTaken(Unit* /*done_by*/, uint32 &damage) OVERRIDE
188 {
189 damage = 0;
190 }
191
193 {
194 if (ChangeTargetTimer <= diff)
195 {
196 if (Unit* temp = Unit::GetUnit(*me, TargetGUID))
197 {
198 me->GetMotionMaster()->MoveFollow(temp, 0.0f, 0.0f);
199 TargetGUID = 0;
200 }
201 else
202 {
203 Position pos;
204 me->GetRandomNearPosition(pos, 40);
205 me->GetMotionMaster()->MovePoint(0, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
206 }
207
208 ChangeTargetTimer = 5000;
209 } else ChangeTargetTimer -= diff;
210 }
211 };
212};
213
214/* Finally, Archimonde's script. His script isn't extremely complex, most are simply spells on timers.
215 The only complicated aspect of the battle is Finger of Death and Doomfire, with Doomfire being the
216 hardest bit to code. Finger of Death is simply a distance check - if no one is in melee range, then
217 select a random target and cast the spell on them. However, if someone IS in melee range, and this
218 is NOT the main tank (creature's victim), then we aggro that player and they become the new victim.
219 For Doomfire, we summon a mob (Doomfire Spirit) for the Doomfire mob to follow. It's spirit will
220 randomly select it's target to follow and then we create the random movement making it unpredictable. */
221
223{
224public:
225 boss_archimonde() : CreatureScript("boss_archimonde") { }
226
228 {
229 return new boss_archimondeAI(creature);
230 }
231
233 {
235 {
236 instance = creature->GetInstanceScript();
237 }
238
240
243
257
262
264 {
265 if (instance)
267
269 damageTaken = 0;
270 WorldTreeGUID = 0;
271
273 FearTimer = 42000;
274 AirBurstTimer = 30000;
275 GripOfTheLegionTimer = std::rand() % 25000 + 5000;
276 DoomfireTimer = 20000;
277 SoulChargeTimer = std::rand() % 30000 + 2000;
278 SoulChargeCount = 0;
279 MeleeRangeCheckTimer = 15000;
280 HandOfDeathTimer = 2000;
281 WispCount = 0; // When ~30 wisps are summoned, Archimonde dies
282 EnrageTimer = 600000; // 10 minutes
283 CheckDistanceTimer = 30000; // This checks if he's too close to the World Tree (75 yards from a point on the tree), if true then he will enrage
284 SummonWispTimer = 0;
285
286 Enraged = false;
287 BelowTenPercent = false;
288 HasProtected = false;
289 IsChanneling = false;
290 }
291
292 void EnterCombat(Unit* /*who*/) OVERRIDE
293 {
294 me->InterruptSpell(CURRENT_CHANNELED_SPELL);
297
298 if (instance)
300 }
301
303 {
304 Talk(SAY_SLAY);
305
306 if (victim && victim->GetTypeId() == TypeID::TYPEID_PLAYER)
307 GainSoulCharge(victim->ToPlayer());
308 }
309
310 void GainSoulCharge(Player* victim)
311 {
312 switch (victim->getClass())
313 {
314 case CLASS_PRIEST:
315 case CLASS_PALADIN:
316 case CLASS_WARLOCK:
317 victim->CastSpell(me, SPELL_SOUL_CHARGE_RED, true);
318 break;
319 case CLASS_MAGE:
320 case CLASS_ROGUE:
321 case CLASS_WARRIOR:
322 victim->CastSpell(me, SPELL_SOUL_CHARGE_YELLOW, true);
323 break;
324 case CLASS_DRUID:
325 case CLASS_SHAMAN:
326 case CLASS_HUNTER:
327 victim->CastSpell(me, SPELL_SOUL_CHARGE_GREEN, true);
328 break;
329 }
330
331 SoulChargeTimer = std::rand() % 30000 + 2000;
333 }
334
335 void JustDied(Unit* killer) OVERRIDE
336 {
339
340 if (instance)
342 }
343
345 {
346 // First we check if our current victim is in melee range or not.
347 Unit* victim = me->GetVictim();
348 if (victim && me->IsWithinDistInMap(victim, me->GetAttackDistance(victim)))
349 return false;
350
351 ThreatContainer::StorageType const &threatlist = me->getThreatManager().getThreatList();
352 if (threatlist.empty())
353 return false;
354
355 std::list<Unit*> targets;
356 ThreatContainer::StorageType::const_iterator itr = threatlist.begin();
357 for (; itr != threatlist.end(); ++itr)
358 {
359 Unit* unit = Unit::GetUnit(*me, (*itr)->getUnitGuid());
360 if (unit && unit->IsAlive())
361 targets.push_back(unit);
362 }
363
364 if (targets.empty())
365 return false;
366
368 Unit* target = targets.front();
369 if (target)
370 {
371 if (!me->IsWithinDistInMap(target, me->GetAttackDistance(target)))
372 return true; // Cast Finger of Death
373 else // This target is closest, he is our new tank
374 me->AddThreat(target, me->getThreatManager().getThreat(me->GetVictim()));
375 }
376
377 return false;
378 }
379
381 {
382 if (summoned->GetEntry() == CREATURE_ANCIENT_WISP)
383 summoned->AI()->AttackStart(me);
384 else
385 {
386 summoned->setFaction(me->getFaction());
387 summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
388 summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
389 }
390
391 if (summoned->GetEntry() == CREATURE_DOOMFIRE_SPIRIT)
392 {
393 DoomfireSpiritGUID = summoned->GetGUID();
394 }
395
396 if (summoned->GetEntry() == CREATURE_DOOMFIRE)
397 {
398 summoned->CastSpell(summoned, SPELL_DOOMFIRE_SPAWN, false);
399 summoned->CastSpell(summoned, SPELL_DOOMFIRE, true, 0, 0, me->GetGUID());
400
401 if (Unit* DoomfireSpirit = Unit::GetUnit(*me, DoomfireSpiritGUID))
402 {
403 summoned->GetMotionMaster()->MoveFollow(DoomfireSpirit, 0.0f, 0.0f);
405 }
406 }
407 }
408
409 //this is code doing close to what the summoning spell would do (spell 31903)
410 void SummonDoomfire(Unit* target)
411 {
412 me->SummonCreature(CREATURE_DOOMFIRE_SPIRIT,
413 target->GetPositionX()+15.0f, target->GetPositionY()+15.0f, target->GetPositionZ(), 0,
415
416 me->SummonCreature(CREATURE_DOOMFIRE,
417 target->GetPositionX()-15.0f, target->GetPositionY()-15.0f, target->GetPositionZ(), 0,
419 }
420
422 {
423 me->InterruptNonMeleeSpells(false);
424
425 bool HasCast = false;
426 uint32 chargeSpell = 0;
427 uint32 unleashSpell = 0;
428
429 switch (std::rand() % 2)
430 {
431 case 0:
432 chargeSpell = SPELL_SOUL_CHARGE_RED;
433 unleashSpell = SPELL_UNLEASH_SOUL_RED;
434 break;
435 case 1:
436 chargeSpell = SPELL_SOUL_CHARGE_YELLOW;
437 unleashSpell = SPELL_UNLEASH_SOUL_YELLOW;
438 break;
439 case 2:
440 chargeSpell = SPELL_SOUL_CHARGE_GREEN;
441 unleashSpell = SPELL_UNLEASH_SOUL_GREEN;
442 break;
443 }
444
445 if (me->HasAura(chargeSpell))
446 {
447 me->RemoveAuraFromStack(chargeSpell);
448 DoCastVictim(unleashSpell);
449 HasCast = true;
451 }
452
453 if (HasCast)
454 SoulChargeTimer = std::rand() % 30000 + 2000;
455 }
456
458 {
459 if (!me->IsInCombat())
460 {
461 if (instance)
462 {
463 // Do not let the raid skip straight to Archimonde. Visible and hostile ONLY if Azagalor is finished.
464 if ((instance->GetData(DATA_AZGALOREVENT) < DONE) && (me->IsVisible() || (me->getFaction() != 35)))
465 {
466 me->SetVisible(false);
467 me->setFaction(35);
468 }
469 else if ((instance->GetData(DATA_AZGALOREVENT) >= DONE) && (!me->IsVisible() || (me->getFaction() == 35)))
470 {
471 me->setFaction(1720);
472 me->SetVisible(true);
473 }
474 }
475
476 if (DrainNordrassilTimer <= diff)
477 {
478 if (!IsChanneling)
479 {
481
482 if (temp)
483 WorldTreeGUID = temp->GetGUID();
484
485 if (Unit* Nordrassil = Unit::GetUnit(*me, WorldTreeGUID))
486 {
487 Nordrassil->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
488 Nordrassil->SetDisplayId(11686);
489 DoCast(Nordrassil, SPELL_DRAIN_WORLD_TREE);
490 IsChanneling = true;
491 }
492 }
493
494 if (Unit* Nordrassil = Unit::GetUnit(*me, WorldTreeGUID))
495 {
496 Nordrassil->CastSpell(me, SPELL_DRAIN_WORLD_TREE_2, true);
498 }
499 } else DrainNordrassilTimer -= diff;
500 }
501
502 if (!UpdateVictim())
503 return;
504
505 if (me->HealthBelowPct(10) && !BelowTenPercent && !Enraged)
506 BelowTenPercent = true;
507
508 if (!Enraged)
509 {
510 if (EnrageTimer <= diff)
511 {
512 if (HealthAbovePct(10))
513 {
514 me->GetMotionMaster()->Clear(false);
515 me->GetMotionMaster()->MoveIdle();
516 Enraged = true;
518 }
519 } else EnrageTimer -= diff;
520
521 if (CheckDistanceTimer <= diff)
522 {
523 // To simplify the check, we simply summon a Creature in the location and then check how far we are from the creature
525 if (Check)
526 {
527 Check->SetVisible(false);
528
529 if (me->IsWithinDistInMap(Check, 75))
530 {
531 me->GetMotionMaster()->Clear(false);
532 me->GetMotionMaster()->MoveIdle();
533 Enraged = true;
535 }
536 }
537 CheckDistanceTimer = 5000;
538 } else CheckDistanceTimer -= diff;
539 }
540
541 if (BelowTenPercent)
542 {
543 if (!HasProtected)
544 {
545 me->GetMotionMaster()->Clear(false);
546 me->GetMotionMaster()->MoveIdle();
547
548 //all members of raid must get this buff
550 HasProtected = true;
551 Enraged = true;
552 }
553
554 if (SummonWispTimer <= diff)
555 {
557 SummonWispTimer = 1500;
558 ++WispCount;
559 } else SummonWispTimer -= diff;
560
561 if (WispCount >= 30)
562 me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
563 }
564
565 if (Enraged)
566 {
567 if (HandOfDeathTimer <= diff)
568 {
570 HandOfDeathTimer = 2000;
571 } else HandOfDeathTimer -= diff;
572 return; // Don't do anything after this point.
573 }
574
575 if (SoulChargeCount)
576 {
577 if (SoulChargeTimer <= diff)
579 else SoulChargeTimer -= diff;
580 }
581
582 if (GripOfTheLegionTimer <= diff)
583 {
585 GripOfTheLegionTimer = std::rand() % 25000 + 5000;
586 } else GripOfTheLegionTimer -= diff;
587
588 if (AirBurstTimer <= diff)
589 {
592 AirBurstTimer = std::rand() % 40000 + 25000;
593 } else AirBurstTimer -= diff;
594
595 if (FearTimer <= diff)
596 {
598 FearTimer = 42000;
599 } else FearTimer -= diff;
600
601 if (DoomfireTimer <= diff)
602 {
605 if (!temp)
606 temp = me->GetVictim();
607
608 //replace with spell cast 31903 once implicitTarget 73 implemented
609 SummonDoomfire(temp);
610
611 //supposedly three doomfire can be up at the same time
612 DoomfireTimer = 20000;
613 } else DoomfireTimer -= diff;
614
615 if (MeleeRangeCheckTimer <= diff)
616 {
618 {
621 }
622
624 } else MeleeRangeCheckTimer -= diff;
625
627 }
628 void WaypointReached(uint32 /*waypointId*/) OVERRIDE { }
629 };
630};
631
633{
634 new boss_archimonde();
635 new npc_doomfire();
637 new npc_ancient_wisp();
638}
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
#define OVERRIDE
Definition Define.h:60
std::uint64_t uint64
Definition Define.h:76
@ IN_PROGRESS
@ DONE
@ NOT_STARTED
@ TYPEID_PLAYER
Definition Object.h:55
@ TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT
Definition Object.h:71
@ TEMPSUMMON_TIMED_DESPAWN
Definition Object.h:70
@ CLASS_HUNTER
@ CLASS_DRUID
@ CLASS_SHAMAN
@ CLASS_PRIEST
@ CLASS_WARRIOR
@ CLASS_WARLOCK
@ CLASS_MAGE
@ CLASS_PALADIN
@ CLASS_ROGUE
@ SPELL_SCHOOL_MASK_NORMAL
@ CURRENT_CHANNELED_SPELL
Definition Unit.h:1110
@ UNIT_FLAG_NON_ATTACKABLE
Definition Unit.h:626
@ UNIT_FLAG_NOT_SELECTABLE
Definition Unit.h:650
@ DIRECT_DAMAGE
Definition Unit.h:614
@ SELECT_TARGET_RANDOM
Definition UnitAI.h:23
@ UNIT_FIELD_FLAGS
@ SAY_SLAY
void AddSC_boss_archimonde()
@ SPELL_DRAIN_WORLD_TREE
@ SPELL_SOUL_CHARGE_RED
@ SPELL_DOOMFIRE
@ SPELL_PROTECTION_OF_ELUNE
@ SPELL_FINGER_OF_DEATH
@ SPELL_UNLEASH_SOUL_GREEN
@ SPELL_DOOMFIRE_SPAWN
@ SPELL_SOUL_CHARGE_GREEN
@ SPELL_FEAR
@ SPELL_UNLEASH_SOUL_RED
@ SPELL_ANCIENT_SPARK
@ SPELL_DENOUEMENT_WISP
@ SPELL_DOOMFIRE_STRIKE
@ SPELL_HAND_OF_DEATH
@ SPELL_GRIP_OF_THE_LEGION
@ SPELL_UNLEASH_SOUL_YELLOW
@ SPELL_DRAIN_WORLD_TREE_2
@ SPELL_AIR_BURST
@ SPELL_SOUL_CHARGE_YELLOW
@ SAY_DEATH
@ SAY_SOUL_CHARGE
@ SAY_AGGRO
@ SAY_AIR_BURST
@ SAY_SLAY
@ SAY_DOOMFIRE
@ SAY_ENRAGE
@ CREATURE_DOOMFIRE_SPIRIT
@ CREATURE_CHANNEL_TARGET
@ CREATURE_ANCIENT_WISP
@ CREATURE_DOOMFIRE
Position const NordrassilLoc
#define SAY_DEATH
#define SAY_AGGRO
@ SAY_ENRAGE
@ SPELL_FEAR
void DoZoneInCombat(Creature *creature=NULL, float maxRangeToNearestTarget=50.0f)
void Talk(uint8 id, WorldObject const *whisperTarget=NULL)
bool UpdateVictim()
CreatureScript(const char *name)
uint64 GetGUID() const
Definition Object.h:119
std::list< HostileReference * > StorageType
void DoMeleeAttackIfReady()
Definition UnitAI.cpp:28
void DoCast(uint32 spellId)
Definition UnitAI.cpp:110
Unit * SelectTarget(SelectAggroTarget targetType, uint32 position=0, float dist=0.0f, bool playerOnly=false, int32 aura=0)
Definition UnitAI.cpp:66
void DoCastVictim(uint32 spellId, bool triggered=false)
Definition UnitAI.cpp:168
Definition Unit.h:1367
void SetVisible(bool x)
Definition Unit.cpp:4163
void CastSpell(SpellCastTargets const &targets, SpellInfo const *spellInfo, CustomSpellValues const *value, TriggerCastFlags triggerFlags=TRIGGERED_NONE, Item *castItem=NULL, AuraEffect const *triggeredByAura=NULL, uint64 originalCaster=0)
uint8 getClass() const
Definition Unit.h:1543
bool IsAlive() const
Definition Unit.h:2032
static Unit * GetUnit(WorldObject &object, uint64 guid)
Definition Unit.cpp:4895
InstanceScript * GetInstanceScript()
Definition Object.cpp:1612
CreatureAI * GetAI(Creature *creature) const OVERRIDE
CreatureAI * GetAI(Creature *creature) const OVERRIDE
CreatureAI * GetAI(Creature *creature) const OVERRIDE
CreatureAI * GetAI(Creature *creature) const OVERRIDE
@ DATA_ARCHIMONDEEVENT
Definition hyjal.h:18
@ DATA_AZGALOREVENT
Definition hyjal.h:20
@ DATA_ARCHIMONDE
Definition hyjal.h:17
float m_positionZ
Definition Object.h:289
float m_positionX
Definition Object.h:287
float GetPositionZ() const
Definition Object.h:330
float m_positionY
Definition Object.h:288
float GetPositionX() const
Definition Object.h:328
float GetPositionY() const
Definition Object.h:329
Creature * me
bool HealthAbovePct(uint32 pct) const
Creature * DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, TempSummonType type, uint32 despawntime)
ScriptedAI(Creature *creature)
void JustDied(Unit *killer) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void JustSummoned(Creature *summoned) OVERRIDE
void KilledUnit(Unit *victim) OVERRIDE
void JustDied(Unit *)
uint32 damageTaken
Definition hyjal_trash.h:34
hyjal_trashAI(Creature *creature)
void DamageTaken(Unit *, uint32 &damage) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
npc_doomfireAI(Creature *creature)
void EnterCombat(Unit *) OVERRIDE
void MoveInLineOfSight(Unit *) OVERRIDE
void DamageTaken(Unit *, uint32 &damage) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void DamageTaken(Unit *, uint32 &damage) OVERRIDE