From 896e7c56563f163bc81d7f1b30add8fa26ad306e Mon Sep 17 00:00:00 2001 From: KJack Date: Sat, 11 Nov 2023 02:13:43 -0500 Subject: [PATCH] (Scripts/AI) ZG20: Improvements to Thekal (Tiger Boss) (#17603) Co-authored-by: AG <43139552+AGandrup@users.noreply.github.com> --- .../rev_1698463311896667349.sql | 18 + src/server/game/Spells/SpellInfo.cpp | 1 - .../EasternKingdoms/ZulGurub/boss_thekal.cpp | 810 ++++++++++-------- 3 files changed, 459 insertions(+), 370 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1698463311896667349.sql diff --git a/data/sql/updates/pending_db_world/rev_1698463311896667349.sql b/data/sql/updates/pending_db_world/rev_1698463311896667349.sql new file mode 100644 index 000000000..a72da09e5 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1698463311896667349.sql @@ -0,0 +1,18 @@ +-- Thekal (14509) +-- Update SAY_AGGRO to include emote ONESHOT_ROAR +-- Update comments +UPDATE `creature_text` SET `Emote`=15, `comment`='High Priest Thekal - SAY_AGGRO' WHERE `CreatureID`=14509 AND `GroupID`=0 AND `ID`=0; +UPDATE `creature_text` SET `comment`='High Priest Thekal - SAY_DEATH' WHERE `CreatureID`=14509 AND `GroupID`=1 AND `ID`=0; +UPDATE `creature_text` SET `comment`='High Priest Thekal - EMOTE_DIES' WHERE `CreatureID`=14509 AND `GroupID`=2 AND `ID`=0; + +-- Thekal (14509) +-- Remove static idle (talking) emote +UPDATE `creature_addon` SET `emote`=0 WHERE `guid`=49310; + +-- Zealot Lor'Khan (11347) +-- Update comments +UPDATE `creature_text` SET `comment`='Zealot LorKhan - EMOTE_ZEALOT_DIES' WHERE `CreatureID`=11347 AND `GroupID`=0 AND `ID`=0; + +-- Zealot Zath (11348) +-- Update comments +UPDATE `creature_text` SET `comment`='Zealot Zath - EMOTE_ZEALOT_DIES' WHERE `CreatureID`=11348 AND `GroupID`=0 AND `ID`=0; diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index c310b5e53..0fc3038c8 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1541,7 +1541,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 ea6c77f97..62a5e6ef9 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp @@ -15,6 +15,7 @@ * with this program. If not, see . */ +#include "SharedDefines.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "TaskScheduler.h" @@ -22,36 +23,40 @@ enum Says { - SAY_AGGRO = 0, - SAY_DEATH = 1, + SAY_AGGRO = 0, + SAY_DEATH = 1, + EMOTE_DIES = 2, - EMOTE_ZEALOT_DIES = 0, - EMOTE_THEKAL_DIES = 2 + EMOTE_ZEALOT_DIES = 0 }; enum Spells { - 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, + // Boss - pre-fight + SPELL_SUMMONTIGERS = 24183, + + // Boss + SPELL_CHARGE = 24193, + SPELL_ENRAGE = 8269, + SPELL_FORCEPUNCH = 24189, + SPELL_FRENZY = 8269, + SPELL_MORTALCLEAVE = 22859, + SPELL_RESURRECTION_IMPACT_VISUAL = 24171, + SPELL_SILENCE = 22666, + SPELL_TIGER_FORM = 24169, // Zealot Lor'Khan Spells - SPELL_SHIELD = 20545, - SPELL_BLOODLUST = 24185, - SPELL_GREATERHEAL = 24208, - SPELL_DISARM = 6713, + SPELL_SHIELD = 20545, + SPELL_BLOODLUST = 24185, + SPELL_GREATERHEAL = 24208, + SPELL_DISARM = 6713, + // Zealot Zath Spells - SPELL_SWEEPINGSTRIKES = 18765, - SPELL_SINISTERSTRIKE = 15581, - SPELL_GOUGE = 12540, - SPELL_KICK = 15614, - SPELL_BLIND = 21060 + SPELL_SWEEPINGSTRIKES = 18765, + SPELL_SINISTERSTRIKE = 15581, + SPELL_GOUGE = 12540, + SPELL_KICK = 15614, + SPELL_BLIND = 21060 }; enum Actions @@ -59,399 +64,466 @@ enum Actions ACTION_RESSURRECT = 1 }; -class boss_thekal : public CreatureScript +struct boss_thekal : public BossAI { -public: - boss_thekal() : CreatureScript("boss_thekal") { } - - struct boss_thekalAI : public BossAI + boss_thekal(Creature* creature) : BossAI(creature, DATA_THEKAL) { - boss_thekalAI(Creature* creature) : BossAI(creature, DATA_THEKAL) + Initialize(); + } + + void Initialize() + { + _enraged = false; + _wasDead = false; + _lorkhanDied = false; + _zathDied = false; + } + + void Reset() override + { + _Reset(); + Initialize(); + + scheduler.CancelAll(); + + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveAurasDueToSpell(SPELL_FRENZY); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->LoadEquipment(1, true); + + if (Creature* zealot = instance->GetCreature(DATA_LORKHAN)) { - Initialize(); + zealot->AI()->Reset(); } - void Initialize() + if (Creature* zealot = instance->GetCreature(DATA_ZATH)) { - Enraged = false; - WasDead = false; - _lorkhanDied = false; - _zathDied = false; + zealot->AI()->Reset(); } - void Reset() override + // emote idle loop + scheduler.Schedule(5s, 25s, [this](TaskContext context) { - _Reset(); - Initialize(); + // pick a random emote from the list of available emotes + me->HandleEmoteCommand( + RAND( + EMOTE_ONESHOT_TALK, + EMOTE_ONESHOT_FLEX, + EMOTE_ONESHOT_POINT + ) + ); + context.Repeat(5s, 25s); + }); - me->SetStandState(UNIT_STAND_STATE_STAND); + 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 JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + + 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 SetData(uint32 /*type*/, uint32 data) override + { + UpdateZealotStatus(data, true); + CheckPhaseTransition(); + + scheduler.Schedule(10s, [this, data](TaskContext /*context*/) + { + if (!_lorkhanDied || !_zathDied || !_wasDead) + { + ReviveZealot(data); + } + }); + } + + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageEffectType, SpellSchoolMask spellSchoolMask) override + { + if (!me->HasAura(SPELL_TIGER_FORM) && damage >= me->GetHealth()) + { + damage = me->GetHealth() - 1; + + if (!_wasDead) + { + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_PASSIVE); + me->SetStandState(UNIT_STAND_STATE_DEAD); + me->AttackStop(); + DoResetThreatList(); + _wasDead = true; + CheckPhaseTransition(); + Talk(EMOTE_DIES); + } + } + + BossAI::DamageTaken(attacker, damage, damageEffectType, spellSchoolMask); + } + + void DoAction(int32 action) override + { + if (action == ACTION_RESSURRECT) + { + me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->RestoreFaction(); me->SetReactState(REACT_AGGRESSIVE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->LoadEquipment(1, true); - - if (Creature* zealot = instance->GetCreature(DATA_LORKHAN)) - { - zealot->AI()->Reset(); - } - - if (Creature* zealot = instance->GetCreature(DATA_ZATH)) - { - zealot->AI()->Reset(); - } - - _scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); + me->SetFullHealth(); + _wasDead = false; } + } - void JustDied(Unit* /*killer*/) override + void UpdateAI(uint32 diff) override + { + if (me->IsInCombat() && !UpdateVictim()) { - _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); - } + return; } - - void JustEngagedWith(Unit* /*who*/) override + else if (me->IsInCombat()) { - _JustEngagedWith(); - - _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); - }); + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); } - - void SetData(uint32 /*type*/, uint32 data) override + else { - UpdateZealotStatus(data, true); - CheckPhaseTransition(); + scheduler.Update(diff); + } + } - _scheduler.Schedule(10s, [this, data](TaskContext /*context*/) { - if (!_lorkhanDied || !_zathDied || !WasDead) + void ReviveZealot(uint32 zealotData) + { + if (Creature* zealot = instance->GetCreature(zealotData)) + { + zealot->Respawn(true); + zealot->SetInCombatWithZone(); + 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*/) + { + me->SetStandState(UNIT_STAND_STATE_STAND); + DoCastSelf(SPELL_RESURRECTION_IMPACT_VISUAL, true); + + scheduler.Schedule(50ms, [this](TaskContext /*context*/) { - ReviveZealot(data); - } - }); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (!me->HasAura(SPELL_TIGER_FORM) && damage >= me->GetHealth()) - { - damage = me->GetHealth() - 1; - - if (!WasDead) - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_PASSIVE); - me->SetStandState(UNIT_STAND_STATE_SLEEP); - me->AttackStop(); - DoResetThreatList(); - WasDead = true; - CheckPhaseTransition(); - Talk(EMOTE_THEKAL_DIES); - } - } - - if (!Enraged && me->HealthBelowPctDamaged(20, damage) && me->HasAura(SPELL_TIGER_FORM)) - { - 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->RestoreFaction(); - me->SetReactState(REACT_AGGRESSIVE); - me->SetFullHealth(); - WasDead = false; - } - } - - void UpdateAI(uint32 diff) override - { - if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim()) - return; - - _scheduler.Update(diff, - std::bind(&BossAI::DoMeleeAttackIfReady, this)); - } - - void ReviveZealot(uint32 zealotData) - { - if (Creature* zealot = instance->GetCreature(zealotData)) - { - zealot->Respawn(true); - zealot->SetInCombatWithZone(); - 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); + }); + + scheduler.Schedule(6s, [this](TaskContext /*context*/) + { + DoCastSelf(SPELL_TIGER_FORM); + me->LoadEquipment(0, true); + me->SetFullHealth(); + me->SetReactState(REACT_AGGRESSIVE); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - _scheduler.Schedule(6s, [this](TaskContext /*context*/) { - DoCastSelf(SPELL_TIGER_FORM); - me->LoadEquipment(0, true); - 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) + { + // charge a random target that is at least 8 yards away (min range of charge is 8 yards) + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, -8.0f)) + { + DoCast(target, SPELL_CHARGE); + DoResetThreatList(); + AttackStart(target); + } + context.Repeat(15s, 22s); + }).Schedule(25s, [this](TaskContext context) + { + DoCastVictim(SPELL_SUMMONTIGERS, true); + context.Repeat(10s, 14s); + }); - _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); - DoResetThreatList(); - AttackStart(target); - } - context.Repeat(15s, 22s); - }).Schedule(25s, [this](TaskContext context) { - DoCastVictim(SPELL_SUMMONTIGERS, true); - context.Repeat(10s, 14s); - }); + // schedule Enrage at 20% health + ScheduleHealthCheckEvent(20, [this] + { + DoCastSelf(SPELL_ENRAGE); }); }); + }); + } + else + { + scheduler.Schedule(10s, [this](TaskContext /*context*/) + { + if (!(_wasDead && _lorkhanDied && _zathDied)) + { + DoAction(ACTION_RESSURRECT); + } + }); + } + } + + private: + bool _lorkhanDied; + bool _zathDied; + bool _enraged; + bool _wasDead; +}; + +struct npc_zealot_lorkhan : public ScriptedAI +{ + npc_zealot_lorkhan(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + InstanceScript* instance; + + void Reset() override + { + _scheduler.CancelAll(); + + _scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + + // emote idle loop + _scheduler.Schedule(5s, 25s, [this](TaskContext context) + { + // pick a random emote from the list of available emotes + me->HandleEmoteCommand( + RAND( + EMOTE_ONESHOT_QUESTION, + EMOTE_ONESHOT_YES, + EMOTE_ONESHOT_NO + ) + ); + context.Repeat(5s, 25s); + }); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.CancelAll(); + + _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 { - _scheduler.Schedule(10s, [this](TaskContext /*context*/) { - if (!(WasDead && _lorkhanDied && _zathDied)) - { - DoAction(ACTION_RESSURRECT); - } - }); + DoCast(thekal, SPELL_GREATERHEAL); } - } - private: - TaskScheduler _scheduler; - GuidVector _catGuids; - bool _lorkhanDied; - bool _zathDied; - bool Enraged; - bool WasDead; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulGurubAI(creature); + context.Repeat(15s, 20s); + }).Schedule(6s, [this](TaskContext context) + { + DoCastVictim(SPELL_DISARM); + context.Repeat(15s, 25s); + }); } + + void JustDied(Unit* /*killer*/) override + { + Talk(EMOTE_ZEALOT_DIES); + + if (Creature* thekal = instance->GetCreature(DATA_THEKAL)) + { + thekal->AI()->SetData(ACTION_RESSURRECT, DATA_LORKHAN); + } + } + + void UpdateAI(uint32 diff) override + { + if (me->IsInCombat() && !UpdateVictim()) + { + return; + } + else if (me->IsInCombat()) + { + _scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } + else + { + _scheduler.Update(diff); + } + } + + private: + TaskScheduler _scheduler; }; -class npc_zealot_lorkhan : public CreatureScript +struct npc_zealot_zath : public ScriptedAI { -public: - npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { } - - struct npc_zealot_lorkhanAI : public ScriptedAI + npc_zealot_zath(Creature* creature) : ScriptedAI(creature) { - npc_zealot_lorkhanAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - - void Reset() override - { - _scheduler.CancelAll(); - - _scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE); - }); - } - - void JustEngagedWith(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 JustDied(Unit* /*killer*/) override - { - Talk(EMOTE_ZEALOT_DIES); - - if (Creature* thekal = instance->GetCreature(DATA_THEKAL)) - { - thekal->AI()->SetData(ACTION_RESSURRECT, DATA_LORKHAN); - } - } - - void UpdateAI(uint32 diff) override - { - if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim()) - return; - - _scheduler.Update(diff, - std::bind(&ScriptedAI::DoMeleeAttackIfReady, this)); - } - - private: - TaskScheduler _scheduler; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulGurubAI(creature); + instance = creature->GetInstanceScript(); } -}; -class npc_zealot_zath : public CreatureScript -{ -public: - npc_zealot_zath() : CreatureScript("npc_zealot_zath") { } + InstanceScript* instance; - struct npc_zealot_zathAI : public ScriptedAI + void Reset() override { - npc_zealot_zathAI(Creature* creature) : ScriptedAI(creature) + _scheduler.CancelAll(); + + _scheduler.SetValidator([this] { - instance = creature->GetInstanceScript(); - } + return !me->HasUnitState(UNIT_STATE_CASTING); + }); - InstanceScript* instance; - - void Reset() override + // emote idle loop + _scheduler.Schedule(5s, 25s, [this](TaskContext context) { - _scheduler.CancelAll(); - - _scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE); - }); - } - - void JustEngagedWith(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())) - { - DoModifyThreatByPercent(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 JustDied(Unit* /*killer*/) override - { - Talk(EMOTE_ZEALOT_DIES); - - if (Creature* thekal = instance->GetCreature(DATA_THEKAL)) - { - thekal->AI()->SetData(ACTION_RESSURRECT, DATA_ZATH); - } - } - - void UpdateAI(uint32 diff) override - { - if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim()) - return; - - _scheduler.Update(diff, - std::bind(&ScriptedAI::DoMeleeAttackIfReady, this)); - } - - private: - TaskScheduler _scheduler; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulGurubAI(creature); + // pick a random emote from the list of available emotes + me->HandleEmoteCommand( + RAND( + EMOTE_ONESHOT_TALK, + EMOTE_ONESHOT_BEG, + EMOTE_ONESHOT_YES + ) + ); + context.Repeat(5s, 25s); + }); } + + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.CancelAll(); + + _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())) + { + DoModifyThreatByPercent(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 JustDied(Unit* /*killer*/) override + { + Talk(EMOTE_ZEALOT_DIES); + + if (Creature* thekal = instance->GetCreature(DATA_THEKAL)) + { + thekal->AI()->SetData(ACTION_RESSURRECT, DATA_ZATH); + } + } + + void UpdateAI(uint32 diff) override + { + if (me->IsInCombat() && !UpdateVictim()) + { + return; + } + else if (me->IsInCombat()) + { + _scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } + else + { + _scheduler.Update(diff); + } + } + + private: + TaskScheduler _scheduler; }; void AddSC_boss_thekal() { - new boss_thekal(); - new npc_zealot_lorkhan(); - new npc_zealot_zath(); + RegisterCreatureAI(boss_thekal); + RegisterCreatureAI(npc_zealot_lorkhan); + RegisterCreatureAI(npc_zealot_zath); }