Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
boss_felblood_kaelthas.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_Felblood_Kaelthas
8SD%Complete: 80
9SDComment: Normal and Heroic Support. Issues: Arcane Spheres do not initially follow targets.
10SDCategory: Magisters' Terrace
11EndScriptData */
12
13#include "ScriptMgr.h"
14#include "ScriptedCreature.h"
15#include "magisters_terrace.h"
16#include "WorldPacket.h"
17#include "Opcodes.h"
18
19enum Says
20{
21 SAY_AGGRO = 0, //This yell should be done when the room is cleared. For now, set it as a movelineofsight yell.
28};
29
30
32{
33 // Phase 1 spells
34 SPELL_FIREBALL_NORMAL = 44189, // Deals 2700-3300 damage at current target
35 SPELL_FIREBALL_HEROIC = 46164, // 4950-6050
36
37 SPELL_PHOENIX = 44194, // Summons a phoenix (Doesn't work?)
38 SPELL_PHOENIX_BURN = 44197, // A spell Phoenix uses to damage everything around
39 SPELL_REBIRTH_DMG = 44196, // DMG if a Phoenix rebirth happen
40
41 SPELL_FLAMESTRIKE1_NORMAL = 44190, // Damage part
42 SPELL_FLAMESTRIKE1_HEROIC = 46163, // Heroic damage part
43 SPELL_FLAMESTRIKE2 = 44191, // Flamestrike indicator before the damage
44 SPELL_FLAMESTRIKE3 = 44192, // Summons the trigger + animation (projectile)
45
46 SPELL_SHOCK_BARRIER = 46165, // Heroic only; 10k damage shield, followed by Pyroblast
47 SPELL_PYROBLAST = 36819, // Heroic only; 45-55k fire damage
48
49// Phase 2 spells
50 SPELL_GRAVITY_LAPSE_INITIAL = 44224, // Cast at the beginning of every Gravity Lapse
51 SPELL_GRAVITY_LAPSE_CHANNEL = 44251, // Channeled; blue beam animation to every enemy in range
52 SPELL_TELEPORT_CENTER = 44218, // Should teleport people to the center. Requires DB entry in spell_target_position.
53 SPELL_GRAVITY_LAPSE_FLY = 44227, // Hastens flyspeed and allows flying for 1 minute. For some reason removes 44226.
54 SPELL_GRAVITY_LAPSE_DOT = 44226, // Knocks up in the air and applies a 300 DPS DoT.
55 SPELL_ARCANE_SPHERE_PASSIVE = 44263, // Passive auras on Arcane Spheres
56 SPELL_POWER_FEEDBACK = 44233 // Stuns him, making him take 50% more damage for 10 seconds. Cast after Gravity Lapse
57};
58
59
60
67
68
70float KaelLocations[3][2]=
71{
72 {148.744659f, 181.377426f},
73 {140.823883f, 195.403046f},
74 {156.574188f, 195.650482f},
75};
76
77#define LOCATION_Z -16.727455f
78
80{
81public:
82 boss_felblood_kaelthas() : CreatureScript("boss_felblood_kaelthas") { }
83
85 {
86 return new boss_felblood_kaelthasAI(c);
87 }
88
90 {
92 {
93 instance = creature->GetInstanceScript();
94 }
95
97
102
103 //Heroic only
105
108 // 0 = No Gravity Lapse
109 // 1 = Casting Gravity Lapse visual
110 // 2 = Teleported people to self
111 // 3 = Knocked people up in the air
112 // 4 = Applied an aura that allows them to fly, channeling visual, relased Arcane Orbs.
113
116
118 // 0 = Not started
119 // 1 = Fireball; Summon Phoenix; Flamestrike
120 // 2 = Gravity Lapses
121
123 {
125 FireballTimer = 0;
126 PhoenixTimer = 10000;
127 FlameStrikeTimer = 25000;
129
130 PyroblastTimer = 60000;
131
134
135 FirstGravityLapse = true;
136 HasTaunted = false;
137
138 Phase = 0;
139
140 if (instance)
142 }
143
144 void JustDied(Unit* /*killer*/) OVERRIDE
145 {
147
148 if (!instance)
149 return;
150
152
153 // Enable the Translocation Orb Exit
155 escapeOrb->RemoveFlag(GAMEOBJECT_FIELD_FLAGS, GO_FLAG_NOT_SELECTABLE);
156 }
157
158 void DamageTaken(Unit* /*done_by*/, uint32 &damage) OVERRIDE
159 {
160 if (damage > me->GetHealth())
161 RemoveGravityLapse(); // Remove Gravity Lapse so that players fall to ground if they kill him when in air.
162 }
163
164 void EnterCombat(Unit* /*who*/) OVERRIDE
165 {
166 if (!instance)
167 return;
168
170 }
171
173
174 {
175 if (!HasTaunted && me->IsWithinDistInMap(who, 40.0f))
176 {
178 HasTaunted = true;
179 }
180
182 }
183
184 void SetThreatList(Creature* summonedUnit)
185 {
186 if (!summonedUnit)
187 return;
188
189 ThreatContainer::StorageType const &threatlist = me->getThreatManager().getThreatList();
190 ThreatContainer::StorageType::const_iterator i = threatlist.begin();
191 for (i = threatlist.begin(); i != threatlist.end(); ++i)
192 {
193 Unit* unit = Unit::GetUnit(*me, (*i)->getUnitGuid());
194 if (unit && unit->IsAlive())
195 {
196 float threat = me->getThreatManager().getThreat(unit);
197 summonedUnit->AddThreat(unit, threat);
198 }
199 }
200 }
201
203 {
204 float x = KaelLocations[0][0];
205 float y = KaelLocations[0][1];
206 me->SetPosition(x, y, LOCATION_Z, 0.0f);
207 ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
208 ThreatContainer::StorageType::const_iterator i = threatlist.begin();
209 for (i = threatlist.begin(); i != threatlist.end(); ++i)
210 {
211 Unit* unit = Unit::GetUnit(*me, (*i)->getUnitGuid());
212 if (unit && (unit->GetTypeId() == TypeID::TYPEID_PLAYER))
213 unit->CastSpell(unit, SPELL_TELEPORT_CENTER, true);
214 }
216 }
217
219 {
220 ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
221 ThreatContainer::StorageType::const_iterator i = threatlist.begin();
222 for (i = threatlist.begin(); i != threatlist.end(); ++i)
223 {
224 Unit* unit = Unit::GetUnit(*me, (*i)->getUnitGuid());
225 if (unit && (unit->GetTypeId() == TypeID::TYPEID_PLAYER))
226 // Knockback into the air
227 unit->CastSpell(unit, SPELL_GRAVITY_LAPSE_DOT, true, 0, 0, me->GetGUID());
228 }
229 }
230
231 void CastGravityLapseFly() // Use Fly Packet hack for now as players can't cast "fly" spells unless in map 530. Has to be done a while after they get knocked into the air...
232 {
233 ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
234 ThreatContainer::StorageType::const_iterator i = threatlist.begin();
235 for (i = threatlist.begin(); i != threatlist.end(); ++i)
236 {
237 Unit* unit = Unit::GetUnit(*me, (*i)->getUnitGuid());
238 if (unit && (unit->GetTypeId() == TypeID::TYPEID_PLAYER))
239 {
240 // Also needs an exception in spell system.
241 unit->CastSpell(unit, SPELL_GRAVITY_LAPSE_FLY, true, 0, 0, me->GetGUID());
242 // Use packet hack
244 data.append(unit->GetPackGUID());
245 data << uint32(0);
246 unit->SendMessageToSet(&data, true);
247 }
248 }
249 }
250
252 {
253 ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
254 ThreatContainer::StorageType::const_iterator i = threatlist.begin();
255 for (i = threatlist.begin(); i != threatlist.end(); ++i)
256 {
257 Unit* unit = Unit::GetUnit(*me, (*i)->getUnitGuid());
258 if (unit && (unit->GetTypeId() == TypeID::TYPEID_PLAYER))
259 {
262
264 data.append(unit->GetPackGUID());
265 data << uint32(0);
266 unit->SendMessageToSet(&data, true);
267 }
268 }
269 }
270
272 {
273 //Return since we have no target
274 if (!UpdateVictim())
275 return;
276
277 switch (Phase)
278 {
279 case 0:
280 {
281 // *Heroic mode only:
282 if (IsHeroic())
283 {
284 if (PyroblastTimer <= diff)
285 {
286 me->InterruptSpell(CURRENT_CHANNELED_SPELL);
287 me->InterruptSpell(CURRENT_GENERIC_SPELL);
290 PyroblastTimer = 60000;
291 } else PyroblastTimer -= diff;
292 }
293
294 if (FireballTimer <= diff)
295 {
297 FireballTimer = std::rand() % 6000 + 2000;
298 } else FireballTimer -= diff;
299
300 if (PhoenixTimer <= diff)
301 {
303
304 uint8 random = std::rand() % 2 + 1;
305 float x = KaelLocations[random][0];
306 float y = KaelLocations[random][1];
307
309 if (Phoenix)
310 {
312 SetThreatList(Phoenix);
313 Phoenix->AI()->AttackStart(target);
314 }
315
317
318 PhoenixTimer = 60000;
319 } else PhoenixTimer -= diff;
320
321 if (FlameStrikeTimer <= diff)
322 {
323 if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
324 {
325 me->InterruptSpell(CURRENT_CHANNELED_SPELL);
326 me->InterruptSpell(CURRENT_GENERIC_SPELL);
327 DoCast(target, SPELL_FLAMESTRIKE3, true);
329 }
330 FlameStrikeTimer = std::rand() % 25000 + 15000;
331 } else FlameStrikeTimer -= diff;
332
333 // Below 50%
334 if (HealthBelowPct(50))
335 {
336 me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, true);
337 me->StopMoving();
338 me->GetMotionMaster()->Clear();
339 me->GetMotionMaster()->MoveIdle();
342 Phase = 1;
343 }
344
346 }
347 break;
348
349 case 1:
350 {
351 if (GravityLapseTimer <= diff)
352 {
353 switch (GravityLapsePhase)
354 {
355 case 0:
356 if (FirstGravityLapse) // Different yells at 50%, and at every following Gravity Lapse
357 {
359 FirstGravityLapse = false;
360
361 if (instance)
362 instance->SetData(DATA_KAELTHAS_STATUES, 1);
363 }
364 else
366
368 GravityLapseTimer = 2000 + diff;// Don't interrupt the visual spell
370 break;
371
372 case 1:
374 GravityLapseTimer = 1000;
376 break;
377
378 case 2:
380 GravityLapseTimer = 1000;
382 break;
383
384 case 3:
386 GravityLapseTimer = 30000;
388
389 for (uint8 i = 0; i < 3; ++i)
390 {
391 Unit* target = NULL;
393
395 if (Orb && target)
396 {
397 Orb->SetSpeed(MOVE_RUN, 0.5f);
398 Orb->AddThreat(target, 1000000.0f);
399 Orb->AI()->AttackStart(target);
400 }
401 }
402
404 break;
405
406 case 4:
407 me->InterruptNonMeleeSpells(false);
411 GravityLapseTimer = 10000;
413 break;
414 }
415 } else GravityLapseTimer -= diff;
416 }
417 break;
418 }
419 }
420 };
421};
422
424{
425public:
426 npc_felkael_flamestrike() : CreatureScript("npc_felkael_flamestrike") { }
427
429 {
430 return new npc_felkael_flamestrikeAI(c);
431 }
432
434 {
436 {
437 }
438
440
442 {
443 FlameStrikeTimer = 5000;
444
446 me->setFaction(14);
447
449 }
450
451 void EnterCombat(Unit* /*who*/) OVERRIDE { }
452 void MoveInLineOfSight(Unit* /*who*/) OVERRIDE { }
453
455 {
456 if (FlameStrikeTimer <= diff)
457 {
459 me->Kill(me);
460 } else FlameStrikeTimer -= diff;
461 }
462 };
463};
464
466{
467public:
468 npc_felkael_phoenix() : CreatureScript("npc_felkael_phoenix") { }
469
471 {
472 return new npc_felkael_phoenixAI(c);
473 }
474
476 {
478 {
479 instance = creature->GetInstanceScript();
480 }
481
487
489 {
491 me->SetDisableGravity(true);
493 BurnTimer = 2000;
494 Death_Timer = 3000;
495 Rebirth = false;
496 FakeDeath = false;
497 }
498
499 void EnterCombat(Unit* /*who*/) OVERRIDE { }
500
501 void DamageTaken(Unit* /*killer*/, uint32 &damage) OVERRIDE
502 {
503 if (damage < me->GetHealth())
504 return;
505
506 //Prevent glitch if in fake death
507 if (FakeDeath)
508 {
509 damage = 0;
510 return;
511 }
512 //Don't really die in all phases of Kael'Thas
513 if (instance && instance->GetData(DATA_KAELTHAS_EVENT) == 0)
514 {
515 //prevent death
516 damage = 0;
517 FakeDeath = true;
518
519 me->InterruptNonMeleeSpells(false);
520 me->SetHealth(0);
521 me->StopMoving();
522 me->ClearComboPointHolders();
523 me->RemoveAllAurasOnDeath();
524 me->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
525 me->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false);
527 me->ClearAllReactives();
528 me->SetTarget(0);
529 me->GetMotionMaster()->Clear();
530 me->GetMotionMaster()->MoveIdle();
531 me->SetStandState(UNIT_STAND_STATE_DEAD);
532 }
533 }
534
535 void JustDied(Unit* /*killer*/) OVERRIDE
536 {
537 me->SummonCreature(CREATURE_PHOENIX_EGG, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType::TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000);
538 }
539
541 {
542 //If we are fake death, we cast revbirth and after that we kill the phoenix to spawn the egg.
543 if (FakeDeath)
544 {
545 if (!Rebirth)
546 {
548 Rebirth = true;
549 }
550
551 if (Rebirth)
552 {
553 if (Death_Timer <= diff)
554 {
555 me->SummonCreature(CREATURE_PHOENIX_EGG, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType::TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000);
556 me->DisappearAndDie();
557 Rebirth = false;
558 } else Death_Timer -= diff;
559 }
560 }
561
562 if (!UpdateVictim())
563 return;
564
565 if (BurnTimer <= diff)
566 {
567 //spell Burn should possible do this, but it doesn't, so do this for now.
568 uint16 dmg = std::rand() % 2050 + 1650;
569 me->DealDamage(me, dmg, 0, DOT, SPELL_SCHOOL_MASK_FIRE, NULL, false);
570 BurnTimer += 2000;
571 } BurnTimer -= diff;
572
574 }
575 };
576};
577
579{
580public:
581 npc_felkael_phoenix_egg() : CreatureScript("npc_felkael_phoenix_egg") { }
582
584 {
585 return new npc_felkael_phoenix_eggAI(c);
586 }
587
589 {
591
593
595 {
596 HatchTimer = 10000;
597 }
598
599 void EnterCombat(Unit* /*who*/) OVERRIDE { }
600 void MoveInLineOfSight(Unit* /*who*/) OVERRIDE { }
601
602
604 {
605 if (HatchTimer <= diff)
606 {
607 me->SummonCreature(CREATURE_PHOENIX, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType::TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000);
608 me->Kill(me);
609 } else HatchTimer -= diff;
610 }
611 };
612};
613
615{
616public:
617 npc_arcane_sphere() : CreatureScript("npc_arcane_sphere") { }
618
620 {
621 return new npc_arcane_sphereAI(c);
622 }
623
625 {
626 npc_arcane_sphereAI(Creature* creature) : ScriptedAI(creature) { Reset(); }
627
630
632 {
633 DespawnTimer = 30000;
634 ChangeTargetTimer = std::rand() % 12000 + 6000;
635
637 me->SetDisableGravity(true);
638 me->setFaction(14);
640 }
641
642 void EnterCombat(Unit* /*who*/) OVERRIDE { }
643
645 {
646 if (DespawnTimer <= diff)
647 me->Kill(me);
648 else
649 DespawnTimer -= diff;
650
651 //Return since we have no target
652 if (!UpdateVictim())
653 return;
654
655 if (ChangeTargetTimer <= diff)
656 {
657 if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
658 {
659 me->AddThreat(target, 1.0f);
660 me->TauntApply(target);
661 AttackStart(target);
662 }
663
664 ChangeTargetTimer = std::rand() % 15000 + 5000;
665 } else ChangeTargetTimer -= diff;
666 }
667 };
668};
669
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
#define OVERRIDE
Definition Define.h:60
std::uint16_t uint16
Definition Define.h:78
@ IN_PROGRESS
@ DONE
@ NOT_STARTED
@ TYPEID_PLAYER
Definition Object.h:55
@ TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN
Definition Object.h:69
@ SPELL_EFFECT_INTERRUPT_CAST
@ IMMUNITY_EFFECT
@ GO_FLAG_NOT_SELECTABLE
@ AURA_STATE_HEALTHLESS_35_PERCENT
@ AURA_STATE_HEALTHLESS_20_PERCENT
@ SPELL_SCHOOL_MASK_FIRE
@ UNIT_STAND_STATE_DEAD
Definition Unit.h:169
@ CURRENT_CHANNELED_SPELL
Definition Unit.h:1110
@ CURRENT_GENERIC_SPELL
Definition Unit.h:1109
@ MOVE_RUN
Definition Unit.h:556
@ UNIT_FLAG_NON_ATTACKABLE
Definition Unit.h:626
@ UNIT_FLAG_NOT_SELECTABLE
Definition Unit.h:650
@ DOT
Definition Unit.h:616
@ SELECT_TARGET_RANDOM
Definition UnitAI.h:23
@ UNIT_FIELD_FLAGS
@ GAMEOBJECT_FIELD_FLAGS
Creatures
#define SAY_DEATH
#define SAY_AGGRO
@ CREATURE_PHOENIX_EGG
@ CREATURE_ARCANE_SPHERE
@ SPELL_POWER_FEEDBACK
@ SPELL_GRAVITY_LAPSE_DOT
@ SPELL_SHOCK_BARRIER
@ SPELL_GRAVITY_LAPSE_CHANNEL
@ SPELL_GRAVITY_LAPSE_FLY
@ SPELL_ARCANE_SPHERE_PASSIVE
@ SPELL_FLAMESTRIKE1_HEROIC
@ SPELL_FIREBALL_HEROIC
@ SPELL_FLAMESTRIKE1_NORMAL
@ SPELL_TELEPORT_CENTER
@ SPELL_GRAVITY_LAPSE_INITIAL
@ SPELL_FIREBALL_NORMAL
float KaelLocations[3][2]
void AddSC_boss_felblood_kaelthas()
#define LOCATION_Z
void append(T value)
Definition ByteBuffer.h:147
virtual void MoveInLineOfSight(Unit *)
void Talk(uint8 id, WorldObject const *whisperTarget=NULL)
bool UpdateVictim()
CreatureAI * AI() const
Definition Creature.h:483
CreatureScript(const char *name)
static GameObject * GetGameObject(WorldObject const &u, uint64 guid)
TypeID GetTypeId() const
Definition Object.h:131
void RemoveFlag(uint16 index, uint32 oldFlag)
Definition Object.cpp:1280
const ByteBuffer & GetPackGUID() const
Definition Object.h:124
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
virtual void AttackStart(Unit *)
Definition UnitAI.cpp:16
Definition Unit.h:1367
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)
void RemoveAurasDueToSpell(uint32 spellId, uint64 casterGUID=0, uint32 reqEffMask=0, AuraRemoveMode removeMode=AURA_REMOVE_BY_DEFAULT)
void AddThreat(Unit *victim, float fThreat, SpellSchoolMask schoolMask=SPELL_SCHOOL_MASK_NORMAL, SpellInfo const *threatSpell=NULL)
Definition Unit.cpp:4463
bool IsAlive() const
Definition Unit.h:2032
static Unit * GetUnit(WorldObject &object, uint64 guid)
Definition Unit.cpp:4895
void SetSpeed(UnitMoveType mtype, float rate, bool forced=false)
Definition Unit.cpp:4318
InstanceScript * GetInstanceScript()
Definition Object.cpp:1612
virtual void SendMessageToSet(WorldPacket *data, bool self)
Definition Object.cpp:2485
CreatureAI * GetAI(Creature *c) const OVERRIDE
CreatureAI * GetAI(Creature *c) const OVERRIDE
CreatureAI * GetAI(Creature *c) const OVERRIDE
CreatureAI * GetAI(Creature *c) const OVERRIDE
CreatureAI * GetAI(Creature *c) const OVERRIDE
@ SMSG_MOVE_SET_CAN_FLY
Definition Opcodes.h:797
@ SMSG_MOVE_UNSET_CAN_FLY
Definition Opcodes.h:814
@ DATA_ESCAPE_ORB
@ DATA_KAELTHAS_EVENT
@ DATA_KAELTHAS_STATUES
bool IsHeroic() const
Creature * me
bool HealthBelowPct(uint32 pct) const
Creature * DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, TempSummonType type, uint32 despawntime)
ScriptedAI(Creature *creature)
void AttackStart(Unit *) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void DamageTaken(Unit *, uint32 &damage) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================