diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp index 34349e80a..c2287f8ba 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp @@ -26,7 +26,10 @@ enum Spells SPELL_GEYSER = 37478, SPELL_SPOUT_VISUAL = 37431, 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 @@ -37,13 +40,12 @@ enum Misc NPC_COILFANG_GUARDIAN = 21873, NPC_COILFANG_AMBUSHER = 21865, +}; - EVENT_PHASE_1 = 1, - EVENT_PHASE_2 = 2, - EVENT_SPELL_WHIRL = 3, - EVENT_SPELL_SPOUT = 4, - EVENT_SPELL_GEYSER = 5, - EVENT_SPELL_SPOUT_PERIODIC = 6 +enum Groups +{ + GROUP_WHIRL = 1, + GROUP_GEYSER = 2 }; const Position positions[MAX_SUMMONS] = @@ -59,152 +61,149 @@ const Position positions[MAX_SUMMONS] = {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() : CreatureScript("boss_the_lurker_below") { } - - CreatureAI* GetAI(Creature* creature) const override + boss_the_lurker_below(Creature* creature) : BossAI(creature, DATA_THE_LURKER_BELOW) { - return GetSerpentShrineAI(creature); + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } - struct boss_the_lurker_belowAI : public BossAI + void Reset() override { - boss_the_lurker_belowAI(Creature* creature) : BossAI(creature, DATA_THE_LURKER_BELOW) { } + BossAI::Reset(); + me->SetReactState(REACT_PASSIVE); + me->SetStandState(UNIT_STAND_STATE_SUBMERGED); + me->SetVisible(false); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } - void Reset() override + void DoAction(int32 action) override + { + if (action == ACTION_START_EVENT) { - BossAI::Reset(); + me->SetReactState(REACT_AGGRESSIVE); + me->setAttackTimer(BASE_ATTACK, 6000); + me->SetVisible(true); + me->UpdateObjectVisibility(true); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetInCombatWithZone(); + } + } + + void AttackStart(Unit* who) override + { + if (who && me->GetReactState() == REACT_AGGRESSIVE) + { + me->Attack(who, true); + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + + SchedulerPhaseOne(38800ms, 91000ms); + } + + 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->SetVisible(false); me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - - // Reset summons - summons.DespawnAll(); - } - - void JustSummoned(Creature* summon) override - { - summon->SetInCombatWithZone(); - summons.Summon(summon); - } - - void DoAction(int32 param) override - { - if (param == ACTION_START_EVENT) + for (uint8 i = 0; i < MAX_SUMMONS; ++i) { - me->SetReactState(REACT_AGGRESSIVE); - me->setAttackTimer(BASE_ATTACK, 6000); - me->SetVisible(true); - me->UpdateObjectVisibility(true); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetInCombatWithZone(); + //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 + { + if (!UpdateVictim()) + return; + + scheduler.Update(diff); + + if (me->getStandState() != UNIT_STAND_STATE_STAND || !me->isAttackReady() || me->GetReactState() != REACT_AGGRESSIVE) + return; + + Unit* target = nullptr; + if (me->IsWithinMeleeRange(me->GetVictim())) + { + target = me->GetVictim(); + } + else + { + ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); + for (ThreatReference const* ref : t_list) + { + if (Unit* threatTarget = ObjectAccessor::GetUnit(*me, ref->getUnitGuid())) + { + if (me->IsWithinMeleeRange(threatTarget)) + { + target = threatTarget; + break; + } + } } } - - void JustDied(Unit* killer) override + if (target) { - BossAI::JustDied(killer); + me->AttackerStateUpdate(target); } - - void AttackStart(Unit* who) override + else if ((target = SelectTarget(SelectTargetMethod::Random, 0))) { - if (who && me->GetReactState() == REACT_AGGRESSIVE) - me->Attack(who, true); + me->CastSpell(target, SPELL_WATER_BOLT, false); } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_SPELL_WHIRL, 18000); - events.ScheduleEvent(EVENT_SPELL_SPOUT, 45000); - events.ScheduleEvent(EVENT_SPELL_GEYSER, 10000); - events.ScheduleEvent(EVENT_PHASE_2, 125000); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (me->HasUnitState(UNIT_STATE_CASTING)) - 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) - return; - - Unit* target = nullptr; - if (me->IsWithinMeleeRange(me->GetVictim())) - target = me->GetVictim(); - else - { - ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) - if (Unit* threatTarget = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) - if (me->IsWithinMeleeRange(threatTarget)) - { - target = threatTarget; - break; - } - } - - if (target) - me->AttackerStateUpdate(target); - else if ((target = SelectTarget(SelectTargetMethod::Random, 0))) - me->CastSpell(target, SPELL_WATER_BOLT, false); - - me->resetAttackTimer(); - } - }; + me->resetAttackTimer(); + } }; class go_strange_pool : public GameObjectScript @@ -315,7 +314,7 @@ public: void AddSC_boss_the_lurker_below() { - new boss_the_lurker_below(); + RegisterSerpentShrineAI(boss_the_lurker_below); new go_strange_pool(); new spell_lurker_below_spout(); new spell_lurker_below_spout_cone();