Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
boss_prince_malchezaar.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_Prince_Malchezzar
8SD%Complete: 100
9SDComment:
10SDCategory: Karazhan
11EndScriptData */
12
13#include "ScriptMgr.h"
14#include "ScriptedCreature.h"
15#include "karazhan.h"
16#include "SpellInfo.h"
17
18// 18 Coordinates for Infernal spawns
20{
21 float x, y;
22};
23
24#define INFERNAL_Z 275.5f
25
27{
28 {-10922.8f, -1985.2f},
29 {-10916.2f, -1996.2f},
30 {-10932.2f, -2008.1f},
31 {-10948.8f, -2022.1f},
32 {-10958.7f, -1997.7f},
33 {-10971.5f, -1997.5f},
34 {-10990.8f, -1995.1f},
35 {-10989.8f, -1976.5f},
36 {-10971.6f, -1973.0f},
37 {-10955.5f, -1974.0f},
38 {-10939.6f, -1969.8f},
39 {-10958.0f, -1952.2f},
40 {-10941.7f, -1954.8f},
41 {-10943.1f, -1988.5f},
42 {-10948.8f, -2005.1f},
43 {-10984.0f, -2019.3f},
44 {-10932.8f, -1979.6f},
45 {-10935.7f, -1996.0f}
46};
47
48//Enfeeble is supposed to reduce hp to 1 and then heal player back to full when it ends
49//Along with reducing healing and regen while enfeebled to 0%
50//This spell effect will only reduce healing
52{
56// SAY_SPECIAL1 = 3, Not used, needs to be implemented, but I don't know where it should be used.
57// SAY_SPECIAL2 = 4, Not used, needs to be implemented, but I don't know where it should be used.
58// SAY_SPECIAL3 = 5, Not used, needs to be implemented, but I don't know where it should be used.
62
64
65 SPELL_ENFEEBLE = 30843, //Enfeeble during phase 1 and 2
67
68 SPELL_SHADOWNOVA = 30852, //Shadownova used during all phases
69 SPELL_SW_PAIN = 30854, //Shadow word pain during phase 1 and 3 (different targeting rules though)
70 SPELL_THRASH_PASSIVE = 12787, //Extra attack chance during phase 2
71 SPELL_SUNDER_ARMOR = 30901, //Sunder armor during phase 2
72 SPELL_THRASH_AURA = 12787, //Passive proc chance for thrash
73 SPELL_EQUIP_AXES = 30857, //Visual for axe equiping
74 SPELL_AMPLIFY_DAMAGE = 39095, //Amplifiy during phase 3
75 SPELL_CLEAVE = 30131, //Same as Nightbane.
76 SPELL_HELLFIRE = 30859, //Infenals' hellfire aura
77 NETHERSPITE_INFERNAL = 17646, //The netherspite infernal creature
78 MALCHEZARS_AXE = 17650, //Malchezar's axes (creatures), summoned during phase 3
79
80 INFERNAL_MODEL_INVISIBLE = 11686, //Infernal Effects
82
83 EQUIP_ID_AXE = 33542 //Axes info
84};
85
86//---------Infernal code first
88{
89public:
90 netherspite_infernal() : CreatureScript("netherspite_infernal") { }
91
93 {
95 HellfireTimer(0), CleanupTimer(0), malchezaar(0), point(NULL) { }
96
97 void Reset() OVERRIDE { }
98 void EnterCombat(Unit* /*who*/) OVERRIDE { }
99 void MoveInLineOfSight(Unit* /*who*/) OVERRIDE { }
100
101
103 {
104 if (HellfireTimer)
105 {
106 if (HellfireTimer <= diff)
107 {
109 HellfireTimer = 0;
110 }
111 else HellfireTimer -= diff;
112 }
113
114 if (CleanupTimer)
115 {
116 if (CleanupTimer <= diff)
117 {
118 Cleanup();
119 CleanupTimer = 0;
120 } else CleanupTimer -= diff;
121 }
122 }
123
125 {
126 if (Unit* unit = Unit::GetUnit(*me, malchezaar))
127 if (Creature* creature = unit->ToCreature())
128 creature->AI()->KilledUnit(who);
129 }
130
131 void SpellHit(Unit* /*who*/, const SpellInfo* spell) OVERRIDE
132 {
133 if (spell->Id == SPELL_INFERNAL_RELAY)
134 {
135 me->SetDisplayId(me->GetUInt32Value(UNIT_FIELD_NATIVE_DISPLAY_ID));
137 HellfireTimer = 4000;
138 CleanupTimer = 170000;
139 }
140 }
141
142 void DamageTaken(Unit* done_by, uint32 &damage) OVERRIDE
143 {
144 if (done_by->GetGUID() != malchezaar)
145 damage = 0;
146 }
147
148 void Cleanup();
149
150 public:
153 private:
156 };
157
159 {
160 return new netherspite_infernalAI(creature);
161 }
162};
163
165{
166public:
167 boss_malchezaar() : CreatureScript("boss_malchezaar") { }
168
170 {
171 boss_malchezaarAI(Creature* creature) : ScriptedAI(creature)
172 {
173 instance = creature->GetInstanceScript();
174 memset(axes, 0, sizeof(axes));
175
176 EnfeebleTimer = 0;
178 ShadowNovaTimer = 0;
179 SWPainTimer = 0;
182 Cleave_Timer = 0;
183 InfernalTimer = 0;
186
187 infernals = { };
188 positions = { };
189
190 for (uint8 i = 0; i < 2; i++)
191 {
192 axes[i] = 0;
193 }
194
195 for (uint8 j = 0; j < 5; j++)
196 {
197 enfeeble_targets[j] = 0;
198 enfeeble_health[j] = 0;
199 }
200
201 phase = 0;
202 }
203
205 {
206 AxesCleanup();
207 ClearWeapons();
209 positions.clear();
210
211 for (uint8 i = 0; i < 5; ++i)
212 {
213 enfeeble_targets[i] = 0;
214 enfeeble_health[i] = 0;
215 }
216
217 for (uint8 i = 0; i < TOTAL_INFERNAL_POINTS; ++i)
218 positions.push_back(&InfernalPoints[i]);
219
220 EnfeebleTimer = 30000;
221 EnfeebleResetTimer = 38000;
222 ShadowNovaTimer = 35500;
223 SWPainTimer = 20000;
224 AmplifyDamageTimer = 5000;
225 Cleave_Timer = 8000;
226 InfernalTimer = 40000;
227 InfernalCleanupTimer = 47000;
228 AxesTargetSwitchTimer = std::rand() % 20000 + 7500;
229 SunderArmorTimer = std::rand() % 10000 + 5000;
230 phase = 1;
231
232 if (instance)
233 instance->HandleGameObject(instance->GetData64(DATA_GO_NETHER_DOOR), true);
234 }
235
236 void KilledUnit(Unit* /*victim*/) OVERRIDE
237 {
238 Talk(SAY_SLAY);
239 }
240
241 void JustDied(Unit* /*killer*/) OVERRIDE
242 {
244
245 AxesCleanup();
246 ClearWeapons();
248 positions.clear();
249
250 for (uint8 i = 0; i < TOTAL_INFERNAL_POINTS; ++i)
251 positions.push_back(&InfernalPoints[i]);
252
253 if (instance)
254 instance->HandleGameObject(instance->GetData64(DATA_GO_NETHER_DOOR), true);
255 }
256
257 void EnterCombat(Unit* /*who*/) OVERRIDE
258 {
260
261 if (instance)
262 instance->HandleGameObject(instance->GetData64(DATA_GO_NETHER_DOOR), false); // Open the door leading further in
263 }
264
266 {
267 //Infernal Cleanup
268 for (std::vector<uint64>::const_iterator itr = infernals.begin(); itr != infernals.end(); ++itr)
269 if (Unit* pInfernal = Unit::GetUnit(*me, *itr))
270 if (pInfernal->IsAlive())
271 {
272 pInfernal->SetVisible(false);
273 pInfernal->setDeathState(DeathState::JUST_DIED);
274 }
275
276 infernals.clear();
277 }
278
280 {
281 for (uint8 i = 0; i < 2; ++i)
282 {
283 Unit* axe = Unit::GetUnit(*me, axes[i]);
284 if (axe && axe->IsAlive())
285 axe->Kill(axe);
286 axes[i] = 0;
287 }
288 }
289
291 {
293
294 //damage
295 const CreatureTemplate* cinfo = me->GetCreatureTemplate();
298 me->UpdateDamagePhysical(WeaponAttackType::BASE_ATTACK);
299 }
300
302 {
303 const SpellInfo* info = sSpellMgr->GetSpellInfo(SPELL_ENFEEBLE_EFFECT);
304 if (!info)
305 return;
306
307 ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList();
308 std::vector<Unit*> targets;
309
310 if (t_list.empty())
311 return;
312
313 //begin + 1, so we don't target the one with the highest threat
314 ThreatContainer::StorageType::const_iterator itr = t_list.begin();
315 std::advance(itr, 1);
316 for (; itr != t_list.end(); ++itr) //store the threat list in a different container
317 if (Unit* target = Unit::GetUnit(*me, (*itr)->getUnitGuid()))
318 if (target->IsAlive() && target->GetTypeId() == TypeID::TYPEID_PLAYER)
319 targets.push_back(target);
320
321 //cut down to size if we have more than 5 targets
322 while (targets.size() > 5)
323 targets.erase(targets.begin()+rand()%targets.size());
324
325 uint32 i = 0;
326 for (std::vector<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter, ++i)
327 if (Unit* target = *iter)
328 {
329 enfeeble_targets[i] = target->GetGUID();
330 enfeeble_health[i] = target->GetHealth();
331
332 target->CastSpell(target, SPELL_ENFEEBLE, true, 0, 0, me->GetGUID());
333 target->SetHealth(1);
334 }
335 }
336
338 {
339 for (uint8 i = 0; i < 5; ++i)
340 {
341 Unit* target = Unit::GetUnit(*me, enfeeble_targets[i]);
342 if (target && target->IsAlive())
343 target->SetHealth(enfeeble_health[i]);
344 enfeeble_targets[i] = 0;
345 enfeeble_health[i] = 0;
346 }
347 }
348
349 void SummonInfernal(const uint32 /*diff*/)
350 {
351 InfernalPoint *point = NULL;
352 Position pos;
353 if ((me->GetMapId() != 532) || positions.empty())
354 me->GetRandomNearPosition(pos, 60);
355 else
356 {
358 pos.Relocate(point->x, point->y, INFERNAL_Z, frand(0.0f, float(M_PI * 2)));
359 }
360
361 Creature* infernal = me->SummonCreature(NETHERSPITE_INFERNAL, pos, TempSummonType::TEMPSUMMON_TIMED_DESPAWN, 180000);
362
363 if (infernal)
364 {
366 infernal->setFaction(me->getFaction());
367 if (point)
369 CAST_AI(netherspite_infernal::netherspite_infernalAI, infernal->AI())->malchezaar=me->GetGUID();
370
371 infernals.push_back(infernal->GetGUID());
372 DoCast(infernal, SPELL_INFERNAL_RELAY);
373 }
374
376 }
377
379 {
380 if (!UpdateVictim())
381 return;
382
383 if (EnfeebleResetTimer && EnfeebleResetTimer <= diff) // Let's not forget to reset that
384 {
387 } else EnfeebleResetTimer -= diff;
388
389 if (me->HasUnitState(UNIT_STATE_STUNNED)) // While shifting to phase 2 malchezaar stuns himself
390 return;
391
392 if (me->GetUInt64Value(UNIT_FIELD_TARGET) != me->GetVictim()->GetGUID())
393 me->SetTarget(me->GetVictim()->GetGUID());
394
395 if (phase == 1)
396 {
397 if (HealthBelowPct(60))
398 {
399 me->InterruptNonMeleeSpells(false);
400
401 phase = 2;
402
403 //animation
405
406 //text
408
409 //passive thrash aura
411
412 //models
414
415 //damage
416 const CreatureTemplate* cinfo = me->GetCreatureTemplate();
419 me->UpdateDamagePhysical(WeaponAttackType::BASE_ATTACK);
420
423 //Sigh, updating only works on main attack, do it manually ....
424 me->SetFloatValue(UNIT_FIELD_MIN_OFF_HAND_DAMAGE, cinfo->mindmg);
425 me->SetFloatValue(UNIT_FIELD_MAX_OFF_HAND_DAMAGE, cinfo->maxdmg);
426
427 me->SetAttackTime(WeaponAttackType::OFF_ATTACK, (me->GetAttackTime(WeaponAttackType::BASE_ATTACK)*150)/100);
428 }
429 }
430 else if (phase == 2)
431 {
432 if (HealthBelowPct(30))
433 {
434 InfernalTimer = 15000;
435
436 phase = 3;
437
438 ClearWeapons();
439
440 //remove thrash
441 me->RemoveAurasDueToSpell(SPELL_THRASH_AURA);
442
444
445 Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
446 for (uint8 i = 0; i < 2; ++i)
447 {
448 Creature* axe = me->SummonCreature(MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TempSummonType::TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
449 if (axe)
450 {
452 axe->setFaction(me->getFaction());
453 axes[i] = axe->GetGUID();
454 if (target)
455 {
456 axe->AI()->AttackStart(target);
457 //axe->getThreatManager().tauntApply(target); //Taunt Apply and fade out does not work properly
458 // So we'll use a hack to add a lot of threat to our target
459 axe->AddThreat(target, 10000000.0f);
460 }
461 }
462 }
463
464 if (ShadowNovaTimer > 35000)
466
467 return;
468 }
469
470 if (SunderArmorTimer <= diff)
471 {
473 SunderArmorTimer = std::rand() % 18000 + 10000;
474 } else SunderArmorTimer -= diff;
475
476 if (Cleave_Timer <= diff)
477 {
479 Cleave_Timer = std::rand() % 12000 + 6000;
480 } else Cleave_Timer -= diff;
481 }
482 else
483 {
484 if (AxesTargetSwitchTimer <= diff)
485 {
486 AxesTargetSwitchTimer = std::rand() % 20000 + 7500;
487
488 if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
489 {
490 for (uint8 i = 0; i < 2; ++i)
491 {
492 if (Unit* axe = Unit::GetUnit(*me, axes[i]))
493 {
494 if (axe->GetVictim())
495 DoModifyThreatPercent(axe->GetVictim(), -100);
496 if (target)
497 axe->AddThreat(target, 1000000.0f);
498 //axe->getThreatManager().tauntFadeOut(axe->GetVictim());
499 //axe->getThreatManager().tauntApply(target);
500 }
501 }
502 }
503 } else AxesTargetSwitchTimer -= diff;
504
505 if (AmplifyDamageTimer <= diff)
506 {
507 if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
509 AmplifyDamageTimer = std::rand() % 30000 + 20000;
510 } else AmplifyDamageTimer -= diff;
511 }
512
513 //Time for global and double timers
514 if (InfernalTimer <= diff)
515 {
516 SummonInfernal(diff);
517 InfernalTimer = phase == 3 ? 14500 : 44500; // 15 secs in phase 3, 45 otherwise
518 } else InfernalTimer -= diff;
519
520 if (ShadowNovaTimer <= diff)
521 {
523 ShadowNovaTimer = phase == 3 ? 31000 : uint32(-1);
524 } else ShadowNovaTimer -= diff;
525
526 if (phase != 2)
527 {
528 if (SWPainTimer <= diff)
529 {
530 Unit* target = NULL;
531 if (phase == 1)
532 target = me->GetVictim(); // the tank
533 else // anyone but the tank
534 target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100, true);
535
536 if (target)
537 DoCast(target, SPELL_SW_PAIN);
538
539 SWPainTimer = 20000;
540 } else SWPainTimer -= diff;
541 }
542
543 if (phase != 3)
544 {
545 if (EnfeebleTimer <= diff)
546 {
548 EnfeebleTimer = 30000;
549 ShadowNovaTimer = 5000;
550 EnfeebleResetTimer = 9000;
551 } else EnfeebleTimer -= diff;
552 }
553
554 if (phase == 2)
556 else
558 }
559
561 {
562 if (me->IsWithinMeleeRange(me->GetVictim()) && !me->IsNonMeleeSpellCasted(false))
563 {
564 //Check for base attack
565 if (me->isAttackReady() && me->GetVictim())
566 {
567 me->AttackerStateUpdate(me->GetVictim());
568 me->resetAttackTimer();
569 }
570 //Check for offhand attack
571 if (me->isAttackReady(WeaponAttackType::OFF_ATTACK) && me->GetVictim())
572 {
573 me->AttackerStateUpdate(me->GetVictim(), WeaponAttackType::OFF_ATTACK);
574 me->resetAttackTimer(WeaponAttackType::OFF_ATTACK);
575 }
576 }
577 }
578
579 void Cleanup(Creature* infernal, InfernalPoint *point)
580 {
581 for (std::vector<uint64>::iterator itr = infernals.begin(); itr!= infernals.end(); ++itr)
582 if (*itr == infernal->GetGUID())
583 {
584 infernals.erase(itr);
585 break;
586 }
587
588 positions.push_back(point);
589 }
590
591 private:
603
604 std::vector<uint64> infernals;
605 std::vector<InfernalPoint*> positions;
606
610
612 };
613
615 {
616 return new boss_malchezaarAI(creature);
617 }
618};
619
621{
622 Creature* pMalchezaar = Unit::GetCreature(*me, malchezaar);
623
624 if (pMalchezaar && pMalchezaar->IsAlive())
625 CAST_AI(boss_malchezaar::boss_malchezaarAI, pMalchezaar->AI())->Cleanup(me, point);
626}
627
629{
630 new boss_malchezaar();
632}
#define M_PI
Definition Common.h:192
@ EQUIP_UNEQUIP
Definition CreatureAI.h:50
@ EQUIP_NO_CHANGE
Definition CreatureAI.h:49
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
@ TYPEID_PLAYER
Definition Object.h:55
@ TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT
Definition Object.h:71
@ TEMPSUMMON_TIMED_DESPAWN
Definition Object.h:70
#define CAST_AI(a, b)
#define sSpellMgr
Definition SpellMgr.h:744
@ JUST_DIED
Definition Unit.h:504
@ UNIT_STATE_STUNNED
Definition Unit.h:515
@ UNIT_FLAG_NOT_SELECTABLE
Definition Unit.h:650
@ SELECT_TARGET_RANDOM
Definition UnitAI.h:23
@ UNIT_FIELD_NATIVE_DISPLAY_ID
@ UNIT_FIELD_FLAGS
@ UNIT_FIELD_TARGET
@ UNIT_FIELD_MAX_OFF_HAND_DAMAGE
@ UNIT_FIELD_MIN_OFF_HAND_DAMAGE
float frand(float min, float max)
Definition Util.cpp:39
@ SPELL_CLEAVE
@ SAY_SLAY
#define SAY_DEATH
#define SAY_AGGRO
#define INFERNAL_Z
static InfernalPoint InfernalPoints[]
@ NETHERSPITE_INFERNAL
@ SPELL_ENFEEBLE_EFFECT
@ SPELL_THRASH_PASSIVE
@ INFERNAL_MODEL_INVISIBLE
@ SPELL_INFERNAL_RELAY
@ SPELL_AMPLIFY_DAMAGE
@ TOTAL_INFERNAL_POINTS
void AddSC_boss_malchezaar()
void Talk(uint8 id, WorldObject const *whisperTarget=NULL)
bool UpdateVictim()
void SetDisplayId(uint32 modelId) OVERRIDE
CreatureAI * AI() const
Definition Creature.h:483
CreatureScript(const char *name)
uint64 GetGUID() const
Definition Object.h:119
void SetFlag(uint16 index, uint32 newFlag)
Definition Object.cpp:1261
Creature * ToCreature()
Definition Object.h:207
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 setFaction(uint32 faction)
Definition Unit.h:1699
void Kill(Unit *victim, bool durabilityLoss=true)
Definition Unit.cpp:6342
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
void SetHealth(uint32 val)
Definition Unit.cpp:5162
static Creature * GetCreature(WorldObject &object, uint64 guid)
Definition Unit.cpp:4905
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
@ DATA_GO_NETHER_DOOR
Definition karazhan.h:35
C::value_type const & SelectRandomContainerElement(C const &container)
Definition Containers.h:48
void Relocate(float x, float y)
Definition Object.h:302
void SetEquipmentSlots(bool loadDefault, int32 mainHand=EQUIP_NO_CHANGE, int32 offHand=EQUIP_NO_CHANGE, int32 ranged=EQUIP_NO_CHANGE)
Creature * me
bool HealthBelowPct(uint32 pct) const
ScriptedAI(Creature *creature)
void DoModifyThreatPercent(Unit *unit, int32 pct)
void Cleanup(Creature *infernal, InfernalPoint *point)
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
std::vector< InfernalPoint * > positions
void SpellHit(Unit *, const SpellInfo *spell) OVERRIDE
void DamageTaken(Unit *done_by, uint32 &damage) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================