diff --git a/data/sql/updates/pending_db_world/rev_1778152540751174200.sql b/data/sql/updates/pending_db_world/rev_1778152540751174200.sql new file mode 100644 index 0000000000..bac4717cb2 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1778152540751174200.sql @@ -0,0 +1,13 @@ +-- Hellfire Channelers formation: groupAI = 3 (MEMBER_ASSIST_LEADER | LEADER_ASSIST_MEMBER), pack-aggro only. Wipe handling lives in the instance script's hard reset. +DELETE FROM `creature_formations` WHERE `leaderGUID`=90978; +INSERT INTO `creature_formations` +(`leaderGUID`,`memberGUID`,`dist`,`angle`,`groupAI`,`point_1`,`point_2`) +VALUES +(90978, 90978, 0, 0, 3, 0, 0), +(90978, 90979, 0, 0, 3, 0, 0), +(90978, 90980, 0, 0, 3, 0, 0), +(90978, 90981, 0, 0, 3, 0, 0), +(90978, 90982, 0, 0, 3, 0, 0); + +-- Hellfire Channeler SAI: drop id=6. Death tracking moved to the instance script (OnUnitDeath). +DELETE FROM `smart_scripts` WHERE `entryorguid`=17256 AND `source_type`=0 AND `id`=6; diff --git a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp index e5046d6a6b..edb20d5417 100644 --- a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp @@ -70,12 +70,6 @@ enum Groups GROUP_EARLY_RELEASE_CHECK = 0 }; -enum Actions -{ - ACTION_INCREASE_HELLFIRE_CHANNELER_DEATH_COUNT = 1, - ACTION_BANISH_SELF = 2 -}; - struct boss_magtheridon : public BossAI { boss_magtheridon(Creature* creature) : BossAI(creature, DATA_MAGTHERIDON) @@ -84,7 +78,6 @@ struct boss_magtheridon : public BossAI void Reset() override { BossAI::Reset(); - _channelersKilled = 0; _currentPhase = 0; _castingQuake = false; _recentlySpoken = false; @@ -194,21 +187,19 @@ struct boss_magtheridon : public BossAI void DoAction(int32 action) override { - if (action == ACTION_INCREASE_HELLFIRE_CHANNELER_DEATH_COUNT) + if (action == ACTION_RELEASE_MAGTHERIDON) { - _channelersKilled++; + if (_magReleased) + return; - if (_channelersKilled >= 5 && !_magReleased) + Talk(SAY_EMOTE_FREE); + Talk(SAY_FREE); + scheduler.CancelGroup(GROUP_EARLY_RELEASE_CHECK); //cancel regular countdown + _magReleased = true; + scheduler.Schedule(3s, [this](TaskContext) { - Talk(SAY_EMOTE_FREE); - Talk(SAY_FREE); - scheduler.CancelGroup(GROUP_EARLY_RELEASE_CHECK); //cancel regular countdown - _magReleased = true; - scheduler.Schedule(3s, [this](TaskContext) - { - ScheduleCombatEvents(); - }); - } + ScheduleCombatEvents(); + }); } else if (action == ACTION_BANISH_SELF) { @@ -222,10 +213,6 @@ struct boss_magtheridon : public BossAI BossAI::JustEngagedWith(who); Talk(SAY_EMOTE_BEGIN); - instance->DoForAllMinions(DATA_MAGTHERIDON, [&](Creature* creature) { - creature->SetInCombatWithZone(); - }); - scheduler.Schedule(60s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/) { Talk(SAY_EMOTE_NEARLY); @@ -260,7 +247,6 @@ private: bool _recentlySpoken; bool _magReleased; uint8 _currentPhase; - uint8 _channelersKilled; TaskScheduler _interruptScheduler; }; diff --git a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/instance_magtheridons_lair.cpp b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/instance_magtheridons_lair.cpp index 21279a5a23..13e44cc26c 100644 --- a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/instance_magtheridons_lair.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/instance_magtheridons_lair.cpp @@ -15,6 +15,7 @@ * with this program. If not, see . */ +#include "CreatureAI.h" #include "InstanceMapScript.h" #include "InstanceScript.h" #include "magtheridons_lair.h" @@ -30,12 +31,6 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM } // END }; -MinionData const minionData[] = -{ - { NPC_HELLFIRE_CHANNELER, DATA_MAGTHERIDON }, - { 0, 0 } // END -}; - class instance_magtheridons_lair : public InstanceMapScript { public: @@ -48,17 +43,28 @@ public: SetHeaders(DataHeader); SetBossNumber(MAX_ENCOUNTER); LoadDoorData(doorData); - LoadMinionData(minionData); LoadBossBoundaries(boundaries); } void Initialize() override { + _channelersSet.clear(); _wardersSet.clear(); + _burningAbyssalsSet.clear(); _cubesSet.clear(); _columnSet.clear(); } + bool IsAnyChannelerAlive() + { + return std::ranges::any_of(_channelersSet, [&](ObjectGuid const& guid) + { + if (Creature* channeler = instance->GetCreature(guid)) + return channeler->IsAlive(); + return false; + }); + } + void OnCreatureCreate(Creature* creature) override { switch (creature->GetEntry()) @@ -67,22 +73,42 @@ public: _magtheridonGUID = creature->GetGUID(); break; case NPC_HELLFIRE_CHANNELER: - AddMinion(creature); + _channelersSet.insert(creature->GetGUID()); break; case NPC_HELLFIRE_WARDER: _wardersSet.insert(creature->GetGUID()); break; + case NPC_BURNING_ABYSSAL: + _burningAbyssalsSet.insert(creature->GetGUID()); + break; } } void OnCreatureRemove(Creature* creature) override { - switch (creature->GetEntry()) - { - case NPC_HELLFIRE_CHANNELER: - RemoveMinion(creature); - break; - } + if (creature->GetEntry() == NPC_BURNING_ABYSSAL) + _burningAbyssalsSet.erase(creature->GetGUID()); + } + + void OnUnitDeath(Unit* unit) override + { + Creature* creature = unit ? unit->ToCreature() : nullptr; + if (!creature || creature->GetEntry() != NPC_HELLFIRE_CHANNELER) + return; + + // IN_PROGRESS guard: stays inert during the hard-reset Respawn(true) cycle. + if (GetBossState(DATA_MAGTHERIDON) != IN_PROGRESS || IsAnyChannelerAlive()) + return; + + if (Creature* magtheridon = instance->GetCreature(_magtheridonGUID)) + magtheridon->AI()->DoAction(ACTION_RELEASE_MAGTHERIDON); + } + + void OnCreatureEvade(Creature* creature) override + { + // Phase-1 wipe signal: Mag is ImmuneToPC so BossAI evade does not fire; a Channeler evade is the trigger. + if (creature->GetEntry() == NPC_HELLFIRE_CHANNELER && GetBossState(DATA_MAGTHERIDON) == IN_PROGRESS) + SetBossState(DATA_MAGTHERIDON, NOT_STARTED); } void OnGameObjectCreate(GameObject* go) override @@ -154,6 +180,18 @@ public: if (state == NOT_STARTED) SetData(DATA_COLLAPSE, GO_READY); + + // Hard reset: vanish Channelers and their lingering Burning Abyssal summons. + if (state == NOT_STARTED || state == FAIL) + { + for (ObjectGuid const& guid : _channelersSet) + if (Creature* channeler = instance->GetCreature(guid)) + channeler->Respawn(true); + + for (ObjectGuid const& guid : _burningAbyssalsSet) + if (Creature* abyssal = instance->GetCreature(guid)) + abyssal->DespawnOrUnsummon(); + } } } return true; @@ -164,9 +202,13 @@ public: switch (type) { case DATA_CHANNELER_COMBAT: + // Force the encounter start: Mag is ImmuneToPC so SetInCombatWithZone alone may miss JustEngagedWith. if (GetBossState(DATA_MAGTHERIDON) != IN_PROGRESS) + { + SetBossState(DATA_MAGTHERIDON, IN_PROGRESS); if (Creature* magtheridon = instance->GetCreature(_magtheridonGUID)) magtheridon->SetInCombatWithZone(); + } break; case DATA_ACTIVATE_CUBES: for (ObjectGuid const& guid : _cubesSet) @@ -178,12 +220,16 @@ public: if (GameObject* column = instance->GetGameObject(guid)) column->SetGoState(GOState(data)); break; + default: + break; } } private: ObjectGuid _magtheridonGUID; + GuidSet _channelersSet; GuidSet _wardersSet; + GuidSet _burningAbyssalsSet; GuidSet _cubesSet; GuidSet _columnSet; }; diff --git a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/magtheridons_lair.h b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/magtheridons_lair.h index 51d025429b..d3e9caa143 100644 --- a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/magtheridons_lair.h +++ b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/magtheridons_lair.h @@ -39,7 +39,14 @@ enum NpcIds NPC_HELLFIRE_CHANNELER = 17256, NPC_HELLFIRE_WARDER = 18829, NPC_HELLFIRE_RAID_TRIGGER = 17376, - NPC_TARGET_TRIGGER = 17474 + NPC_TARGET_TRIGGER = 17474, + NPC_BURNING_ABYSSAL = 17454 +}; + +enum MagtheridonActions +{ + ACTION_RELEASE_MAGTHERIDON = 1, + ACTION_BANISH_SELF = 2 }; enum GoIds