TrinityCore
boss_twinemperors.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* ScriptData
19SDName: Boss_Twinemperors
20SD%Complete: 95
21SDComment:
22SDCategory: Temple of Ahn'Qiraj
23EndScriptData */
24
25#include "ScriptMgr.h"
26#include "InstanceScript.h"
27#include "MotionMaster.h"
28#include "ScriptedCreature.h"
29#include "SpellInfo.h"
30#include "temple_of_ahnqiraj.h"
31
33{
35 SPELL_TWIN_TELEPORT = 800, // CTRA watches for this spell to start its teleport timer
36 SPELL_TWIN_TELEPORT_VISUAL = 26638, // visual
45};
46
48{
49 SOUND_VL_AGGRO = 8657, //8657 - Aggro - To Late
50 SOUND_VL_KILL = 8658, //8658 - Kill - You will not
51 SOUND_VL_DEATH = 8659, //8659 - Death
52 SOUND_VN_DEATH = 8660, //8660 - Death - Feel
53 SOUND_VN_AGGRO = 8661, //8661 - Aggro - Let none
54 SOUND_VN_KILL = 8662, //8661 - Kill - your fate
55};
56
57enum Misc
58{
60 VEKLOR_DIST = 20, // VL will not come to melee when attacking
61 TELEPORTTIME = 30000
62};
63
64static constexpr float PULL_RANGE = 50.0f;
65
67{
69 {
70 Initialize();
71 }
72
74 {
75 Heal_Timer = 0; // first heal immediately when they get close together
77 AfterTeleport = false;
78 tspellcast = false;
80 Abuse_Bug_Timer = urand(10000, 17000);
81 BugsTimer = 2000;
82
83 DontYellWhenDead = false;
84 EnrageTimer = 15 * 60000;
85 }
86
95
96 virtual bool IAmVeklor() = 0;
97 virtual void Reset() override = 0;
98 virtual void CastSpellOnBug(Creature* target) = 0;
99
101 {
102 Initialize();
104 _Reset();
105 }
106
108 {
110 }
111
112 void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
113 {
114 Unit* pOtherBoss = GetOtherBoss();
115 if (pOtherBoss)
116 {
117 float dPercent = ((float)damage) / ((float)me->GetMaxHealth());
118 int odmg = (int)(dPercent * ((float)pOtherBoss->GetMaxHealth()));
119 int ohealth = pOtherBoss->GetHealth()-odmg;
120 pOtherBoss->SetHealth(ohealth > 0 ? ohealth : 0);
121 if (ohealth <= 0)
122 {
123 pOtherBoss->setDeathState(JUST_DIED);
125 }
126 }
127 }
128
129 void JustDied(Unit* /*killer*/) override
130 {
131 Creature* pOtherBoss = GetOtherBoss();
132 if (pOtherBoss)
133 {
134 pOtherBoss->SetHealth(0);
135 pOtherBoss->setDeathState(JUST_DIED);
137 ENSURE_AI(boss_twinemperorsAI, pOtherBoss->AI())->DontYellWhenDead = true;
138 }
139 if (!DontYellWhenDead) // I hope AI is not threaded
141 _JustDied();
142 }
143
144 void KilledUnit(Unit* /*victim*/) override
145 {
147 }
148
149 void JustEngagedWith(Unit* who) override
150 {
152 Creature* pOtherBoss = GetOtherBoss();
153 if (pOtherBoss)
154 {
156 // is near I dont know how to do that
157 if (!pOtherBoss->IsInCombat())
158 {
159 ScriptedAI* otherAI = ENSURE_AI(ScriptedAI, pOtherBoss->AI());
161 otherAI->AttackStart(who);
162 otherAI->DoZoneInCombat();
163 }
164 }
165 }
166
167 void SpellHit(WorldObject* caster, SpellInfo const* spellInfo) override
168 {
169 if (caster == me)
170 return;
171
172 Creature* pOtherBoss = GetOtherBoss();
173 if (spellInfo->Id != SPELL_HEAL_BROTHER || !pOtherBoss)
174 return;
175
176 // add health so we keep same percentage for both brothers
177 uint32 mytotal = me->GetMaxHealth(), histotal = pOtherBoss->GetMaxHealth();
178 float mult = ((float)mytotal) / ((float)histotal);
179 if (mult < 1)
180 mult = 1.0f/mult;
181 #define HEAL_BROTHER_AMOUNT 30000.0f
182 uint32 largerAmount = (uint32)((HEAL_BROTHER_AMOUNT * mult) - HEAL_BROTHER_AMOUNT);
183
184 if (mytotal > histotal)
185 {
186 uint32 h = me->GetHealth()+largerAmount;
187 me->SetHealth(std::min(mytotal, h));
188 }
189 else
190 {
191 uint32 h = pOtherBoss->GetHealth()+largerAmount;
192 pOtherBoss->SetHealth(std::min(histotal, h));
193 }
194 }
195
197 {
198 if (IAmVeklor()) // this spell heals caster and the other brother so let VN cast it
199 return;
200
201 if (Heal_Timer <= diff)
202 {
203 Unit* pOtherBoss = GetOtherBoss();
204 if (pOtherBoss && pOtherBoss->IsWithinDist(me, 60))
205 {
206 DoCast(pOtherBoss, SPELL_HEAL_BROTHER);
207 Heal_Timer = 1000;
208 }
209 } else Heal_Timer -= diff;
210 }
211
213 {
215
216 if (IAmVeklor())
217 return; // mechanics handled by veknilash so they teleport exactly at the same time and to correct coordinates
218
219 Creature* pOtherBoss = GetOtherBoss();
220 if (pOtherBoss)
221 {
222 //me->MonsterYell("Teleporting ...", LANG_UNIVERSAL, 0);
223 Position thisPos;
224 thisPos.Relocate(me);
225 Position otherPos;
226 otherPos.Relocate(pOtherBoss);
227 pOtherBoss->UpdatePosition(thisPos);
228 me->UpdatePosition(otherPos);
229
231 ENSURE_AI(boss_twinemperorsAI, pOtherBoss->AI())->SetAfterTeleport();
232 }
233 }
234
236 {
238 DoStopAttack();
242 AfterTeleport = true;
243 AfterTeleportTimer = 2000;
244 tspellcast = false;
245 }
246
248 {
249 if (AfterTeleport)
250 {
251 if (!tspellcast)
252 {
256 }
257
258 tspellcast = true;
259
260 if (AfterTeleportTimer <= diff)
261 {
262 AfterTeleport = false;
264 if (Unit* nearu = me->SelectNearestTarget(100))
265 {
266 //DoYell(nearu->GetName(), LANG_UNIVERSAL, 0);
267 AttackStart(nearu);
268 AddThreat(nearu, 10000);
269 }
270 return true;
271 }
272 else
273 {
274 AfterTeleportTimer -= diff;
275 // update important timers which would otherwise get skipped
276 if (EnrageTimer > diff)
277 EnrageTimer -= diff;
278 else
279 EnrageTimer = 0;
280 if (Teleport_Timer > diff)
281 Teleport_Timer -= diff;
282 else
283 Teleport_Timer = 0;
284 return false;
285 }
286 }
287 else
288 {
289 return true;
290 }
291 }
292
293 void MoveInLineOfSight(Unit* who) override
294
295 {
296 if (!who || me->GetVictim())
297 return;
298
299 if (me->CanCreatureAttack(who))
300 {
301 float attackRadius = me->GetAttackDistance(who);
302 if (attackRadius < PULL_RANGE)
303 attackRadius = PULL_RANGE;
304 if (me->IsWithinDistInMap(who, attackRadius) && me->GetDistanceZ(who) <= /*CREATURE_Z_ATTACK_RANGE*/7 /*there are stairs*/)
305 {
306 //if (who->HasStealthAura())
307 // who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
308 AttackStart(who);
309 }
310 }
311 }
312
314 {
315 std::list<Creature*> lUnitList;
316 me->GetCreatureListWithEntryInGrid(lUnitList, 15316, 150.0f);
317 me->GetCreatureListWithEntryInGrid(lUnitList, 15317, 150.0f);
318
319 if (lUnitList.empty())
320 return nullptr;
321
322 Creature* nearb = nullptr;
323
324 for (std::list<Creature*>::const_iterator iter = lUnitList.begin(); iter != lUnitList.end(); ++iter)
325 {
326 Creature* c = *iter;
327 if (c)
328 {
329 if (c->isDead())
330 {
331 c->Respawn();
333 c->RemoveAllAuras();
334 }
336 {
337 if (!nearb || (rand32() % 4) == 0)
338 nearb = c;
339 }
340 }
341 }
342 return nearb;
343 }
344
346 {
347 if (BugsTimer < diff || Abuse_Bug_Timer <= diff)
348 {
350 if (Abuse_Bug_Timer <= diff)
351 {
352 if (c)
353 {
355 Abuse_Bug_Timer = urand(10000, 17000);
356 }
357 else
358 {
359 Abuse_Bug_Timer = 1000;
360 }
361 }
362 else
363 {
364 Abuse_Bug_Timer -= diff;
365 }
366 BugsTimer = 2000;
367 }
368 else
369 {
370 BugsTimer -= diff;
371 Abuse_Bug_Timer -= diff;
372 }
373 }
374
376 {
377 if (EnrageTimer <= diff)
378 {
379 if (!me->IsNonMeleeSpellCast(true))
380 {
382 EnrageTimer = 60 * 60000;
383 }
384 else
385 EnrageTimer = 0;
386 }
387 else
388 EnrageTimer -= diff;
389 }
390};
391
393{
394public:
395 boss_veknilash() : CreatureScript("boss_veknilash") { }
396
397 CreatureAI* GetAI(Creature* creature) const override
398 {
399 return GetAQ40AI<boss_veknilashAI>(creature);
400 }
401
403 {
404 bool IAmVeklor() override {return false;}
406 {
407 Initialize();
408 }
409
411 {
412 UpperCut_Timer = urand(14000, 29000);
413 UnbalancingStrike_Timer = urand(8000, 18000);
414 Scarabs_Timer = urand(7000, 14000);
415 }
416
420
421 void Reset() override
422 {
423 TwinReset();
424 Initialize();
425 }
426
427 void CastSpellOnBug(Creature* target) override
428 {
431 target->AddAura(SPELL_MUTATE_BUG, target);
432 target->SetFullHealth();
433 }
434
435 void UpdateAI(uint32 diff) override
436 {
437 //Return since we have no target
438 if (!UpdateVictim())
439 return;
440
441 if (!TryActivateAfterTTelep(diff))
442 return;
443
444 //UnbalancingStrike_Timer
445 if (UnbalancingStrike_Timer <= diff)
446 {
448 UnbalancingStrike_Timer = 8000 + rand32() % 12000;
449 } else UnbalancingStrike_Timer -= diff;
450
451 if (UpperCut_Timer <= diff)
452 {
454 if (randomMelee)
455 DoCast(randomMelee, SPELL_UPPERCUT);
456 UpperCut_Timer = 15000 + rand32() % 15000;
457 } else UpperCut_Timer -= diff;
458
459 HandleBugs(diff);
460
461 //Heal brother when 60yrds close
462 TryHealBrother(diff);
463
464 //Teleporting to brother
465 if (Teleport_Timer <= diff)
466 {
468 } else Teleport_Timer -= diff;
469
470 CheckEnrage(diff);
471 }
472 };
473
474};
475
477{
478public:
479 boss_veklor() : CreatureScript("boss_veklor") { }
480
481 CreatureAI* GetAI(Creature* creature) const override
482 {
483 return GetAQ40AI<boss_veklorAI>(creature);
484 }
485
487 {
488 bool IAmVeklor() override {return true;}
490 {
491 Initialize();
492 }
493
495 {
497 Blizzard_Timer = urand(15000, 20000);
498 ArcaneBurst_Timer = 1000;
499 Scorpions_Timer = urand(7000, 14000);
500 }
501
506
507 void Reset() override
508 {
509 TwinReset();
510 Initialize();
511 }
512
513 void CastSpellOnBug(Creature* target) override
514 {
516 target->AddAura(SPELL_EXPLODEBUG, target);
517 target->SetFullHealth();
518 }
519
520 void UpdateAI(uint32 diff) override
521 {
522 //Return since we have no target
523 if (!UpdateVictim())
524 return;
525
526 // reset arcane burst after teleport - we need to do this because
527 // when VL jumps to VN's location there will be a warrior who will get only 2s to run away
528 // which is almost impossible
529 if (AfterTeleport)
530 ArcaneBurst_Timer = 5000;
531 if (!TryActivateAfterTTelep(diff))
532 return;
533
534 //ShadowBolt_Timer
535 if (ShadowBolt_Timer <= diff)
536 {
537 if (!me->IsWithinDist(me->GetVictim(), 45.0f))
539 else
541 ShadowBolt_Timer = 2000;
542 } else ShadowBolt_Timer -= diff;
543
544 //Blizzard_Timer
545 if (Blizzard_Timer <= diff)
546 {
547 if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45, true))
548 DoCast(target, SPELL_BLIZZARD);
549 Blizzard_Timer = 15000 + rand32() % 15000;
550 } else Blizzard_Timer -= diff;
551
552 if (ArcaneBurst_Timer <= diff)
553 {
555 {
557 ArcaneBurst_Timer = 5000;
558 }
559 } else ArcaneBurst_Timer -= diff;
560
561 HandleBugs(diff);
562
563 //Heal brother when 60yrds close
564 TryHealBrother(diff);
565
566 //Teleporting to brother
567 if (Teleport_Timer <= diff)
568 {
570 } else Teleport_Timer -= diff;
571
572 CheckEnrage(diff);
573
574 //VL doesn't melee
575 //DoMeleeAttackIfReady();
576 }
577
578 void AttackStart(Unit* who) override
579 {
580 if (!who)
581 return;
582
583 if (who->isTargetableForAttack())
584 {
585 // VL doesn't melee
586 if (me->Attack(who, false))
587 {
589 AddThreat(who, 0.0f);
590 }
591 }
592 }
593 };
594
595};
596
598{
599 new boss_veknilash();
600 new boss_veklor();
601}
uint32_t uint32
Definition: Define.h:142
#define NOMINAL_MELEE_RANGE
Definition: ObjectDefines.h:44
Spells
Definition: PlayerAI.cpp:32
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
uint32 rand32()
Definition: Random.cpp:70
@ UNIT_DYNFLAG_LOOTABLE
@ FACTION_MONSTER
@ FACTION_CREATURE
#define ENSURE_AI(a, b)
Definition: UnitAI.h:29
DamageEffectType
Definition: UnitDefines.h:131
@ JUST_DIED
Definition: Unit.h:247
@ UNIT_STATE_STUNNED
Definition: Unit.h:258
#define HEAL_BROTHER_AMOUNT
@ VEKLOR_DIST
@ ABUSE_BUG_RANGE
@ TELEPORTTIME
@ SPELL_EXPLODEBUG
@ SPELL_UNBALANCING_STRIKE
@ SPELL_HEAL_BROTHER
@ SPELL_MUTATE_BUG
@ SPELL_SHADOWBOLT
@ SPELL_BLIZZARD
@ SPELL_TWIN_TELEPORT_VISUAL
@ SPELL_ARCANEBURST
@ SPELL_TWIN_TELEPORT
@ SPELL_BERSERK
@ SPELL_UPPERCUT
static constexpr float PULL_RANGE
@ SOUND_VN_KILL
@ SOUND_VL_KILL
@ SOUND_VL_AGGRO
@ SOUND_VL_DEATH
@ SOUND_VN_AGGRO
@ SOUND_VN_DEATH
void AddSC_boss_twinemperors()
InstanceScript *const instance
void JustEngagedWith(Unit *who) override
void _JustDied()
void DoZoneInCombat()
Definition: CreatureAI.h:161
bool UpdateVictim()
Definition: CreatureAI.cpp:245
void AttackStart(Unit *victim) override
== Triggered Actions Requested ==================
Definition: CreatureAI.cpp:328
Creature *const me
Definition: CreatureAI.h:61
void Respawn(bool force=false)
Definition: Creature.cpp:2303
void setDeathState(DeathState s) override
Definition: Creature.cpp:2197
float GetAttackDistance(Unit const *player) const
Definition: Creature.cpp:2153
bool CanCreatureAttack(Unit const *victim, bool force=true) const
Definition: Creature.cpp:2686
Unit * SelectNearestTarget(float dist=0, bool playerOnly=false) const
Definition: Creature.cpp:2493
CreatureAI * AI() const
Definition: Creature.h:214
Creature * GetCreature(uint32 type)
void MoveChase(Unit *target, Optional< ChaseRange > dist={}, Optional< ChaseAngle > angle={})
void SetDynamicFlag(uint32 flag)
Definition: Object.h:169
uint32 const Id
Definition: SpellInfo.h:325
Unit * GetCurrentVictim()
SpellCastResult DoCastVictim(uint32 spellId, CastSpellExtraArgs const &args={})
Definition: UnitAI.cpp:180
Unit * SelectTarget(SelectTargetMethod targetType, uint32 offset=0, float dist=0.0f, bool playerOnly=false, bool withTank=true, int32 aura=0)
Definition: UnitAI.cpp:79
SpellCastResult DoCast(uint32 spellId)
Definition: UnitAI.cpp:89
Definition: Unit.h:627
void ClearUnitState(uint32 f)
Definition: Unit.h:733
bool isTargetableForAttack(bool checkFakeDeath=true) const
Definition: Unit.cpp:8168
void SetHealth(uint64 val)
Definition: Unit.cpp:9346
void SetFullHealth()
Definition: Unit.h:790
ThreatManager & GetThreatManager()
Definition: Unit.h:1063
void SetFaction(uint32 faction) override
Definition: Unit.h:859
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition: Unit.cpp:3089
MotionMaster * GetMotionMaster()
Definition: Unit.h:1652
bool IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled=false, bool skipAutorepeat=false, bool isAutoshoot=false, bool skipInstant=true) const
Definition: Unit.cpp:3059
Aura * AddAura(uint32 spellId, Unit *target)
Definition: Unit.cpp:11618
void AddUnitState(uint32 f)
Definition: Unit.h:731
virtual bool UpdatePosition(float x, float y, float z, float ang, bool teleport=false)
Definition: Unit.cpp:12392
uint64 GetMaxHealth() const
Definition: Unit.h:777
bool Attack(Unit *victim, bool meleeAttack)
Definition: Unit.cpp:5670
uint64 GetHealth() const
Definition: Unit.h:776
Unit * GetVictim() const
Definition: Unit.h:715
void RemoveAllAuras()
Definition: Unit.cpp:4242
virtual void setDeathState(DeathState s)
Definition: Unit.cpp:8592
bool IsInCombat() const
Definition: Unit.h:1043
bool isDead() const
Definition: Unit.h:1166
void GetCreatureListWithEntryInGrid(Container &creatureContainer, uint32 entry, float maxSearchRange=250.0f) const
Definition: Object.cpp:3312
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition: Object.cpp:1147
float GetDistanceZ(WorldObject const *obj) const
Definition: Object.cpp:1048
bool IsWithinDist(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition: Object.cpp:1142
CreatureAI * GetAI(Creature *creature) const override
CreatureAI * GetAI(Creature *creature) const override
constexpr void Relocate(float x, float y)
Definition: Position.h:63
void AttackStart(Unit *) override
== Triggered Actions Requested ==================
void ResetThreatList(Unit *who=nullptr)
void DoPlaySoundToSet(WorldObject *source, uint32 soundId)
void AddThreat(Unit *victim, float amount, Unit *who=nullptr)
void HandleBugs(uint32 diff)
bool TryActivateAfterTTelep(uint32 diff)
void JustDied(Unit *) override
void CheckEnrage(uint32 diff)
Creature * RespawnNearbyBugsAndGetOne()
void MoveInLineOfSight(Unit *who) override
virtual void CastSpellOnBug(Creature *target)=0
void SpellHit(WorldObject *caster, SpellInfo const *spellInfo) override
void TryHealBrother(uint32 diff)
void DamageTaken(Unit *, uint32 &damage, DamageEffectType, SpellInfo const *) override
virtual void Reset() override=0
void KilledUnit(Unit *) override
void JustEngagedWith(Unit *who) override
virtual bool IAmVeklor()=0
boss_twinemperorsAI(Creature *creature)
boss_veklorAI(Creature *creature)
void CastSpellOnBug(Creature *target) override
void UpdateAI(uint32 diff) override
void AttackStart(Unit *who) override
== Triggered Actions Requested ==================
void CastSpellOnBug(Creature *target) override
void UpdateAI(uint32 diff) override
@ DATA_VEKNILASH
@ DATA_TWIN_EMPERORS
@ DATA_VEKLOR