From 37ac84e71b5fdff6ec8dc9adbd16a4b3676b84ac Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sun, 19 Jul 2026 08:37:12 -0300 Subject: [PATCH] fix(Scripts/Ulduar): Mimiron Rocket Strike prefers ranged players (#26685) Co-authored-by: Unholychick Co-authored-by: Claude Opus 4.8 --- .../rev_1784434823383895200.sql | 6 + .../Northrend/Ulduar/Ulduar/boss_mimiron.cpp | 118 ++++++++++++++---- 2 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1784434823383895200.sql diff --git a/data/sql/updates/pending_db_world/rev_1784434823383895200.sql b/data/sql/updates/pending_db_world/rev_1784434823383895200.sql new file mode 100644 index 000000000..48e54b576 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1784434823383895200.sql @@ -0,0 +1,6 @@ +-- Mimiron Rocket Strike: spell-driven target selection (prefers ranged players) +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_mimiron_rocket_strike', 'spell_mimiron_rocket_strike_target_select'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(64402, 'spell_mimiron_rocket_strike'), +(65034, 'spell_mimiron_rocket_strike'), +(63681, 'spell_mimiron_rocket_strike_target_select'); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 54102ba97..96a31b557 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -16,9 +16,11 @@ */ #include "AchievementCriteriaScript.h" +#include "Containers.h" #include "CreatureScript.h" #include "GameObjectScript.h" #include "GameTime.h" +#include "GridNotifiers.h" #include "MapMgr.h" #include "PassiveAI.h" #include "Player.h" @@ -54,6 +56,10 @@ enum SpellData SPELL_HEAT_WAVE = 64533, SPELL_ROCKET_STRIKE_AURA = 64064, + SPELL_ROCKET_STRIKE_SINGLE = 64402, // VX-001 fires one of the two mounted rockets + SPELL_ROCKET_STRIKE_BOTH = 65034, // VX-001 fires both mounted rockets + SPELL_ROCKET_STRIKE_TARGET = 63681, // Cast by a fired rocket; picks the impact target (prefers ranged) + SPELL_SUMMON_ROCKET_STRIKE = 63036, // Summons the ground strike at the chosen target NPC_ROCKET_VISUAL = 34050, NPC_ROCKET_STRIKE_N = 34047, @@ -1342,38 +1348,19 @@ struct npc_ulduar_vx001 : public ScriptedAI _events.Repeat(10s); break; case EVENT_SPELL_ROCKET_STRIKE: - if (Vehicle* vk = me->GetVehicleKit()) - { - for( int i = 0; i < (_phase / 2); ++i ) - { - uint8 index = (_phase == 2 ? rand() % 2 : i); - if (Unit* r = vk->GetPassenger(5 + index)) - if (Player* temp = SelectTargetFromPlayerList(100.0f)) - { - if (Creature* trigger = me->SummonCreature(NPC_ROCKET_STRIKE_N, temp->GetPositionX(), temp->GetPositionY(), temp->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 6000)) - trigger->CastSpell(trigger, SPELL_ROCKET_STRIKE_AURA, true); - Position exitPos = r->GetPosition(); - exitPos.m_positionX += cos(me->GetOrientation()) * 2.35f; - exitPos.m_positionY += std::sin(me->GetOrientation()) * 2.35f; - exitPos.m_positionZ += 2.0f * _phase; - r->_ExitVehicle(&exitPos); - me->RemoveAurasByType(SPELL_AURA_CONTROL_VEHICLE, r->GetGUID()); - if (r->IsCreature()) - r->ToCreature()->AI()->SetData(0, 0); - } - } - _events.Repeat(20s); - _events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 10s); - } + me->CastSpell(me, _phase == 2 ? SPELL_ROCKET_STRIKE_SINGLE : SPELL_ROCKET_STRIKE_BOTH, true); + _events.Repeat(20s); + _events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 10s); break; case EVENT_REINSTALL_ROCKETS: if (Vehicle* vk = me->GetVehicleKit()) { for (uint8 i = 5; i <= 6; ++i) - if (!vk->GetPassenger(i)) - if (TempSummon* accessory = me->SummonCreature(NPC_ROCKET_VISUAL, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 4.0f, me->GetOrientation(), TEMPSUMMON_MANUAL_DESPAWN)) - if (!me->HandleSpellClick(accessory, i)) - accessory->UnSummon(); + if (Unit* rocket = vk->GetPassenger(i)) + rocket->SetDisplayId(rocket->GetNativeDisplayId()); // restore the fired rocket's visual + else if (TempSummon* accessory = me->SummonCreature(NPC_ROCKET_VISUAL, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 4.0f, me->GetOrientation(), TEMPSUMMON_MANUAL_DESPAWN)) + if (!me->HandleSpellClick(accessory, i)) + accessory->UnSummon(); } break; case EVENT_SPELL_RAPID_BURST: @@ -1822,6 +1809,75 @@ class spell_ulduar_mimiron_mine_explosion : public SpellScript } }; +// 64402, 65034 - Rocket Strike +class spell_mimiron_rocket_strike : public SpellScript +{ + PrepareSpellScript(spell_mimiron_rocket_strike); + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + // The single-rocket cast picks just one of the two mounted rockets; the "both" cast keeps both. + if (GetSpellInfo()->Id == SPELL_ROCKET_STRIKE_SINGLE && GetCaster()->IsVehicle()) + if (Unit* rocket = GetCaster()->GetVehicleKit()->GetPassenger(urand(5, 6))) + { + targets.clear(); + targets.push_back(rocket); + } + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + // The fired rocket resolves its own impact target, keeping VX-001 as the original caster for credit. + GetHitUnit()->CastSpell((Unit*)nullptr, SPELL_ROCKET_STRIKE_TARGET, TRIGGERED_FULL_MASK, nullptr, nullptr, GetCaster()->GetGUID()); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_rocket_strike::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// 63681 - Rocket Strike +class spell_mimiron_rocket_strike_target_select : public SpellScript +{ + PrepareSpellScript(spell_mimiron_rocket_strike_target_select); + + void FilterTargets(std::list& targets) + { + targets.remove_if([](WorldObject* target) { return !target->IsPlayer(); }); + if (targets.empty()) + return; + + WorldObject* target = Acore::Containers::SelectRandomContainerElement(targets); + + // Prefer players out of melee range; only strike someone within 15y if nobody is further out (patch 3.1.3). + targets.remove_if(Acore::AllWorldObjectsInRange(GetCaster(), 15.0f)); + + if (!targets.empty()) + target = Acore::Containers::SelectRandomContainerElement(targets); + + targets.clear(); + targets.push_back(target); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + ObjectGuid originalCaster = GetOriginalCaster() ? GetOriginalCaster()->GetGUID() : GetCaster()->GetGUID(); + GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ROCKET_STRIKE, TRIGGERED_FULL_MASK, nullptr, nullptr, originalCaster); + GetCaster()->SetDisplayId(11686); // hide the spent rocket until it is reloaded + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_rocket_strike_target_select::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_target_select::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + struct npc_ulduar_mimiron_rocket : public NullCreatureAI { npc_ulduar_mimiron_rocket(Creature* creature) : NullCreatureAI(creature) {} @@ -2265,6 +2321,12 @@ struct npc_ulduar_rocket_strike_trigger : public NullCreatureAI { npc_ulduar_rocket_strike_trigger(Creature* creature) : NullCreatureAI(creature) {} + void InitializeAI() override + { + me->CastSpell(me, SPELL_ROCKET_STRIKE_AURA, true); + me->DespawnOrUnsummon(6s); + } + void SpellHitTarget(Unit* target, SpellInfo const* spell) override { if (!target || !spell) @@ -2340,6 +2402,8 @@ void AddSC_boss_mimiron() RegisterSpellScript(spell_mimiron_rapid_burst_aura); RegisterSpellScript(spell_mimiron_p3wx2_laser_barrage_aura); RegisterSpellScript(spell_ulduar_mimiron_mine_explosion); + RegisterSpellScript(spell_mimiron_rocket_strike); + RegisterSpellScript(spell_mimiron_rocket_strike_target_select); new go_ulduar_do_not_push_this_button(); RegisterUlduarCreatureAI(npc_ulduar_flames_initial); RegisterUlduarCreatureAI(npc_ulduar_flames_spread);