(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 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)
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SharedDefines.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "TaskScheduler.h"
@@ -24,28 +25,32 @@ enum Says
{
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,
// Boss - pre-fight
SPELL_SUMMONTIGERS = 24183,
// Boss
SPELL_CHARGE = 24193,
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
SPELL_SHIELD = 20545,
SPELL_BLOODLUST = 24185,
SPELL_GREATERHEAL = 24208,
SPELL_DISARM = 6713,
// Zealot Zath Spells
SPELL_SWEEPINGSTRIKES = 18765,
SPELL_SINISTERSTRIKE = 15581,
@@ -59,22 +64,17 @@ 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_thekalAI(Creature* creature) : BossAI(creature, DATA_THEKAL)
boss_thekal(Creature* creature) : BossAI(creature, DATA_THEKAL)
{
Initialize();
}
void Initialize()
{
Enraged = false;
WasDead = false;
_enraged = false;
_wasDead = false;
_lorkhanDied = false;
_zathDied = false;
}
@@ -84,8 +84,11 @@ public:
_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);
@@ -99,7 +102,21 @@ public:
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);
});
@@ -125,14 +142,17 @@ public:
{
_JustEngagedWith();
_scheduler.CancelAll();
_scheduler.Schedule(4s, [this](TaskContext context) {
scheduler.CancelAll();
scheduler.Schedule(4s, [this](TaskContext context)
{
DoCastVictim(SPELL_MORTALCLEAVE);
context.Repeat(15s, 20s);
}).Schedule(9s, [this](TaskContext context) {
}).Schedule(9s, [this](TaskContext context)
{
DoCastVictim(SPELL_SILENCE);
context.Repeat(20s, 25s);
}).Schedule(16s, [this](TaskContext context) {
}).Schedule(16s, [this](TaskContext context)
{
DoCastSelf(SPELL_BLOODLUST);
context.Repeat(20s, 28s);
});
@@ -143,38 +163,35 @@ public:
UpdateZealotStatus(data, true);
CheckPhaseTransition();
_scheduler.Schedule(10s, [this, data](TaskContext /*context*/) {
if (!_lorkhanDied || !_zathDied || !WasDead)
scheduler.Schedule(10s, [this, data](TaskContext /*context*/)
{
if (!_lorkhanDied || !_zathDied || !_wasDead)
{
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())
{
damage = me->GetHealth() - 1;
if (!WasDead)
if (!_wasDead)
{
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE);
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->SetStandState(UNIT_STAND_STATE_DEAD);
me->AttackStop();
DoResetThreatList();
WasDead = true;
_wasDead = true;
CheckPhaseTransition();
Talk(EMOTE_THEKAL_DIES);
Talk(EMOTE_DIES);
}
}
if (!Enraged && me->HealthBelowPctDamaged(20, damage) && me->HasAura(SPELL_TIGER_FORM))
{
DoCastSelf(SPELL_ENRAGE);
Enraged = true;
}
BossAI::DamageTaken(attacker, damage, damageEffectType, spellSchoolMask);
}
void DoAction(int32 action) override
@@ -186,18 +203,26 @@ public:
me->RestoreFaction();
me->SetReactState(REACT_AGGRESSIVE);
me->SetFullHealth();
WasDead = false;
_wasDead = false;
}
}
void UpdateAI(uint32 diff) override
{
if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim())
if (me->IsInCombat() && !UpdateVictim())
{
return;
_scheduler.Update(diff,
}
else if (me->IsInCombat())
{
scheduler.Update(diff,
std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
else
{
scheduler.Update(diff);
}
}
void ReviveZealot(uint32 zealotData)
{
@@ -223,43 +248,63 @@ public:
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->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);
me->LoadEquipment(0, true);
me->SetFullHealth();
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);
context.Repeat();
}).Schedule(4s, [this](TaskContext context) {
}).Schedule(4s, [this](TaskContext context)
{
DoCastVictim(SPELL_FORCEPUNCH);
context.Repeat(16s, 21s);
}).Schedule(12s, [this](TaskContext context) {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
}).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) {
}).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))
scheduler.Schedule(10s, [this](TaskContext /*context*/)
{
if (!(_wasDead && _lorkhanDied && _zathDied))
{
DoAction(ACTION_RESSURRECT);
}
@@ -268,28 +313,15 @@ public:
}
private:
TaskScheduler _scheduler;
GuidVector _catGuids;
bool _lorkhanDied;
bool _zathDied;
bool Enraged;
bool WasDead;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<boss_thekalAI>(creature);
}
bool _enraged;
bool _wasDead;
};
class npc_zealot_lorkhan : public CreatureScript
struct npc_zealot_lorkhan : public ScriptedAI
{
public:
npc_zealot_lorkhan() : CreatureScript("npc_zealot_lorkhan") { }
struct npc_zealot_lorkhanAI : public ScriptedAI
{
npc_zealot_lorkhanAI(Creature* creature) : ScriptedAI(creature)
npc_zealot_lorkhan(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
@@ -302,16 +334,34 @@ public:
_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
{
_scheduler.Schedule(1s, [this](TaskContext context) {
_scheduler.CancelAll();
_scheduler.Schedule(1s, [this](TaskContext context)
{
DoCastSelf(SPELL_SHIELD);
context.Repeat(1min);
}).Schedule(32s, [this](TaskContext context) {
}).Schedule(32s, [this](TaskContext context)
{
Unit* thekal = instance->GetCreature(DATA_THEKAL);
Unit* zath = instance->GetCreature(DATA_ZATH);
@@ -332,7 +382,8 @@ public:
}
context.Repeat(15s, 20s);
}).Schedule(6s, [this](TaskContext context) {
}).Schedule(6s, [this](TaskContext context)
{
DoCastVictim(SPELL_DISARM);
context.Repeat(15s, 25s);
});
@@ -350,31 +401,28 @@ public:
void UpdateAI(uint32 diff) override
{
if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim())
if (me->IsInCombat() && !UpdateVictim())
{
return;
}
else if (me->IsInCombat())
{
_scheduler.Update(diff,
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
else
{
_scheduler.Update(diff);
}
}
private:
TaskScheduler _scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<npc_zealot_lorkhanAI>(creature);
}
};
class npc_zealot_zath : public CreatureScript
struct npc_zealot_zath : public ScriptedAI
{
public:
npc_zealot_zath() : CreatureScript("npc_zealot_zath") { }
struct npc_zealot_zathAI : public ScriptedAI
{
npc_zealot_zathAI(Creature* creature) : ScriptedAI(creature)
npc_zealot_zath(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
@@ -387,22 +435,42 @@ public:
_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
{
_scheduler.Schedule(13s, [this](TaskContext context) {
_scheduler.CancelAll();
_scheduler.Schedule(13s, [this](TaskContext context)
{
DoCastSelf(SPELL_SWEEPINGSTRIKES);
context.Repeat(1min);
}).Schedule(16s, [this](TaskContext context) {
}).Schedule(16s, [this](TaskContext context)
{
DoCastSelf(SPELL_BLOODLUST);
context.Repeat(22s, 26s);
}).Schedule(8s, [this](TaskContext context) {
}).Schedule(8s, [this](TaskContext context)
{
DoCastVictim(SPELL_SINISTERSTRIKE);
context.Repeat(8s, 16s);
}).Schedule(25s, [this](TaskContext context) {
}).Schedule(25s, [this](TaskContext context)
{
DoCastVictim(SPELL_GOUGE);
if (DoGetThreat(me->GetVictim()))
@@ -411,10 +479,12 @@ public:
}
context.Repeat(17s, 27s);
}).Schedule(18s, [this](TaskContext context) {
}).Schedule(18s, [this](TaskContext context)
{
DoCastVictim(SPELL_KICK);
context.Repeat(15s, 25s);
}).Schedule(5s, [this](TaskContext context) {
}).Schedule(5s, [this](TaskContext context)
{
DoCastVictim(SPELL_BLIND);
context.Repeat(10s, 20s);
});
@@ -432,26 +502,28 @@ public:
void UpdateAI(uint32 diff) override
{
if (me->GetReactState() != REACT_PASSIVE && !UpdateVictim())
if (me->IsInCombat() && !UpdateVictim())
{
return;
}
else if (me->IsInCombat())
{
_scheduler.Update(diff,
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
else
{
_scheduler.Update(diff);
}
}
private:
TaskScheduler _scheduler;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulGurubAI<npc_zealot_zathAI>(creature);
}
};
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);
}