diff options
author | offl <11556157+offl@users.noreply.github.com> | 2022-06-20 20:02:48 +0300 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-09-05 18:58:32 +0200 |
commit | 6f3406a825b8bb2671f11b6a10c23e6a38a5f840 (patch) | |
tree | b77c61e7c67633bb930544e742b490276cc89369 | |
parent | fd018d6c2271192bcf170ab894a4659e51554eb5 (diff) |
Scripts/UB: Rework The Black Stalker (#28040)
(cherry picked from commit c025fcef743122ccd1c2a8e79463941c72ee7e12)
-rw-r--r-- | sql/updates/world/master/2022_09_05_08_world_2022_06_20_01_world.sql | 17 | ||||
-rw-r--r-- | src/server/scripts/Outland/CoilfangReservoir/TheUnderbog/boss_the_black_stalker.cpp | 362 |
2 files changed, 233 insertions, 146 deletions
diff --git a/sql/updates/world/master/2022_09_05_08_world_2022_06_20_01_world.sql b/sql/updates/world/master/2022_09_05_08_world_2022_06_20_01_world.sql new file mode 100644 index 00000000000..cc2e8915b81 --- /dev/null +++ b/sql/updates/world/master/2022_09_05_08_world_2022_06_20_01_world.sql @@ -0,0 +1,17 @@ +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ( +'spell_the_black_stalker_levitate', +'spell_the_black_stalker_levitation_pulse', +'spell_the_black_stalker_someone_grab_me', +'spell_the_black_stalker_magnetic_pull', +'spell_the_black_stalker_summon_spore_strider'); +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(31704,'spell_the_black_stalker_levitate'), +(31701,'spell_the_black_stalker_levitation_pulse'), +(31702,'spell_the_black_stalker_someone_grab_me'), +(31703,'spell_the_black_stalker_magnetic_pull'), +(38756,'spell_the_black_stalker_summon_spore_strider'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 31702; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,31702,0,0,31,0,3,17992,0,0,0,0,"","Group 0: Spell 'Someone Grab Me' (Effect 0) targets creature 'Coilfang Invisible Vacuum Dummy'"); 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); } |