diff options
author | offl <11556157+offl@users.noreply.github.com> | 2021-12-09 17:10:24 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-09 17:10:24 +0200 |
commit | 36ee9714157e3af4e2b6b3bb9a753e28360ccd58 (patch) | |
tree | 835b5bc6c5e067130ee6a82a2bba2e5dcc4afae5 /src | |
parent | 8516984ffe1a0241b5cfc47f2d0124291503aec1 (diff) |
Scripts/HoS: Update Sjonnir (#27357)
Diffstat (limited to 'src')
-rw-r--r-- | src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_sjonnir.cpp | 427 |
1 files changed, 315 insertions, 112 deletions
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_sjonnir.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_sjonnir.cpp index 58654003721..4cb30ef4b87 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_sjonnir.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_sjonnir.cpp @@ -21,100 +21,157 @@ #include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "SpellInfo.h" +#include "SpellMgr.h" +#include "SpellScript.h" -enum Spells +enum Texts { - SPELL_LIGHTING_RING = 51849, // Periodic Trigger (interval 2s) spell = 50841 - SPELL_LIGHTING_RING_1 = 50840, // Periodic Trigger (interval 2s) spell = 50841 - SPELL_STATIC_CHARGE = 50834, // Periodic Trigger 2s interval, spell =50835 - SPELL_CHAIN_LIGHTING = 50830, - SPELL_LIGHTING_SHIELD = 50831, - SPELL_FRENZY = 28747 + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + EMOTE_FRENZY = 3 }; -enum Yells +enum Spells { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2 + SPELL_LIGHTNING_RING_1 = 50840, + SPELL_LIGHTNING_RING_2 = 51849, + SPELL_STATIC_CHARGE = 50834, + SPELL_CHAIN_LIGHTNING = 50830, + SPELL_LIGHTNING_SHIELD = 50831, + SPELL_FRENZY = 28747, + + SPELL_SUMMON_IRON_DWARF_PERIODIC = 50789, // 59860 not used + SPELL_SUMMON_IRON_DWARF_1 = 50790, + SPELL_SUMMON_IRON_DWARF_2 = 50791, + SPELL_SUMMON_IRON_TROGG_PERIODIC = 50792, + SPELL_SUMMON_IRON_TROGG_1 = 50793, + SPELL_SUMMON_IRON_TROGG_2 = 50794, + SPELL_SUMMON_MALFORMED_OOZE_PERIODIC = 50801, + SPELL_SUMMON_MALFORMED_OOZE_1 = 50802, + SPELL_SUMMON_MALFORMED_OOZE_2 = 50803, + SPELL_SUMMON_EARTHEN_DWARF_PERIODIC = 50824, + SPELL_SUMMON_EARTHEN_DWARF_1 = 50825, + SPELL_SUMMON_EARTHEN_DWARF_2 = 50826, + + // Malformed Ooze + SPELL_OOZE_COMBINE_PERIODIC = 50741, + SPELL_OOZE_COMBINE_EFFECT = 50742, + SPELL_SUMMON_IRON_SLUDGE = 50747, + + // Iron Sludge + SPELL_IRON_SLUDGE_SPAWN_VISUAL = 50777, + SPELL_TOXIC_VOLLEY = 50838 }; -enum SjonnirCreatures +enum Creatures { - NPC_FORGED_IRON_TROGG = 27979, - NPC_MALFORMED_OOZE = 27981, - NPC_FORGED_IRON_DWARF = 27982, - NPC_IRON_SLUDGE = 28165, - NPC_EARTHEN_DWARF = 27980 + NPC_FORGED_IRON_TROGG = 27979, + NPC_FORGED_IRON_DWARF = 27982, + NPC_EARTHEN_DWARF = 27980 }; enum Misc { - ACTION_OOZE_DEAD = 1, - DATA_ABUSE_THE_OOZE = 2 + POINT_CENTER = 0, + POINT_COMBINE = 1, + + ACTION_SLUDGE_DEAD = 1, + DATA_ABUSE_THE_OOZE = 2 }; enum Events { - EVENT_CHAIN_LIGHTNING = 1, + EVENT_CHAIN_LIGHTNING = 1, EVENT_LIGHTNING_SHIELD, EVENT_STATIC_CHARGE, - EVENT_LIGHTNING_RING, - EVENT_SUMMON, - EVENT_FRENZY, + EVENT_LIGHTNING_RING_1, + EVENT_LIGHTNING_RING_2, + EVENT_FRENZY }; -Position const PipeLocations[] = -{ - { 1295.44f, 734.07f, 200.3f, 0.0f }, // left - { 1297.7f, 595.6f, 199.9f, 0.0f } // right -}; - -Position const CenterPoint = { 1295.21f, 667.157f, 189.691f, 0.0f }; +Position const CenterPoint = { 1293.8799f, 666.942f, 189.60754f, 0.0f }; struct boss_sjonnir : public BossAI { - boss_sjonnir(Creature* creature) : BossAI(creature, DATA_SJONNIR) - { - Initialize(); - } + boss_sjonnir(Creature* creature) : BossAI(creature, DATA_SJONNIR), + _sludgesKilled(0), _summonsTroggs(false), _summonsOozes(false), _summonsDwarfs(false), _frenzied(false) { } - void Initialize() + void JustEngagedWith(Unit* who) override { - abuseTheOoze = 0; + if (!instance->CheckRequiredBosses(DATA_SJONNIR, who->ToPlayer())) + { + EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK); + return; + } + + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + DoCastSelf(SPELL_SUMMON_IRON_DWARF_PERIODIC); + + events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 10s, 15s); + events.ScheduleEvent(EVENT_STATIC_CHARGE, 15s, 20s); + events.ScheduleEvent(EVENT_LIGHTNING_RING_1, 30s); + /// @todo: Schedule both in combat and out of combat + events.ScheduleEvent(EVENT_LIGHTNING_SHIELD, 5s, 15s); } - void Reset() override + void JustSummoned(Creature* summoned) override { - _Reset(); - Initialize(); + switch (summoned->GetEntry()) + { + case NPC_FORGED_IRON_DWARF: + case NPC_FORGED_IRON_TROGG: + // AttackStart(me->GetVictim()) does not work in case of very first spawn + if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat)) + summoned->AI()->AttackStart(target); + break; + case NPC_EARTHEN_DWARF: + summoned->AI()->AttackStart(me); + break; + } + summons.Summon(summoned); } - void JustEngagedWith(Unit* who) override + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override { - if (!instance->CheckRequiredBosses(DATA_SJONNIR, who->ToPlayer())) + if (me->HealthBelowPctDamaged(75, damage) && !_summonsTroggs) { - EnterEvadeMode(); - return; + _summonsTroggs = true; + me->RemoveAurasDueToSpell(SPELL_SUMMON_IRON_DWARF_PERIODIC); + DoCastSelf(SPELL_SUMMON_IRON_TROGG_PERIODIC, true); } - BossAI::JustEngagedWith(who); - Talk(SAY_AGGRO); + if (me->HealthBelowPctDamaged(50, damage) && !_summonsOozes) + { + _summonsOozes = true; + me->RemoveAurasDueToSpell(sSpellMgr->GetSpellIdForDifficulty(SPELL_SUMMON_IRON_TROGG_PERIODIC, me)); + DoCastSelf(SPELL_SUMMON_MALFORMED_OOZE_PERIODIC, true); + } + + if (me->HealthBelowPctDamaged(25, damage) && !_summonsDwarfs) + { + _summonsDwarfs = true; + me->RemoveAurasDueToSpell(sSpellMgr->GetSpellIdForDifficulty(SPELL_SUMMON_MALFORMED_OOZE_PERIODIC, me)); + DoCastSelf(SPELL_SUMMON_EARTHEN_DWARF_PERIODIC, true); + } - events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 3s, 8s); - events.ScheduleEvent(EVENT_LIGHTNING_SHIELD, 20s, 25s); - events.ScheduleEvent(EVENT_STATIC_CHARGE, 20s, 25s); - events.ScheduleEvent(EVENT_LIGHTNING_RING, 30s, 35s); - events.ScheduleEvent(EVENT_SUMMON, 5s); - events.ScheduleEvent(EVENT_FRENZY, 5min); + if (me->HealthBelowPctDamaged(20, damage) && !_frenzied) + { + _frenzied = true; + // Old removed, more powerful added + events.CancelEvent(EVENT_LIGHTNING_RING_1); + events.ScheduleEvent(EVENT_FRENZY, 0s); + events.ScheduleEvent(EVENT_LIGHTNING_RING_2, 0s); + } } - void JustSummoned(Creature* summon) override + void EnterEvadeMode(EvadeReason /*why*/) override { - summon->GetMotionMaster()->MovePoint(0, CenterPoint); - /*if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - summon->AI()->AttackStart(target);*/ - summons.Summon(summon); + summons.DespawnAll(); + /// @todo: Despawn Brann too but respawn his pre-fight version (https://www.youtube.com/watch?v=hxAxbjGfuDw) + _DespawnAtEvade(); } void JustDied(Unit* /*killer*/) override @@ -131,14 +188,14 @@ struct boss_sjonnir : public BossAI void DoAction(int32 action) override { - if (action == ACTION_OOZE_DEAD) - ++abuseTheOoze; + if (action == ACTION_SLUDGE_DEAD) + ++_sludgesKilled; } uint32 GetData(uint32 type) const override { if (type == DATA_ABUSE_THE_OOZE) - return abuseTheOoze; + return _sludgesKilled; return 0; } @@ -158,39 +215,30 @@ struct boss_sjonnir : public BossAI switch (eventId) { case EVENT_CHAIN_LIGHTNING: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - DoCast(target, SPELL_CHAIN_LIGHTING); - events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 10s, 15s); + DoCastVictim(SPELL_CHAIN_LIGHTNING); + events.Repeat(10s, 15s); break; case EVENT_LIGHTNING_SHIELD: - DoCast(me, SPELL_LIGHTING_SHIELD); + if (!me->HasAura(sSpellMgr->GetSpellIdForDifficulty(SPELL_LIGHTNING_SHIELD, me))) + DoCastSelf(SPELL_LIGHTNING_SHIELD); + events.Repeat(5s, 15s); break; case EVENT_STATIC_CHARGE: - DoCastVictim(SPELL_STATIC_CHARGE); - events.ScheduleEvent(EVENT_STATIC_CHARGE, 20s, 25s); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true, true)) + DoCast(target, SPELL_STATIC_CHARGE); + events.Repeat(20s, 27s); break; - case EVENT_LIGHTNING_RING: - DoCast(me, SPELL_LIGHTING_RING); - events.ScheduleEvent(EVENT_LIGHTNING_RING, 30s, 35s); + case EVENT_LIGHTNING_RING_1: + DoCastSelf(SPELL_LIGHTNING_RING_1); + events.Repeat(50s); break; - case EVENT_SUMMON: - { - uint8 summonPipe = urand(0, 1); - if (HealthAbovePct(75)) - me->SummonCreature(NPC_FORGED_IRON_DWARF, PipeLocations[summonPipe], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30s); - else if (HealthAbovePct(50)) - me->SummonCreature(NPC_FORGED_IRON_TROGG, PipeLocations[summonPipe], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30s); - else if (HealthAbovePct(25)) - me->SummonCreature(NPC_MALFORMED_OOZE, PipeLocations[summonPipe], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30s); - else - me->SummonCreature(NPC_EARTHEN_DWARF, PipeLocations[summonPipe], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30s); - - events.ScheduleEvent(EVENT_SUMMON, 20s); + case EVENT_LIGHTNING_RING_2: + DoCastSelf(SPELL_LIGHTNING_RING_2); + events.Repeat(15s); break; - } case EVENT_FRENZY: - /// @todo: add emote - DoCast(me, SPELL_FRENZY, true); + Talk(EMOTE_FRENZY); + DoCastSelf(SPELL_FRENZY); break; default: break; @@ -203,65 +251,215 @@ struct boss_sjonnir : public BossAI DoMeleeAttackIfReady(); } - private: - uint8 abuseTheOoze; +private: + uint8 _sludgesKilled; + bool _summonsTroggs; + bool _summonsOozes; + bool _summonsDwarfs; + bool _frenzied; }; struct npc_malformed_ooze : public ScriptedAI { - npc_malformed_ooze(Creature* creature) : ScriptedAI(creature) + npc_malformed_ooze(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override { - Initialize(); + me->SetCorpseDelay(5, true); + me->SetReactState(REACT_PASSIVE); } - void Initialize() + void JustAppeared() override { - _mergeTimer = 10000; + me->GetMotionMaster()->MovePoint(POINT_CENTER, CenterPoint); } - void Reset() override + void MovementInform(uint32 type, uint32 id) override { - Initialize(); + if (type == POINT_MOTION_TYPE && id == POINT_CENTER) + { + DoCastSelf(SPELL_OOZE_COMBINE_PERIODIC); + me->GetMotionMaster()->MoveRandom(10); + } } - void UpdateAI(uint32 diff) override + /* This is far from correct implementation. Once ooze reaches center point, it casts periodic aura. When spell hits another ooze, both + caster and target removes periodic aura and caster starts moving to target. Target does not stop random movement. Once caster is close + enough to target, caster casts a spell to combine with target. Target despawns instantly, caster despawns after 1sec. + + Since target does not stop random movement, that causes problems because combine spell is used when oozes are close enough to each other + but with current implementation it takes too much time to reach target, as result there may be too much oozes trying to combine at + the same time. + Increasing radius at which combine spell can be used or trying to change the way oozes chases target only creates more problems because + combine spell may target not required target but just ooze which was closer to caster. That leaves multiple spawns which can't combine + anymore since combining process was started(auras were removed) but wasn't finished sucessfully + + Currently target is forced to stop random movement and caster just casts spell when is close enough to target. Spells has additional + conditions in DB. That makes oozes combine every time sucessfully but not too fast and give players more time too kill sludges + Ideally both spells should be scripted, filtering targets(if possible) and casting spell on target which was stored previously may look + overcomplicated but saves from casting spell on wrong target. Or maybe solving the problem with moving to stored target will be enough */ + void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override { - if (_mergeTimer <= diff) + Creature* creatureTarget = target->ToCreature(); + if (!creatureTarget) + return; + + switch (spellInfo->Id) { - if (Creature* temp = me->FindNearestCreature(NPC_MALFORMED_OOZE, 3.0f, true)) - { - DoSpawnCreature(NPC_IRON_SLUDGE, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20s); - temp->DisappearAndDie(); - me->DisappearAndDie(); - } - _mergeTimer = 3000; + case SPELL_OOZE_COMBINE_EFFECT: + _combineTarget = creatureTarget->GetGUID(); + me->RemoveAurasDueToSpell(SPELL_OOZE_COMBINE_PERIODIC); + creatureTarget->RemoveAurasDueToSpell(SPELL_OOZE_COMBINE_PERIODIC); + creatureTarget->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->MovePoint(POINT_COMBINE, creatureTarget->GetPosition()); + + _scheduler.Schedule(1s, [this](TaskContext task) + { + Creature* combineTarget = ObjectAccessor::GetCreature(*me, _combineTarget); + // Completely unclear what should happen in this case or in case when caster dies + if (!combineTarget || !combineTarget->IsAlive()) + { + me->DespawnOrUnsummon(); + return; + } + + if (me->GetExactDist2d(combineTarget) <= 0.1f) + DoCast(combineTarget, SPELL_SUMMON_IRON_SLUDGE); + else + { + me->GetMotionMaster()->MovePoint(POINT_COMBINE, combineTarget->GetPosition()); + task.Repeat(); + } + }); + break; + case SPELL_SUMMON_IRON_SLUDGE: + creatureTarget->DespawnOrUnsummon(); + me->DespawnOrUnsummon(1s); + break; } - else - _mergeTimer -= diff; + } + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } + +private: + TaskScheduler _scheduler; + ObjectGuid _combineTarget; +}; + +struct npc_iron_sludge : public ScriptedAI +{ + npc_iron_sludge(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void JustAppeared() override + { + me->SetCorpseDelay(4, true); + DoCastSelf(SPELL_IRON_SLUDGE_SPAWN_VISUAL); + + if (Creature* sjonnir = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SJONNIR))) + sjonnir->AI()->JustSummoned(me); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.Schedule(3s, 6s, [this](TaskContext task) + { + DoCastSelf(SPELL_TOXIC_VOLLEY); + task.Repeat(3s, 6s); + }); + } + + void JustDied(Unit* /*killer*/) override + { + if (Creature* sjonnir = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SJONNIR))) + sjonnir->AI()->DoAction(ACTION_SLUDGE_DEAD); + } + + void UpdateAI(uint32 diff) override + { if (!UpdateVictim()) return; - DoMeleeAttackIfReady(); + _scheduler.Update(diff, [this] + { + DoMeleeAttackIfReady(); + }); } private: - uint32 _mergeTimer; + InstanceScript* _instance; + TaskScheduler _scheduler; }; -struct npc_iron_sludge : public ScriptedAI +/* 50789 - Summon Iron Dwarf + 50792 - Summon Iron Trogg + 59859 - Summon Iron Trogg + 50801 - Summon Malformed Ooze + 59858 - Summon Malformed Ooze + 50824 - Summon Earthen Dwarf */ +class spell_sjonnir_periodic_summon : public AuraScript { - npc_iron_sludge(Creature* creature) : ScriptedAI(creature) + PrepareAuraScript(spell_sjonnir_periodic_summon); + +public: + spell_sjonnir_periodic_summon(uint32 leftPipeSpell, uint32 rightPipeSpell) + : AuraScript(), _leftPipeSpell(leftPipeSpell), _rightPipeSpell(rightPipeSpell) { } + +private: + bool Validate(SpellInfo const* /*spellInfo*/) override { - instance = creature->GetInstanceScript(); + return ValidateSpellInfo({ _leftPipeSpell, _rightPipeSpell }); } - InstanceScript* instance; + void AfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->CastSpell(GetTarget(), RAND(_leftPipeSpell, _rightPipeSpell), true); + } - void JustDied(Unit* /*killer*/) override + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + GetTarget()->CastSpell(GetTarget(), RAND(_leftPipeSpell, _rightPipeSpell), true); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_sjonnir_periodic_summon::AfterApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_sjonnir_periodic_summon::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + + uint32 _leftPipeSpell; + uint32 _rightPipeSpell; +}; + +// 50777 - Iron Sludge Spawn Visual +class spell_sjonnir_iron_sludge_spawn_visual : public AuraScript +{ + PrepareAuraScript(spell_sjonnir_iron_sludge_spawn_visual); + + void AfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // They're indeed passive but I'm not sure enough if it's handled by this aura or directly in script + if (Creature* creature = GetTarget()->ToCreature()) + creature->SetReactState(REACT_PASSIVE); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* creature = GetTarget()->ToCreature()) + { + creature->SetReactState(REACT_AGGRESSIVE); + if (creature->IsAIEnabled() && creature->IsAlive()) + creature->AI()->DoZoneInCombat(); + } + } + + void Register() override { - if (Creature* sjonnir = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SJONNIR))) - sjonnir->AI()->DoAction(ACTION_OOZE_DEAD); + AfterEffectApply += AuraEffectApplyFn(spell_sjonnir_iron_sludge_spawn_visual::AfterApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_sjonnir_iron_sludge_spawn_visual::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; @@ -290,5 +488,10 @@ void AddSC_boss_sjonnir() RegisterHallsOfStoneCreatureAI(boss_sjonnir); RegisterHallsOfStoneCreatureAI(npc_malformed_ooze); RegisterHallsOfStoneCreatureAI(npc_iron_sludge); + RegisterSpellScriptWithArgs(spell_sjonnir_periodic_summon, "spell_sjonnir_periodic_summon_iron_dwarf", SPELL_SUMMON_IRON_DWARF_1, SPELL_SUMMON_IRON_DWARF_2); + RegisterSpellScriptWithArgs(spell_sjonnir_periodic_summon, "spell_sjonnir_periodic_summon_iron_trogg", SPELL_SUMMON_IRON_TROGG_1, SPELL_SUMMON_IRON_TROGG_2); + RegisterSpellScriptWithArgs(spell_sjonnir_periodic_summon, "spell_sjonnir_periodic_summon_malformed_ooze", SPELL_SUMMON_MALFORMED_OOZE_1, SPELL_SUMMON_MALFORMED_OOZE_2); + RegisterSpellScriptWithArgs(spell_sjonnir_periodic_summon, "spell_sjonnir_periodic_summon_earthen_dwarf", SPELL_SUMMON_EARTHEN_DWARF_1, SPELL_SUMMON_EARTHEN_DWARF_2); + RegisterSpellScript(spell_sjonnir_iron_sludge_spawn_visual); new achievement_abuse_the_ooze(); } |