(Scripts/AI) ZG20: Improvements to Thekal (Tiger Boss) (#17603)

Co-authored-by: AG <43139552+AGandrup@users.noreply.github.com>
This commit is contained in:
KJack
2023-11-11 02:13:43 -05:00
committed by GitHub
parent f7fdc7cb6c
commit 896e7c5656
3 changed files with 459 additions and 370 deletions
@@ -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;
-1
View File
@@ -1541,7 +1541,6 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a
case 2584: // Waiting to Resurrect case 2584: // Waiting to Resurrect
case 22011: // Spirit Heal Channel case 22011: // Spirit Heal Channel
case 22012: // Spirit Heal case 22012: // Spirit Heal
case 24171: // Resurrection Impact Visual
case 42792: // Recently Dropped Flag case 42792: // Recently Dropped Flag
case 43681: // Inactive case 43681: // Inactive
case 44535: // Spirit Heal (mana) case 44535: // Spirit Heal (mana)
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "SharedDefines.h"
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "TaskScheduler.h" #include "TaskScheduler.h"
@@ -24,28 +25,32 @@ enum Says
{ {
SAY_AGGRO = 0, SAY_AGGRO = 0,
SAY_DEATH = 1, SAY_DEATH = 1,
EMOTE_DIES = 2,
EMOTE_ZEALOT_DIES = 0, EMOTE_ZEALOT_DIES = 0
EMOTE_THEKAL_DIES = 2
}; };
enum Spells enum Spells
{ {
SPELL_MORTALCLEAVE = 22859, // Boss - pre-fight
SPELL_SILENCE = 22666, SPELL_SUMMONTIGERS = 24183,
SPELL_TIGER_FORM = 24169,
SPELL_RESURRECT = 24173, // Boss
SPELL_FRENZY = 8269,
SPELL_FORCEPUNCH = 24189,
SPELL_CHARGE = 24193, SPELL_CHARGE = 24193,
SPELL_ENRAGE = 8269, SPELL_ENRAGE = 8269,
SPELL_SUMMONTIGERS = 24183, 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 // Zealot Lor'Khan Spells
SPELL_SHIELD = 20545, SPELL_SHIELD = 20545,
SPELL_BLOODLUST = 24185, SPELL_BLOODLUST = 24185,
SPELL_GREATERHEAL = 24208, SPELL_GREATERHEAL = 24208,
SPELL_DISARM = 6713, SPELL_DISARM = 6713,
// Zealot Zath Spells // Zealot Zath Spells
SPELL_SWEEPINGSTRIKES = 18765, SPELL_SWEEPINGSTRIKES = 18765,
SPELL_SINISTERSTRIKE = 15581, SPELL_SINISTERSTRIKE = 15581,
@@ -59,22 +64,17 @@ enum Actions
ACTION_RESSURRECT = 1 ACTION_RESSURRECT = 1
}; };
class boss_thekal : public CreatureScript struct boss_thekal : public BossAI
{ {
public: boss_thekal(Creature* creature) : BossAI(creature, DATA_THEKAL)
boss_thekal() : CreatureScript("boss_thekal") { }
struct boss_thekalAI : public BossAI
{
boss_thekalAI(Creature* creature) : BossAI(creature, DATA_THEKAL)
{ {
Initialize(); Initialize();
} }
void Initialize() void Initialize()
{ {
Enraged = false; _enraged = false;
WasDead = false; _wasDead = false;
_lorkhanDied = false; _lorkhanDied = false;
_zathDied = false; _zathDied = false;
} }
@@ -84,8 +84,11 @@ public:
_Reset(); _Reset();
Initialize(); Initialize();
scheduler.CancelAll();
me->SetStandState(UNIT_STAND_STATE_STAND); me->SetStandState(UNIT_STAND_STATE_STAND);
me->SetReactState(REACT_AGGRESSIVE); me->SetReactState(REACT_AGGRESSIVE);
me->RemoveAurasDueToSpell(SPELL_FRENZY);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->LoadEquipment(1, true); me->LoadEquipment(1, true);
@@ -99,7 +102,21 @@ public:
zealot->AI()->Reset(); zealot->AI()->Reset();
} }
_scheduler.SetValidator([this] // 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_TALK,
EMOTE_ONESHOT_FLEX,
EMOTE_ONESHOT_POINT
)
);
context.Repeat(5s, 25s);
});
scheduler.SetValidator([this]
{ {
return !me->HasUnitState(UNIT_STATE_CASTING); return !me->HasUnitState(UNIT_STATE_CASTING);
}); });
@@ -125,14 +142,17 @@ public:
{ {
_JustEngagedWith(); _JustEngagedWith();
_scheduler.CancelAll(); scheduler.CancelAll();
_scheduler.Schedule(4s, [this](TaskContext context) { scheduler.Schedule(4s, [this](TaskContext context)
{
DoCastVictim(SPELL_MORTALCLEAVE); DoCastVictim(SPELL_MORTALCLEAVE);
context.Repeat(15s, 20s); context.Repeat(15s, 20s);
}).Schedule(9s, [this](TaskContext context) { }).Schedule(9s, [this](TaskContext context)
{
DoCastVictim(SPELL_SILENCE); DoCastVictim(SPELL_SILENCE);
context.Repeat(20s, 25s); context.Repeat(20s, 25s);
}).Schedule(16s, [this](TaskContext context) { }).Schedule(16s, [this](TaskContext context)
{
DoCastSelf(SPELL_BLOODLUST); DoCastSelf(SPELL_BLOODLUST);
context.Repeat(20s, 28s); context.Repeat(20s, 28s);
}); });
@@ -143,38 +163,35 @@ public:
UpdateZealotStatus(data, true); UpdateZealotStatus(data, true);
CheckPhaseTransition(); CheckPhaseTransition();
_scheduler.Schedule(10s, [this, data](TaskContext /*context*/) { scheduler.Schedule(10s, [this, data](TaskContext /*context*/)
if (!_lorkhanDied || !_zathDied || !WasDead) {
if (!_lorkhanDied || !_zathDied || !_wasDead)
{ {
ReviveZealot(data); ReviveZealot(data);
} }
}); });
} }
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageEffectType, SpellSchoolMask spellSchoolMask) override
{ {
if (!me->HasAura(SPELL_TIGER_FORM) && damage >= me->GetHealth()) if (!me->HasAura(SPELL_TIGER_FORM) && damage >= me->GetHealth())
{ {
damage = me->GetHealth() - 1; damage = me->GetHealth() - 1;
if (!WasDead) if (!_wasDead)
{ {
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE); me->SetReactState(REACT_PASSIVE);
me->SetStandState(UNIT_STAND_STATE_SLEEP); me->SetStandState(UNIT_STAND_STATE_DEAD);
me->AttackStop(); me->AttackStop();
DoResetThreatList(); DoResetThreatList();
WasDead = true; _wasDead = true;
CheckPhaseTransition(); CheckPhaseTransition();
Talk(EMOTE_THEKAL_DIES); Talk(EMOTE_DIES);
} }
} }
if (!Enraged && me->HealthBelowPctDamaged(20, damage) && me->HasAura(SPELL_TIGER_FORM)) BossAI::DamageTaken(attacker, damage, damageEffectType, spellSchoolMask);
{
DoCastSelf(SPELL_ENRAGE);
Enraged = true;
}
} }
void DoAction(int32 action) override void DoAction(int32 action) override
@@ -186,18 +203,26 @@ public:
me->RestoreFaction(); me->RestoreFaction();
me->SetReactState(REACT_AGGRESSIVE); me->SetReactState(REACT_AGGRESSIVE);
me->SetFullHealth(); me->SetFullHealth();
WasDead = false; _wasDead = false;
} }
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim()) if (me->IsInCombat() && !UpdateVictim())
{
return; return;
}
_scheduler.Update(diff, else if (me->IsInCombat())
{
scheduler.Update(diff,
std::bind(&BossAI::DoMeleeAttackIfReady, this)); std::bind(&BossAI::DoMeleeAttackIfReady, this));
} }
else
{
scheduler.Update(diff);
}
}
void ReviveZealot(uint32 zealotData) void ReviveZealot(uint32 zealotData)
{ {
@@ -223,43 +248,63 @@ public:
void CheckPhaseTransition() void CheckPhaseTransition()
{ {
if (WasDead && _lorkhanDied && _zathDied) if (_wasDead && _lorkhanDied && _zathDied)
{
scheduler.Schedule(3s, [this](TaskContext /*context*/)
{ {
_scheduler.Schedule(3s, [this](TaskContext /*context*/) {
Talk(SAY_AGGRO);
me->SetStandState(UNIT_STAND_STATE_STAND); me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); DoCastSelf(SPELL_RESURRECTION_IMPACT_VISUAL, true);
_scheduler.Schedule(6s, [this](TaskContext /*context*/) { scheduler.Schedule(50ms, [this](TaskContext /*context*/)
{
Talk(SAY_AGGRO);
});
scheduler.Schedule(6s, [this](TaskContext /*context*/)
{
DoCastSelf(SPELL_TIGER_FORM); DoCastSelf(SPELL_TIGER_FORM);
me->LoadEquipment(0, true); me->LoadEquipment(0, true);
me->SetFullHealth();
me->SetReactState(REACT_AGGRESSIVE); me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
_scheduler.Schedule(30s, [this](TaskContext context) { scheduler.Schedule(30s, [this](TaskContext context)
{
DoCastSelf(SPELL_FRENZY); DoCastSelf(SPELL_FRENZY);
context.Repeat(); context.Repeat();
}).Schedule(4s, [this](TaskContext context) { }).Schedule(4s, [this](TaskContext context)
{
DoCastVictim(SPELL_FORCEPUNCH); DoCastVictim(SPELL_FORCEPUNCH);
context.Repeat(16s, 21s); context.Repeat(16s, 21s);
}).Schedule(12s, [this](TaskContext context) { }).Schedule(12s, [this](TaskContext context)
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) {
// 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); DoCast(target, SPELL_CHARGE);
DoResetThreatList(); DoResetThreatList();
AttackStart(target); AttackStart(target);
} }
context.Repeat(15s, 22s); context.Repeat(15s, 22s);
}).Schedule(25s, [this](TaskContext context) { }).Schedule(25s, [this](TaskContext context)
{
DoCastVictim(SPELL_SUMMONTIGERS, true); DoCastVictim(SPELL_SUMMONTIGERS, true);
context.Repeat(10s, 14s); context.Repeat(10s, 14s);
}); });
// schedule Enrage at 20% health
ScheduleHealthCheckEvent(20, [this]
{
DoCastSelf(SPELL_ENRAGE);
});
}); });
}); });
} }
else else
{ {
_scheduler.Schedule(10s, [this](TaskContext /*context*/) { scheduler.Schedule(10s, [this](TaskContext /*context*/)
if (!(WasDead && _lorkhanDied && _zathDied)) {
if (!(_wasDead && _lorkhanDied && _zathDied))
{ {
DoAction(ACTION_RESSURRECT); DoAction(ACTION_RESSURRECT);
} }
@@ -268,28 +313,15 @@ public:
} }
private: private:
TaskScheduler _scheduler;
GuidVector _catGuids;
bool _lorkhanDied; bool _lorkhanDied;
bool _zathDied; bool _zathDied;
bool Enraged; bool _enraged;
bool WasDead; bool _wasDead;
}; };
CreatureAI* GetAI(Creature* creature) const override struct npc_zealot_lorkhan : public ScriptedAI
{ {
return GetZulGurubAI<boss_thekalAI>(creature); npc_zealot_lorkhan(Creature* creature) : ScriptedAI(creature)
}
};
class npc_zealot_lorkhan : public CreatureScript
{
public:
npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { }
struct npc_zealot_lorkhanAI : public ScriptedAI
{
npc_zealot_lorkhanAI(Creature* creature) : ScriptedAI(creature)
{ {
instance = creature->GetInstanceScript(); instance = creature->GetInstanceScript();
} }
@@ -302,16 +334,34 @@ public:
_scheduler.SetValidator([this] _scheduler.SetValidator([this]
{ {
return !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE); 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 void JustEngagedWith(Unit* /*who*/) override
{ {
_scheduler.Schedule(1s, [this](TaskContext context) { _scheduler.CancelAll();
_scheduler.Schedule(1s, [this](TaskContext context)
{
DoCastSelf(SPELL_SHIELD); DoCastSelf(SPELL_SHIELD);
context.Repeat(1min); context.Repeat(1min);
}).Schedule(32s, [this](TaskContext context) { }).Schedule(32s, [this](TaskContext context)
{
Unit* thekal = instance->GetCreature(DATA_THEKAL); Unit* thekal = instance->GetCreature(DATA_THEKAL);
Unit* zath = instance->GetCreature(DATA_ZATH); Unit* zath = instance->GetCreature(DATA_ZATH);
@@ -332,7 +382,8 @@ public:
} }
context.Repeat(15s, 20s); context.Repeat(15s, 20s);
}).Schedule(6s, [this](TaskContext context) { }).Schedule(6s, [this](TaskContext context)
{
DoCastVictim(SPELL_DISARM); DoCastVictim(SPELL_DISARM);
context.Repeat(15s, 25s); context.Repeat(15s, 25s);
}); });
@@ -350,31 +401,28 @@ public:
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim()) if (me->IsInCombat() && !UpdateVictim())
{
return; return;
}
else if (me->IsInCombat())
{
_scheduler.Update(diff, _scheduler.Update(diff,
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this)); std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
else
{
_scheduler.Update(diff);
}
} }
private: private:
TaskScheduler _scheduler; TaskScheduler _scheduler;
}; };
CreatureAI* GetAI(Creature* creature) const override struct npc_zealot_zath : public ScriptedAI
{ {
return GetZulGurubAI<npc_zealot_lorkhanAI>(creature); npc_zealot_zath(Creature* creature) : ScriptedAI(creature)
}
};
class npc_zealot_zath : public CreatureScript
{
public:
npc_zealot_zath() : CreatureScript("npc_zealot_zath") { }
struct npc_zealot_zathAI : public ScriptedAI
{
npc_zealot_zathAI(Creature* creature) : ScriptedAI(creature)
{ {
instance = creature->GetInstanceScript(); instance = creature->GetInstanceScript();
} }
@@ -387,22 +435,42 @@ public:
_scheduler.SetValidator([this] _scheduler.SetValidator([this]
{ {
return !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE); 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_TALK,
EMOTE_ONESHOT_BEG,
EMOTE_ONESHOT_YES
)
);
context.Repeat(5s, 25s);
}); });
} }
void JustEngagedWith(Unit* /*who*/) override void JustEngagedWith(Unit* /*who*/) override
{ {
_scheduler.Schedule(13s, [this](TaskContext context) { _scheduler.CancelAll();
_scheduler.Schedule(13s, [this](TaskContext context)
{
DoCastSelf(SPELL_SWEEPINGSTRIKES); DoCastSelf(SPELL_SWEEPINGSTRIKES);
context.Repeat(1min); context.Repeat(1min);
}).Schedule(16s, [this](TaskContext context) { }).Schedule(16s, [this](TaskContext context)
{
DoCastSelf(SPELL_BLOODLUST); DoCastSelf(SPELL_BLOODLUST);
context.Repeat(22s, 26s); context.Repeat(22s, 26s);
}).Schedule(8s, [this](TaskContext context) { }).Schedule(8s, [this](TaskContext context)
{
DoCastVictim(SPELL_SINISTERSTRIKE); DoCastVictim(SPELL_SINISTERSTRIKE);
context.Repeat(8s, 16s); context.Repeat(8s, 16s);
}).Schedule(25s, [this](TaskContext context) { }).Schedule(25s, [this](TaskContext context)
{
DoCastVictim(SPELL_GOUGE); DoCastVictim(SPELL_GOUGE);
if (DoGetThreat(me->GetVictim())) if (DoGetThreat(me->GetVictim()))
@@ -411,10 +479,12 @@ public:
} }
context.Repeat(17s, 27s); context.Repeat(17s, 27s);
}).Schedule(18s, [this](TaskContext context) { }).Schedule(18s, [this](TaskContext context)
{
DoCastVictim(SPELL_KICK); DoCastVictim(SPELL_KICK);
context.Repeat(15s, 25s); context.Repeat(15s, 25s);
}).Schedule(5s, [this](TaskContext context) { }).Schedule(5s, [this](TaskContext context)
{
DoCastVictim(SPELL_BLIND); DoCastVictim(SPELL_BLIND);
context.Repeat(10s, 20s); context.Repeat(10s, 20s);
}); });
@@ -432,26 +502,28 @@ public:
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim()) if (me->IsInCombat() && !UpdateVictim())
{
return; return;
}
else if (me->IsInCombat())
{
_scheduler.Update(diff, _scheduler.Update(diff,
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this)); std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
else
{
_scheduler.Update(diff);
}
} }
private: private:
TaskScheduler _scheduler; TaskScheduler _scheduler;
}; };
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<npc_zealot_zathAI>(creature);
}
};
void AddSC_boss_thekal() void AddSC_boss_thekal()
{ {
new boss_thekal(); RegisterCreatureAI(boss_thekal);
new npc_zealot_lorkhan(); RegisterCreatureAI(npc_zealot_lorkhan);
new npc_zealot_zath(); RegisterCreatureAI(npc_zealot_zath);
} }