From 5e45615d29849fa079319cb3b40df71a83db352d Mon Sep 17 00:00:00 2001 From: Stephen Schlie Date: Sat, 3 Oct 2026 07:49:15 -0700 Subject: [PATCH] fix(Scripts/AzjolNerub): Hadronox encounter door mob spawns (#27905) Co-authored-by: Claude Sonnet 5.5 Co-authored-by: Treeston Co-authored-by: sogladev --- .../rev_1790969107725680033.sql | 19 +++++ .../rev_1791001008978942047.sql | 2 + .../AzjolNerub/AzjolNerub/azjol_nerub.h | 4 + .../AzjolNerub/AzjolNerub/boss_hadronox.cpp | 81 +++++++++++++++---- .../AzjolNerub/instance_azjol_nerub.cpp | 14 ++-- 5 files changed, 100 insertions(+), 20 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1790969107725680033.sql create mode 100644 data/sql/updates/pending_db_world/rev_1791001008978942047.sql diff --git a/data/sql/updates/pending_db_world/rev_1790969107725680033.sql b/data/sql/updates/pending_db_world/rev_1790969107725680033.sql new file mode 100644 index 0000000000..506651a204 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1790969107725680033.sql @@ -0,0 +1,19 @@ +-- Lower Hadronox door adds: attack Hadronox when out of combat and despawn on death, like their 29062-29064 counterparts +DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `entryorguid` IN (29096, 29097, 29098); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(29096,0,0,0,0,0,100,2,6000,9000,17000,32000,0,0,11,53317,32,0,0,0,0,5,0,0,0,53317,0,0,0,0,'Anub\'ar Champion - In Combat - Cast \'Rend\' (Phase 1) (No Repeat) (Dungeon)'), +(29096,0,1,0,0,0,100,4,4000,7000,15000,18000,0,0,11,59343,32,0,0,0,0,5,0,0,0,59343,0,0,0,0,'Anub\'ar Champion - In Combat - Cast \'Rend\' (Phase 1) (No Repeat) (Dungeon)'), +(29096,0,2,0,105,0,25,2,14000,17000,14000,17000,0,5,11,53394,1,0,0,0,0,7,0,0,0,0,0,0,0,0,'Anub\'ar Champion - Target Casting - Cast \'Pummel\' (Phase 1) (No Repeat) (Dungeon)'), +(29096,0,3,0,105,0,25,4,9000,12000,9000,12000,0,5,11,59344,1,0,0,0,0,7,0,0,0,0,0,0,0,0,'Anub\'ar Champion - Target Casting - Cast \'Pummel\' (Phase 1) (No Repeat) (Dungeon)'), +(29096,0,4,0,1,0,100,0,10000,10000,1000,1000,0,0,49,0,0,0,0,0,0,11,28921,50,1,0,0,0,0,0,'Anub\'ar Champion Out of Combat - Start Attacking'), +(29096,0,5,0,6,0,100,512,0,0,0,0,0,0,41,10000,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Anub\'ar Champion - On Just Died - Despawn In 10000 ms'), +(29097,0,0,0,0,0,100,2,4000,7000,9000,12000,0,0,11,53330,32,0,0,0,0,5,0,0,0,53330,0,0,0,0,'Anub\'ar Crypt Fiend - In Combat - Cast \'Infected Wound\' (Phase 1) (No Repeat) (Dungeon)'), +(29097,0,1,0,0,0,100,4,4000,7000,9000,12000,0,0,11,59348,32,0,0,0,0,5,0,0,0,59348,0,0,0,0,'Anub\'ar Crypt Fiend - In Combat - Cast \'Infected Wound\' (Phase 1) (No Repeat) (Dungeon)'), +(29097,0,2,0,0,0,100,2,9000,12000,13000,17000,0,0,11,53322,1,0,0,0,0,5,0,0,0,0,0,0,0,0,'Anub\'ar Crypt Fiend - In Combat - Cast \'Crushing Webs\' (Phase 1) (No Repeat) (Dungeon)'), +(29097,0,3,0,0,0,100,4,9000,12000,10000,13000,0,0,11,59347,1,0,0,0,0,5,0,0,0,0,0,0,0,0,'Anub\'ar Crypt Fiend - In Combat - Cast \'Crushing Webs\' (Phase 1) (No Repeat) (Dungeon)'), +(29097,0,4,0,1,0,100,0,10000,10000,1000,1000,0,0,49,0,0,0,0,0,0,11,28921,50,1,0,0,0,0,0,'Anub\'ar Crypt Fiend Out of Combat - Start Attacking'), +(29097,0,5,0,6,0,100,512,0,0,0,0,0,0,41,10000,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Anub\'ar Crypt Fiend - On Just Died - Despawn In 10000 ms'), +(29098,0,0,0,0,0,100,6,0,0,4000,6000,0,0,11,53333,64,0,0,0,0,2,0,0,0,0,0,0,0,0,'Anub\'ar Necromancer - Combat CMC - Cast \'Shadow Bolt\' (Dungeon)'), +(29098,0,1,0,0,0,100,6,14000,17000,23000,27000,0,0,11,53334,1,0,0,0,0,1,0,0,0,0,0,0,0,0,'Anub\'ar Necromancer - Combat - Cast \'Animate Bones\' (Dungeon)'), +(29098,0,2,0,1,0,100,0,10000,10000,1000,1000,0,0,49,0,0,0,0,0,0,11,28921,50,1,0,0,0,0,0,'Anub\'ar Necromancer Out of Combat - Start Attacking'), +(29098,0,3,0,6,0,100,512,0,0,0,0,0,0,41,10000,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Anub\'ar Necromancer - On Just Died - Despawn In 10000 ms'); diff --git a/data/sql/updates/pending_db_world/rev_1791001008978942047.sql b/data/sql/updates/pending_db_world/rev_1791001008978942047.sql new file mode 100644 index 0000000000..fcf7bfd29b --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1791001008978942047.sql @@ -0,0 +1,2 @@ +-- Hadronox adds no longer use these paths, they pathfind down the ramp +DELETE FROM `waypoint_data` WHERE `id` IN (3000012, 3000013, 3000014); diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h index 7dbf96aff1..f707b0d23d 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h @@ -54,6 +54,10 @@ enum ANIds NPC_ANUB_AR_CHAMPION = 29062, NPC_ANUB_AR_NECROMANCER = 29063, NPC_ANUB_AR_CRYPTFIEND = 29064, + // Summoned by the lower Hadronox door (53090-53092) + NPC_ANUB_AR_CHAMPION_LOWER = 29096, + NPC_ANUB_AR_CRYPT_FIEND_LOWER = 29097, + NPC_ANUB_AR_NECROMANCER_LOWER = 29098, GO_KRIKTHIR_DOORS = 192395, GO_ANUBARAK_DOORS1 = 192396, diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index 4d119ed5e8..7fd20f9945 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -17,6 +17,7 @@ #include "AchievementCriteriaScript.h" #include "CreatureScript.h" +#include "PathGenerator.h" #include "Player.h" #include "ScriptedCreature.h" #include "SpellScriptLoader.h" @@ -30,6 +31,9 @@ enum Spells SPELL_SUMMON_ANUBAR_CHAMPION = 53064, SPELL_SUMMON_ANUBAR_CRYPT_FIEND = 53065, SPELL_SUMMON_ANUBAR_NECROMANCER = 53066, + SPELL_SUMMON_ANUBAR_CHAMPION_LOWER = 53090, + SPELL_SUMMON_ANUBAR_CRYPT_FIEND_LOWER = 53091, + SPELL_SUMMON_ANUBAR_NECROMANCER_LOWER = 53092, SPELL_SUMMON_ANUBAR_CHAMPION_PERIODIC = 53035, SPELL_SUMMON_ANUBAR_NECROMANCER_PERIODIC = 53036, SPELL_SUMMON_ANUBAR_CRYPT_FIEND_PERIODIC = 53037, @@ -123,6 +127,22 @@ enum Misc ACTION_PACK_WALK = 3, }; +static Position const LowerDoorPosition = { 581.0f, 608.5f, 739.0f }; +static Position const RampUpperLanding = { 571.498718f, 576.978333f, 727.582947f }; +static Position const RampBottom = { 532.775f, 535.233f, 681.056f }; + +// Stops of the add path from each door. The first one is right outside the door's web wall and is reached in a +// straight line because the navmesh does not pass through the webbing; the others are reached down the ramp by +// pathfinding. +static std::array, 3> const doorPaths = +{{ + { { 485.314606f, 611.418640f, 771.428406f }, { 513.574341f, 587.022156f, 736.229065f }, RampUpperLanding, + RampBottom }, + { { 575.760437f, 611.516418f, 771.427368f }, { 537.920410f, 580.436157f, 732.796692f }, RampUpperLanding, + RampBottom }, + { { 588.930725f, 598.233276f, 739.142151f }, { 601.289246f, 583.259644f, 725.443054f }, RampBottom }, +}}; + static const std::array hadronoxSteps = {{ { 562.191f, 514.068f, 696.50710f }, @@ -202,19 +222,45 @@ struct boss_hadronox : public BossAI case NPC_ANUB_AR_CHAMPION: case NPC_ANUB_AR_NECROMANCER: case NPC_ANUB_AR_CRYPTFIEND: - // Xinef: cannot use pathfinding... - if (summon->GetDistance(477.0f, 618.0f, 771.0f) < 5.0f) - summon->GetMotionMaster()->MoveWaypoint(3000012, false); - else if (summon->GetDistance(583.0f, 617.0f, 771.0f) < 5.0f) - summon->GetMotionMaster()->MoveWaypoint(3000013, false); - else if (summon->GetDistance(581.0f, 608.5f, 739.0f) < 5.0f) - summon->GetMotionMaster()->MoveWaypoint(3000014, false); + case NPC_ANUB_AR_CHAMPION_LOWER: + case NPC_ANUB_AR_CRYPT_FIEND_LOWER: + case NPC_ANUB_AR_NECROMANCER_LOWER: + MoveDownRamp(summon); break; default: break; } } + void MoveDownRamp(Creature* summon) + { + auto const& doorPath = *std::min_element(doorPaths.begin(), doorPaths.end(), + [summon](std::vector const& a, std::vector const& b) + { + return summon->GetExactDistSq(a.front()) < summon->GetExactDistSq(b.front()); + }); + + Movement::PointsArray path; + path.emplace_back(summon->GetPositionX(), summon->GetPositionY(), summon->GetPositionZ()); + path.emplace_back(doorPath.front().GetPositionX(), doorPath.front().GetPositionY(), + doorPath.front().GetPositionZ()); + + PathGenerator pathGenerator(summon); + for (std::size_t i = 1; i < doorPath.size(); ++i) + { + Position const& from = doorPath[i - 1]; + Position const& to = doorPath[i]; + bool const found = pathGenerator.CalculatePath(from.GetPositionX(), from.GetPositionY(), + from.GetPositionZ(), to.GetPositionX(), to.GetPositionY(), to.GetPositionZ(), false); + if (found && (pathGenerator.GetPathType() & PATHFIND_NORMAL)) + path.insert(path.end(), std::next(pathGenerator.GetPath().begin()), pathGenerator.GetPath().end()); + else + path.emplace_back(to.GetPositionX(), to.GetPositionY(), to.GetPositionZ()); + } + + summon->GetMotionMaster()->MoveSplinePath(&path, FORCED_MOVEMENT_RUN); + } + void KilledUnit(Unit* victim) override { if (!me->IsAlive() || !victim->HasAura(SPELL_LEECH_POISON)) @@ -610,22 +656,28 @@ class spell_hadronox_summon_periodic_aura : public AuraScript PrepareAuraScript(spell_hadronox_summon_periodic_aura); public: - spell_hadronox_summon_periodic_aura(int32 delay, uint32 spellEntry) : _delay(delay), _spellEntry(spellEntry) { } + static constexpr int32 SUMMON_PERIOD = 15'000; + + spell_hadronox_summon_periodic_aura(int32 delay, uint32 spellEntry, uint32 lowerDoorSpellEntry) : _delay(delay), _spellEntry(spellEntry), _lowerDoorSpellEntry(lowerDoorSpellEntry) { } bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_WEB_FRONT_DOORS }); + return ValidateSpellInfo({ SPELL_WEB_FRONT_DOORS, _spellEntry, _lowerDoorSpellEntry }); } - void HandlePeriodic(AuraEffect const* /*aurEff*/) + void HandlePeriodic(AuraEffect const* aurEff) { PreventDefaultAction(); + // The three auras start 5s apart (see OnApply) and each repeats every 15s, so a door spawns one add per 5s. + // The core re-arms the timer with the DBC amplitude before each tick, so pin the period here. + GetAura()->GetEffect(aurEff->GetEffIndex())->SetPeriodicTimer(SUMMON_PERIOD); + Unit* owner = GetUnitOwner(); if (InstanceScript* instance = owner->GetInstanceScript()) if (!instance->IsBossDone(DATA_HADRONOX) != NOT_STARTED) { if (!owner->HasAura(SPELL_WEB_FRONT_DOORS)) - owner->CastSpell(owner, _spellEntry, true); + owner->CastSpell(owner, owner->GetDistance(LowerDoorPosition) < 5.0f ? _lowerDoorSpellEntry : _spellEntry, true); else if (!instance->IsEncounterInProgress()) owner->RemoveAurasDueToSpell(SPELL_WEB_FRONT_DOORS); } @@ -645,6 +697,7 @@ public: private: int32 _delay; uint32 _spellEntry; + uint32 _lowerDoorSpellEntry; }; class spell_hadronox_leech_poison_aura : public AuraScript @@ -709,9 +762,9 @@ void AddSC_boss_hadronox() RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_champion); RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_crypt_fiend); RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_necromancer); - RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_champion_aura", 15'000, SPELL_SUMMON_ANUBAR_CHAMPION); - RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_necromancer_aura", 10'000, SPELL_SUMMON_ANUBAR_NECROMANCER); - RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_crypt_fiend_aura", 5'000, SPELL_SUMMON_ANUBAR_CRYPT_FIEND); + RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_champion_aura", 5'000, SPELL_SUMMON_ANUBAR_CHAMPION, SPELL_SUMMON_ANUBAR_CHAMPION_LOWER); + RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_necromancer_aura", 10'000, SPELL_SUMMON_ANUBAR_NECROMANCER, SPELL_SUMMON_ANUBAR_NECROMANCER_LOWER); + RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_crypt_fiend_aura", 15'000, SPELL_SUMMON_ANUBAR_CRYPT_FIEND, SPELL_SUMMON_ANUBAR_CRYPT_FIEND_LOWER); RegisterSpellScript(spell_hadronox_leech_poison_aura); RegisterSpellScript(spell_hadronox_web_grab); new achievement_hadronox_denied(); diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp index 6e449d0848..253211bb79 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp @@ -46,12 +46,14 @@ ObjectData const creatureData[] = ObjectData const summonData[] = { - { NPC_SKITTERING_SWARMER, DATA_KRIKTHIR }, - { NPC_SKITTERING_INFECTIOR, DATA_KRIKTHIR }, - { NPC_ANUB_AR_CHAMPION, DATA_HADRONOX }, - { NPC_ANUB_AR_NECROMANCER, DATA_HADRONOX }, - { NPC_ANUB_AR_CRYPTFIEND, DATA_HADRONOX }, - { NPC_WORLD_TRIGGER_LAOI, DATA_HADRONOX }, + { NPC_SKITTERING_SWARMER, DATA_KRIKTHIR }, + { NPC_SKITTERING_INFECTIOR, DATA_KRIKTHIR }, + { NPC_ANUB_AR_CHAMPION, DATA_HADRONOX }, + { NPC_ANUB_AR_NECROMANCER, DATA_HADRONOX }, + { NPC_ANUB_AR_CRYPTFIEND, DATA_HADRONOX }, + { NPC_ANUB_AR_CHAMPION_LOWER, DATA_HADRONOX }, + { NPC_ANUB_AR_CRYPT_FIEND_LOWER, DATA_HADRONOX }, + { NPC_ANUB_AR_NECROMANCER_LOWER, DATA_HADRONOX }, { 0, 0 } };