aboutsummaryrefslogtreecommitdiff
path: root/src/server/scripts/Outland
diff options
context:
space:
mode:
authoroffl <11556157+offl@users.noreply.github.com>2022-06-20 20:02:48 +0300
committerShauren <shauren.trinity@gmail.com>2022-09-05 18:58:32 +0200
commit6f3406a825b8bb2671f11b6a10c23e6a38a5f840 (patch)
treeb77c61e7c67633bb930544e742b490276cc89369 /src/server/scripts/Outland
parentfd018d6c2271192bcf170ab894a4659e51554eb5 (diff)
Scripts/UB: Rework The Black Stalker (#28040)
(cherry picked from commit c025fcef743122ccd1c2a8e79463941c72ee7e12)
Diffstat (limited to 'src/server/scripts/Outland')
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheUnderbog/boss_the_black_stalker.cpp362
1 files changed, 216 insertions, 146 deletions
diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheUnderbog/boss_the_black_stalker.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheUnderbog/boss_the_black_stalker.cpp
index 114580546b5..8a0473f979c 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/TheUnderbog/boss_the_black_stalker.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/TheUnderbog/boss_the_black_stalker.cpp
@@ -15,187 +15,257 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Boss_the_black_stalker
-SD%Complete: 95
-SDComment: Timers may be incorrect
-SDCategory: Coilfang Resevoir, Underbog
-EndScriptData */
-
-#include "ScriptMgr.h"
-#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
+#include "ScriptMgr.h"
+#include "SpellScript.h"
#include "the_underbog.h"
+/*
+How levitation sequence works: boss casts Levitate and it triggers a chain of spells, target(any target, player or pet, any position in
+threat list) eventually gets pulled towards by randomly selected trigger. Then target becomes protected from Pull Towards by Suspension
+aura which is triggered every 1 sec up to 4 times. Since it has stun mechanic, diminishing returns cuts off its duration every cast in
+half (20 > 10 > 5 > 0). Eventually player becomes immune to Suspension and vulnerable to another pull towards.
+Whole levitate sequence is designed to pull player towards up to 3 times. Usually it works like this: player gets pulled towards,
+gets protected by Suspension from Pull Towards next 2 times. If player is unlucky, boss can cast Levitate on same player again, in that case
+player can be pulled towards 2 times in a row without any protection from fall damage by Suspension(case from sniffs).
+
+However currently diminishing returns affects Suspension after first cast, its duration is 10 instead of 20 seconds and player will be
+immune to 4th cast. That allows to pull player towards when levitation sequence ends. Levitation sequence has sensetive design and looks
+like lack of delays between packets makes it work differently too.
+Of course as was said above player can be pulled towards 2 times in a row but that looks like a rare case.
+*/
+
enum Spells
{
- SPELL_LEVITATE = 31704,
- SPELL_SUSPENSION = 31719,
- SPELL_LEVITATION_PULSE = 31701,
- SPELL_MAGNETIC_PULL = 31705,
- SPELL_CHAIN_LIGHTNING = 31717,
- SPELL_STATIC_CHARGE = 31715,
- SPELL_SUMMON_SPORE_STRIDER = 38755
+ SPELL_LEVITATE = 31704,
+ SPELL_CHAIN_LIGHTNING = 31717,
+ SPELL_STATIC_CHARGE = 31715, // Never seen any cast on retail, probably because of shared cooldown with Chain Lightning
+ SPELL_SUMMON_PLAYER = 20279, // NYI, may be 20311 or any other
+ SPELL_SUMMON_SPORE_STRIDER_SCRIPT = 38756,
+
+ SPELL_LEVITATION_PULSE = 31701,
+ SPELL_SOMEONE_GRAB_ME = 31702,
+ SPELL_MAGNETIC_PULL = 31703,
+ SPELL_SUSPENSION_PRIMER = 31720,
+ SPELL_SUSPENSION = 31719,
+
+ SPELL_SUMMON_SPORE_STRIDER = 38755
};
-enum CreatureIdS
+enum Events
{
- ENTRY_SPORE_STRIDER = 22299
+ EVENT_LEASH_CHECK = 1,
+ EVENT_LEVITATE,
+ EVENT_CHAIN_LIGHTNING,
+ EVENT_STATIC_CHARGE,
+ EVENT_SUMMON_SPORE_STRIDER
};
-class boss_the_black_stalker : public CreatureScript
+struct boss_the_black_stalker : public ScriptedAI
{
-public:
- boss_the_black_stalker() : CreatureScript("boss_the_black_stalker") { }
+ boss_the_black_stalker(Creature* creature) : ScriptedAI(creature), _summons(creature) { }
- CreatureAI* GetAI(Creature* creature) const override
+ void Reset() override
{
- return GetTheUnderbogAI<boss_the_black_stalkerAI>(creature);
+ _events.Reset();
+ _summons.DespawnAll();
}
- struct boss_the_black_stalkerAI : public ScriptedAI
+ void JustEngagedWith(Unit* /*who*/) override
{
- boss_the_black_stalkerAI(Creature* creature) : ScriptedAI(creature), Striders(creature)
- {
- Initialize();
- InAir = false;
- }
+ _events.ScheduleEvent(EVENT_LEASH_CHECK, 5s);
+ _events.ScheduleEvent(EVENT_LEVITATE, 8s, 18s);
+ _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 0s, 3s);
+ _events.ScheduleEvent(EVENT_STATIC_CHARGE, 10s);
+ if (IsHeroic())
+ _events.ScheduleEvent(EVENT_SUMMON_SPORE_STRIDER, 20s, 30s);
+ }
- void Initialize()
- {
- Levitate_Timer = 12000;
- ChainLightning_Timer = 6000;
- StaticCharge_Timer = 10000;
- SporeStriders_Timer = 10000 + rand32() % 5000;
- check_Timer = 5000;
- LevitatedTarget.Clear();
- LevitatedTarget_Timer = 0;
- }
+ void JustSummoned(Creature* summon) override
+ {
+ _summons.Summon(summon);
- uint32 SporeStriders_Timer;
- uint32 Levitate_Timer;
- uint32 ChainLightning_Timer;
- uint32 StaticCharge_Timer;
- ObjectGuid LevitatedTarget;
- uint32 LevitatedTarget_Timer;
- bool InAir;
- uint32 check_Timer;
- SummonList Striders;
-
- void Reset() override
- {
- Initialize();
- Striders.DespawnAll();
- }
+ if (me->IsEngaged())
+ DoZoneInCombat(summon);
+ }
- void JustEngagedWith(Unit* /*who*/) override { }
+ void JustDied(Unit* /*killer*/) override
+ {
+ _summons.DespawnAll();
+ }
- void JustSummoned(Creature* summon) override
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = _events.ExecuteEvent())
{
- if (summon && summon->GetEntry() == ENTRY_SPORE_STRIDER)
+ switch (eventId)
{
- Striders.Summon(summon);
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
- summon->AI()->AttackStart(target);
- else
- if (me->GetVictim())
- summon->AI()->AttackStart(me->GetVictim());
+ case EVENT_LEASH_CHECK:
+ {
+ float x, y, z, o;
+ me->GetHomePosition(x, y, z, o);
+ if (!me->IsWithinDist3d(x, y, z, 60))
+ {
+ EnterEvadeMode();
+ return;
+ }
+ _events.Repeat(1s);
+ break;
+ }
+ case EVENT_LEVITATE:
+ DoCastSelf(SPELL_LEVITATE);
+ _events.Repeat(18s, 24s);
+ break;
+ case EVENT_CHAIN_LIGHTNING:
+ DoCastVictim(SPELL_CHAIN_LIGHTNING);
+ _events.Repeat(6s, 12s);
+ break;
+ case EVENT_STATIC_CHARGE:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30, true))
+ DoCast(target, SPELL_STATIC_CHARGE);
+ _events.Repeat(10s);
+ break;
+ case EVENT_SUMMON_SPORE_STRIDER:
+ DoCastSelf(SPELL_SUMMON_SPORE_STRIDER_SCRIPT);
+ _events.Repeat(15s, 25s);
+ break;
+ default:
+ break;
}
- }
- void JustDied(Unit* /*killer*/) override
- {
- Striders.DespawnAll();
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
+ DoMeleeAttackIfReady();
+ }
- // Evade if too far
- if (check_Timer <= diff)
- {
- float x, y, z, o;
- me->GetHomePosition(x, y, z, o);
- if (!me->IsWithinDist3d(x, y, z, 60))
- {
- EnterEvadeMode();
- return;
- }
- check_Timer = 1000;
- } else check_Timer -= diff;
+private:
+ EventMap _events;
+ SummonList _summons;
+};
- // Spore Striders
- if (IsHeroic() && SporeStriders_Timer <= diff)
- {
- DoCast(me, SPELL_SUMMON_SPORE_STRIDER);
- SporeStriders_Timer = 10000 + rand32() % 5000;
- } else SporeStriders_Timer -= diff;
+// 31704 - Levitate
+class spell_the_black_stalker_levitate : public SpellScript
+{
+ PrepareSpellScript(spell_the_black_stalker_levitate);
- // Levitate
- if (!LevitatedTarget.IsEmpty())
- {
- if (LevitatedTarget_Timer <= diff)
- {
- if (Unit* target = ObjectAccessor::GetUnit(*me, LevitatedTarget))
- {
- if (!target->HasAura(SPELL_LEVITATE))
- {
- LevitatedTarget.Clear();
- return;
- }
- if (InAir)
- {
- target->AddAura(SPELL_SUSPENSION, target);
- LevitatedTarget.Clear();
- }
- else
- {
- target->CastSpell(target, SPELL_MAGNETIC_PULL, true);
- InAir = true;
- LevitatedTarget_Timer = 1500;
- }
- }
- else
- LevitatedTarget.Clear();
- } else LevitatedTarget_Timer -= diff;
- }
- if (Levitate_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
- {
- DoCast(target, SPELL_LEVITATE);
- LevitatedTarget = target->GetGUID();
- LevitatedTarget_Timer = 2000;
- InAir = false;
- }
- Levitate_Timer = 12000 + rand32() % 3000;
- } else Levitate_Timer -= diff;
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_LEVITATION_PULSE });
+ }
- // Chain Lightning
- if (ChainLightning_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- DoCast(target, SPELL_CHAIN_LIGHTNING);
- ChainLightning_Timer = 7000;
- } else ChainLightning_Timer -= diff;
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetHitUnit()->CastSpell(GetHitUnit(), SPELL_LEVITATION_PULSE, true);
+ }
- // Static Charge
- if (StaticCharge_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30, true))
- DoCast(target, SPELL_STATIC_CHARGE);
- StaticCharge_Timer = 10000;
- } else StaticCharge_Timer -= diff;
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_black_stalker_levitate::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
- DoMeleeAttackIfReady();
- }
- };
+// 31701 - Levitation Pulse
+class spell_the_black_stalker_levitation_pulse : public SpellScript
+{
+ PrepareSpellScript(spell_the_black_stalker_levitation_pulse);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_SOMEONE_GRAB_ME });
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetCaster(), SPELL_SOMEONE_GRAB_ME, true);
+ }
+ void Register() override
+ {
+ OnEffectHit += SpellEffectFn(spell_the_black_stalker_levitation_pulse::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
+
+// 31702 - Someone Grab Me
+class spell_the_black_stalker_someone_grab_me : public SpellScript
+{
+ PrepareSpellScript(spell_the_black_stalker_someone_grab_me);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_MAGNETIC_PULL, SPELL_SUSPENSION });
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (!GetCaster()->HasAura(SPELL_SUSPENSION))
+ GetHitUnit()->CastSpell(GetCaster(), SPELL_MAGNETIC_PULL);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_black_stalker_someone_grab_me::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
+
+// 31703 - Magnetic Pull
+class spell_the_black_stalker_magnetic_pull : public SpellScript
+{
+ PrepareSpellScript(spell_the_black_stalker_magnetic_pull);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_SUSPENSION_PRIMER });
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUSPENSION_PRIMER, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_black_stalker_magnetic_pull::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
+
+// 38756 - Summon Spore Strider
+class spell_the_black_stalker_summon_spore_strider : public SpellScript
+{
+ PrepareSpellScript(spell_the_black_stalker_summon_spore_strider);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_SUMMON_SPORE_STRIDER });
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ for (uint8 i = 0; i < 3; i++)
+ GetCaster()->CastSpell(GetCaster(), SPELL_SUMMON_SPORE_STRIDER, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHit += SpellEffectFn(spell_the_black_stalker_summon_spore_strider::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
};
void AddSC_boss_the_black_stalker()
{
- new boss_the_black_stalker();
+ RegisterTheUnderbogCreatureAI(boss_the_black_stalker);
+ RegisterSpellScript(spell_the_black_stalker_levitate);
+ RegisterSpellScript(spell_the_black_stalker_levitation_pulse);
+ RegisterSpellScript(spell_the_black_stalker_someone_grab_me);
+ RegisterSpellScript(spell_the_black_stalker_magnetic_pull);
+ RegisterSpellScript(spell_the_black_stalker_summon_spore_strider);
}