fix(Scripts/RuinsOfAhnQiraj): Rework Ayamiss (#12738)

* fix(Scripts/RuinsOfAhnQiraj): Rework Ayamiss

- Use serverside spells to handle the Hive'Zara Swarmers
- Adjust pathing and timers
- Rewrite its script to TaskScheduler

Co-Authored-By: Gultask <100873791+Gultask@users.noreply.github.com>

* fix swarmers not being summoned if the boss is casting & movement


* Update landing path for swarmers

* fix: Update the looping script

* correct trash ability

* fix ayamiss landing

Co-authored-by: Gultask <100873791+Gultask@users.noreply.github.com>
This commit is contained in:
Skjalf
2022-08-15 06:10:55 -03:00
committed by GitHub
parent 523d4fbb08
commit 4fa0d6b0cc
2 changed files with 394 additions and 82 deletions

View File

@@ -18,7 +18,9 @@
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "ruins_of_ahnqiraj.h"
#include "TaskScheduler.h"
enum Spells
{
@@ -28,22 +30,31 @@ enum Spells
SPELL_FRENZY = 8269,
SPELL_LASH = 25852,
SPELL_FEED = 25721,
SPELL_THRASH = 3391,
// Server-side spells
SPELL_SUMMON_LARVA_A = 26538,
SPELL_SUMMON_LARVA_B = 26539,
SPELL_LARVA_AGGRO_EFFECT = 25724, // Unknown purpose
SPELL_LARVA_FEAR_EFFECT = 25726, // Unknown purpose
SPELL_SUMMON_LARVA_A = 26538,
SPELL_SUMMON_LARVA_B = 26539,
SPELL_LARVA_AGGRO_EFFECT = 25724, // Unknown purpose
SPELL_LARVA_FEAR_EFFECT = 25726, // Unknown purpose
SPELL_SUMMON_HIVEZARA_SWARMER = 25708,
SPELL_HIVEZARA_SWARMER_TELEPORT_1 = 25709,
SPELL_HIVEZARA_SWARMER_TELEPORT_2 = 25825,
SPELL_HIVEZARA_SWARMER_TELEPORT_3 = 25826,
SPELL_HIVEZARA_SWARMER_TELEPORT_4 = 25827,
SPELL_HIVEZARA_SWARMER_TELEPORT_5 = 25828,
SPELL_HIVEZARA_SWARMER_TELEPORT_TRIGGER = 25830,
SPELL_HIVEZARA_SWARMER_START_LOOP = 25711,
SPELL_HIVEZARA_SWARMER_LOOP_1 = 25833,
SPELL_HIVEZARA_SWARMER_LOOP_2 = 25834,
SPELL_HIVEZARA_SWARMER_LOOP_3 = 25835,
SPELL_HIVEZARA_SWARMER_SWARM = 25844
};
enum Events
enum Misc
{
EVENT_STINGER_SPRAY = 1,
EVENT_POISON_STINGER = 2,
EVENT_SUMMON_SWARMER = 3,
EVENT_SWARMER_ATTACK = 4,
EVENT_PARALYZE = 5,
EVENT_LASH = 6
MAX_SWARMER_COUNT = 28,
ACTION_SWARMER_SWARM = 1,
};
enum Emotes
@@ -66,8 +77,6 @@ enum Points
const Position AyamissAirPos = { -9689.292f, 1547.912f, 48.02729f, 0.0f };
const Position AltarPos = { -9717.18f, 1517.72f, 27.4677f, 0.0f };
/// @todo These below are probably incorrect, taken from SD2
const Position SwarmerPos = { -9647.352f, 1578.062f, 55.32f, 0.0f };
struct boss_ayamiss : public BossAI
{
@@ -79,6 +88,7 @@ struct boss_ayamiss : public BossAI
_phase = PHASE_AIR;
_enraged = false;
SetCombatMovement(false);
_scheduler.CancelAll();
}
void JustSummoned(Creature* who) override
@@ -86,12 +96,14 @@ struct boss_ayamiss : public BossAI
switch (who->GetEntry())
{
case NPC_HIVEZARA_SWARMER:
who->CastSpell(who, SPELL_HIVEZARA_SWARMER_TELEPORT_TRIGGER, true);
_swarmers.push_back(who->GetGUID());
break;
case NPC_HIVEZARA_LARVA:
who->GetMotionMaster()->MovePoint(POINT_PARALYZE, AltarPos);
break;
}
summons.Summon(who);
}
@@ -104,6 +116,7 @@ struct boss_ayamiss : public BossAI
else if (type == WAYPOINT_MOTION_TYPE && id == POINT_GROUND)
{
SetCombatMovement(true);
me->m_Events.AddEventAtOffset([this]()
{
if (me->GetVictim())
@@ -115,6 +128,55 @@ struct boss_ayamiss : public BossAI
}
}
void ScheduleTasks()
{
_scheduler.Schedule(20s, 30s, [this](TaskContext context)
{
DoCastSelf(SPELL_STINGER_SPRAY);
context.Repeat(15s, 20s);
}).Schedule(5s, [this](TaskContext context) {
DoCastVictim(SPELL_POISON_STINGER);
context.SetGroup(PHASE_AIR);
context.Repeat(2s, 3s);
}).Schedule(5s, [this](TaskContext context) {
DoCastAOE(SPELL_SUMMON_HIVEZARA_SWARMER, true);
if (_swarmers.size() >= MAX_SWARMER_COUNT)
{
DoCastAOE(SPELL_HIVEZARA_SWARMER_SWARM, true);
}
context.Repeat(RAND(2400ms, 3600ms));
}).Schedule(15s, [this](TaskContext context) {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0, true))
{
DoCast(target, SPELL_PARALYZE, true);
instance->SetGuidData(DATA_PARALYZED, target->GetGUID());
DoCastAOE(RAND(SPELL_SUMMON_LARVA_A, SPELL_SUMMON_LARVA_B), true);
}
context.Repeat();
});
}
void DoAction(int32 action) override
{
if (action == ACTION_SWARMER_SWARM)
{
for (ObjectGuid const& guid : _swarmers)
{
if (Creature* swarmer = me->GetMap()->GetCreature(guid))
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
{
swarmer->AI()->AttackStart(target);
}
}
}
_swarmers.clear();
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->ClearUnitState(UNIT_STATE_ROOT);
@@ -125,14 +187,10 @@ struct boss_ayamiss : public BossAI
void EnterCombat(Unit* attacker) override
{
BossAI::EnterCombat(attacker);
events.ScheduleEvent(EVENT_STINGER_SPRAY, 20s, 30s);
events.ScheduleEvent(EVENT_POISON_STINGER, 5s);
events.ScheduleEvent(EVENT_SUMMON_SWARMER, 5s);
events.ScheduleEvent(EVENT_SWARMER_ATTACK, 60s);
events.ScheduleEvent(EVENT_PARALYZE, 15s);
me->SetCanFly(true);
me->SetDisableGravity(true);
me->GetMotionMaster()->MovePoint(POINT_AIR, AyamissAirPos);
ScheduleTasks();
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
@@ -144,9 +202,19 @@ struct boss_ayamiss : public BossAI
me->SetCanFly(false);
me->SetDisableGravity(false);
me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false);
events.ScheduleEvent(EVENT_LASH, 5s, 8s);
events.CancelEvent(EVENT_POISON_STINGER);
DoResetThreat();
_scheduler.Schedule(5s, 8s, [this](TaskContext context) {
DoCastVictim(SPELL_LASH);
context.Repeat(8s, 15s);
}).Schedule(16s, [this](TaskContext context)
{
DoCastSelf(SPELL_THRASH);
context.Repeat();
});
_scheduler.DelayAll(5s);
_scheduler.CancelGroup(PHASE_AIR);
}
if (!_enraged && me->HealthBelowPctDamaged(20, damage))
@@ -162,66 +230,14 @@ struct boss_ayamiss : public BossAI
if (!UpdateVictim())
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_STINGER_SPRAY:
DoCastSelf(SPELL_STINGER_SPRAY);
events.ScheduleEvent(EVENT_STINGER_SPRAY, 15s, 20s);
break;
case EVENT_POISON_STINGER:
DoCastVictim(SPELL_POISON_STINGER);
events.ScheduleEvent(EVENT_POISON_STINGER, 2s, 3s);
break;
case EVENT_PARALYZE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0, true))
{
DoCast(target, SPELL_PARALYZE, true);
instance->SetGuidData(DATA_PARALYZED, target->GetGUID());
DoCastAOE(RAND(SPELL_SUMMON_LARVA_A, SPELL_SUMMON_LARVA_B), true);
}
events.ScheduleEvent(EVENT_PARALYZE, 15s);
break;
case EVENT_SWARMER_ATTACK:
for (ObjectGuid const& guid : _swarmers)
{
if (Creature* swarmer = me->GetMap()->GetCreature(guid))
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
{
swarmer->AI()->AttackStart(target);
}
}
}
_swarmers.clear();
events.ScheduleEvent(EVENT_SWARMER_ATTACK, 60s);
break;
case EVENT_SUMMON_SWARMER:
{
Position const offset = { 0.0f, 0.0f, 20.0f, 0.0f };
Position spawnpos = me->GetRandomPoint(SwarmerPos, 80.0f);
spawnpos.RelocateOffset(offset);
if (Creature* wasp = me->SummonCreature(NPC_HIVEZARA_SWARMER, spawnpos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
{
wasp->GetMotionMaster()->MoveRandom(10.0f);
}
events.ScheduleEvent(EVENT_SUMMON_SWARMER, 5s);
break;
}
case EVENT_LASH:
DoCastVictim(SPELL_LASH);
events.ScheduleEvent(EVENT_LASH, 8s, 15s);
break;
}
}
DoMeleeAttackIfReady();
_scheduler.Update(diff,
std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
private:
GuidList _swarmers;
uint8 _phase;
bool _enraged;
TaskScheduler _scheduler;
Position homePos;
};
@@ -234,14 +250,11 @@ struct npc_hive_zara_larva : public ScriptedAI
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE)
if (type == POINT_MOTION_TYPE && id == POINT_PARALYZE)
{
if (id == POINT_PARALYZE)
if (Player* target = ObjectAccessor::GetPlayer(*me, _instance->GetGuidData(DATA_PARALYZED)))
{
if (Player* target = ObjectAccessor::GetPlayer(*me, _instance->GetGuidData(DATA_PARALYZED)))
{
DoCast(target, SPELL_FEED);
}
DoCast(target, SPELL_FEED);
}
}
}
@@ -255,7 +268,6 @@ struct npc_hive_zara_larva : public ScriptedAI
}
void MoveInLineOfSight(Unit* who) override
{
if (_instance->GetBossState(DATA_AYAMISS) == IN_PROGRESS)
return;
@@ -282,8 +294,151 @@ private:
InstanceScript* _instance;
};
struct npc_hive_zara_swarmer : public ScriptedAI
{
npc_hive_zara_swarmer(Creature* creature) : ScriptedAI(creature) { }
void PathEndReached(uint32 /*pathId*/) override
{
// Delay is required because we are calling the movement generator from inside the pathing hook.
// If we issue another call here, it will be flushed before it is executed.
me->m_Events.AddEventAtOffset([this]()
{
DoCastSelf(SPELL_HIVEZARA_SWARMER_START_LOOP);
}, 1s);
}
};
struct WaspTeleportData
{
uint32 spellId;
uint32 pathId;
};
class spell_ayamiss_swarmer_teleport_trigger : public SpellScript
{
PrepareSpellScript(spell_ayamiss_swarmer_teleport_trigger);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo
( {
SPELL_HIVEZARA_SWARMER_TELEPORT_1, SPELL_HIVEZARA_SWARMER_TELEPORT_2,
SPELL_HIVEZARA_SWARMER_TELEPORT_3, SPELL_HIVEZARA_SWARMER_TELEPORT_4,
SPELL_HIVEZARA_SWARMER_TELEPORT_5
});
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
Unit* caster = GetCaster();
WaspTeleportData telData[5] =
{
{ SPELL_HIVEZARA_SWARMER_TELEPORT_1, NPC_HIVEZARA_SWARMER * 10 },
{ SPELL_HIVEZARA_SWARMER_TELEPORT_2, (NPC_HIVEZARA_SWARMER + 1) * 10 },
{ SPELL_HIVEZARA_SWARMER_TELEPORT_3, (NPC_HIVEZARA_SWARMER + 2) * 10 },
{ SPELL_HIVEZARA_SWARMER_TELEPORT_4, (NPC_HIVEZARA_SWARMER + 3) * 10 },
{ SPELL_HIVEZARA_SWARMER_TELEPORT_5, (NPC_HIVEZARA_SWARMER + 4) * 10 }
};
WaspTeleportData data = Acore::Containers::SelectRandomContainerElement(telData);
caster->CastSpell((Unit*)nullptr, data.spellId, true);
uint32 pathId = data.pathId;
caster->m_Events.AddEventAtOffset([caster, pathId]()
{
caster->GetMotionMaster()->MovePath(pathId, false);
}, 1s);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_ayamiss_swarmer_teleport_trigger::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_ayamiss_swarmer_swarm : public SpellScript
{
PrepareSpellScript(spell_ayamiss_swarmer_swarm);
bool Load() override
{
return GetCaster()->GetEntry() == NPC_AYAMISS;
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
GetCaster()->ToCreature()->AI()->DoAction(ACTION_SWARMER_SWARM);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_ayamiss_swarmer_swarm::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
class spell_ayamiss_swarmer_start_loop : public SpellScript
{
PrepareSpellScript(spell_ayamiss_swarmer_start_loop);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_HIVEZARA_SWARMER_LOOP_1, SPELL_HIVEZARA_SWARMER_LOOP_2, SPELL_HIVEZARA_SWARMER_LOOP_3 });
}
bool Load() override
{
return GetCaster()->GetEntry() == NPC_HIVEZARA_SWARMER;
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
uint32 loopSpells[3] = { SPELL_HIVEZARA_SWARMER_LOOP_1, SPELL_HIVEZARA_SWARMER_LOOP_2, SPELL_HIVEZARA_SWARMER_LOOP_3 };
GetCaster()->CastSpell((Unit*)nullptr, Acore::Containers::SelectRandomContainerElement(loopSpells));
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_ayamiss_swarmer_start_loop::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_gen_ayamiss_swarmer_loop: public SpellScript
{
PrepareSpellScript(spell_gen_ayamiss_swarmer_loop);
public:
spell_gen_ayamiss_swarmer_loop(uint32 pathId) : SpellScript(), _pathId(pathId) { }
bool Load() override
{
return GetCaster()->GetEntry() == NPC_HIVEZARA_SWARMER;
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
GetCaster()->ToCreature()->GetMotionMaster()->Clear();
GetCaster()->ToCreature()->GetMotionMaster()->MovePath(_pathId, false);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_gen_ayamiss_swarmer_loop::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
private:
uint32 _pathId;
};
void AddSC_boss_ayamiss()
{
RegisterRuinsOfAhnQirajCreatureAI(boss_ayamiss);
RegisterRuinsOfAhnQirajCreatureAI(npc_hive_zara_larva);
RegisterRuinsOfAhnQirajCreatureAI(npc_hive_zara_swarmer);
RegisterSpellScript(spell_ayamiss_swarmer_teleport_trigger);
RegisterSpellScript(spell_ayamiss_swarmer_swarm);
RegisterSpellScript(spell_ayamiss_swarmer_start_loop);
RegisterSpellScriptWithArgs(spell_gen_ayamiss_swarmer_loop, "spell_gen_ayamiss_swarmer_loop_1", (NPC_HIVEZARA_SWARMER + 5) * 10);
RegisterSpellScriptWithArgs(spell_gen_ayamiss_swarmer_loop, "spell_gen_ayamiss_swarmer_loop_2", (NPC_HIVEZARA_SWARMER + 6) * 10);
RegisterSpellScriptWithArgs(spell_gen_ayamiss_swarmer_loop, "spell_gen_ayamiss_swarmer_loop_3", (NPC_HIVEZARA_SWARMER + 7) * 10);
}