diff --git a/data/sql/updates/pending_db_world/rev_1654045846799908700.sql b/data/sql/updates/pending_db_world/rev_1654045846799908700.sql
new file mode 100644
index 000000000..cfe478d94
--- /dev/null
+++ b/data/sql/updates/pending_db_world/rev_1654045846799908700.sql
@@ -0,0 +1,9 @@
+--
+DELETE FROM `creature_text` WHERE `CreatureID` IN (11347, 11348) AND `GroupId` = 0;
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration` ,`Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(11347, 0, 0, '%s dies.', 16, 0, 100, 0, 0, 0, 8251, 3, ''),
+(11348, 0, 0, '%s dies.', 16, 0, 100, 0, 0, 0, 8251, 3, '');
+
+DELETE FROM `creature_text` WHERE `CreatureID` = 14509 AND `GroupId` = 2;
+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, 8251, 3, '');
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 5ae1f8768..5215bbb05 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -387,6 +387,8 @@ public:
void ModifyThreatPercentTemp(Unit* victim, int32 percent, Milliseconds duration);
+ void ResetFaction() { SetFaction(GetCreatureTemplate()->faction); }
+
protected:
bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 Entry, uint32 vehId, const CreatureData* data = nullptr);
bool InitEntry(uint32 entry, const CreatureData* data = nullptr);
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
index 58c0a87fc..e049edcc5 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
@@ -15,34 +15,32 @@
* with this program. If not, see .
*/
-/* ScriptData
-SDName: Boss_Thekal
-SD%Complete: 95
-SDComment: Almost finished.
-SDCategory: Zul'Gurub
-EndScriptData */
-
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "TaskScheduler.h"
#include "zulgurub.h"
enum Says
{
SAY_AGGRO = 0,
- SAY_DEATH = 1
+ SAY_DEATH = 1,
+
+ EMOTE_ZEALOT_DIES = 0,
+ EMOTE_THEKAL_DIES = 2
};
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
+ SPELL_MORTALCLEAVE = 22859,
+ SPELL_SILENCE = 22666,
+ SPELL_TIGER_FORM = 24169,
+ SPELL_RESURRECT = 24173,
+ SPELL_FRENZY = 8269,
+ SPELL_FORCEPUNCH = 24189,
+ SPELL_CHARGE = 24193,
+ SPELL_ENRAGE = 8269,
+ SPELL_SUMMONTIGERS = 24183,
+
// Zealot Lor'Khan Spells
SPELL_SHIELD = 20545,
SPELL_BLOODLUST = 24185,
@@ -56,23 +54,9 @@ enum Spells
SPELL_BLIND = 21060
};
-enum Events
+enum Actions
{
- 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
-};
-
-enum Phases
-{
- PHASE_ONE = 1,
- PHASE_TWO = 2
+ ACTION_RESSURRECT = 1
};
class boss_thekal : public CreatureScript
@@ -82,170 +66,240 @@ public:
struct boss_thekalAI : public BossAI
{
- boss_thekalAI(Creature* creature) : BossAI(creature, DATA_THEKAL) { }
+ boss_thekalAI(Creature* creature) : BossAI(creature, DATA_THEKAL)
+ {
+ Initialize();
+ }
bool Enraged;
bool WasDead;
- void Reset() override
+ void Initialize()
{
- if (events.IsInPhase(PHASE_TWO))
- me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
- _Reset();
Enraged = false;
WasDead = false;
+ _lorkhanDied = false;
+ _zathDied = false;
+ }
+
+ void Reset() override
+ {
+ _Reset();
+ Initialize();
+
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
+ if (Creature* zealot = instance->GetCreature(DATA_LORKHAN))
+ {
+ zealot->AI()->Reset();
+ zealot->ResetFaction();
+ }
+
+ if (Creature* zealot = instance->GetCreature(DATA_ZATH))
+ {
+ zealot->AI()->Reset();
+ zealot->ResetFaction();
+ }
+
+ // TODO: do this in formations, once a flag is added to prevent leaders from respawning as well.
+ std::list creatureList;
+ GetCreatureListWithEntryInGrid(creatureList, me, NPC_ZULGURUB_TIGER, 15.0f);
+
+ if (_catGuids.empty())
+ {
+ for (Creature* creature : creatureList)
+ {
+ _catGuids.push_back(creature->GetGUID());
+ if (!creature->IsAlive())
+ {
+ creature->Respawn(true);
+ }
+ }
+ }
+ else
+ {
+ for (ObjectGuid guid : _catGuids)
+ {
+ if (Creature* creature = ObjectAccessor::GetCreature(*me, guid))
+ {
+ if (!creature->IsAlive())
+ {
+ creature->Respawn(true);
+ }
+ }
+ }
+ }
+
+ _scheduler.SetValidator([this]
+ {
+ return !me->HasUnitState(UNIT_STATE_CASTING);
+ });
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
+
+ if (Creature* zealot = instance->GetCreature(DATA_LORKHAN))
+ {
+ zealot->Kill(zealot, zealot);
+ }
+
+ if (Creature* zealot = instance->GetCreature(DATA_ZATH))
+ {
+ zealot->Kill(zealot, zealot);
+ }
}
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- events.ScheduleEvent(EVENT_MORTALCLEAVE, 4000, 0, PHASE_ONE); // Phase 1
- events.ScheduleEvent(EVENT_SILENCE, 9000, 0, PHASE_ONE); // Phase 1
- events.ScheduleEvent(EVENT_CHECK_TIMER, 10000, 0, PHASE_ONE); // Phase 1
- events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10000, 0, PHASE_ONE); // Phase 1
- Talk(SAY_AGGRO);
+
+ _scheduler.CancelAll();
+ _scheduler.Schedule(4s, [this](TaskContext context) {
+ DoCastVictim(SPELL_MORTALCLEAVE);
+ context.Repeat(15s, 20s);
+ }).Schedule(9s, [this](TaskContext context) {
+ DoCastVictim(SPELL_SILENCE);
+ context.Repeat(20s, 25s);
+ }).Schedule(16s, [this](TaskContext context) {
+ DoCastSelf(SPELL_BLOODLUST);
+ context.Repeat(20s, 28s);
+ });
}
- void JustReachedHome() override
+ void SetData(uint32 /*type*/, uint32 data) override
{
- instance->SetBossState(DATA_THEKAL, NOT_STARTED);
+ UpdateZealotStatus(data, true);
+ CheckPhaseTransition();
+
+ _scheduler.Schedule(10s, [this, data](TaskContext /*context*/) {
+ if ((!_lorkhanDied || !_zathDied) && !WasDead)
+ {
+ ReviveZealot(data);
+ }
+ });
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
+ {
+ if (!WasDead && damage >= me->GetHealth())
+ {
+ damage = me->GetHealth() - 1;
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->SetReactState(REACT_PASSIVE);
+ me->SetStandState(UNIT_STAND_STATE_SLEEP);
+ me->AttackStop();
+ WasDead = true;
+ CheckPhaseTransition();
+ Talk(EMOTE_THEKAL_DIES);
+ }
+
+ if (!Enraged && me->HealthBelowPctDamaged(20, damage) && me->GetEntry() != NPC_HIGH_PRIEST_THEKAL)
+ {
+ DoCastSelf(SPELL_ENRAGE);
+ Enraged = true;
+ }
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_RESSURRECT)
+ {
+ me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->ResetFaction();
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->SetFullHealth();
+ WasDead = false;
+ }
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
+ if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim())
return;
- events.Update(diff);
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_MORTALCLEAVE:
- DoCastVictim(SPELL_MORTALCLEAVE, true);
- events.ScheduleEvent(EVENT_MORTALCLEAVE, urand(15000, 20000), 0, PHASE_ONE);
- break;
- case EVENT_SILENCE:
- DoCastVictim(SPELL_SILENCE, true);
- events.ScheduleEvent(EVENT_SILENCE, urand(20000, 25000), 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)
- {
- DoCast(me, SPELL_TIGER_FORM); // SPELL_AURA_TRANSFORM
- me->SetObjectScale(2.00f);
- me->SetStandState(UNIT_STAND_STATE_STAND);
- me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- /*
- const CreatureTemplate* 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->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 40.0f, true); // hack
- DoResetThreat();
- events.ScheduleEvent(EVENT_FRENZY, 30000, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_FORCEPUNCH, 4000, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_SPELL_CHARGE, 12000, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_ENRAGE, 32000, 0, PHASE_TWO); // Phase 2
- events.ScheduleEvent(EVENT_SUMMONTIGERS, 25000, 0, PHASE_TWO); // Phase 2
- events.SetPhase(PHASE_TWO);
- }
- events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10000, 0, PHASE_ONE);
- break;
- case EVENT_CHECK_TIMER:
- //Check_Timer for the death of LorKhan and Zath.
- if (!WasDead)
- {
- if (instance->GetBossState(DATA_LORKHAN) == SPECIAL)
- {
- //Resurrect LorKhan
- if (Unit* pLorKhan = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_LORKHAN)))
- {
- pLorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pLorKhan->SetFaction(FACTION_MONSTER);
- pLorKhan->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- pLorKhan->SetFullHealth();
- instance->SetData(DATA_LORKHAN, DONE);
- }
- }
-
- if (instance->GetBossState(DATA_ZATH) == SPECIAL)
- {
- //Resurrect Zath
- if (Unit* pZath = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_ZATH)))
- {
- pZath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pZath->SetFaction(FACTION_MONSTER);
- pZath->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- pZath->SetFullHealth();
- instance->SetBossState(DATA_ZATH, DONE);
- }
- }
- }
- events.ScheduleEvent(EVENT_CHECK_TIMER, 5000, 0, PHASE_ONE);
- break;
- case EVENT_FRENZY:
- DoCast(me, SPELL_FRENZY);
- events.ScheduleEvent(EVENT_FRENZY, 30000, 0, PHASE_TWO);
- break;
- case EVENT_FORCEPUNCH:
- DoCastVictim(SPELL_FORCEPUNCH, true);
- events.ScheduleEvent(EVENT_FORCEPUNCH, urand(16000, 21000), 0, PHASE_TWO);
- break;
- case EVENT_CHARGE:
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- {
- DoCast(target, SPELL_CHARGE);
- DoResetThreat();
- AttackStart(target);
- }
- events.ScheduleEvent(EVENT_CHARGE, urand(15000, 22000), 0, PHASE_TWO);
- break;
- case EVENT_ENRAGE:
- if (HealthBelowPct(11) && !Enraged)
- {
- DoCast(me, SPELL_ENRAGE);
- Enraged = true;
- }
- events.ScheduleEvent(EVENT_ENRAGE, 30000);
- break;
- case EVENT_SUMMONTIGERS:
- DoCastVictim(SPELL_SUMMONTIGERS, true);
- events.ScheduleEvent(EVENT_SUMMONTIGERS, urand(10000, 14000), 0, PHASE_TWO);
- break;
- 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->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->SetStandState(UNIT_STAND_STATE_SLEEP);
- me->AttackStop();
- instance->SetBossState(DATA_THEKAL, SPECIAL);
- WasDead = true;
- }
- }
- DoMeleeAttackIfReady();
+ _scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
+
+ void ReviveZealot(uint32 zealotData)
+ {
+ if (Creature* zealot = instance->GetCreature(zealotData))
+ {
+ zealot->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
+ zealot->ResetFaction();
+ zealot->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ zealot->SetReactState(REACT_AGGRESSIVE);
+ zealot->SetFullHealth();
+ UpdateZealotStatus(zealotData, false);
+ }
+ }
+
+ void UpdateZealotStatus(uint32 data, bool dead)
+ {
+ if (data == DATA_LORKHAN)
+ {
+ _lorkhanDied = dead;
+ }
+ else if (data == DATA_ZATH)
+ {
+ _zathDied = dead;
+ }
+ }
+
+ void CheckPhaseTransition()
+ {
+ if (WasDead && _lorkhanDied && _zathDied)
+ {
+ _scheduler.Schedule(3s, [this](TaskContext /*context*/) {
+ Talk(SAY_AGGRO);
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ DoResetThreat();
+
+ _scheduler.Schedule(6s, [this](TaskContext /*context*/) {
+ DoCastSelf(SPELL_TIGER_FORM);
+ me->SetReactState(REACT_AGGRESSIVE);
+
+ _scheduler.Schedule(30s, [this](TaskContext context) {
+ DoCastSelf(SPELL_FRENZY);
+ context.Repeat();
+ }).Schedule(4s, [this](TaskContext context) {
+ DoCastVictim(SPELL_FORCEPUNCH);
+ context.Repeat(16s, 21s);
+ }).Schedule(12s, [this](TaskContext context) {
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
+ {
+ DoCast(target, SPELL_CHARGE);
+ DoResetThreat();
+ AttackStart(target);
+ }
+ context.Repeat(15s, 22s);
+ }).Schedule(25s, [this](TaskContext context) {
+ DoCastVictim(SPELL_SUMMONTIGERS, true);
+ context.Repeat(10s, 14s);
+ });
+ });
+ });
+ }
+ else
+ {
+ _scheduler.Schedule(10s, [this](TaskContext /*context*/) {
+ DoAction(ACTION_RESSURRECT);
+ });
+ }
+ }
+
+ private:
+ TaskScheduler _scheduler;
+ GuidVector _catGuids;
+ bool _lorkhanDied;
+ bool _zathDied;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -254,7 +308,6 @@ public:
}
};
-//Zealot Lor'Khan
class npc_zealot_lorkhan : public CreatureScript
{
public:
@@ -267,138 +320,86 @@ public:
instance = creature->GetInstanceScript();
}
- uint32 Shield_Timer;
- uint32 BloodLust_Timer;
- uint32 GreaterHeal_Timer;
- uint32 Disarm_Timer;
- uint32 Check_Timer;
-
- bool FakeDeath;
-
InstanceScript* instance;
void Reset() override
{
- Shield_Timer = 1000;
- BloodLust_Timer = 16000;
- GreaterHeal_Timer = 32000;
- Disarm_Timer = 6000;
- Check_Timer = 10000;
-
- FakeDeath = false;
-
- instance->SetBossState(DATA_LORKHAN, NOT_STARTED);
-
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->SetReactState(REACT_AGGRESSIVE);
+
+ _scheduler.CancelAll();
+
+ _scheduler.SetValidator([this]
+ {
+ return !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE);
+ });
}
void EnterCombat(Unit* /*who*/) override
{
+ _scheduler.Schedule(1s, [this](TaskContext context) {
+ DoCastSelf(SPELL_SHIELD);
+ context.Repeat(1min);
+ }).Schedule(32s, [this](TaskContext context) {
+ Unit* thekal = instance->GetCreature(DATA_THEKAL);
+ Unit* zath = instance->GetCreature(DATA_ZATH);
+
+ if (!thekal || !zath)
+ return;
+
+ if ((me->GetHealthPct() <= thekal->GetHealthPct()) || (me->GetHealthPct() <= zath->GetHealthPct()))
+ {
+ DoCastSelf(SPELL_GREATERHEAL);
+ }
+ else if (zath->GetHealthPct() <= thekal->GetHealthPct())
+ {
+ DoCast(zath, SPELL_GREATERHEAL);
+ }
+ else
+ {
+ DoCast(thekal, SPELL_GREATERHEAL);
+ }
+
+ context.Repeat(15s, 20s);
+ }).Schedule(6s, [this](TaskContext context) {
+ DoCastVictim(SPELL_DISARM);
+ context.Repeat(15s, 25s);
+ });
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
+ {
+ if (damage >= me->GetHealth() && me->HasReactState(REACT_AGGRESSIVE))
+ {
+ Talk(EMOTE_ZEALOT_DIES);
+ me->RemoveAllAuras();
+ me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->SetStandState(UNIT_STAND_STATE_SLEEP);
+ me->SetReactState(REACT_PASSIVE);
+ me->InterruptNonMeleeSpells(false);
+ me->AttackStop();
+
+ damage = 0;
+
+ if (Creature* thekal = instance->GetCreature(DATA_THEKAL))
+ {
+ thekal->AI()->SetData(ACTION_RESSURRECT, DATA_LORKHAN);
+ }
+ }
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
+ if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim())
return;
- //Shield_Timer
- if (Shield_Timer <= diff)
- {
- DoCast(me, SPELL_SHIELD);
- Shield_Timer = 61000;
- }
- else Shield_Timer -= diff;
-
- //BloodLust_Timer
- if (BloodLust_Timer <= diff)
- {
- DoCast(me, SPELL_BLOODLUST);
- BloodLust_Timer = 20000 + rand() % 8000;
- }
- else BloodLust_Timer -= diff;
-
- //Casting Greaterheal to Thekal or Zath if they are in meele range.
- if (GreaterHeal_Timer <= diff)
- {
- Unit* pThekal = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THEKAL));
- Unit* pZath = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_ZATH));
-
- if (!pThekal || !pZath)
- return;
-
- switch (urand(0, 1))
- {
- case 0:
- if (me->IsWithinMeleeRange(pThekal))
- DoCast(pThekal, SPELL_GREATERHEAL);
- break;
- case 1:
- if (me->IsWithinMeleeRange(pZath))
- DoCast(pZath, SPELL_GREATERHEAL);
- break;
- }
-
- GreaterHeal_Timer = 15000 + rand() % 5000;
- }
- else GreaterHeal_Timer -= diff;
-
- //Disarm_Timer
- if (Disarm_Timer <= diff)
- {
- DoCastVictim(SPELL_DISARM);
- Disarm_Timer = 15000 + rand() % 10000;
- }
- else Disarm_Timer -= diff;
-
- //Check_Timer for the death of LorKhan and Zath.
- if (!FakeDeath && Check_Timer <= diff)
- {
- if (instance->GetBossState(DATA_THEKAL) == SPECIAL)
- {
- //Resurrect Thekal
- if (Unit* pThekal = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THEKAL)))
- {
- pThekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pThekal->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- pThekal->SetFaction(FACTION_MONSTER);
- pThekal->SetFullHealth();
- }
- }
-
- if (instance->GetBossState(DATA_ZATH) == SPECIAL)
- {
- //Resurrect Zath
- if (Unit* pZath = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_ZATH)))
- {
- pZath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pZath->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- pZath->SetFaction(FACTION_MONSTER);
- pZath->SetFullHealth();
- }
- }
-
- Check_Timer = 5000;
- }
- else Check_Timer -= diff;
-
- if (!HealthAbovePct(5))
- {
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
- me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->SetStandState(UNIT_STAND_STATE_SLEEP);
- me->SetFaction(FACTION_FRIENDLY);
- me->AttackStop();
-
- instance->SetBossState(DATA_LORKHAN, SPECIAL);
-
- FakeDeath = true;
- }
-
- DoMeleeAttackIfReady();
+ _scheduler.Update(diff,
+ std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
}
+
+ private:
+ TaskScheduler _scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -407,14 +408,10 @@ public:
}
};
-//Zealot Zath
class npc_zealot_zath : public CreatureScript
{
public:
- npc_zealot_zath()
- : CreatureScript("npc_zealot_zath")
- {
- }
+ npc_zealot_zath() : CreatureScript("npc_zealot_zath") { }
struct npc_zealot_zathAI : public ScriptedAI
{
@@ -423,135 +420,82 @@ public:
instance = creature->GetInstanceScript();
}
- uint32 SweepingStrikes_Timer;
- uint32 SinisterStrike_Timer;
- uint32 Gouge_Timer;
- uint32 Kick_Timer;
- uint32 Blind_Timer;
- uint32 Check_Timer;
-
- bool FakeDeath;
-
InstanceScript* instance;
void Reset() override
{
- SweepingStrikes_Timer = 13000;
- SinisterStrike_Timer = 8000;
- Gouge_Timer = 25000;
- Kick_Timer = 18000;
- Blind_Timer = 5000;
- Check_Timer = 10000;
-
- FakeDeath = false;
-
- instance->SetBossState(DATA_ZATH, NOT_STARTED);
-
me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->SetReactState(REACT_AGGRESSIVE);
+
+ _scheduler.CancelAll();
+
+ _scheduler.SetValidator([this]
+ {
+ return !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE);
+ });
}
void EnterCombat(Unit* /*who*/) override
{
+ _scheduler.Schedule(13s, [this](TaskContext context) {
+ DoCastSelf(SPELL_SWEEPINGSTRIKES);
+ context.Repeat(1min);
+ }).Schedule(16s, [this](TaskContext context) {
+ DoCastSelf(SPELL_BLOODLUST);
+ context.Repeat(22s, 26s);
+ }).Schedule(8s, [this](TaskContext context) {
+ DoCastVictim(SPELL_SINISTERSTRIKE);
+ context.Repeat(8s, 16s);
+ }).Schedule(25s, [this](TaskContext context) {
+ DoCastVictim(SPELL_GOUGE);
+
+ if (DoGetThreat(me->GetVictim()))
+ {
+ DoModifyThreatPercent(me->GetVictim(), -100);
+ }
+
+ context.Repeat(17s, 27s);
+ }).Schedule(18s, [this](TaskContext context) {
+ DoCastVictim(SPELL_KICK);
+ context.Repeat(15s, 25s);
+ }).Schedule(5s, [this](TaskContext context) {
+ DoCastVictim(SPELL_BLIND);
+ context.Repeat(10s, 20s);
+ });
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
+ {
+ if (damage >= me->GetHealth() && me->HasReactState(REACT_AGGRESSIVE))
+ {
+ Talk(EMOTE_ZEALOT_DIES);
+ me->RemoveAllAuras();
+ me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->SetStandState(UNIT_STAND_STATE_SLEEP);
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+
+ damage = 0;
+
+ if (Creature* thekal = instance->GetCreature(DATA_THEKAL))
+ {
+ thekal->AI()->SetData(ACTION_RESSURRECT, DATA_ZATH);
+ }
+ }
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
+ if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim())
return;
- //SweepingStrikes_Timer
- if (SweepingStrikes_Timer <= diff)
- {
- DoCastVictim(SPELL_SWEEPINGSTRIKES);
- SweepingStrikes_Timer = 22000 + rand() % 4000;
- }
- else SweepingStrikes_Timer -= diff;
-
- //SinisterStrike_Timer
- if (SinisterStrike_Timer <= diff)
- {
- DoCastVictim(SPELL_SINISTERSTRIKE);
- SinisterStrike_Timer = 8000 + rand() % 8000;
- }
- else SinisterStrike_Timer -= diff;
-
- //Gouge_Timer
- if (Gouge_Timer <= diff)
- {
- DoCastVictim(SPELL_GOUGE);
-
- if (DoGetThreat(me->GetVictim()))
- DoModifyThreatPercent(me->GetVictim(), -100);
-
- Gouge_Timer = 17000 + rand() % 10000;
- }
- else Gouge_Timer -= diff;
-
- //Kick_Timer
- if (Kick_Timer <= diff)
- {
- DoCastVictim(SPELL_KICK);
- Kick_Timer = 15000 + rand() % 10000;
- }
- else Kick_Timer -= diff;
-
- //Blind_Timer
- if (Blind_Timer <= diff)
- {
- DoCastVictim(SPELL_BLIND);
- Blind_Timer = 10000 + rand() % 10000;
- }
- else Blind_Timer -= diff;
-
- //Check_Timer for the death of LorKhan and Zath.
- if (!FakeDeath && Check_Timer <= diff)
- {
- if (instance->GetBossState(DATA_LORKHAN) == SPECIAL)
- {
- //Resurrect LorKhan
- if (Unit* pLorKhan = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_LORKHAN)))
- {
- pLorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pLorKhan->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- pLorKhan->SetFaction(FACTION_MONSTER);
- pLorKhan->SetFullHealth();
- }
- }
-
- if (instance->GetBossState(DATA_THEKAL) == SPECIAL)
- {
- //Resurrect Thekal
- if (Unit* pThekal = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THEKAL)))
- {
- pThekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pThekal->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- pThekal->SetFaction(FACTION_MONSTER);
- pThekal->SetFullHealth();
- }
- }
-
- Check_Timer = 5000;
- }
- else Check_Timer -= diff;
-
- if (!HealthAbovePct(5))
- {
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- me->RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
- me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->SetStandState(UNIT_STAND_STATE_SLEEP);
- me->SetFaction(FACTION_FRIENDLY);
- me->AttackStop();
-
- instance->SetBossState(DATA_ZATH, SPECIAL);
-
- FakeDeath = true;
- }
-
- DoMeleeAttackIfReady();
+ _scheduler.Update(diff,
+ std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
}
+
+ private:
+ TaskScheduler _scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp
index 91c7bd67f..eee9e00cb 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp
@@ -29,7 +29,14 @@ EndScriptData */
DoorData const doorData[] =
{
{ GO_FORCEFIELD, DATA_ARLOKK, DOOR_TYPE_ROOM },
- { 0, 0, DOOR_TYPE_ROOM } // END
+ { 0, 0, DOOR_TYPE_ROOM }
+};
+
+ObjectData const creatureData[] =
+{
+ { NPC_HIGH_PRIEST_THEKAL, DATA_THEKAL },
+ { NPC_ZEALOT_LORKHAN, DATA_LORKHAN },
+ { NPC_ZEALOT_ZATH, DATA_ZATH }
};
class instance_zulgurub : public InstanceMapScript
@@ -43,21 +50,15 @@ public:
{
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
+ LoadObjectData(creatureData, nullptr);
}
void OnCreatureCreate(Creature* creature) override
{
+ InstanceScript::OnCreatureCreate(creature);
+
switch (creature->GetEntry())
{
- case NPC_ZEALOT_LORKHAN:
- _zealotLorkhanGUID = creature->GetGUID();
- break;
- case NPC_ZEALOT_ZATH:
- _zealotZathGUID = creature->GetGUID();
- break;
- case NPC_HIGH_PRIEST_THEKAL:
- _highPriestTekalGUID = creature->GetGUID();
- break;
case NPC_JINDO_THE_HEXXER:
_jindoTheHexxerGUID = creature->GetGUID();
break;
@@ -75,11 +76,10 @@ public:
void OnGameObjectCreate(GameObject* go) override
{
+ InstanceScript::OnGameObjectCreate(go);
+
switch (go->GetEntry())
{
- case GO_FORCEFIELD:
- AddDoor(go, true);
- break;
case GO_GONG_OF_BETHEKK:
_goGongOfBethekkGUID = go->GetGUID();
if (GetBossState(DATA_ARLOKK) == DONE)
@@ -92,28 +92,10 @@ public:
}
}
- void OnGameObjectRemove(GameObject* go) override
- {
- switch (go->GetEntry())
- {
- case GO_FORCEFIELD:
- AddDoor(go, false);
- break;
- default:
- break;
- }
- }
-
ObjectGuid GetGuidData(uint32 uiData) const override
{
switch (uiData)
{
- case DATA_LORKHAN:
- return _zealotLorkhanGUID;
- case DATA_ZATH:
- return _zealotZathGUID;
- case DATA_THEKAL:
- return _highPriestTekalGUID;
case DATA_JINDO:
return _jindoTheHexxerGUID;
case NPC_ARLOKK:
@@ -170,12 +152,9 @@ public:
OUT_LOAD_INST_DATA_COMPLETE;
}
private:
- //If all High Priest bosses were killed. Lorkhan, Zath and Ohgan are added too.
- //Storing Lorkhan, Zath and Thekal because we need to cast on them later. Jindo is needed for healfunction too.
+ // If all High Priest bosses were killed. Ohgan is added too.
+ // Jindo is needed for healfunction.
- ObjectGuid _zealotLorkhanGUID;
- ObjectGuid _zealotZathGUID;
- ObjectGuid _highPriestTekalGUID;
ObjectGuid _jindoTheHexxerGUID;
ObjectGuid _vilebranchSpeakerGUID;
ObjectGuid _arlokkGUID;
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h
index 00a8e20cb..8c44120d1 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h
@@ -58,7 +58,8 @@ enum CreatureIds
NPC_OHGAN = 14988, // Mandokir Event
NPC_VILEBRANCH_SPEAKER = 11391, // Mandokir Event
NPC_CHAINED_SPIRT = 15117, // Mandokir Event
- NPC_HAKKAR = 14834
+ NPC_HAKKAR = 14834,
+ NPC_ZULGURUB_TIGER = 11361
};
enum GameobjectIds