refactor(Scripts/SSC): The Lurker Below boss script updated (#17082)
* initial * restore me->CastSpell() because apparently there is no overload of docastself that has triggerflags if I understand it correctly * fix timers may need some work - comments/thoughts welcome * ms * make kitzunu slightly less sad * fixes * revert * Update boss_lurker_below.cpp Co-authored-by: Angelo Venturini <Nefertumm@github.com> * Update boss_lurker_below.cpp --------- Co-authored-by: Angelo Venturini <Nefertumm@github.com>
This commit is contained in:
@@ -26,7 +26,10 @@ enum Spells
|
|||||||
SPELL_GEYSER = 37478,
|
SPELL_GEYSER = 37478,
|
||||||
SPELL_SPOUT_VISUAL = 37431,
|
SPELL_SPOUT_VISUAL = 37431,
|
||||||
SPELL_SPOUT_PERIODIC = 37430,
|
SPELL_SPOUT_PERIODIC = 37430,
|
||||||
SPELL_LURKER_SPAWN_TRIGGER = 54587 // Needed for achievement
|
SPELL_LURKER_SPAWN_TRIGGER = 54587, // Needed for achievement
|
||||||
|
|
||||||
|
SPELL_CLEAR_ALL_DEBUFFS = 34098,
|
||||||
|
SPELL_SUBMERGE_VISUAL = 28819,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Misc
|
enum Misc
|
||||||
@@ -37,13 +40,12 @@ enum Misc
|
|||||||
|
|
||||||
NPC_COILFANG_GUARDIAN = 21873,
|
NPC_COILFANG_GUARDIAN = 21873,
|
||||||
NPC_COILFANG_AMBUSHER = 21865,
|
NPC_COILFANG_AMBUSHER = 21865,
|
||||||
|
};
|
||||||
|
|
||||||
EVENT_PHASE_1 = 1,
|
enum Groups
|
||||||
EVENT_PHASE_2 = 2,
|
{
|
||||||
EVENT_SPELL_WHIRL = 3,
|
GROUP_WHIRL = 1,
|
||||||
EVENT_SPELL_SPOUT = 4,
|
GROUP_GEYSER = 2
|
||||||
EVENT_SPELL_GEYSER = 5,
|
|
||||||
EVENT_SPELL_SPOUT_PERIODIC = 6
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Position positions[MAX_SUMMONS] =
|
const Position positions[MAX_SUMMONS] =
|
||||||
@@ -59,20 +61,16 @@ const Position positions[MAX_SUMMONS] =
|
|||||||
{42.471519f, -445.115295f, -19.769423f, 0.0f}
|
{42.471519f, -445.115295f, -19.769423f, 0.0f}
|
||||||
};
|
};
|
||||||
|
|
||||||
class boss_the_lurker_below : public CreatureScript
|
struct boss_the_lurker_below : public BossAI
|
||||||
{
|
{
|
||||||
public:
|
boss_the_lurker_below(Creature* creature) : BossAI(creature, DATA_THE_LURKER_BELOW)
|
||||||
boss_the_lurker_below() : CreatureScript("boss_the_lurker_below") { }
|
|
||||||
|
|
||||||
CreatureAI* GetAI(Creature* creature) const override
|
|
||||||
{
|
{
|
||||||
return GetSerpentShrineAI<boss_the_lurker_belowAI>(creature);
|
scheduler.SetValidator([this]
|
||||||
|
{
|
||||||
|
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct boss_the_lurker_belowAI : public BossAI
|
|
||||||
{
|
|
||||||
boss_the_lurker_belowAI(Creature* creature) : BossAI(creature, DATA_THE_LURKER_BELOW) { }
|
|
||||||
|
|
||||||
void Reset() override
|
void Reset() override
|
||||||
{
|
{
|
||||||
BossAI::Reset();
|
BossAI::Reset();
|
||||||
@@ -80,20 +78,11 @@ public:
|
|||||||
me->SetStandState(UNIT_STAND_STATE_SUBMERGED);
|
me->SetStandState(UNIT_STAND_STATE_SUBMERGED);
|
||||||
me->SetVisible(false);
|
me->SetVisible(false);
|
||||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||||
|
|
||||||
// Reset summons
|
|
||||||
summons.DespawnAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JustSummoned(Creature* summon) override
|
void DoAction(int32 action) override
|
||||||
{
|
{
|
||||||
summon->SetInCombatWithZone();
|
if (action == ACTION_START_EVENT)
|
||||||
summons.Summon(summon);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoAction(int32 param) override
|
|
||||||
{
|
|
||||||
if (param == ACTION_START_EVENT)
|
|
||||||
{
|
{
|
||||||
me->SetReactState(REACT_AGGRESSIVE);
|
me->SetReactState(REACT_AGGRESSIVE);
|
||||||
me->setAttackTimer(BASE_ATTACK, 6000);
|
me->setAttackTimer(BASE_ATTACK, 6000);
|
||||||
@@ -105,23 +94,74 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JustDied(Unit* killer) override
|
|
||||||
{
|
|
||||||
BossAI::JustDied(killer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AttackStart(Unit* who) override
|
void AttackStart(Unit* who) override
|
||||||
{
|
{
|
||||||
if (who && me->GetReactState() == REACT_AGGRESSIVE)
|
if (who && me->GetReactState() == REACT_AGGRESSIVE)
|
||||||
|
{
|
||||||
me->Attack(who, true);
|
me->Attack(who, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void JustEngagedWith(Unit* /*who*/) override
|
void JustEngagedWith(Unit* who) override
|
||||||
{
|
{
|
||||||
events.ScheduleEvent(EVENT_SPELL_WHIRL, 18000);
|
BossAI::JustEngagedWith(who);
|
||||||
events.ScheduleEvent(EVENT_SPELL_SPOUT, 45000);
|
|
||||||
events.ScheduleEvent(EVENT_SPELL_GEYSER, 10000);
|
SchedulerPhaseOne(38800ms, 91000ms);
|
||||||
events.ScheduleEvent(EVENT_PHASE_2, 125000);
|
}
|
||||||
|
|
||||||
|
void SchedulerPhaseOne(std::chrono::milliseconds spoutTimer, std::chrono::milliseconds p2Timer)
|
||||||
|
{
|
||||||
|
scheduler.Schedule(10900ms, GROUP_GEYSER, [this](TaskContext context)
|
||||||
|
{
|
||||||
|
DoCastRandomTarget(SPELL_GEYSER);
|
||||||
|
context.Repeat(10200ms, 54900ms);
|
||||||
|
}).Schedule(18150ms, GROUP_WHIRL, [this](TaskContext context)
|
||||||
|
{
|
||||||
|
DoCastSelf(SPELL_WHIRL);
|
||||||
|
context.Repeat(34150ms, 68550ms);
|
||||||
|
}).Schedule(spoutTimer, [this](TaskContext context)
|
||||||
|
{
|
||||||
|
Talk(EMOTE_TAKE_BREATH);
|
||||||
|
me->CastSpell(me, SPELL_SPOUT_VISUAL, TRIGGERED_IGNORE_SET_FACING);
|
||||||
|
me->SetReactState(REACT_PASSIVE);
|
||||||
|
me->SetFacingToObject(me->GetVictim());
|
||||||
|
me->SetTarget();
|
||||||
|
scheduler.RescheduleGroup(GROUP_GEYSER, 25s);
|
||||||
|
scheduler.RescheduleGroup(GROUP_WHIRL, 18s);
|
||||||
|
scheduler.Schedule(3s, [this](TaskContext)
|
||||||
|
{
|
||||||
|
me->InterruptNonMeleeSpells(false);
|
||||||
|
DoCastSelf(SPELL_SPOUT_PERIODIC, true);
|
||||||
|
});
|
||||||
|
context.Repeat(60s);
|
||||||
|
}).Schedule(p2Timer, [this](TaskContext)
|
||||||
|
{
|
||||||
|
//phase2
|
||||||
|
scheduler.CancelAll();
|
||||||
|
DoCastSelf(SPELL_SUBMERGE_VISUAL, true);
|
||||||
|
DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true);
|
||||||
|
me->SetStandState(UNIT_STAND_STATE_SUBMERGED);
|
||||||
|
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||||
|
for (uint8 i = 0; i < MAX_SUMMONS; ++i)
|
||||||
|
{
|
||||||
|
//needs sniffed spell probably
|
||||||
|
me->SummonCreature(i < 6 ? NPC_COILFANG_AMBUSHER : NPC_COILFANG_GUARDIAN, positions[i].GetPositionX(), positions[i].GetPositionY(), positions[i].GetPositionZ(), positions[i].GetAngle(me), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
|
||||||
|
}
|
||||||
|
SchedulerPhaseTwo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SchedulerPhaseTwo()
|
||||||
|
{
|
||||||
|
scheduler.Schedule(60s, [this](TaskContext)
|
||||||
|
{
|
||||||
|
me->setAttackTimer(BASE_ATTACK, 6000);
|
||||||
|
me->SetStandState(UNIT_STAND_STATE_STAND);
|
||||||
|
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||||
|
|
||||||
|
scheduler.CancelAll();
|
||||||
|
SchedulerPhaseOne(10000ms, 90750ms);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateAI(uint32 diff) override
|
void UpdateAI(uint32 diff) override
|
||||||
@@ -129,82 +169,41 @@ public:
|
|||||||
if (!UpdateVictim())
|
if (!UpdateVictim())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
scheduler.Update(diff);
|
||||||
return;
|
|
||||||
|
|
||||||
events.Update(diff);
|
|
||||||
|
|
||||||
switch (events.ExecuteEvent())
|
|
||||||
{
|
|
||||||
case EVENT_SPELL_WHIRL:
|
|
||||||
me->CastSpell(me, SPELL_WHIRL, false);
|
|
||||||
events.ScheduleEvent(EVENT_SPELL_WHIRL, 18000);
|
|
||||||
break;
|
|
||||||
case EVENT_SPELL_GEYSER:
|
|
||||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
|
||||||
me->CastSpell(target, SPELL_GEYSER, false);
|
|
||||||
events.ScheduleEvent(EVENT_SPELL_GEYSER, 10000);
|
|
||||||
break;
|
|
||||||
case EVENT_SPELL_SPOUT:
|
|
||||||
Talk(EMOTE_TAKE_BREATH);
|
|
||||||
me->CastSpell(me, SPELL_SPOUT_VISUAL, TRIGGERED_IGNORE_SET_FACING);
|
|
||||||
me->SetReactState(REACT_PASSIVE);
|
|
||||||
me->SetFacingToObject(me->GetVictim());
|
|
||||||
me->SetTarget();
|
|
||||||
events.ScheduleEvent(EVENT_SPELL_SPOUT, 60000);
|
|
||||||
events.RescheduleEvent(EVENT_SPELL_WHIRL, 18000);
|
|
||||||
events.RescheduleEvent(EVENT_SPELL_GEYSER, 25000);
|
|
||||||
events.ScheduleEvent(EVENT_SPELL_SPOUT_PERIODIC, 3000);
|
|
||||||
break;
|
|
||||||
case EVENT_SPELL_SPOUT_PERIODIC:
|
|
||||||
me->InterruptNonMeleeSpells(false);
|
|
||||||
me->CastSpell(me, SPELL_SPOUT_PERIODIC, true);
|
|
||||||
break;
|
|
||||||
case EVENT_PHASE_2:
|
|
||||||
events.Reset();
|
|
||||||
events.ScheduleEvent(EVENT_PHASE_1, 60000);
|
|
||||||
me->SetStandState(UNIT_STAND_STATE_SUBMERGED);
|
|
||||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
|
||||||
for (uint8 i = 0; i < MAX_SUMMONS; ++i)
|
|
||||||
me->SummonCreature(i < 6 ? NPC_COILFANG_AMBUSHER : NPC_COILFANG_GUARDIAN, positions[i].GetPositionX(), positions[i].GetPositionY(), positions[i].GetPositionZ(), positions[i].GetAngle(me), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
|
|
||||||
break;
|
|
||||||
case EVENT_PHASE_1:
|
|
||||||
me->setAttackTimer(BASE_ATTACK, 6000);
|
|
||||||
me->SetStandState(UNIT_STAND_STATE_STAND);
|
|
||||||
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
|
||||||
|
|
||||||
events.Reset();
|
|
||||||
events.ScheduleEvent(EVENT_SPELL_SPOUT, 10000);
|
|
||||||
events.ScheduleEvent(EVENT_PHASE_2, 120000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (me->getStandState() != UNIT_STAND_STATE_STAND || !me->isAttackReady() || me->GetReactState() != REACT_AGGRESSIVE)
|
if (me->getStandState() != UNIT_STAND_STATE_STAND || !me->isAttackReady() || me->GetReactState() != REACT_AGGRESSIVE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Unit* target = nullptr;
|
Unit* target = nullptr;
|
||||||
if (me->IsWithinMeleeRange(me->GetVictim()))
|
if (me->IsWithinMeleeRange(me->GetVictim()))
|
||||||
|
{
|
||||||
target = me->GetVictim();
|
target = me->GetVictim();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList();
|
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList();
|
||||||
for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr)
|
for (ThreatReference const* ref : t_list)
|
||||||
if (Unit* threatTarget = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
|
{
|
||||||
|
if (Unit* threatTarget = ObjectAccessor::GetUnit(*me, ref->getUnitGuid()))
|
||||||
|
{
|
||||||
if (me->IsWithinMeleeRange(threatTarget))
|
if (me->IsWithinMeleeRange(threatTarget))
|
||||||
{
|
{
|
||||||
target = threatTarget;
|
target = threatTarget;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (target)
|
if (target)
|
||||||
|
{
|
||||||
me->AttackerStateUpdate(target);
|
me->AttackerStateUpdate(target);
|
||||||
|
}
|
||||||
else if ((target = SelectTarget(SelectTargetMethod::Random, 0)))
|
else if ((target = SelectTarget(SelectTargetMethod::Random, 0)))
|
||||||
|
{
|
||||||
me->CastSpell(target, SPELL_WATER_BOLT, false);
|
me->CastSpell(target, SPELL_WATER_BOLT, false);
|
||||||
|
}
|
||||||
me->resetAttackTimer();
|
me->resetAttackTimer();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class go_strange_pool : public GameObjectScript
|
class go_strange_pool : public GameObjectScript
|
||||||
@@ -315,7 +314,7 @@ public:
|
|||||||
|
|
||||||
void AddSC_boss_the_lurker_below()
|
void AddSC_boss_the_lurker_below()
|
||||||
{
|
{
|
||||||
new boss_the_lurker_below();
|
RegisterSerpentShrineAI(boss_the_lurker_below);
|
||||||
new go_strange_pool();
|
new go_strange_pool();
|
||||||
new spell_lurker_below_spout();
|
new spell_lurker_below_spout();
|
||||||
new spell_lurker_below_spout_cone();
|
new spell_lurker_below_spout_cone();
|
||||||
|
|||||||
Reference in New Issue
Block a user