aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWyrserth <wyrserth@protonmail.com>2019-06-27 21:03:29 +0200
committerGitHub <noreply@github.com>2019-06-27 21:03:29 +0200
commit365e2f191efa5576c0045a9ebdffe63e1ea3bac7 (patch)
tree91f7ea7197eefa210d0533a29b1c22497016fd91
parentcb524a06b74cc98de0a3ff27ae2eb7aaf9fd7424 (diff)
Script/ZulGurub: rewrite High Priest Thekal's encounter script (#23429)
-rw-r--r--sql/updates/world/3.3.5/2019_06_27_03_world.sql7
-rw-r--r--src/server/game/Spells/SpellInfo.cpp1
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp706
3 files changed, 375 insertions, 339 deletions
diff --git a/sql/updates/world/3.3.5/2019_06_27_03_world.sql b/sql/updates/world/3.3.5/2019_06_27_03_world.sql
new file mode 100644
index 00000000000..61d250858d0
--- /dev/null
+++ b/sql/updates/world/3.3.5/2019_06_27_03_world.sql
@@ -0,0 +1,7 @@
+DELETE FROM `creature_text` WHERE `CreatureID` IN (14509, 11347, 11348) AND `GroupId`=2;
+DELETE FROM `creature_text` WHERE `CreatureID`=14509 AND `GroupId`=3;
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(14509, 2, 0, "%s dies.", 16, 0, 100, 0, 0, 0, 12195, 0, "High Priest Thekal"),
+(14509, 3, 0, "%s goes into a frenzy!", 16, 0, 100, 0, 0, 0, 1191, 0, "High Priest Thekal"),
+(11347, 2, 0, "%s dies.", 16, 0, 100, 0, 0, 0, 10453, 0, "Zealoth Lor'khan"),
+(11348, 2, 0, "%s dies.", 16, 0, 100, 0, 0, 0, 12195, 0, "Zealot Zath");
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 910fbcfc557..b8c3d17b1e1 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -1573,7 +1573,6 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a
case 2584: // Waiting to Resurrect
case 22011: // Spirit Heal Channel
case 22012: // Spirit Heal
- case 24171: // Resurrection Impact Visual
case 42792: // Recently Dropped Flag
case 43681: // Inactive
case 44535: // Spirit Heal (mana)
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
index 3d839e7bd0f..4474c7e7a45 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
@@ -17,52 +17,66 @@
*/
#include "zulgurub.h"
+#include "CellImpl.h"
+#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
+#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
enum Says
{
- SAY_AGGRO = 0,
- SAY_DEATH = 1
+ TALK_TIGER_PHASE = 0,
+ TALK_DEATH = 1,
+ TALK_FAKE_DEATH = 2,
+ TALK_FRENZY = 3
};
enum Spells
{
- SPELL_MORTALCLEAVE = 22859, // Phase 1
- SPELL_SILENCE = 22666, // Phase 1
- SPELL_TIGER_FORM = 24169, // Phase 1
- SPELL_RESURRECT = 24173, // Phase 1 // Not used in script.
- SPELL_FRENZY = 8269, // Phase 2
- SPELL_FORCEPUNCH = 24189, // Phase 2
- SPELL_CHARGE = 24193, // Phase 2
- SPELL_ENRAGE = 8269, // Phase 2
- SPELL_SUMMONTIGERS = 24183, // Phase 2
- // Zealot Lor'Khan Spells
+ SPELL_RESURRECT = 24173, // ToDo: find out how this should be used
+ SPELL_RESURRECT_VISUAL = 24171,
+
+ // High Priest Thekal
+ // Phase 1
+ SPELL_MORTALCLEAVE = 22859,
+ SPELL_SILENCE = 22666,
+ // Phase 2
+ SPELL_TIGER_FORM = 24169,
+ SPELL_FRENZY = 8269,
+ SPELL_FORCEPUNCH = 24189,
+ SPELL_CHARGE = 24193,
+ SPELL_SUMMONTIGERS = 24183,
+
+ // Zealot Lor'Khan
SPELL_SHIELD = 20545,
SPELL_BLOODLUST = 24185,
SPELL_GREATERHEAL = 24208,
SPELL_DISARM = 6713,
- // Zealot Zath Spells
+
+ // Zealot Zath
SPELL_SWEEPINGSTRIKES = 18765,
SPELL_SINISTERSTRIKE = 15581,
SPELL_GOUGE = 12540,
SPELL_KICK = 15614,
- SPELL_BLIND = 21060
+ SPELL_BLIND = 21060,
+
+ SPELL_PERMANENT_FEIGN_DEATH = 29266
};
-enum Events
+enum ThekalEvents
{
- EVENT_MORTALCLEAVE = 1, // Phase 1
- EVENT_SILENCE = 2, // Phase 1
- EVENT_CHECK_TIMER = 3, // Phase 1
- EVENT_RESURRECT_TIMER = 4, // Phase 1
- EVENT_FRENZY = 5, // Phase 2
- EVENT_FORCEPUNCH = 6, // Phase 2
- EVENT_SPELL_CHARGE = 7, // Phase 2
- EVENT_ENRAGE = 8, // Phase 2
- EVENT_SUMMONTIGERS = 9 // Phase 2
+ EVENT_MORTALCLEAVE = 1,
+ EVENT_SILENCE,
+ EVENT_RESURRECT_TIMER,
+ EVENT_CHANGE_PHASE_1,
+ EVENT_CHANGE_PHASE_2,
+ EVENT_CHANGE_PHASE_3,
+
+ EVENT_FORCEPUNCH,
+ EVENT_SPELL_CHARGE,
+ EVENT_SUMMONTIGERS
};
enum Phases
@@ -71,10 +85,13 @@ enum Phases
PHASE_TWO = 2
};
-// AWFUL HACK WARNING
-// To whoever reads this: Zul'Gurub needs your love
-// Need to do this calculation to increase/decrease Thekal's damage by 40% (probably some aura missing)
-// This is only to compile the scripts after the aura calculation revamp
+// Resurrection is handled by the main boss' script, dispatching resurrection as needed
+enum Data
+{
+ DATA_FAKE_DEATH = 1,
+ DATA_RESURRECTED
+};
+
float const DamageIncrease = 40.0f;
float const DamageDecrease = 100.f / (1.f + DamageIncrease / 100.f) - 100.f;
@@ -93,43 +110,134 @@ class boss_thekal : public CreatureScript
void Initialize()
{
_enraged = false;
- _wasDead = false;
+ _isThekalDead = false;
+ _isLorkhanDead = false;
+ _isZathDead = false;
+ _isResurrectTimerActive = false;
+ _isChangingPhase = false;
+ }
+
+ void EnterEvadeMode(EvadeReason why) override
+ {
+ if (!_EnterEvadeMode(why))
+ return;
+ me->AddUnitState(UNIT_STATE_EVADE);
+ me->GetMotionMaster()->MoveTargetedHome();
+ Reset();
}
void Reset() override
{
if (events.IsInPhase(PHASE_TWO))
- me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease); // hack
+ me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageDecrease);
+ me->SetControlled(false, UNIT_STATE_ROOT);
+ events.Reset();
_Reset();
Initialize();
+ me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
- Talk(SAY_DEATH);
+ Talk(TALK_DEATH);
+
+ // Adds are still feign-deathing, so kill them when the encounter is over
+ if (Creature* creature = instance->GetCreature(DATA_LORKHAN))
+ creature->KillSelf();
+ if (Creature* creature = instance->GetCreature(DATA_ZATH))
+ creature->KillSelf();
+ instance->SetBossState(DATA_LORKHAN, DONE);
+ instance->SetBossState(DATA_ZATH, DONE);
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
- events.ScheduleEvent(EVENT_MORTALCLEAVE, 4s, 0, PHASE_ONE); // Phase 1
- events.ScheduleEvent(EVENT_SILENCE, 9s, 0, PHASE_ONE); // Phase 1
- events.ScheduleEvent(EVENT_CHECK_TIMER, 10s, 0, PHASE_ONE); // Phase 1
- events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10s, 0, PHASE_ONE); // Phase 1
- Talk(SAY_AGGRO);
+ events.SetPhase(PHASE_ONE);
+ events.ScheduleEvent(EVENT_MORTALCLEAVE, 4s, 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_SILENCE, 9s, 0, PHASE_ONE);
}
- void JustReachedHome() override
+ void SetData(uint32 type, uint32 data) override
{
- instance->SetBossState(DATA_THEKAL, NOT_STARTED);
+ if (type == DATA_FAKE_DEATH)
+ {
+ // A mob died
+ switch (data)
+ {
+ case NPC_HIGH_PRIEST_THEKAL:
+ _isThekalDead = true;
+ break;
+ case NPC_ZEALOT_LORKHAN:
+ _isLorkhanDead = true;
+ break;
+ case NPC_ZEALOT_ZATH:
+ _isZathDead = true;
+ break;
+ }
+
+ if (_isThekalDead && _isLorkhanDead && _isZathDead)
+ {
+ _isResurrectTimerActive = false;
+ events.Reset();
+ events.ScheduleEvent(EVENT_CHANGE_PHASE_1, 3s);
+ }
+ else
+ {
+ // Start resurrection timer if not already started, otherwise ignore
+ if (!_isResurrectTimerActive)
+ {
+ events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10s);
+ _isResurrectTimerActive = true;
+ }
+ }
+ }
+ else if (type == DATA_RESURRECTED)
+ {
+ Creature* creature = nullptr;
+ if (data == NPC_HIGH_PRIEST_THEKAL)
+ creature = me;
+ else if (data == NPC_ZEALOT_LORKHAN)
+ creature = instance->GetCreature(DATA_LORKHAN);
+ else if (data == NPC_ZEALOT_ZATH)
+ creature = instance->GetCreature(DATA_ZATH);
+
+ // Resurrect
+ if (creature)
+ {
+ creature->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH);
+ creature->SetFullHealth();
+ creature->SetImmuneToPC(false, true);
+ creature->SetImmuneToNPC(false, true);
+ }
+ }
}
- void UpdateAI(uint32 diff) override
+ void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
- if (!UpdateVictim())
- return;
+ if (damage >= me->GetHealth() && events.IsInPhase(PHASE_ONE))
+ {
+ Talk(TALK_FAKE_DEATH);
+ me->RemoveAllAuras();
+ me->SetImmuneToPC(true, true);
+ me->SetImmuneToNPC(true, true);
+ DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true);
+ events.DelayEvents(10s);
+ SetData(DATA_FAKE_DEATH, me->GetEntry());
+ damage = 0;
+ }
+ else if (events.IsInPhase(PHASE_TWO) && !_enraged && me->HealthBelowPct(10))
+ {
+ DoCastSelf(SPELL_FRENZY);
+ Talk(TALK_FRENZY);
+ _enraged = true;
+ }
+ }
+ void UpdateAI(uint32 diff) override
+ {
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
@@ -140,74 +248,72 @@ class boss_thekal : public CreatureScript
switch (eventId)
{
case EVENT_MORTALCLEAVE:
- DoCastVictim(SPELL_MORTALCLEAVE, true);
+ DoCastVictim(SPELL_MORTALCLEAVE);
events.ScheduleEvent(EVENT_MORTALCLEAVE, 15s, 20s, 0, PHASE_ONE);
break;
case EVENT_SILENCE:
- DoCastVictim(SPELL_SILENCE, true);
+ DoCastVictim(SPELL_SILENCE);
events.ScheduleEvent(EVENT_SILENCE, 20s, 25s, 0, PHASE_ONE);
break;
case EVENT_RESURRECT_TIMER:
- // Thekal will transform to Tiger if he died and was not resurrected after 10 seconds.
- if (_wasDead)
+ {
+ // If only one or two of the three mobs were killed, resurrect them
+ _isResurrectTimerActive = false;
+
+ if (_isThekalDead)
{
- DoCast(me, SPELL_TIGER_FORM); // SPELL_AURA_TRANSFORM
- me->SetObjectScale(2.00f);
- me->SetStandState(UNIT_STAND_STATE_STAND);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- /*
- CreatureTemplate const* cinfo = me->GetCreatureTemplate();
- me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 40)));
- me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 40)));
- me->UpdateDamagePhysical(BASE_ATTACK);
- */
- me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageIncrease); // hack
- ResetThreatList();
- events.ScheduleEvent(EVENT_FRENZY, 30s, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_FORCEPUNCH, 4s, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_SPELL_CHARGE, 12s, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_ENRAGE, 32s, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_SUMMONTIGERS, 25s, 0, PHASE_TWO); // Phase 2
- events.SetPhase(PHASE_TWO);
+ DoCastSelf(SPELL_RESURRECT_VISUAL);
+ SetData(DATA_RESURRECTED, me->GetEntry());
+ _isThekalDead = false;
}
- events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10s, 0, PHASE_ONE);
- break;
- case EVENT_CHECK_TIMER:
- // Check_Timer for the death of LorKhan and Zath.
- if (!_wasDead)
+
+ if (_isLorkhanDead)
{
- if (instance->GetBossState(DATA_LORKHAN) == SPECIAL)
+ if (Creature* lorkhan = instance->GetCreature(DATA_LORKHAN))
{
- // Resurrect LorKhan
- if (Creature* lorKhan = instance->GetCreature(DATA_LORKHAN))
- {
- lorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- lorKhan->SetFaction(FACTION_MONSTER);
- lorKhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- lorKhan->SetFullHealth();
- instance->SetData(DATA_LORKHAN, DONE);
- }
+ lorkhan->AI()->DoCastSelf(SPELL_RESURRECT_VISUAL);
+ SetData(DATA_RESURRECTED, lorkhan->GetEntry());
}
+ _isLorkhanDead = false;
+ }
- if (instance->GetBossState(DATA_ZATH) == SPECIAL)
+ if (_isZathDead)
+ {
+ if (Creature* zath = instance->GetCreature(DATA_ZATH))
{
- // Resurrect Zath
- if (Creature* zath = instance->GetCreature(DATA_ZATH))
- {
- zath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- zath->SetFaction(FACTION_MONSTER);
- zath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- zath->SetFullHealth();
- instance->SetBossState(DATA_ZATH, DONE);
- }
+ zath->AI()->DoCastSelf(SPELL_RESURRECT_VISUAL);
+ SetData(DATA_RESURRECTED, zath->GetEntry());
}
+ _isZathDead = false;
}
- events.ScheduleEvent(EVENT_CHECK_TIMER, 5s, 0, PHASE_ONE);
break;
- case EVENT_FRENZY:
- DoCast(me, SPELL_FRENZY);
- events.ScheduleEvent(EVENT_FRENZY, 30s, 0, PHASE_TWO);
+ }
+ case EVENT_CHANGE_PHASE_1:
+ _isChangingPhase = true;
+ me->SetControlled(true, UNIT_STATE_ROOT);
+ me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH);
+ me->SetFullHealth();
+ DoCastSelf(SPELL_RESURRECT_VISUAL);
+ events.ScheduleEvent(EVENT_CHANGE_PHASE_2, 1s);
+ break;
+ case EVENT_CHANGE_PHASE_2:
+ Talk(TALK_TIGER_PHASE);
+ events.ScheduleEvent(EVENT_CHANGE_PHASE_3, 1s);
break;
+ case EVENT_CHANGE_PHASE_3:
+ {
+ // Trigger phase change
+ _isChangingPhase = false;
+ DoCastSelf(SPELL_TIGER_FORM);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ me->ApplyStatPctModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, DamageIncrease);
+ ResetThreatList();
+ me->SetControlled(false, UNIT_STATE_ROOT);
+ events.ScheduleEvent(EVENT_FORCEPUNCH, 4s, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_SPELL_CHARGE, 12s, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_SUMMONTIGERS, 25s, 0, PHASE_TWO);
+ events.SetPhase(PHASE_TWO);
+ }
case EVENT_FORCEPUNCH:
DoCastVictim(SPELL_FORCEPUNCH, true);
events.ScheduleEvent(EVENT_FORCEPUNCH, 16s, 21s, 0, PHASE_TWO);
@@ -221,14 +327,6 @@ class boss_thekal : public CreatureScript
}
events.ScheduleEvent(EVENT_CHARGE, 15s, 22s, 0, PHASE_TWO);
break;
- case EVENT_ENRAGE:
- if (HealthBelowPct(11) && !_enraged)
- {
- DoCast(me, SPELL_ENRAGE);
- _enraged = true;
- }
- events.ScheduleEvent(EVENT_ENRAGE, 30s);
- break;
case EVENT_SUMMONTIGERS:
DoCastVictim(SPELL_SUMMONTIGERS, true);
events.ScheduleEvent(EVENT_SUMMONTIGERS, 10s, 14s, 0, PHASE_TWO);
@@ -236,32 +334,24 @@ class boss_thekal : public CreatureScript
default:
break;
}
+ }
- if (me->IsFullHealth() && _wasDead)
- _wasDead = false;
-
- if ((events.IsInPhase(PHASE_ONE)) && !_wasDead && !HealthAbovePct(5))
- {
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetStandState(UNIT_STAND_STATE_SLEEP);
- me->AttackStop();
- instance->SetBossState(DATA_THEKAL, SPECIAL);
- _wasDead = true;
- }
+ if (_isChangingPhase)
+ return;
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
- }
+ if (!UpdateVictim())
+ return;
DoMeleeAttackIfReady();
}
private:
bool _enraged;
- bool _wasDead;
+ bool _isThekalDead;
+ bool _isLorkhanDead;
+ bool _isZathDead;
+ bool _isResurrectTimerActive;
+ bool _isChangingPhase;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -270,147 +360,135 @@ class boss_thekal : public CreatureScript
}
};
+enum LorkhanEvents
+{
+ EVENT_SHIELD = 1,
+ EVENT_BLOODLUST,
+ EVENT_GREATER_HEAL,
+ EVENT_DISARM
+};
+
+// Find which one of the three creatures Lor'Khan should heal (self, Thekal or Zath)
+class LorKhanSelectTargetToHeal
+{
+ public:
+ LorKhanSelectTargetToHeal(Unit const* obj, float range) : i_obj(obj), i_range(range), i_hp(0) { }
+
+ bool operator()(Unit* u)
+ {
+ if (u->GetTypeId() != TYPEID_UNIT || !u->IsAlive() || !u->IsInCombat())
+ return false;
+
+ if (u->ToCreature()->GetEntry() != NPC_HIGH_PRIEST_THEKAL && u->GetEntry() != NPC_ZEALOT_LORKHAN && u->GetEntry() != NPC_ZEALOT_ZATH)
+ return false;
+
+ // Don't allow to heal a target that is waiting for resurrection
+ if (u->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
+ return false;
+
+ if ((u->GetMaxHealth() - u->GetHealth() > i_hp) && i_obj->IsWithinDistInMap(u, i_range))
+ {
+ i_hp = u->GetMaxHealth() - u->GetHealth();
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ Unit const* i_obj;
+ float i_range;
+ uint32 i_hp;
+};
+
// Zealot Lor'Khan
class npc_zealot_lorkhan : public CreatureScript
{
- public: npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { }
+public: npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { }
struct npc_zealot_lorkhanAI : public ScriptedAI
{
- npc_zealot_lorkhanAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript())
+ npc_zealot_lorkhanAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }
+
+ void Reset() override
{
- Initialize();
+ _events.Reset();
+ me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
}
- void Initialize()
+ void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
- _shieldTimer = 1000;
- _bloodLustTimer = 16000;
- _greaterHealTimer = 32000;
- _disarmTimer = 6000;
- _checkTimer = 10000;
- _fakeDeath = false;
+ if (damage >= me->GetHealth())
+ {
+ Talk(TALK_FAKE_DEATH);
+ me->RemoveAllAuras();
+ me->SetImmuneToPC(true, true);
+ me->SetImmuneToNPC(true, true);
+ DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true);
+ me->AttackStop();
+ if (Creature* thekal = _instance->GetCreature(DATA_THEKAL))
+ thekal->AI()->SetData(DATA_FAKE_DEATH, me->GetEntry());
+ _events.DelayEvents(10s);
+ damage = 0;
+ }
}
- void Reset() override
+ void JustEngagedWith(Unit* /*who*/) override
{
- Initialize();
-
- _instance->SetBossState(DATA_LORKHAN, NOT_STARTED);
- me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ _events.ScheduleEvent(EVENT_SHIELD, 1s);
+ _events.ScheduleEvent(EVENT_BLOODLUST, 16s);
+ _events.ScheduleEvent(EVENT_GREATER_HEAL, 32s);
+ _events.ScheduleEvent(EVENT_DISARM, 6s);
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
- return;
+ _events.Update(diff);
- if (_shieldTimer <= diff)
- {
- DoCast(me, SPELL_SHIELD);
- _shieldTimer = 61000;
- }
- else
- _shieldTimer -= diff;
-
- if (_bloodLustTimer <= diff)
- {
- DoCast(me, SPELL_BLOODLUST);
- _bloodLustTimer = 20000 + rand32() % 8000;
- }
- else
- _bloodLustTimer -= diff;
-
- // Casting Greaterheal to Thekal or Zath if they are in meele range.
- if (_greaterHealTimer <= diff)
- {
- Creature* thekal = _instance->GetCreature(DATA_THEKAL);
- Creature* zath = _instance->GetCreature(DATA_ZATH);
- if (!thekal && !zath)
- return;
-
- bool roll = roll_chance_i(50);
- Creature* target = roll ? (thekal ? thekal : zath) : (zath ? zath : thekal);
- // As we check already above to ensure at least 1 out of Thekal/Zath is not null, target must be not null
- ASSERT(target);
-
- if (!me->IsWithinMeleeRange(target))
- target = roll ? zath : thekal;
-
- if (me->IsWithinMeleeRange(target))
- DoCast(target, SPELL_GREATERHEAL);
-
- _greaterHealTimer = 15000 + rand32() % 5000;
- }
- else
- _greaterHealTimer -= diff;
-
- if (_disarmTimer <= diff)
- {
- DoCastVictim(SPELL_DISARM);
- _disarmTimer = 15000 + rand32() % 10000;
- }
- else
- _disarmTimer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- // Check for the death of LorKhan and Zath.
- if (!_fakeDeath && _checkTimer <= diff)
+ if (uint32 eventId = _events.ExecuteEvent())
{
- if (_instance->GetBossState(DATA_THEKAL) == SPECIAL)
+ switch (eventId)
{
- // Resurrect Thekal
- if (Creature* thekal = _instance->GetCreature(DATA_THEKAL))
+ case EVENT_SHIELD:
+ DoCastSelf(SPELL_SHIELD);
+ _events.ScheduleEvent(EVENT_SHIELD, 61s);
+ break;
+ case EVENT_BLOODLUST:
+ DoCastSelf(SPELL_BLOODLUST);
+ _events.ScheduleEvent(EVENT_BLOODLUST, 20s, 28s);
+ break;
+ case EVENT_GREATER_HEAL:
{
- thekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- thekal->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- thekal->SetFaction(FACTION_MONSTER);
- thekal->SetFullHealth();
- }
- }
+ Unit* target = nullptr;
+ LorKhanSelectTargetToHeal check(me, 100.0f);
+ Trinity::UnitLastSearcher<LorKhanSelectTargetToHeal> searcher(me, target, check);
+ Cell::VisitAllObjects(me, searcher, 100.0f);
- if (_instance->GetBossState(DATA_ZATH) == SPECIAL)
- {
- // Resurrect Zath
- if (Creature* zath = _instance->GetCreature(DATA_ZATH))
- {
- zath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- zath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- zath->SetFaction(FACTION_MONSTER);
- zath->SetFullHealth();
+ if (target)
+ DoCast(target, SPELL_GREATERHEAL);
+
+ _events.ScheduleEvent(EVENT_GREATER_HEAL, 15s, 20s);
+ break;
}
+ case EVENT_DISARM:
+ DoCastVictim(SPELL_DISARM);
+ _events.ScheduleEvent(EVENT_DISARM, 15s, 25s);
+ break;
}
-
- _checkTimer = 5000;
}
- else
- _checkTimer -= diff;
- if (!HealthAbovePct(5))
- {
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetStandState(UNIT_STAND_STATE_SLEEP);
- me->SetFaction(FACTION_FRIENDLY);
- me->AttackStop();
-
- _instance->SetBossState(DATA_LORKHAN, SPECIAL);
-
- _fakeDeath = true;
- }
+ if (!UpdateVictim())
+ return;
DoMeleeAttackIfReady();
}
private:
- uint32 _shieldTimer;
- uint32 _bloodLustTimer;
- uint32 _greaterHealTimer;
- uint32 _disarmTimer;
- uint32 _checkTimer;
- bool _fakeDeath;
+ EventMap _events;
InstanceScript* _instance;
};
@@ -420,6 +498,15 @@ class npc_zealot_lorkhan : public CreatureScript
}
};
+enum ZathEvents
+{
+ EVENT_SWEEPING_STRIKES = 1,
+ EVENT_SINISTER_STRIKE,
+ EVENT_GOUGE,
+ EVENT_KICK,
+ EVENT_BLIND,
+};
+
// Zealot Zath
class npc_zealot_zath : public CreatureScript
{
@@ -428,139 +515,82 @@ class npc_zealot_zath : public CreatureScript
struct npc_zealot_zathAI : public ScriptedAI
{
- npc_zealot_zathAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript())
+ npc_zealot_zathAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }
+
+ void Reset() override
{
- Initialize();
+ _events.Reset();
+ me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
}
- void Initialize()
+ void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
- _sweepingStrikesTimer = 13000;
- _sinisterStrikeTimer = 8000;
- _gougeTimer = 25000;
- _kickTimer = 18000;
- _blindTimer = 5000;
- _checkTimer = 10000;
- _fakeDeath = false;
+ if (damage >= me->GetHealth())
+ {
+ Talk(TALK_FAKE_DEATH);
+ me->RemoveAllAuras();
+ me->SetImmuneToPC(true, true);
+ me->SetImmuneToNPC(true, true);
+ DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true);
+ me->AttackStop();
+ if (Creature* thekal = _instance->GetCreature(DATA_THEKAL))
+ thekal->AI()->SetData(DATA_FAKE_DEATH, me->GetEntry());
+ _events.DelayEvents(10s);
+ damage = 0;
+ }
}
- void Reset() override
+ void JustEngagedWith(Unit* /*who*/) override
{
- Initialize();
-
- _instance->SetBossState(DATA_ZATH, NOT_STARTED);
-
- me->SetStandState(UNIT_STAND_STATE_STAND);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ _events.ScheduleEvent(EVENT_SWEEPING_STRIKES, 13s);
+ _events.ScheduleEvent(EVENT_SINISTER_STRIKE, 8s);
+ _events.ScheduleEvent(EVENT_GOUGE, 25s);
+ _events.ScheduleEvent(EVENT_KICK, 18s);
+ _events.ScheduleEvent(EVENT_BLIND, 5s);
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
- return;
-
- if (_sweepingStrikesTimer <= diff)
- {
- DoCastVictim(SPELL_SWEEPINGSTRIKES);
- _sweepingStrikesTimer = 22000 + rand32() % 4000;
- }
- else
- _sweepingStrikesTimer -= diff;
-
- if (_sinisterStrikeTimer <= diff)
- {
- DoCastVictim(SPELL_SINISTERSTRIKE);
- _sinisterStrikeTimer = 8000 + rand32() % 8000;
- }
- else
- _sinisterStrikeTimer -= diff;
-
- if (_gougeTimer <= diff)
- {
- DoCastVictim(SPELL_GOUGE);
-
- if (GetThreat(me->GetVictim()))
- ModifyThreatByPercent(me->GetVictim(), -100);
-
- _gougeTimer = 17000 + rand32() % 10000;
- }
- else
- _gougeTimer -= diff;
-
- if (_kickTimer <= diff)
- {
- DoCastVictim(SPELL_KICK);
- _kickTimer = 15000 + rand32() % 10000;
- }
- else
- _kickTimer -= diff;
-
- if (_blindTimer <= diff)
- {
- DoCastVictim(SPELL_BLIND);
- _blindTimer = 10000 + rand32() % 10000;
- }
- else
- _blindTimer -= diff;
+ _events.Update(diff);
- // Check for the death of LorKhan and Thekal.
- if (!_fakeDeath && _checkTimer <= diff)
+ if (uint32 eventId = _events.ExecuteEvent())
{
- if (_instance->GetBossState(DATA_LORKHAN) == SPECIAL)
- {
- // Resurrect LorKhan
- if (Creature* lorkhan = _instance->GetCreature(DATA_LORKHAN))
- {
- lorkhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- lorkhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- lorkhan->SetFaction(14);
- lorkhan->SetFullHealth();
- }
- }
-
- if (_instance->GetBossState(DATA_THEKAL) == SPECIAL)
+ switch (eventId)
{
- // Resurrect Thekal
- if (Creature* thekal = _instance->GetCreature(DATA_THEKAL))
- {
- thekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- thekal->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- thekal->SetFaction(FACTION_MONSTER);
- thekal->SetFullHealth();
- }
+ case EVENT_SWEEPING_STRIKES:
+ DoCastVictim(SPELL_SWEEPINGSTRIKES);
+ _events.ScheduleEvent(EVENT_SWEEPING_STRIKES, 22s, 26s);
+ break;
+ case EVENT_SINISTER_STRIKE:
+ DoCastVictim(SPELL_SINISTERSTRIKE);
+ _events.ScheduleEvent(EVENT_SINISTER_STRIKE, 8s, 16s);
+ break;
+ case EVENT_GOUGE:
+ DoCastVictim(SPELL_GOUGE);
+ if (GetThreat(me->GetVictim()))
+ ModifyThreatByPercent(me->GetVictim(), -100);
+ _events.ScheduleEvent(EVENT_GOUGE, 17s, 27s);
+ break;
+ case EVENT_KICK:
+ DoCastVictim(SPELL_KICK);
+ _events.ScheduleEvent(EVENT_KICK, 15s, 25s);
+ break;
+ case EVENT_BLIND:
+ DoCastVictim(SPELL_BLIND);
+ _events.ScheduleEvent(EVENT_BLIND, 10s, 20s);
+ break;
}
-
- _checkTimer = 5000;
}
- else
- _checkTimer -= diff;
- if (!HealthAbovePct(5))
- {
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetStandState(UNIT_STAND_STATE_SLEEP);
- me->SetFaction(35);
- me->AttackStop();
-
- _instance->SetBossState(DATA_ZATH, SPECIAL);
-
- _fakeDeath = true;
- }
+ if (!UpdateVictim())
+ return;
DoMeleeAttackIfReady();
}
private:
- uint32 _sweepingStrikesTimer;
- uint32 _sinisterStrikeTimer;
- uint32 _gougeTimer;
- uint32 _kickTimer;
- uint32 _blindTimer;
- uint32 _checkTimer;
- bool _fakeDeath;
+ EventMap _events;
InstanceScript* _instance;
};