From 5eaa5d57398524fbaefb5e3cd31b762d250bcddf Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:30:51 -0300 Subject: [PATCH 01/42] fix(Core/Battlegrounds): Don't Perform Exploit Prevention Checks on GameMasters (#24520) --- src/server/game/Battlegrounds/Battleground.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 326caf426..13c6f6bf0 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -336,6 +336,9 @@ inline void Battleground::_CheckSafePositions(uint32 diff) for (auto const& [playerGuid, player] : GetPlayers()) { + if (player->IsGameMaster()) + continue; + Position pos = player->GetPosition(); Position const* startPos = GetTeamStartPosition(player->GetBgTeamId()); From 44401dc32c042ec31608b02709a1c8823fd9fffb Mon Sep 17 00:00:00 2001 From: sogladev Date: Mon, 26 Jan 2026 18:48:19 +0100 Subject: [PATCH 02/42] fix(DB/Creature): Add missing vendor gossip_menu_options to some vendors (#24522) --- .../updates/pending_db_world/rev_1769426225116765733.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769426225116765733.sql diff --git a/data/sql/updates/pending_db_world/rev_1769426225116765733.sql b/data/sql/updates/pending_db_world/rev_1769426225116765733.sql new file mode 100644 index 000000000..eed06caa3 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769426225116765733.sql @@ -0,0 +1,8 @@ +-- +DELETE FROM `gossip_menu_option` WHERE `MenuID` IN (7815, 7820, 8760, 10363) AND `OptionID` = 1; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, +`OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(7815, 1, 1, 'Let me browse your goods.', 8097, 3, 128, 0, 0, 0, 0, '', 0, 0), +(7820, 1, 1, 'Let me browse your goods.', 8097, 3, 128, 0, 0, 0, 0, '', 0, 0), +(8760, 1, 1, 'Let me browse your goods.', 8097, 3, 128, 0, 0, 0, 0, '', 0, 0), +(10363, 1, 1, 'Let me browse your goods.', 8097, 3, 128, 0, 0, 0, 0, '', 0, 0); From 4980cac6acd709887de4bd92f1904d4673faafa0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 26 Jan 2026 17:49:40 +0000 Subject: [PATCH 03/42] chore(DB): import pending files Referenced commit(s): 44401dc32c042ec31608b02709a1c8823fd9fffb --- .../rev_1769426225116765733.sql => db_world/2026_01_26_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769426225116765733.sql => db_world/2026_01_26_00.sql} (93%) diff --git a/data/sql/updates/pending_db_world/rev_1769426225116765733.sql b/data/sql/updates/db_world/2026_01_26_00.sql similarity index 93% rename from data/sql/updates/pending_db_world/rev_1769426225116765733.sql rename to data/sql/updates/db_world/2026_01_26_00.sql index eed06caa3..3550eb62a 100644 --- a/data/sql/updates/pending_db_world/rev_1769426225116765733.sql +++ b/data/sql/updates/db_world/2026_01_26_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_24_01 -> 2026_01_26_00 -- DELETE FROM `gossip_menu_option` WHERE `MenuID` IN (7815, 7820, 8760, 10363) AND `OptionID` = 1; INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, From f8882d3e96571b4d0580244e9206c71228790cc7 Mon Sep 17 00:00:00 2001 From: sogladev Date: Mon, 26 Jan 2026 20:17:39 +0100 Subject: [PATCH 04/42] fix(DB/Loot): Adjust Weather-Journal Drops (#24524) --- .../pending_db_world/rev_1769446568744943092.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769446568744943092.sql diff --git a/data/sql/updates/pending_db_world/rev_1769446568744943092.sql b/data/sql/updates/pending_db_world/rev_1769446568744943092.sql new file mode 100644 index 000000000..bab8224be --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769446568744943092.sql @@ -0,0 +1,15 @@ +-- +-- Remove Weather-Beaten Journal from Dark Runed Chest ref +DELETE FROM `reference_loot_template` WHERE (`Entry` = 35037) AND (`Item` IN (34109)); + +-- Add Weather-Beaten Journal to Bag of Fishing Treasures +SET @CHANCE := 20; +DELETE FROM `item_loot_template` WHERE (`Entry` = 35348) AND (`Item` IN (34109)); +INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(35348, 34109, 0, @CHANCE, 0, 1, 0, 1, 1, 'Weather-Beaten Journal'); + +-- Add Weather-Beaten Journal to Bag of Fishing Treasures +SET @CHANCE := 15; +DELETE FROM `item_loot_template` WHERE (`Entry` = 34863) AND (`Item` IN (34109)); +INSERT INTO `item_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(34863, 34109, 0, @CHANCE, 0, 1, 0, 1, 1, 'Weather-Beaten Journal'); From d2f5903bf16ce0db07d916eb7e64723fd1a733bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 26 Jan 2026 19:18:43 +0000 Subject: [PATCH 05/42] chore(DB): import pending files Referenced commit(s): f8882d3e96571b4d0580244e9206c71228790cc7 --- .../rev_1769446568744943092.sql => db_world/2026_01_26_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769446568744943092.sql => db_world/2026_01_26_01.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1769446568744943092.sql b/data/sql/updates/db_world/2026_01_26_01.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1769446568744943092.sql rename to data/sql/updates/db_world/2026_01_26_01.sql index bab8224be..15598ad98 100644 --- a/data/sql/updates/pending_db_world/rev_1769446568744943092.sql +++ b/data/sql/updates/db_world/2026_01_26_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_26_00 -> 2026_01_26_01 -- -- Remove Weather-Beaten Journal from Dark Runed Chest ref DELETE FROM `reference_loot_template` WHERE (`Entry` = 35037) AND (`Item` IN (34109)); From 380fa3835d265e83a4cd578add8505973dfd3dbc Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:22:25 -0300 Subject: [PATCH 06/42] fix(DB/SAI): Improve Crystalcore Devastator (#24487) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../rev_1769031393367311800.sql | 20 +++++++++++++++++++ .../TempestKeep/Eye/instance_the_eye.cpp | 18 ----------------- 2 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1769031393367311800.sql diff --git a/data/sql/updates/pending_db_world/rev_1769031393367311800.sql b/data/sql/updates/pending_db_world/rev_1769031393367311800.sql new file mode 100644 index 000000000..63a1e1676 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769031393367311800.sql @@ -0,0 +1,20 @@ +-- +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 20040); +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 +(20040, 0, 0, 0, 0, 0, 100, 0, 20950, 25000, 17000, 29900, 0, 0, 11, 37102, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - In Combat - Cast \'Knock Away\''), +(20040, 0, 1, 0, 0, 1, 100, 0, 20000, 30000, 20000, 30000, 0, 0, 11, 35035, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - In Combat - Start Countercharge Phase (Phase 1)'), +(20040, 0, 2, 0, 8, 1, 100, 0, 35035, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - On Spell \'Countercharge\' Successful - Enter Countercharge Phase'), +(20040, 0, 3, 6, 105, 2, 10, 0, 3600, 3600, 3600, 3600, 0, 50, 11, 35039, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - On Hostile Casting in Range - Cast \'Countercharge\' if I have a Charge (Phase 2)'), +(20040, 0, 4, 5, 8, 0, 100, 512, 34946, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - On Spellhit \'Golem Repair\' - Store Targetlist'), +(20040, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2004000, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - On Spellhit \'Golem Repair\' - Run Script'), +(20040, 0, 6, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 28, 35035, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - On Hostile Casting in Range - If Interrupt Successful, Remove Charge'), +(20040, 0, 7, 0, 0, 2, 50, 0, 1000, 1000, 1000, 1000, 0, 0, 11, 35035, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - In Combat, Every 1000ms - Refresh Charge on Self (Phase 2)'), +(20040, 0, 8, 0, 0, 2, 100, 0, 15000, 15000, 15000, 15000, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - In Combat - After 15s, Return to Initial Phase (Phase 2)'), +(20040, 0, 9, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crystalcore Devastator - On Aggro - Set Initial Phase'); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceEntry` = 20040) AND (`ConditionTypeOrReference` = 1) AND (`ConditionValue1` = 35035); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 8, 20040, 0, 0, 1, 1, 35035, 0, 0, 1, 0, 0, '', 'Only Refresh Countercharge (35035) Aura if it is not already present'), +(22, 4, 20040, 0, 0, 1, 1, 35035, 0, 0, 0, 0, 0, '', 'Only Interrupt if Crystalcore Devastator has a charge of Countercharge (35035) to spend'); + +DELETE FROM `spell_script_names` WHERE `spell_id` = 35035 AND `ScriptName` = 'spell_the_eye_countercharge_aura'; diff --git a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp index 60e43f0f3..91d41787c 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp @@ -146,25 +146,7 @@ public: } }; -class spell_the_eye_countercharge_aura : public AuraScript -{ - PrepareAuraScript(spell_the_eye_countercharge_aura); - - bool PrepareProc(ProcEventInfo& /*eventInfo*/) - { - // xinef: prevent charge drop - PreventDefaultAction(); - return true; - } - - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_the_eye_countercharge_aura::PrepareProc); - } -}; - void AddSC_instance_the_eye() { new instance_the_eye(); - RegisterSpellScript(spell_the_eye_countercharge_aura); } From dc1a7f305c2878c74a90525fb3ed96bbdfc0460d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 26 Jan 2026 23:23:41 +0000 Subject: [PATCH 07/42] chore(DB): import pending files Referenced commit(s): 380fa3835d265e83a4cd578add8505973dfd3dbc --- .../rev_1769031393367311800.sql => db_world/2026_01_26_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769031393367311800.sql => db_world/2026_01_26_02.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1769031393367311800.sql b/data/sql/updates/db_world/2026_01_26_02.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1769031393367311800.sql rename to data/sql/updates/db_world/2026_01_26_02.sql index 63a1e1676..2e41f4c89 100644 --- a/data/sql/updates/pending_db_world/rev_1769031393367311800.sql +++ b/data/sql/updates/db_world/2026_01_26_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_26_01 -> 2026_01_26_02 -- DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 20040); 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 From 5a4df387d87d154fe6c2b7629c1ee1bf10d0e689 Mon Sep 17 00:00:00 2001 From: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:23:15 +0000 Subject: [PATCH 08/42] fix(DB/NPC) - Adds Disorient and Sap immunities to Marauding Crust Burster (#24528) Co-authored-by: FlyingArowana --- data/sql/updates/pending_db_world/rev_1769473478697917600.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769473478697917600.sql diff --git a/data/sql/updates/pending_db_world/rev_1769473478697917600.sql b/data/sql/updates/pending_db_world/rev_1769473478697917600.sql new file mode 100644 index 000000000..f7d18b05c --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769473478697917600.sql @@ -0,0 +1,2 @@ +-- Adds "Disoriented" and "Sapped" Immunties to "Maruading Crust Burster" outcome from MoP and Retail +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` | 2 | 536870912 WHERE `entry` = 16857; From b6f1d4094e3de289f608db9ea4954e126ea199e7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jan 2026 09:24:18 +0000 Subject: [PATCH 09/42] chore(DB): import pending files Referenced commit(s): 5a4df387d87d154fe6c2b7629c1ee1bf10d0e689 --- .../rev_1769473478697917600.sql => db_world/2026_01_27_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769473478697917600.sql => db_world/2026_01_27_00.sql} (83%) diff --git a/data/sql/updates/pending_db_world/rev_1769473478697917600.sql b/data/sql/updates/db_world/2026_01_27_00.sql similarity index 83% rename from data/sql/updates/pending_db_world/rev_1769473478697917600.sql rename to data/sql/updates/db_world/2026_01_27_00.sql index f7d18b05c..79bdc4ac7 100644 --- a/data/sql/updates/pending_db_world/rev_1769473478697917600.sql +++ b/data/sql/updates/db_world/2026_01_27_00.sql @@ -1,2 +1,3 @@ +-- DB update 2026_01_26_02 -> 2026_01_27_00 -- Adds "Disoriented" and "Sapped" Immunties to "Maruading Crust Burster" outcome from MoP and Retail UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` | 2 | 536870912 WHERE `entry` = 16857; From bc72c4fb8d0e7673eb3fbc99237ec49f342be315 Mon Sep 17 00:00:00 2001 From: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> Date: Tue, 27 Jan 2026 09:25:36 +0000 Subject: [PATCH 10/42] fix(DB/Gossip) - Add missing gossip vendor option for Juno Dufrain (#24519) Co-authored-by: FlyingArowana --- .../updates/pending_db_world/rev_1769390171050432700.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769390171050432700.sql diff --git a/data/sql/updates/pending_db_world/rev_1769390171050432700.sql b/data/sql/updates/pending_db_world/rev_1769390171050432700.sql new file mode 100644 index 000000000..bbfeeed38 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769390171050432700.sql @@ -0,0 +1,7 @@ +-- Updates the trainer option from "Train me." to "I seek training in Fishing." +UPDATE `gossip_menu_option` SET `OptionText` = 'I seek training in Fishing.', `OptionBroadcastTextID` = 34245 WHERE `MenuID` = 10437 AND `OptionID` = 0; + +-- Adds "I would like to buy from you." to gossip menu. Can't parse sniff to validate which 1 out of 5 "I would like to buy from you." is the correct one. +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 10437 AND `OptionID` = 1; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(10437, 1, 1, 'I would like to buy from you.', 9992, 3, 128, 0, 0, 0, 0, '', 0, 0); From 557aab01b48e4732a43a8053243b35329a9d26a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jan 2026 09:26:44 +0000 Subject: [PATCH 11/42] chore(DB): import pending files Referenced commit(s): bc72c4fb8d0e7673eb3fbc99237ec49f342be315 --- .../rev_1769390171050432700.sql => db_world/2026_01_27_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769390171050432700.sql => db_world/2026_01_27_01.sql} (94%) diff --git a/data/sql/updates/pending_db_world/rev_1769390171050432700.sql b/data/sql/updates/db_world/2026_01_27_01.sql similarity index 94% rename from data/sql/updates/pending_db_world/rev_1769390171050432700.sql rename to data/sql/updates/db_world/2026_01_27_01.sql index bbfeeed38..047d9b919 100644 --- a/data/sql/updates/pending_db_world/rev_1769390171050432700.sql +++ b/data/sql/updates/db_world/2026_01_27_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_27_00 -> 2026_01_27_01 -- Updates the trainer option from "Train me." to "I seek training in Fishing." UPDATE `gossip_menu_option` SET `OptionText` = 'I seek training in Fishing.', `OptionBroadcastTextID` = 34245 WHERE `MenuID` = 10437 AND `OptionID` = 0; From 83f5a351b9a2e49796e81ecb7409b0f3c253ca13 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:50:25 -0300 Subject: [PATCH 12/42] fix(DB/Creature): Set Loot Table and AI for phased Malykriss creatures (#24530) --- .../rev_1769512511614626500.sql | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769512511614626500.sql diff --git a/data/sql/updates/pending_db_world/rev_1769512511614626500.sql b/data/sql/updates/pending_db_world/rev_1769512511614626500.sql new file mode 100644 index 000000000..8193f7bcf --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769512511614626500.sql @@ -0,0 +1,58 @@ +-- +UPDATE `creature_template` SET `lootid` = 30921 WHERE (`entry` = 31321); +DELETE FROM `creature_loot_template` WHERE (`Entry` = 31321); + +UPDATE `creature_template` SET `lootid` = 30922 WHERE (`entry` = 31320); +DELETE FROM `creature_loot_template` WHERE (`Entry` = 31320); + +UPDATE `creature_template` SET `lootid` = 31255 WHERE (`entry` = 31322); +DELETE FROM `creature_loot_template` WHERE `Entry` = 31322; + +UPDATE `creature_template` SET `lootid` = 30894 WHERE (`entry` = 31323); +DELETE FROM `creature_loot_template` WHERE `Entry` = 31323; + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 31322; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 31322); +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 +(31322, 0, 0, 0, 0, 0, 100, 0, 5200, 9500, 11750, 16250, 0, 0, 11, 60960, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Saronite Shaper - In Combat - Cast \'War Stomp\''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 31320; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 31320); +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 +(31320, 0, 0, 1, 8, 0, 100, 512, 58283, 0, 0, 0, 0, 0, 33, 31481, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Umbral Brute - On Spellhit \'Throw Rock\' - Quest Credit'), +(31320, 0, 1, 2, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Umbral Brute - On Spellhit \'Throw Rock\' - Say Line 0'), +(31320, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 2, 1692, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Umbral Brute - On Spellhit \'Throw Rock\' - Set Faction 1692'), +(31320, 0, 3, 4, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 2, 1693, 0, 0, 0, 0, 0, 19, 31321, 15, 0, 0, 0, 0, 0, 0, 'Umbral Brute - On Spellhit \'Throw Rock\' - Set Faction 1693'), +(31320, 0, 4, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 19, 31321, 15, 0, 0, 0, 0, 0, 0, 'Umbral Brute - On Spellhit \'Throw Rock\' - Start Attacking'), +(31320, 0, 5, 0, 25, 0, 100, 512, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Umbral Brute - On Reset - Reset Faction'), +(31320, 0, 6, 0, 2, 0, 100, 1, 0, 30, 0, 0, 0, 0, 11, 50420, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Umbral Brute - Between 0-30% Health - Cast \'Enrage\' (No Repeat)'), +(31320, 0, 7, 0, 0, 0, 100, 0, 3000, 4000, 7000, 8000, 0, 0, 11, 13446, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Umbral Brute - In Combat - Cast \'Strike\''), +(31320, 0, 8, 0, 0, 0, 100, 0, 6000, 7000, 10000, 11000, 0, 0, 11, 10966, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Umbral Brute - In Combat - Cast \'Uppercut\''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 31321; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 31321); +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 +(31321, 0, 0, 0, 25, 0, 100, 512, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Runesmith - On Reset - Reset Faction'), +(31321, 0, 1, 0, 0, 0, 100, 0, 3200, 7500, 9750, 13250, 0, 0, 11, 46202, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Runesmith - In Combat - Cast \'Pierce Armor\''); + +UPDATE `gameobject` SET `phaseMask` = `phaseMask`|4 WHERE `id` = 193004 AND `guid` IN (99724, 99725, 99726, 99727); + +DELETE FROM `creature_text` WHERE (`CreatureID` = 30922); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(30922, 0, 0, 'No one throws rocks at me!', 12, 0, 100, 0, 0, 0, 32217, 0, 'Matchmaker (13147)'), +(30922, 0, 1, 'You wouldn\'t like me when I\'m angry...', 12, 0, 100, 0, 0, 0, 32218, 0, 'Matchmaker (13147)'), +(30922, 0, 2, 'Finally! An excuse to squash you!', 12, 0, 100, 0, 0, 0, 32220, 0, 'Matchmaker (13147)'), +(30922, 0, 3, 'You die for good this time, skeleton!', 12, 0, 100, 0, 0, 0, 32221, 0, 'Matchmaker (13147)'), +(30922, 0, 4, 'Last straw...', 12, 0, 100, 0, 0, 0, 32222, 0, 'Matchmaker (13147)'), +(30922, 0, 5, 'I was waiting for a reason to tear you bone from bone!', 12, 0, 100, 0, 0, 0, 32223, 0, 'Matchmaker (13147)'), +(30922, 0, 6, 'That\'s the last mistake you\'ll make, skeleton.', 12, 0, 100, 0, 0, 0, 32224, 0, 'Matchmaker (13147)'); + +DELETE FROM `creature_text` WHERE (`CreatureID` = 31320); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(31320, 0, 0, 'No one throws rocks at me!', 12, 0, 100, 0, 0, 0, 32217, 0, 'Matchmaker (13147)'), +(31320, 0, 1, 'You wouldn\'t like me when I\'m angry...', 12, 0, 100, 0, 0, 0, 32218, 0, 'Matchmaker (13147)'), +(31320, 0, 2, 'Finally! An excuse to squash you!', 12, 0, 100, 0, 0, 0, 32220, 0, 'Matchmaker (13147)'), +(31320, 0, 3, 'You die for good this time, skeleton!', 12, 0, 100, 0, 0, 0, 32221, 0, 'Matchmaker (13147)'), +(31320, 0, 4, 'Last straw...', 12, 0, 100, 0, 0, 0, 32222, 0, 'Matchmaker (13147)'), +(31320, 0, 5, 'I was waiting for a reason to tear you bone from bone!', 12, 0, 100, 0, 0, 0, 32223, 0, 'Matchmaker (13147)'), +(31320, 0, 6, 'That\'s the last mistake you\'ll make, skeleton.', 12, 0, 100, 0, 0, 0, 32224, 0, 'Matchmaker (13147)'); From 47b9bcb6da6f9f7b71d480d45d3da7f56eec42ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jan 2026 14:51:35 +0000 Subject: [PATCH 13/42] chore(DB): import pending files Referenced commit(s): 83f5a351b9a2e49796e81ecb7409b0f3c253ca13 --- .../rev_1769512511614626500.sql => db_world/2026_01_27_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769512511614626500.sql => db_world/2026_01_27_02.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1769512511614626500.sql b/data/sql/updates/db_world/2026_01_27_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1769512511614626500.sql rename to data/sql/updates/db_world/2026_01_27_02.sql index 8193f7bcf..d39d4fa9e 100644 --- a/data/sql/updates/pending_db_world/rev_1769512511614626500.sql +++ b/data/sql/updates/db_world/2026_01_27_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_27_01 -> 2026_01_27_02 -- UPDATE `creature_template` SET `lootid` = 30921 WHERE (`entry` = 31321); DELETE FROM `creature_loot_template` WHERE (`Entry` = 31321); From 987beb2778df568c259d6ac63ca10c53a50101c3 Mon Sep 17 00:00:00 2001 From: Dan1l-sh <99441165+Dan1l-sh@users.noreply.github.com> Date: Tue, 27 Jan 2026 19:51:48 +0300 Subject: [PATCH 14/42] fix(Scripts/Ulduar): YoggSaron get z for tentacles spawn with GetHeight (#24515) --- .../scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp index 1fa645637..1b4ce7bf5 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp @@ -558,8 +558,10 @@ public: { uint32 dist = urand(38, 48); float o = rand_norm() * M_PI * 2; - float Zplus = (dist - 38) / 6.5f; - if (Creature* cr = me->SummonCreature(entry, me->GetPositionX() + dist * cos(o), me->GetPositionY() + dist * std::sin(o), 327.2 + Zplus, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) + float spawnX = me->GetPositionX() + dist * cos(o); + float spawnY = me->GetPositionY() + dist * std::sin(o); + float spawnZ = me->GetMap()->GetHeight(me->GetPhaseMask(), spawnX, spawnY, 330.0f); + if (Creature* cr = me->SummonCreature(entry, spawnX, spawnY, spawnZ, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) { cr->CastSpell(cr, SPELL_TENTACLE_ERUPT, true); cr->CastSpell(cr, SPELL_VOID_ZONE_SMALL, true); From 27042f326c0c9ab7cdbd778e1bf3824eb20da814 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:54:43 -0300 Subject: [PATCH 15/42] fix(DB/SAI): Improve Throwing Down scripting (#24488) --- .../rev_1769035253783591900.sql | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769035253783591900.sql diff --git a/data/sql/updates/pending_db_world/rev_1769035253783591900.sql b/data/sql/updates/pending_db_world/rev_1769035253783591900.sql new file mode 100644 index 000000000..51e67a684 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769035253783591900.sql @@ -0,0 +1,31 @@ +-- +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28352); +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 +(28352, 0, 0, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 50, 190555, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - On Respawn - Summon Gameobject \'Nerubian Crater\''), +(28352, 0, 1, 2, 8, 0, 100, 1, 51381, 0, 0, 0, 0, 0, 80, 2835200, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - On Spellhit \'Toss Grenade\' - Run Script (No Repeat)'), +(28352, 0, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 33, 28352, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - On Spellhit \'Toss Grenade\' - Quest Credit (No Repeat)'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2835200); +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 +(2835200, 9, 0, 0, 0, 0, 100, 0, 4200, 4200, 0, 0, 0, 0, 11, 44762, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Camera Shake - Med\''), +(2835200, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 48456, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Shredder Smoke\''), +(2835200, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51435, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Summon Skimmer\''), +(2835200, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51435, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Summon Skimmer\''), +(2835200, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51435, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Summon Skimmer\''), +(2835200, 9, 5, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51435, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Summon Skimmer\''), +(2835200, 9, 6, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51435, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Summon Skimmer\''), +(2835200, 9, 7, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51435, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Cast \'Summon Skimmer\''), +(2835200, 9, 8, 0, 0, 0, 100, 0, 11300, 11300, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 20, 190555, 5, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Despawn Instant'), +(2835200, 9, 9, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nethurbian Crater KC Bunny - Actionlist - Despawn Instant'); + +DELETE FROM `gameobject` WHERE `id` = 190555 AND `guid` BETWEEN 58160 AND 58168; +DELETE FROM `creature` WHERE `id1` = 28369 AND `guid` BETWEEN 115685 AND 115691; +UPDATE `creature` SET `spawntimesecs` = 120 WHERE `id1` = 28352 AND `guid` BETWEEN 114082 AND 114091; + +UPDATE `creature_template` SET `AIName` = 'SmartAI', `speed_run` = 2 WHERE `entry` = 28369; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28369); +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 +(28369, 0, 0, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 40148, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Burning Skimmer - On Respawn - Cast \'Immolation\''), +(28369, 0, 1, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Burning Skimmer - On Respawn - Set Run On'), +(28369, 0, 2, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 89, 15, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Burning Skimmer - On Respawn - Start Random Movement'), +(28369, 0, 3, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 37, 10000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Burning Skimmer - On Respawn - Kill Self'); From 16844ccd42d0679c0e5cd541bea835e7c7325d92 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jan 2026 16:56:35 +0000 Subject: [PATCH 16/42] chore(DB): import pending files Referenced commit(s): 987beb2778df568c259d6ac63ca10c53a50101c3 --- .../rev_1769035253783591900.sql => db_world/2026_01_27_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769035253783591900.sql => db_world/2026_01_27_03.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1769035253783591900.sql b/data/sql/updates/db_world/2026_01_27_03.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1769035253783591900.sql rename to data/sql/updates/db_world/2026_01_27_03.sql index 51e67a684..2dd0ebed5 100644 --- a/data/sql/updates/pending_db_world/rev_1769035253783591900.sql +++ b/data/sql/updates/db_world/2026_01_27_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_27_02 -> 2026_01_27_03 -- DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28352); 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 From 91784d2df7a894aca55de94aaa1e72b53fc61844 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:31:43 -0300 Subject: [PATCH 17/42] fix(DB/Spell): Gluttonous Lurkers (#24532) --- .../pending_db_world/rev_1769526587939155300.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769526587939155300.sql diff --git a/data/sql/updates/pending_db_world/rev_1769526587939155300.sql b/data/sql/updates/pending_db_world/rev_1769526587939155300.sql new file mode 100644 index 000000000..24e4bc24a --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769526587939155300.sql @@ -0,0 +1,11 @@ +-- +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28203); +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 +(28203, 0, 0, 1, 8, 0, 100, 512, 50918, 0, 0, 0, 0, 0, 11, 50919, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Gorged Lurking Basilisk - On Spellhit \'Gluttonous Lurkers: Create Basilisk Crystals Cover\' - Cast \'Create Basilisk Crystals\''), +(28203, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gorged Lurking Basilisk - On Spellhit \'Gluttonous Lurkers: Create Basilisk Crystals Cover\' - Despawn Instant'); + +DELETE FROM `creature_template_addon` WHERE (`entry` = 28203); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(28203, 0, 0, 0, 1, 0, 0, '50917'); + +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` = 28203 AND `spell_id` = 50919; From bdd20c837ee2273061541f0f66fcc3a195183bb2 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:31:56 -0300 Subject: [PATCH 18/42] fix(DB/SmartAI): Lebronski should only be Bluff-able during his event (#24533) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../rev_1769530367742669100.sql | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769530367742669100.sql diff --git a/data/sql/updates/pending_db_world/rev_1769530367742669100.sql b/data/sql/updates/pending_db_world/rev_1769530367742669100.sql new file mode 100644 index 000000000..43a09361f --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769530367742669100.sql @@ -0,0 +1,27 @@ +-- +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24718); +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 +(24718, 0, 0, 0, 10, 0, 100, 512, 1, 5, 60000, 60000, 0, 0, 80, 2471800, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Within 1-5 Range Out of Combat LoS - Run Script'), +(24718, 0, 1, 0, 8, 1, 100, 512, 44562, 0, 5000, 5000, 0, 0, 80, 2471801, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - On Spellhit \'Bluff\' - Run Script (Phase 1)'), +(24718, 0, 2, 0, 60, 1, 100, 0, 60000, 60000, 60000, 60000, 0, 0, 80, 2471802, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - On Update - Reset Self (Phase 1)'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2471800); +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 +(2471800, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Say Line 1'), +(2471800, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Set Event Phase 1'), +(2471800, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Set Rooted On'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2471801); +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 +(2471801, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Set Event Phase 0'), +(2471801, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 19, 24823, 20, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Say Line 0'), +(2471801, 9, 2, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Say Line 2'), +(2471801, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 19, 24823, 20, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Set Data 0 1'), +(2471801, 9, 4, 0, 0, 0, 100, 0, 10000, 10000, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Set Rooted Off'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2471802); +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 +(2471802, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Set Event Phase 0'), +(2471802, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lebronski - Actionlist - Set Rooted Off'); + +UPDATE `conditions` SET `Comment` = 'Only fire Event for \'Iron Rune Constructs and You: The Bluff\' if Invoker is Iron Rune Construct (24823)' WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 1) AND (`SourceEntry` = 24718) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 24823) AND (`ConditionValue3` = 0); From 5710567d05eeba5c0817f28e0af068c6278ff005 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jan 2026 19:32:51 +0000 Subject: [PATCH 19/42] chore(DB): import pending files Referenced commit(s): 91784d2df7a894aca55de94aaa1e72b53fc61844 --- .../rev_1769526587939155300.sql => db_world/2026_01_27_04.sql} | 1 + .../rev_1769530367742669100.sql => db_world/2026_01_27_05.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1769526587939155300.sql => db_world/2026_01_27_04.sql} (96%) rename data/sql/updates/{pending_db_world/rev_1769530367742669100.sql => db_world/2026_01_27_05.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1769526587939155300.sql b/data/sql/updates/db_world/2026_01_27_04.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1769526587939155300.sql rename to data/sql/updates/db_world/2026_01_27_04.sql index 24e4bc24a..92cf741ce 100644 --- a/data/sql/updates/pending_db_world/rev_1769526587939155300.sql +++ b/data/sql/updates/db_world/2026_01_27_04.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_27_03 -> 2026_01_27_04 -- DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28203); 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 diff --git a/data/sql/updates/pending_db_world/rev_1769530367742669100.sql b/data/sql/updates/db_world/2026_01_27_05.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1769530367742669100.sql rename to data/sql/updates/db_world/2026_01_27_05.sql index 43a09361f..3921bd720 100644 --- a/data/sql/updates/pending_db_world/rev_1769530367742669100.sql +++ b/data/sql/updates/db_world/2026_01_27_05.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_27_04 -> 2026_01_27_05 -- DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24718); 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 From c4eec5231bed3f67cb7ae80b9b79883f383b68ad Mon Sep 17 00:00:00 2001 From: zackchadwick <73787423+zackchadwick@users.noreply.github.com> Date: Tue, 27 Jan 2026 20:38:25 -0500 Subject: [PATCH 20/42] fix(DB/creature): Howling Fjord Rope Swing Invisible Vehicle Stalker visibility (#24101) Co-authored-by: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Co-authored-by: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> --- .../updates/pending_db_world/rev_1765697713697231306.sql | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1765697713697231306.sql diff --git a/data/sql/updates/pending_db_world/rev_1765697713697231306.sql b/data/sql/updates/pending_db_world/rev_1765697713697231306.sql new file mode 100644 index 000000000..7f9be847a --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1765697713697231306.sql @@ -0,0 +1,9 @@ +-- +SET @ENTRY := 24705; + +UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 128 WHERE `entry` = @ENTRY; + +DELETE FROM `creature_template_model` WHERE `CreatureID` = @ENTRY; +INSERT INTO `creature_template_model` (`CreatureID`, `Idx`, `CreatureDisplayID`, `DisplayScale`, `Probability`, `VerifiedBuild`) VALUES +(@ENTRY, 0, 1126, 1, 0, 51831), +(@ENTRY, 1, 11686, 1, 1, 51831); From 6043acac0ab309824f5ffb14bd52162e8e6a1482 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 28 Jan 2026 01:39:28 +0000 Subject: [PATCH 21/42] chore(DB): import pending files Referenced commit(s): c4eec5231bed3f67cb7ae80b9b79883f383b68ad --- .../rev_1765697713697231306.sql => db_world/2026_01_28_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1765697713697231306.sql => db_world/2026_01_28_00.sql} (89%) diff --git a/data/sql/updates/pending_db_world/rev_1765697713697231306.sql b/data/sql/updates/db_world/2026_01_28_00.sql similarity index 89% rename from data/sql/updates/pending_db_world/rev_1765697713697231306.sql rename to data/sql/updates/db_world/2026_01_28_00.sql index 7f9be847a..d82f99233 100644 --- a/data/sql/updates/pending_db_world/rev_1765697713697231306.sql +++ b/data/sql/updates/db_world/2026_01_28_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_27_05 -> 2026_01_28_00 -- SET @ENTRY := 24705; From b79ef2bedf951cbe203b515e535a36bc9b9b0319 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:24:03 -0600 Subject: [PATCH 22/42] feat(Core/Quest): Add BreadcrumbForQuestId field to quest_template_addon (#24079) Co-authored-by: blinkysc Co-authored-by: sogladev Co-authored-by: Gultask <100873791+Gultask@users.noreply.github.com> Co-authored-by: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> --- .../rev_1765373936949009445.sql | 288 ++++++++++++++++++ src/server/game/Entities/Player/Player.h | 1 + .../game/Entities/Player/PlayerQuest.cpp | 36 ++- src/server/game/Globals/ObjectMgr.cpp | 50 ++- src/server/game/Globals/ObjectMgr.h | 12 + src/server/game/Quests/QuestDef.cpp | 23 +- src/server/game/Quests/QuestDef.h | 2 + 7 files changed, 396 insertions(+), 16 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1765373936949009445.sql diff --git a/data/sql/updates/pending_db_world/rev_1765373936949009445.sql b/data/sql/updates/pending_db_world/rev_1765373936949009445.sql new file mode 100644 index 000000000..9398e81d5 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1765373936949009445.sql @@ -0,0 +1,288 @@ +-- +-- Add BreadcrumbForQuestId column to quest_template_addon +-- + +ALTER TABLE `quest_template_addon` +ADD COLUMN `BreadcrumbForQuestId` mediumint unsigned NOT NULL DEFAULT '0' AFTER `ExclusiveGroup`; + +-- Assisting Arch Druid Runetotem +UPDATE `quest_template_addon` SET `NextQuestID` = 0, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 3761 WHERE `ID` IN (936, 3762, 3784); + +-- Assisting Arch Druid Staghelm +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 3764 WHERE `ID` IN (3763, 3789, 3790, 10520); + +-- Lost Deathstalkers +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 429 WHERE `ID` = 428; + +-- On Guard in Stonetalon +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1085 WHERE `ID` = 1070; + +-- The Crown of Will +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 518 WHERE `ID` = 495; + +-- Camp Mojache +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 7489 WHERE `ID` = 7492; + +-- Feathermoon Stronghold +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 7488 WHERE `ID` = 7494; + +-- Journey to Stonetalon Peak +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1057 WHERE `ID` = 1056; + +-- Castpipe's Task +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2930 WHERE `ID` = 2931; + +-- Kayneth Stillwind +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1011 WHERE `ID` = 4581; + +-- Carendin Summons +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1472 WHERE `ID` = 10605; + +-- Seeking Strahad (Horde) +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 1801 WHERE `ID` IN (2996, 3001); + +-- Seeking Strahad (Alliance) +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1758 WHERE `ID` = 1798; + +-- A Helping Hand +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 9517 WHERE `ID` = 9533; + +-- Trouble In Darkshore? +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 729 WHERE `ID` = 730; + +-- Yorus Barleybrew +UPDATE `quest_template_addon` SET `NextQuestID` = 0, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 1699 WHERE `ID` IN (1698, 10371); + +-- Speak with Ruga +UPDATE `quest_template_addon` SET `PrevQuestId` = 0 WHERE `ID` = 1824; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1824 WHERE `ID` = 1823; + +-- A Strange One +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 4505 WHERE `ID` = 6605; + +-- A Call to Arms: The Plaguelands! (horde) +UPDATE `quest_template_addon` SET `NextQuestID` = 0, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 5096 WHERE `ID` IN (5093, 5094, 5095, 10374); + +-- A Call to Arms: The Plaguelands! (alliance) +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 5092 WHERE `ID` IN (5066, 5090, 5091, 10373); + +-- Trouble in Winterspring! +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 5082 WHERE `ID` = 6603; + +-- Neeka Bloodscar +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1420 WHERE `ID` = 1418; + +-- Taking Back Silithus +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 8280 WHERE `ID` IN (8275, 8276); + +-- To Winterspring! & Starfall +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 5244 WHERE `ID` IN (5249, 5250); + +-- Tinkee Steamboil +UPDATE `quest_template_addon` SET `PrevQuestID` = 4810 WHERE `ID` = 4734; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 4734 WHERE `ID` = 4907; + +-- Westbrook Garrison Needs Help! +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 11 WHERE `ID` = 239; + +-- Elmore's Task +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 353; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 353 WHERE `ID` = 1097; + +-- Brother Anton +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 261 WHERE `ID` = 6141; + +-- Chillwind Camp +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 8414; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 8414 WHERE `ID` = 8415; + +-- Report to Jennea +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1920 WHERE `ID` = 1919; + +-- High Sorcerer Andromath +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1938 WHERE `ID` = 1939; + +-- Vital Supplies +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 1395; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1395 WHERE `ID` = 1477; + +-- Tabetha's Task +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2846 WHERE `ID` = 2861; + +-- James Hyal +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 1302; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1302 WHERE `ID` = 1301; + +-- Morgan Stern +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1204 WHERE `ID` = 1260; + +-- Mayara Brightwing +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 4764 WHERE `ID` = 4766; + +-- Tinkmaster Overspark +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2922 WHERE `ID` = 2923; + +-- Knowledge of the Orb of Orahil +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 1799 WHERE `ID` IN (4965, 4967, 4968, 4969); + +-- In Search of Menara Voidrender +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 1796 WHERE `ID` IN (4736, 4737, 4738, 4739); + +-- Gakin's Summons / The Slaughtered Lamb +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 1688 WHERE `ID` IN (1685, 1715); + +-- Gakin's Summons +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 1716; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1716 WHERE `ID` = 1717; + +-- Jonespyre's Request +UPDATE `quest_template_addon` SET `PrevQuestID` = 3785 WHERE `ID` = 3791; +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 3791 WHERE `ID` IN (3787, 3788); + +-- Malin's Request +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 691 WHERE `ID` = 690; + +-- Report to Anastasia +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1960 WHERE `ID` = 1959; + +-- Speak with Deino +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1944 WHERE `ID` = 1943; + +-- Torwa Pathfinder +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 9052; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 9052 WHERE `ID` = 9063; + +-- The Hermit +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 148 WHERE `ID` = 165; + +-- Deliveries to Sven +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 95 WHERE `ID` = 164; + +-- Raven Hill +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 5 WHERE `ID` = 163; + +-- Enraged Wildkin +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 4861 WHERE `ID` = 6604; + +-- Ironband's Excavation +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 297 WHERE `ID` = 436; + +-- Stonegear's Search +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 466 WHERE `ID` = 467; + +-- Mountaineer Stormpike's Task +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1338 WHERE `ID` = 1339; + +-- Report to Mountaineer Rockgar +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 455; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 455 WHERE `ID` = 468; + +-- Find Bingles +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2038 WHERE `ID` = 2039; + +-- The Greenwarden +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 276 WHERE `ID` = 463; + +-- Rejold's New Brew +UPDATE `quest_template_addon` SET `PrevQuestID` = 315 WHERE `ID` = 413; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 413 WHERE `ID` = 415; + +-- Klockmort's Essentials +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2924 WHERE `ID` = 2925; + +-- Speak with Shoni +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2040 WHERE `ID` = 2041; + +-- Imperial Plate Armor +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 7652 WHERE `ID` IN (10891, 10892); + +-- I Know A Guy... / To Gadgetzan You Go! +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 6610 WHERE `ID` IN (6611, 6612); + +-- Alliance Trauma +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 6624 WHERE `ID` = 6625; + +-- I Got Nothin' Left! +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 6607 WHERE `ID` = 6609; + +-- Trollbane +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 639 WHERE `ID` = 638; + +-- Horde Trauma +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 6622 WHERE `ID` = 6623; + +-- The Hermit of Witch Hill / The Hermit of Swamplight Manor +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1218 WHERE `ID` IN (11177, 11225); + +-- Vivian Lagrave +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 4134 WHERE `ID` = 4133; + +-- Vivian Lagrave and the Darkstone Tablet +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 4768 WHERE `ID` = 4769; + +-- Yuka Screwspigot +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 4136 WHERE `ID` = 4324; + +-- Help Watcher Biggs +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1396 WHERE `ID` = 9609; + +-- Taking a Stand +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 12503 WHERE `ID` = 12795; +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` IN (12503, 12596); + +-- The Exiles of Ulduar +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 12930 WHERE `ID` = 12885; + +-- Assist Exarch Orelis +UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` = 10241; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 10241 WHERE `ID` = 11038; + +-- Off To Area 52 / Out of This World Produce! / A Strange Vision / Parts for the Rocket-Chief / A Mystifying Vision +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 10186 WHERE `ID` IN (10183, 11036, 11037, 11040, 11042); + +-- Horde Warlock Voidwalker questlines +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 0 WHERE `ID` IN (10789, 1478, 1473, 1471, 10790, 1506, 1501, 1504, 10788, 9529, 9619); +UPDATE `quest_template_addon` SET `PrevQuestId` = 1473 WHERE `ID` = 1471; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1473 WHERE `ID` IN (10789, 1478); +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1501 WHERE `ID` IN (10790, 1506); +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 9529 WHERE `ID` = 10788; + +-- Horde Mage level 10 quests +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 0 WHERE `ID` IN (1881, 1883, 9402); +UPDATE `quest_template_addon` SET `PrevQuestId` = 0 WHERE `ID` = 1884; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1882 WHERE `ID` = 1881; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1884 WHERE `ID` = 1883; +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 1882 WHERE `ID` IN (1882, 1884, 9402); + +-- Alliance Mage level 10 quests +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 0 WHERE `ID` IN (1860, 1879); +UPDATE `quest_template_addon` SET `PrevQuestId` = 0 WHERE `ID` = 1880; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1861 WHERE `ID` = 1860; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 1880 WHERE `ID` = 1879; +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 1861 WHERE `ID` IN (1861, 1880, 9595); + +-- SI:7 / Erion's Behest / Kingly Shakedown +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 2260 WHERE `ID` = 2259; +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 2298 WHERE `ID` = 2299; +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 2281 WHERE `ID` IN (2260, 2298, 2300); + +-- Alliance Rogue level 10 quests +UPDATE `quest_template_addon` SET `PrevQuestId` = 0, `ExclusiveGroup` = 2206 WHERE `ID` IN (2206, 2238, 2242); +UPDATE `quest_template_addon` SET `NextQuestId` = 0, `ExclusiveGroup` = 0 WHERE `ID` IN (2218, 2205, 2241); +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2238 WHERE `ID` = 2218; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2206 WHERE `ID` = 2205; +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 2242 WHERE `ID` = 2241; + +-- Find Sage Mistwalker (breadcrumb for The Artifacts of Steel Gate) +UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `ExclusiveGroup` = 0 WHERE (`ID` = 11286); +UPDATE `quest_template_addon` SET `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 11286 WHERE `ID` = 11287; + +-- Judgment Day Comes! (Alliance/Horde breadcrumbs for Honor Above All Else) +UPDATE `quest_template_addon` SET `BreadcrumbForQuestId` = 13036 WHERE `ID` IN (13226, 13227); + +-- Remove redundant conditions now handled by BreadcrumbForQuestId +-- Quest 8280 (Securing the Supply Lines) no longer needs conditions requiring 8275/8276 rewarded +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 19 AND `SourceEntry` = 8280 AND `ConditionTypeOrReference` = 8 AND `ConditionValue1` IN (8275, 8276); + +-- Judgment Day Comes! (13226/13227) no longer needs conditions checking 13036 (Honor Above All Else) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 19 AND `SourceEntry` IN (13226, 13227) AND `ConditionValue1` = 13036; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3dcf8497f..92b42d735 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1446,6 +1446,7 @@ public: bool SatisfyQuestConditions(Quest const* qInfo, bool msg); bool SatisfyQuestTimed(Quest const* qInfo, bool msg) const; bool SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const; + bool SatisfyQuestBreadcrumb(Quest const* qInfo, bool msg) const; bool SatisfyQuestNextChain(Quest const* qInfo, bool msg) const; bool SatisfyQuestPrevChain(Quest const* qInfo, bool msg) const; bool SatisfyQuestDay(Quest const* qInfo, bool msg) const; diff --git a/src/server/game/Entities/Player/PlayerQuest.cpp b/src/server/game/Entities/Player/PlayerQuest.cpp index a2a99a4bd..edfe48c9a 100644 --- a/src/server/game/Entities/Player/PlayerQuest.cpp +++ b/src/server/game/Entities/Player/PlayerQuest.cpp @@ -239,7 +239,7 @@ bool Player::CanSeeStartQuest(Quest const* quest) if (!sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) && SatisfyQuestSkill(quest, false) && SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) && SatisfyQuestPreviousQuest(quest, false) && SatisfyQuestNextChain(quest, false) && - SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) && + SatisfyQuestPrevChain(quest, false) && SatisfyQuestBreadcrumb(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) && SatisfyQuestMonth(quest, false) && SatisfyQuestSeasonal(quest, false)) { return GetLevel() + sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= quest->GetMinLevel(); @@ -256,6 +256,7 @@ bool Player::CanTakeQuest(Quest const* quest, bool msg) && SatisfyQuestSkill(quest, msg) && SatisfyQuestReputation(quest, msg) && SatisfyQuestPreviousQuest(quest, msg) && SatisfyQuestTimed(quest, msg) && SatisfyQuestNextChain(quest, msg) && SatisfyQuestPrevChain(quest, msg) + && SatisfyQuestBreadcrumb(quest, msg) && SatisfyQuestDay(quest, msg) && SatisfyQuestWeek(quest, msg) && SatisfyQuestMonth(quest, msg) && SatisfyQuestSeasonal(quest, msg) && SatisfyQuestConditions(quest, msg); @@ -1217,6 +1218,39 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const return true; } +bool Player::SatisfyQuestBreadcrumb(Quest const* qInfo, bool msg) const +{ + uint32 breadcrumbForQuestId = qInfo->GetBreadcrumbForQuestId(); + if (breadcrumbForQuestId) + { + QuestStatus status = GetQuestStatus(breadcrumbForQuestId); + if (status != QUEST_STATUS_NONE || IsQuestRewarded(breadcrumbForQuestId)) + { + if (msg) + SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); + + return false; + } + } + + if (std::vector const* breadcrumbs = sObjectMgr->GetBreadcrumbsForQuest(qInfo->GetQuestId())) + { + for (uint32 breadcrumbQuestId : *breadcrumbs) + { + QuestStatus status = GetQuestStatus(breadcrumbQuestId); + if (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE || status == QUEST_STATUS_FAILED) + { + if (msg) + SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); + + return false; + } + } + } + + return true; +} + bool Player::SatisfyQuestNextChain(Quest const* qInfo, bool msg) const { uint32 nextQuest = qInfo->GetNextQuestInChain(); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b0f3f5613..175bdd7c2 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -4738,10 +4738,10 @@ void ObjectMgr::LoadQuests() } // Load `quest_template_addon` - // 0 1 2 3 4 5 6 7 8 - result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay, " - //9 10 11 12 13 14 15 16 17 - "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, SpecialFlags FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId"); + // 0 1 2 3 4 5 6 7 8 + result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, " + //9 10 11 12 13 14 15 16 17 18 + "RewardMailDelay, RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, SpecialFlags FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId"); if (!result) { @@ -5318,12 +5318,54 @@ void ObjectMgr::LoadQuests() if (qinfo->ExclusiveGroup) mExclusiveQuestGroups.insert(std::pair(qinfo->ExclusiveGroup, qinfo->GetQuestId())); + + if (uint32 breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId()) + { + if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end()) + LOG_ERROR("sql.sql", "Quest {} has BreadcrumbForQuestId {}, but no such quest exists", qinfo->GetQuestId(), breadcrumbForQuestId); + else + _breadcrumbsForQuest[breadcrumbForQuestId].push_back(qinfo->GetQuestId()); + + if (qinfo->GetNextQuestId()) + LOG_ERROR("sql.sql", "Quest {} is a breadcrumb quest but also has NextQuestID {} set", qinfo->GetQuestId(), qinfo->GetNextQuestId()); + if (qinfo->GetExclusiveGroup()) + LOG_ERROR("sql.sql", "Quest {} is a breadcrumb quest but also has ExclusiveGroup {} set", qinfo->GetQuestId(), qinfo->GetExclusiveGroup()); + } + if (qinfo->TimeAllowed) qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED); if (qinfo->RequiredPlayerKills) qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL); } + for (auto const& [questId, quest] : _questTemplates) + { + if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, questId, nullptr)) + continue; + + uint32 breadcrumbForQuestId = quest->GetBreadcrumbForQuestId(); + if (!breadcrumbForQuestId) + continue; + + std::set visitedQuests; + visitedQuests.insert(questId); + + while (breadcrumbForQuestId) + { + if (!visitedQuests.insert(breadcrumbForQuestId).second) + { + LOG_ERROR("sql.sql", "Breadcrumb quests {} and {} form a loop", questId, breadcrumbForQuestId); + break; + } + + Quest const* targetQuest = GetQuestTemplate(breadcrumbForQuestId); + if (!targetQuest) + break; + + breadcrumbForQuestId = targetQuest->GetBreadcrumbForQuestId(); + } + } + // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) { diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 36028c0e3..4ac1e33b6 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1145,6 +1145,18 @@ public: ExclusiveQuestGroups mExclusiveQuestGroups; + typedef std::unordered_map> BreadcrumbQuestMap; + BreadcrumbQuestMap _breadcrumbsForQuest; + + [[nodiscard]] std::vector const* GetBreadcrumbsForQuest(uint32 questId) const + { + auto itr = _breadcrumbsForQuest.find(questId); + if (itr != _breadcrumbsForQuest.end()) + return &itr->second; + + return nullptr; + } + MailLevelReward const* GetMailLevelReward(uint32 level, uint32 raceMask) { MailLevelRewardContainer::const_iterator map_itr = _mailLevelRewardStore.find(level); diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp index dff4763a4..867471737 100644 --- a/src/server/game/Quests/QuestDef.cpp +++ b/src/server/game/Quests/QuestDef.cpp @@ -177,17 +177,18 @@ void Quest::LoadQuestTemplateAddon(Field* fields) PrevQuestId = fields[4].Get(); NextQuestId = fields[5].Get(); ExclusiveGroup = fields[6].Get(); - RewardMailTemplateId = fields[7].Get(); - RewardMailDelay = fields[8].Get(); - RequiredSkillId = fields[9].Get(); - RequiredSkillPoints = fields[10].Get(); - RequiredMinRepFaction = fields[11].Get(); - RequiredMaxRepFaction = fields[12].Get(); - RequiredMinRepValue = fields[13].Get(); - RequiredMaxRepValue = fields[14].Get(); - StartItemCount = fields[15].Get(); - RewardMailSenderEntry = fields[16].Get(); - SpecialFlags = fields[17].Get(); + BreadcrumbForQuestId = fields[7].Get(); + RewardMailTemplateId = fields[8].Get(); + RewardMailDelay = fields[9].Get(); + RequiredSkillId = fields[10].Get(); + RequiredSkillPoints = fields[11].Get(); + RequiredMinRepFaction = fields[12].Get(); + RequiredMaxRepFaction = fields[13].Get(); + RequiredMinRepValue = fields[14].Get(); + RequiredMaxRepValue = fields[15].Get(); + StartItemCount = fields[16].Get(); + RewardMailSenderEntry = fields[17].Get(); + SpecialFlags = fields[18].Get(); if ((SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT) && !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT)) { diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index 97bd2f759..f074592c0 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -249,6 +249,7 @@ public: [[nodiscard]] int32 GetPrevQuestId() const { return PrevQuestId; } [[nodiscard]] uint32 GetNextQuestId() const { return NextQuestId; } [[nodiscard]] int32 GetExclusiveGroup() const { return ExclusiveGroup; } + [[nodiscard]] uint32 GetBreadcrumbForQuestId() const { return BreadcrumbForQuestId; } [[nodiscard]] uint32 GetNextQuestInChain() const { return RewardNextQuest; } [[nodiscard]] uint32 GetCharTitleId() const { return RewardTitleId; } [[nodiscard]] uint32 GetPlayersSlain() const { return RequiredPlayerKills; } @@ -390,6 +391,7 @@ protected: int32 PrevQuestId = 0; uint32 NextQuestId = 0; int32 ExclusiveGroup = 0; + uint32 BreadcrumbForQuestId = 0; uint32 RewardMailTemplateId = 0; uint32 RewardMailDelay = 0; uint32 RequiredSkillId = 0; From 734fec241de4914d0cb1df4afc877b82a2a89e65 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 29 Jan 2026 12:25:20 +0000 Subject: [PATCH 23/42] chore(DB): import pending files Referenced commit(s): b79ef2bedf951cbe203b515e535a36bc9b9b0319 --- .../rev_1765373936949009445.sql => db_world/2026_01_29_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1765373936949009445.sql => db_world/2026_01_29_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1765373936949009445.sql b/data/sql/updates/db_world/2026_01_29_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1765373936949009445.sql rename to data/sql/updates/db_world/2026_01_29_00.sql index 9398e81d5..e49a071b9 100644 --- a/data/sql/updates/pending_db_world/rev_1765373936949009445.sql +++ b/data/sql/updates/db_world/2026_01_29_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_28_00 -> 2026_01_29_00 -- -- Add BreadcrumbForQuestId column to quest_template_addon -- From 88417d467f596519496ed7d62b36d8f1c2ef67da Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 29 Jan 2026 10:13:12 -0300 Subject: [PATCH 24/42] fix(Scripts/MagistersTerrace): Set up boss boundaries for Kael'Thas (#24472) --- .../MagistersTerrace/boss_priestess_delrissa.cpp | 16 +++++++++------- .../instance_magisters_terrace.cpp | 6 ++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp index 72ee8b461..aeb35cbf3 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp @@ -89,14 +89,16 @@ struct boss_priestess_delrissa : public BossAI void InitializeAI() override { ScriptedAI::InitializeAI(); - std::list helpersList; - for (uint8 i = 0; i < MAX_HELPERS_COUNT; ++i) - helpersList.push_back(helpersEntries[i]); - Acore::Containers::RandomResize(helpersList, MAX_ACTIVE_HELPERS); - uint8 j = 0; - for (std::list::const_iterator itr = helpersList.begin(); itr != helpersList.end(); ++itr, ++j) - me->SummonCreature(*itr, helpersLocations[j], TEMPSUMMON_MANUAL_DESPAWN, 0); + if (instance->GetBossState(DATA_DELRISSA) != DONE) + { + std::vector helpersList(std::begin(helpersEntries), std::end(helpersEntries)); + Acore::Containers::RandomResize(helpersList, MAX_ACTIVE_HELPERS); + + uint8 j = 0; + for (uint32 entry : helpersList) + me->SummonCreature(entry, helpersLocations[j++], TEMPSUMMON_MANUAL_DESPAWN, 0); + } } void JustSummoned(Creature* summon) override diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index 6b6d656fa..e59ef59c7 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -52,6 +52,11 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM } // END }; +BossBoundaryData const boundaries = +{ + { DATA_KAELTHAS, new RectangleBoundary(118.64f, 178.63f, 125.69f, 210.70f) } +}; + Position const KalecgosSpawnPos = { 164.3747f, -397.1197f, 2.151798f, 1.66219f }; class instance_magisters_terrace : public InstanceMapScript @@ -65,6 +70,7 @@ public: { SetHeaders(DataHeader); SetBossNumber(MAX_ENCOUNTER); + LoadBossBoundaries(boundaries); SetPersistentDataCount(MAX_PERSISTENT_DATA); LoadObjectData(creatureData, gameobjectData); LoadDoorData(doorData); From f777fabfa16702e196eb26bd556499a84ccd37eb Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 29 Jan 2026 10:14:14 -0300 Subject: [PATCH 25/42] fix(Scripts/HallsOfStone): Implement Brann Bronzebeard Taunt Script (#24490) Co-authored-by: Xfurry <172444+xfurry@users.noreply.github.com> --- .../rev_1769054598121789800.sql | 20 ++++++++++++++++ .../Ulduar/HallsOfStone/brann_bronzebeard.cpp | 24 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 data/sql/updates/pending_db_world/rev_1769054598121789800.sql diff --git a/data/sql/updates/pending_db_world/rev_1769054598121789800.sql b/data/sql/updates/pending_db_world/rev_1769054598121789800.sql new file mode 100644 index 000000000..ab5d98867 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769054598121789800.sql @@ -0,0 +1,20 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id` = 51774 AND `ScriptName` = 'spell_taunt_brann'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(51774, 'spell_taunt_brann'); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 51774) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 28070) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 51774, 0, 0, 31, 0, 3, 28070, 0, 0, 0, 0, '', 'Target of Taunt (51774) is Brann Bronzebeard (28070) in Halls of Stone Tribunal Event.'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 27983) AND (`source_type` = 0) AND (`id` IN (2)); +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 +(27983, 0, 2, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51774, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Dark Rune Protector - On Respawn - Cast \'Taunt\''); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 27984) AND (`source_type` = 0) AND (`id` IN (4)); +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 +(27984, 0, 4, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51774, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Dark Rune Stormcaller - On Respawn - Cast \'Taunt\''); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 27985) AND (`source_type` = 0) AND (`id` IN (3)); +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 +(27985, 0, 3, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 51774, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Golem Custodian - On Respawn - Cast \'Taunt\''); diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp index e24adc5ac..7d5f74059 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp @@ -753,7 +753,6 @@ public: if (cr) { cr->AI()->AttackStart(me); - cr->AddThreat(me, 0.0f); cr->SetInCombatWithZone(); } } @@ -911,6 +910,28 @@ void brann_bronzebeard::brann_bronzebeardAI::WaypointReached(uint32 id) } } +// 51774 - Taunt +class spell_taunt_brann : public SpellScript +{ + PrepareSpellScript(spell_taunt_brann); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Unit* target = GetHitUnit(); + if (!caster || !target) + return; + + uint32 spellId = GetEffectValue(); // 51775 + target->CastSpell(caster, spellId, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_taunt_brann::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + class spell_hos_dark_matter : public AuraScript { PrepareAuraScript(spell_hos_dark_matter); @@ -953,4 +974,5 @@ void AddSC_brann_bronzebeard() new brann_bronzebeard(); RegisterSpellScript(spell_hos_dark_matter); RegisterSpellScript(spell_hos_dark_matter_size); + RegisterSpellScript(spell_taunt_brann); } From c87a061f01a74bff64842d85418ea04cf79633f9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 29 Jan 2026 13:15:17 +0000 Subject: [PATCH 26/42] chore(DB): import pending files Referenced commit(s): f777fabfa16702e196eb26bd556499a84ccd37eb --- .../rev_1769054598121789800.sql => db_world/2026_01_29_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769054598121789800.sql => db_world/2026_01_29_01.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1769054598121789800.sql b/data/sql/updates/db_world/2026_01_29_01.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1769054598121789800.sql rename to data/sql/updates/db_world/2026_01_29_01.sql index ab5d98867..de3f08ce4 100644 --- a/data/sql/updates/pending_db_world/rev_1769054598121789800.sql +++ b/data/sql/updates/db_world/2026_01_29_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_29_00 -> 2026_01_29_01 -- DELETE FROM `spell_script_names` WHERE `spell_id` = 51774 AND `ScriptName` = 'spell_taunt_brann'; INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES From 5f849af3415a813724022f66beb1659720685389 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 29 Jan 2026 10:39:53 -0300 Subject: [PATCH 27/42] fix(DB/SAI): Set 'Rampage' Event NPCs to Active to avoid deloading and breaking quest progression (#24537) --- .../rev_1769546309171668300.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769546309171668300.sql diff --git a/data/sql/updates/pending_db_world/rev_1769546309171668300.sql b/data/sql/updates/pending_db_world/rev_1769546309171668300.sql new file mode 100644 index 000000000..654fdf4c6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769546309171668300.sql @@ -0,0 +1,18 @@ +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 2895202) AND (`source_type` = 9) AND (`id` IN (5)); +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 +(2895202, 9, 5, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Akali - Quest Reset Script - Set Active Off'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2895200); +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 +(2895200, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Akali - Quest Success Script - Set Active On'), +(2895200, 9, 1, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Akali - Quest Success Script - Say Line 0'), +(2895200, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 15, 12721, 0, 0, 0, 0, 0, 18, 60, 0, 0, 0, 0, 0, 0, 0, 'Akali - Quest Success Script - Quest Credit \'Rampage\''), +(2895200, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Akali - Quest Success Script - Summon Creature Group 1'), +(2895200, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 92, 0, 45579, 0, 0, 0, 0, 9, 28988, 0, 100, 0, 0, 0, 0, 0, 'Akali - Actionlist - Interrupt Spell \'Fire Channeling\''), +(2895200, 9, 5, 0, 0, 0, 100, 0, 4600, 4600, 0, 0, 0, 0, 19, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Akali - Quest Success Script - Remove Flag Immune To NPC\'s'), +(2895200, 9, 6, 0, 0, 0, 100, 0, 55000, 55000, 0, 0, 0, 0, 12, 28996, 8, 0, 0, 0, 0, 8, 0, 0, 0, 0, 6882.03, -4571, 442.312, 2.37365, 'Akali - Quest Success Script - Summon Creature \'Prophet of Akali\''); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28996) AND (`source_type` = 0) AND (`id` IN (7)); +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 +(28996, 0, 7, 0, 37, 0, 100, 0, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Prophet of Akali - On Initialize - Set Active On'); From 0df7baabdc3f49bec57890b5d35b305f66761d1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 29 Jan 2026 13:41:03 +0000 Subject: [PATCH 28/42] chore(DB): import pending files Referenced commit(s): 5f849af3415a813724022f66beb1659720685389 --- .../rev_1769546309171668300.sql => db_world/2026_01_29_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769546309171668300.sql => db_world/2026_01_29_02.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1769546309171668300.sql b/data/sql/updates/db_world/2026_01_29_02.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1769546309171668300.sql rename to data/sql/updates/db_world/2026_01_29_02.sql index 654fdf4c6..a35896818 100644 --- a/data/sql/updates/pending_db_world/rev_1769546309171668300.sql +++ b/data/sql/updates/db_world/2026_01_29_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_29_01 -> 2026_01_29_02 -- DELETE FROM `smart_scripts` WHERE (`entryorguid` = 2895202) AND (`source_type` = 9) AND (`id` IN (5)); 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 From e216838d546bcf82a9db20377670ecc5b6fb65b3 Mon Sep 17 00:00:00 2001 From: sogladev Date: Thu, 29 Jan 2026 18:15:43 +0100 Subject: [PATCH 29/42] fix(Core/Vehicle): fix crash when using MC on a player riding a vehicle (#24551) Co-authored-by: Shauren --- src/server/game/Entities/Unit/Unit.cpp | 2 +- src/server/game/Entities/Vehicle/Vehicle.cpp | 6 ++++++ src/server/game/Entities/Vehicle/Vehicle.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index cf9b395e7..0225d9b46 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -18618,7 +18618,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au ASSERT(type != CHARM_TYPE_POSSESS || charmer->IsPlayer()); if (type == CHARM_TYPE_VEHICLE && !IsVehicle()) // pussywizard throw 1; - ASSERT((type == CHARM_TYPE_VEHICLE) == IsVehicle()); + ASSERT((type == CHARM_TYPE_VEHICLE) == (GetVehicleKit() && GetVehicleKit()->IsControllableVehicle())); LOG_DEBUG("entities.unit", "SetCharmedBy: charmer {} ({}), charmed {} ({}), type {}.", charmer->GetEntry(), charmer->GetGUID().ToString(), GetEntry(), GetGUID().ToString(), uint32(type)); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index d18e721ad..5fbc91a4e 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -26,6 +26,7 @@ #include "TemporarySummon.h" #include "Unit.h" #include "Util.h" +#include Vehicle::Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry) : _me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntry), _status(STATUS_NONE) @@ -562,6 +563,11 @@ bool Vehicle::IsVehicleInUse() return false; } +bool Vehicle::IsControllableVehicle() const +{ + return std::ranges::any_of(Seats, [](auto const& seat) { return seat.second.SeatInfo->CanControl(); }); +} + void Vehicle::TeleportVehicle(float x, float y, float z, float ang) { _me->GetMap()->LoadGrid(x, y); diff --git a/src/server/game/Entities/Vehicle/Vehicle.h b/src/server/game/Entities/Vehicle/Vehicle.h index 04594870b..d7359760c 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -51,6 +51,7 @@ public: void RemoveAllPassengers(); void Dismiss(); bool IsVehicleInUse(); + [[nodiscard]] bool IsControllableVehicle() const; void TeleportVehicle(float x, float y, float z, float ang); SeatMap Seats; From 2c81a4ee124880d52a3230a4a3881bf98c012887 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:20:16 -0300 Subject: [PATCH 30/42] fix(Core/SAI): Don't set Combat Movement for creatures without its associated flag (#24523) --- src/server/game/AI/SmartScripts/SmartScript.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index f714491bf..e199cbc09 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -714,7 +714,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (me->IsRooted()) // Rooted inhabit type, never move/reposition continue; - CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); + if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) + CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); + continue; } else if (distanceToTarget < spellMinRange || !(isWithinLOSInMap || isSpellIgnoreLOS)) From fe9ddea8148c4132ea80cc8cd661a4956904963f Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 29 Jan 2026 18:47:49 -0300 Subject: [PATCH 31/42] fix(DB/Creature): Correct Legacy Typo in Level 83 Creature Armor Value (#24553) Co-authored-by: Lumber-Jack <5568820+Lumber-Jack-2@users.noreply.github.com> Co-authored-by: Shauren --- data/sql/updates/pending_db_world/rev_1769720096498570000.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1769720096498570000.sql diff --git a/data/sql/updates/pending_db_world/rev_1769720096498570000.sql b/data/sql/updates/pending_db_world/rev_1769720096498570000.sql new file mode 100644 index 000000000..c3c094185 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769720096498570000.sql @@ -0,0 +1,3 @@ +-- Wotlk armor value +UPDATE `creature_classlevelstats` SET `basearmor`=10643 WHERE `level`=83 AND `class`=1; +UPDATE `creature_classlevelstats` SET `basearmor`=10643 WHERE `level`=83 AND `class`=2; From ae34d41c4a5a3ecae7c1cde07650e4a4199c322d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 29 Jan 2026 21:49:01 +0000 Subject: [PATCH 32/42] chore(DB): import pending files Referenced commit(s): fe9ddea8148c4132ea80cc8cd661a4956904963f --- .../rev_1769720096498570000.sql => db_world/2026_01_29_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769720096498570000.sql => db_world/2026_01_29_03.sql} (81%) diff --git a/data/sql/updates/pending_db_world/rev_1769720096498570000.sql b/data/sql/updates/db_world/2026_01_29_03.sql similarity index 81% rename from data/sql/updates/pending_db_world/rev_1769720096498570000.sql rename to data/sql/updates/db_world/2026_01_29_03.sql index c3c094185..cdd8e0d14 100644 --- a/data/sql/updates/pending_db_world/rev_1769720096498570000.sql +++ b/data/sql/updates/db_world/2026_01_29_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_29_02 -> 2026_01_29_03 -- Wotlk armor value UPDATE `creature_classlevelstats` SET `basearmor`=10643 WHERE `level`=83 AND `class`=1; UPDATE `creature_classlevelstats` SET `basearmor`=10643 WHERE `level`=83 AND `class`=2; From fa3734c71ac22dad72c6f3e9cbdb47a1ddaccc6c Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:00:34 -0300 Subject: [PATCH 33/42] fix(Scripts/BoreanTundra): Set Last Rites NPCs Active during event so it progresses without requiring player nearby (#24538) --- src/server/scripts/Northrend/zone_borean_tundra.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index ec6a55f17..960e72b15 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -1472,6 +1472,11 @@ public: me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); _events.ScheduleEvent(EVENT_THASSARIAN_SCRIPT_1, 2s); } + else if (type == POINT_MOTION_TYPE && param == 0) + { + if (Creature* talbot = ObjectAccessor::GetCreature(*me, _talbotGUID)) + me->SetFacingToObject(talbot); + } } void UpdateAI(uint32 diff) override @@ -1804,6 +1809,7 @@ public: { if (action == 0) { + me->setActive(true); _playerGUID = player->GetGUID(); CloseGossipMenuFor(player); me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); From 640004b8f3634850ff18c35583accb4ef4cc8312 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:04:01 -0300 Subject: [PATCH 34/42] fix(Core/Unit): Allow Spellsteal to handle Proc Auras and HoTs (#24536) --- src/server/game/Entities/Unit/Unit.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 0225d9b46..591488e89 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -5177,7 +5177,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U // Xinef: if stealer has same aura Aura* curAura = stealer->GetAura(aura->GetId()); if (!curAura || (!curAura->IsPermanent() && curAura->GetDuration() < (int32)dur)) - if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), effMask, stealer, nullptr, &baseDamage[0], nullptr, aura->GetCasterGUID())) + if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), effMask, stealer, nullptr, &baseDamage[0], nullptr, stealer->GetGUID())) { // created aura must not be single target aura,, so stealer won't loose it on recast if (newAura->IsSingleTarget()) @@ -5189,6 +5189,13 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U } // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]); + + // Reset periodic timers so stolen HoTs tick properly + // For stolen auras we need fresh tick counters since no ticks have occurred yet + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (AuraEffect* aurEff = newAura->GetEffect(i)) + aurEff->ResetPeriodic(true); + newAura->ApplyForTargets(); } } From c7e752f03eb42f9bfa7642586d149688920d21e3 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Thu, 29 Jan 2026 22:53:34 -0300 Subject: [PATCH 35/42] fix(Scripts/EoE): Remove red dragon hack (#24541) Co-authored-by: Trista --- .../rev_1769733183070394400.sql | 5 ++ .../Nexus/EyeOfEternity/boss_malygos.cpp | 70 ++++++++++++++++--- .../Nexus/EyeOfEternity/eye_of_eternity.h | 3 +- .../instance_eye_of_eternity.cpp | 26 ++----- 4 files changed, 71 insertions(+), 33 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1769733183070394400.sql diff --git a/data/sql/updates/pending_db_world/rev_1769733183070394400.sql b/data/sql/updates/pending_db_world/rev_1769733183070394400.sql new file mode 100644 index 000000000..debdbaccc --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1769733183070394400.sql @@ -0,0 +1,5 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id` IN (56072, 56070); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(56072, 'spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger'), +(56070, 'spell_wyrmrest_skytalon_summon_red_dragon_buddy'); diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index bdf1ba118..813349aea 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -703,20 +703,14 @@ public: Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); if (!PlayerList.IsEmpty()) for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player* pPlayer = i->GetSource()) + if (Player* player = i->GetSource()) { - sScriptMgr->AnticheatSetUnderACKmount(pPlayer); + sScriptMgr->AnticheatSetUnderACKmount(player); - if (!pPlayer->IsAlive() || pPlayer->IsGameMaster()) + if (!player->IsAlive() || player->IsGameMaster()) continue; - if (Creature* c = me->SummonCreature(NPC_WYRMREST_SKYTALON, pPlayer->GetPositionX(), pPlayer->GetPositionY(), pPlayer->GetPositionZ() - 20.0f, 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) - { - c->SetFaction(pPlayer->GetFaction()); - //pPlayer->CastCustomSpell(60683, SPELLVALUE_BASE_POINT0, 1, c, true); - c->m_Events.AddEventAtOffset(new EoEDrakeEnterVehicleEvent(*c, pPlayer->GetGUID()), 500ms); - AttackStart(c); - } + player->CastSpell(player, SPELL_SUMMON_RED_DRAGON_BUDDY, true); } events.RescheduleEvent(EVENT_SAY_PHASE_3_INTRO, 3s, 1); @@ -1433,6 +1427,19 @@ public: { npc_eoe_wyrmrest_skytalonAI(Creature* pCreature) : VehicleAI(pCreature) { } + void IsSummonedBy(WorldObject* summoner) override + { + me->SetDisableGravity(true); + if (summoner && summoner->IsPlayer()) + { + ObjectGuid summonerGUID = summoner->GetGUID(); + me->m_Events.AddEventAtOffset([summonerGUID, this] { + if (Player* rider = ObjectAccessor::GetPlayer(*me, summonerGUID)) + DoCast(rider, SPELL_RIDE_RED_DRAGON, true); + }, 2s); + } + } + void PassengerBoarded(Unit* pass, int8 /*seat*/, bool apply) override { if (apply) @@ -1517,6 +1524,47 @@ class spell_eoe_ph3_surge_of_power : public SpellScript } }; +// 56070 - Summon Red Dragon Buddy +class spell_wyrmrest_skytalon_summon_red_dragon_buddy : public SpellScript +{ + PrepareSpellScript(spell_wyrmrest_skytalon_summon_red_dragon_buddy); + + bool Load() override + { + return GetCaster()->IsPlayer(); + } + + void SetDest(SpellDestination& dest) + { + dest.Relocate(GetCaster()->GetPosition()); + // Adjust effect summon position to lower Z + Position const offset = { 0.0f, 0.0f, -80.0f, 0.0f }; + dest.RelocateOffset(offset); + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_wyrmrest_skytalon_summon_red_dragon_buddy::SetDest, EFFECT_0, TARGET_DEST_CASTER_RADIUS); + } +}; + +// 56072 - Ride Red Dragon Buddy +class spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger : public SpellScript +{ + PrepareSpellScript(spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + target->CastSpell(GetCaster(), GetEffectValue(), true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_boss_malygos() { new boss_malygos(); @@ -1528,6 +1576,8 @@ void AddSC_boss_malygos() new npc_scion_of_eternity(); new npc_hover_disk(); new npc_eoe_wyrmrest_skytalon(); + RegisterSpellScript(spell_wyrmrest_skytalon_summon_red_dragon_buddy); + RegisterSpellScript(spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger); RegisterSpellScript(spell_eoe_ph3_surge_of_power); } diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h index 88ad0e920..cfdac7ee6 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h @@ -46,7 +46,6 @@ enum NPCs NPC_HOVER_DISK = 30248, NPC_ARCANE_OVERLOAD = 30282, NPC_SURGE_OF_POWER = 30334, - NPC_WYRMREST_SKYTALON = 30161, NPC_STATIC_FIELD = 30592, NPC_ALEXSTRASZA = 32295, }; @@ -75,6 +74,8 @@ enum eSpells SPELL_HASTE = 57060, SPELL_ALEXSTRASZA_GIFT = 61028, + SPELL_SUMMON_RED_DRAGON_BUDDY = 56070, + SPELL_RIDE_RED_DRAGON = 56072 }; enum eAchiev diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp index 41a86c063..fea436c4b 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp @@ -21,18 +21,6 @@ #include "Vehicle.h" #include "eye_of_eternity.h" -bool EoEDrakeEnterVehicleEvent::Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) -{ - if (Player* p = ObjectAccessor::GetPlayer(_owner, _playerGUID)) - if (p->IsInWorld() && !p->IsDuringRemoveFromWorld() && !p->isBeingLoaded() && p->FindMap() == _owner.FindMap()) - { - p->CastCustomSpell(60683, SPELLVALUE_BASE_POINT0, 1, &_owner, true); - return true; - } - _owner.DespawnOrUnsummon(1ms); - return true; -} - class instance_eye_of_eternity : public InstanceMapScript { public: @@ -68,7 +56,7 @@ public: return EncounterStatus == IN_PROGRESS; } - void OnPlayerEnter(Player* pPlayer) override + void OnPlayerEnter(Player* player) override { if (EncounterStatus == DONE) { @@ -79,18 +67,12 @@ public: go->SetPhaseMask(2, true); // no floor, so put players on drakes - if (pPlayer) + if (player) { - if (!pPlayer->IsAlive()) + if (!player->IsAlive()) return; - if (Creature* c = pPlayer->SummonCreature(NPC_WYRMREST_SKYTALON, pPlayer->GetPositionX(), pPlayer->GetPositionY(), pPlayer->GetPositionZ() - 20.0f, 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) - { - c->SetCanFly(true); - c->SetFaction(pPlayer->GetFaction()); - //pPlayer->CastCustomSpell(60683, SPELLVALUE_BASE_POINT0, 1, c, true); - c->m_Events.AddEventAtOffset(new EoEDrakeEnterVehicleEvent(*c, pPlayer->GetGUID()), 500ms); - } + player->CastSpell(player, SPELL_SUMMON_RED_DRAGON_BUDDY, true); } } } From eae4ed19beeaf0b9337ad03da1f17edd3f35bac3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 30 Jan 2026 01:54:41 +0000 Subject: [PATCH 36/42] chore(DB): import pending files Referenced commit(s): c7e752f03eb42f9bfa7642586d149688920d21e3 --- .../rev_1769733183070394400.sql => db_world/2026_01_30_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1769733183070394400.sql => db_world/2026_01_30_00.sql} (85%) diff --git a/data/sql/updates/pending_db_world/rev_1769733183070394400.sql b/data/sql/updates/db_world/2026_01_30_00.sql similarity index 85% rename from data/sql/updates/pending_db_world/rev_1769733183070394400.sql rename to data/sql/updates/db_world/2026_01_30_00.sql index debdbaccc..e4bc6e645 100644 --- a/data/sql/updates/pending_db_world/rev_1769733183070394400.sql +++ b/data/sql/updates/db_world/2026_01_30_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_29_03 -> 2026_01_30_00 -- DELETE FROM `spell_script_names` WHERE `spell_id` IN (56072, 56070); INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES From ce7566defb23eca26b6a383495a54c44be9f317e Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Fri, 30 Jan 2026 00:12:35 -0300 Subject: [PATCH 37/42] fix(Scripts/ShatteredHalls): Don't let Flame Arrows stack in Blood Guard Porung Gauntlet Event (#24434) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: sudlud --- .../ShatteredHalls/boss_porung.cpp | 21 ++++++++++++++++++- .../instance_shattered_halls.cpp | 15 +++++++++++++ .../ShatteredHalls/shattered_halls.h | 7 +++++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_porung.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_porung.cpp index aa5856be6..8ce8f9e21 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_porung.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_porung.cpp @@ -291,10 +291,29 @@ class spell_tsh_shoot_flame_arrow : public SpellScript unitList.remove_if([&](WorldObject* target) -> bool { - return !target->SelectNearestPlayer(15.0f); + if (!target) + return true; + + if (!target->SelectNearestPlayer(15.0f)) + return true; + + if (target->FindNearestGameObject(GO_BLAZE, 6.0f)) + return true; + + // Don't stack arrows on the same target + if (InstanceScript* instance = caster->GetInstanceScript()) + if (target->GetGUID() == instance->GetGuidData(DATA_LAST_FLAME_ARROW)) + return true; + + return false; }); Acore::Containers::RandomResize(unitList, 1); + + // Replace last arrow GUID + if (!unitList.empty()) + if (InstanceScript* instance = caster->GetInstanceScript()) + instance->SetGuidData(DATA_LAST_FLAME_ARROW, unitList.front()->GetGUID()); } void HandleScriptEffect(SpellEffIndex effIndex) diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp index a25b4dacd..8198d5f3b 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/instance_shattered_halls.cpp @@ -117,11 +117,25 @@ public: return PrisonerGUID[data - DATA_PRISONER_1]; case DATA_EXECUTIONER: return ExecutionerGUID; + case DATA_LAST_FLAME_ARROW: + return LastArrowGUID; } return ObjectGuid::Empty; } + void SetGuidData(uint32 data, ObjectGuid value) override + { + switch (data) + { + case DATA_LAST_FLAME_ARROW: + LastArrowGUID = value; + break; + default: + break; + } + } + void Update(uint32 diff) override { if (RescueTimer && RescueTimer < 100 * MINUTE * IN_MILLISECONDS) @@ -164,6 +178,7 @@ public: } protected: + ObjectGuid LastArrowGUID; ObjectGuid ExecutionerGUID; ObjectGuid PrisonerGUID[3]; uint32 RescueTimer; diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h index 31b10add2..713e399ed 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/shattered_halls.h @@ -37,7 +37,8 @@ enum DataTypes DATA_EXECUTIONER = 14, DATA_OMROGG_LEFT_HEAD = 15, DATA_OMROGG_RIGHT_HEAD = 16, - DATA_WARCHIEF_PORTAL = 17 + DATA_WARCHIEF_PORTAL = 17, + DATA_LAST_FLAME_ARROW = 18 }; enum CreatureIds @@ -49,6 +50,7 @@ enum CreatureIds NPC_SH_ZEALOT = 17462, NPC_SH_ARCHER = 17427, NPC_SH_SCOUT = 17693, + NPC_SH_FLAME_ARROW = 17687, // Warchief Kargath NPC_WARCHIEF_KARGATH = 16808, @@ -76,7 +78,8 @@ enum CreatureIds enum GameobjectIds { GO_GRAND_WARLOCK_CHAMBER_DOOR_1 = 182539, - GO_GRAND_WARLOCK_CHAMBER_DOOR_2 = 182540 + GO_GRAND_WARLOCK_CHAMBER_DOOR_2 = 182540, + GO_BLAZE = 181915 }; enum SpellIds From 9aa3ca3de0a33262fdc748155b6e8a83dcfb5f97 Mon Sep 17 00:00:00 2001 From: Dan1l-sh <99441165+Dan1l-sh@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:10:33 +0300 Subject: [PATCH 38/42] fix(DB/SAI) fix terokk dawnfall quest (#24511) Co-authored-by: Gultask <100873791+Gultask@users.noreply.github.com> --- .../2026_01_24_01_terokk_dawnfall.sql | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 data/sql/updates/pending_db_world/2026_01_24_01_terokk_dawnfall.sql diff --git a/data/sql/updates/pending_db_world/2026_01_24_01_terokk_dawnfall.sql b/data/sql/updates/pending_db_world/2026_01_24_01_terokk_dawnfall.sql new file mode 100644 index 000000000..4e7e9b2ae --- /dev/null +++ b/data/sql/updates/pending_db_world/2026_01_24_01_terokk_dawnfall.sql @@ -0,0 +1,53 @@ +DELETE FROM `smart_scripts` WHERE `entryorguid` = 23377 AND `id` = 3 AND `source_type` = 0; +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 +(23377, 0, 3, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 53, 1, 23377, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Skyguard Ace - On Respawn - Start Waypoint Movement'); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 23377 AND `id` IN (4, 5, 6) AND `source_type` = 0; +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 +(23377,0,4,0,38,0,100,0,2,2,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0.0,0.0,0.0,0.0,'On Data Set - Talk 2'), +(23377,0,5,0,38,0,100,0,3,3,0,0,0,0,1,3,0,0,0,0,0,1,0,0,0,0,0.0,0.0,0.0,0.0,'On Data Set - Talk 3'); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 2183800 AND `source_type` = 9; +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 +(2183800, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Set Immune Flags'), +(2183800, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 24240, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Summon Lightning Spell'), +(2183800, 9, 2, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 11, 39579, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Shadow Form'), +(2183800, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Summon Talk'), +(2183800, 9, 4, 0, 0, 0, 100, 0, 10000, 10000, 0, 0, 0, 0, 19, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Remove Immune Flags'), +(2183800, 9, 5, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Attack Invoker'); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 2183801 AND `source_type` = 9; +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 +(2183801, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 40726, 0, 0, 0, 0, 0, 5, 0, 1, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Cast "Chosen One"'), +(2183801, 9, 1, 0, 0, 0, 100, 0, 500, 500, 0, 0, 0, 0, 11, 40722, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Terokk - On Script - Cast "Will of the Arakkoa God"'); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 21838 AND `id` = 19 AND `source_type` = 0; +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 +(21838, 0, 19, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 12, 23377, 3, 240000, 0, 0, 0, 8, 0, 0, 0, 0, -3771.6, 3499.32, 317.88, 2.5, 'Terokk - On Aggro Summon "Skyguard Ace"'); + +UPDATE `smart_scripts` SET `target_type` = 19 WHERE `entryorguid` = 21838 AND `id` IN (4, 7, 13, 14) AND `source_type` = 0; +UPDATE `smart_scripts` SET `action_param1` = 2, `action_param2` = 2, `comment` = 'Terokk - Between 0-50% Health - Set Data 2 2 (Skyguard Ace) (No Repeat)' WHERE `entryorguid` = 21838 AND `id` = 4 AND `source_type` = 0; +UPDATE `smart_scripts` SET `target_param1` = 23377 WHERE `entryorguid` = 21838 AND `id` IN (4, 7, 13, 14) AND `source_type` = 0; +UPDATE `smart_scripts` SET `action_param1` = 1, `action_param2` = 1 WHERE `entryorguid` = 21838 AND `id` = 7 AND `source_type` = 0; + +DELETE FROM `creature_text` WHERE `CreatureID` = 21838 AND `GroupID` = 3; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES + (21838,3,0,'Who calls me to this world? The stars are not yet aligned... my powers fail me! You will pay for this!',14,0,100.0,0,0,0,21639,0,'Terokk'); + +UPDATE `creature_text` SET `Text` = 'Kwa! You cannot kill me, I am immortal!' WHERE `CreatureID` = 21838 AND `GroupID` = 1; +UPDATE `creature_text` SET `BroadcastTextId` = 24020 WHERE `CreatureID` = 21838 AND `GroupID` = 1; + +DELETE FROM `creature_text` WHERE `CreatureID` = 23377; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(23377,1,0,'Quickly! Use the flames and support ground troops. Its ancient magic should cleanse Terokk''s shield.',14,0,100.0,0,0,0,24021,0,'Skyguard Ace'), +(23377,2,0,'Enemy sighted! Fall into formation and prepare for bombing maneuvers!',14,0,100.0,0,0,0,21439,0,'Skyguard Ace'), +(23377,3,0,'They did it! Enemy down! Return to base!',14,0,100.0,0,0,0,21437,0,'Skyguard Ace'); + +UPDATE `creature_template` SET `unit_flags` = 2 WHERE `entry` = 23377; + +DELETE FROM `waypoints` WHERE `entry` = 23377; +INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `point_comment`) VALUES +(23377, 1, -3774.55, 3514.51, 325.551, NULL, 0, 'Skyguard Ace'), +(23377, 2, -3794.88, 3497.48, 325.551, NULL, 0, 'Skyguard Ace'), +(23377, 3, -3808.63, 3515.7, 325.551, NULL, 0, 'Skyguard Ace'), +(23377, 4, -3783.6, 3524.59, 325.551, NULL, 0, 'Skyguard Ace'); From e05ce3120014a3c6ac5053ec6f0c748eee17c079 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 30 Jan 2026 13:11:38 +0000 Subject: [PATCH 39/42] chore(DB): import pending files Referenced commit(s): 9aa3ca3de0a33262fdc748155b6e8a83dcfb5f97 --- .../2026_01_30_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/2026_01_24_01_terokk_dawnfall.sql => db_world/2026_01_30_01.sql} (99%) diff --git a/data/sql/updates/pending_db_world/2026_01_24_01_terokk_dawnfall.sql b/data/sql/updates/db_world/2026_01_30_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/2026_01_24_01_terokk_dawnfall.sql rename to data/sql/updates/db_world/2026_01_30_01.sql index 4e7e9b2ae..30a247c2e 100644 --- a/data/sql/updates/pending_db_world/2026_01_24_01_terokk_dawnfall.sql +++ b/data/sql/updates/db_world/2026_01_30_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_01_30_00 -> 2026_01_30_01 DELETE FROM `smart_scripts` WHERE `entryorguid` = 23377 AND `id` = 3 AND `source_type` = 0; 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 (23377, 0, 3, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 53, 1, 23377, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 'Skyguard Ace - On Respawn - Start Waypoint Movement'); From dab8d7c6f6153e5797c418fc9a9a5289096835ab Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 30 Jan 2026 14:13:53 +0100 Subject: [PATCH 40/42] fix(Core/Movement): fix calculation first Catmull-Rom spline point (#24449) Co-authored-by: DanVS <33371360+DanVS@users.noreply.github.com> Co-authored-by: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> --- src/server/game/Movement/Spline/MoveSpline.cpp | 4 ++-- src/server/game/Movement/Spline/Spline.cpp | 14 ++++++++++---- src/server/game/Movement/Spline/Spline.h | 9 +++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/server/game/Movement/Spline/MoveSpline.cpp b/src/server/game/Movement/Spline/MoveSpline.cpp index 513a789ef..0d9581a2e 100644 --- a/src/server/game/Movement/Spline/MoveSpline.cpp +++ b/src/server/game/Movement/Spline/MoveSpline.cpp @@ -128,11 +128,11 @@ namespace Movement // MoveSplineFlag::Enter_Cycle support dropped //if (splineflags & SPLINEFLAG_ENTER_CYCLE) //cyclic_point = 1; // shouldn't be modified, came from client - spline.init_cyclic_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], cyclic_point); + spline.init_cyclic_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], cyclic_point, args.initialOrientation); } else { - spline.init_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()]); + spline.init_spline(&args.path[0], args.path.size(), modes[args.flags.isSmooth()], args.initialOrientation); } // init spline timestamps diff --git a/src/server/game/Movement/Spline/Spline.cpp b/src/server/game/Movement/Spline/Spline.cpp index 9c65619b5..5baf32027 100644 --- a/src/server/game/Movement/Spline/Spline.cpp +++ b/src/server/game/Movement/Spline/Spline.cpp @@ -199,18 +199,20 @@ namespace Movement return length; } - void SplineBase::init_spline(const Vector3* controls, index_type count, EvaluationMode m) + void SplineBase::init_spline(const Vector3* controls, index_type count, EvaluationMode m, float orientation) { m_mode = m; cyclic = false; + initialOrientation = orientation; (this->*initializers[m_mode])(controls, count, cyclic, 0); } - void SplineBase::init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point) + void SplineBase::init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point, float orientation) { m_mode = m; cyclic = true; + initialOrientation = orientation; (this->*initializers[m_mode])(controls, count, cyclic, cyclic_point); } @@ -253,14 +255,18 @@ namespace Movement if (cyclic_point == 0) points[0] = controls[count - 1]; else - points[0] = controls[0].lerp(controls[1], -1); + points[0] = controls[0] - G3D::Vector3{ std::cos(initialOrientation), std::sin(initialOrientation), 0.0f }; points[high_index + 1] = controls[cyclic_point]; points[high_index + 2] = controls[cyclic_point + 1]; } else { - points[0] = controls[0].lerp(controls[1], -1); + if (m_mode == ModeCatmullrom) + points[0] = controls[0] - G3D::Vector3{ std::cos(initialOrientation), std::sin(initialOrientation), 0.0f }; + else + points[0] = controls[0].lerp(controls[1], -1); + points[high_index + 1] = controls[count - 1]; } diff --git a/src/server/game/Movement/Spline/Spline.h b/src/server/game/Movement/Spline/Spline.h index 9645eb607..ef0b6aee5 100644 --- a/src/server/game/Movement/Spline/Spline.h +++ b/src/server/game/Movement/Spline/Spline.h @@ -48,6 +48,7 @@ namespace Movement uint8 m_mode{UninitializedMode}; bool cyclic{false}; + float initialOrientation{0}; enum { @@ -116,8 +117,8 @@ namespace Movement [[nodiscard]] const Vector3& getPoint(index_type i) const { return points[i];} /** Initializes spline. Don't call other methods while spline not initialized. */ - void init_spline(const Vector3* controls, index_type count, EvaluationMode m); - void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point); + void init_spline(const Vector3* controls, index_type count, EvaluationMode m, float orientation); + void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point, float orientation); /** As i can see there are a lot of ways how spline can be initialized would be no harm to have some custom initializers. */ @@ -170,8 +171,8 @@ namespace Movement void computeIndex(float t, index_type& out_idx, float& out_u) const; /** Initializes spline. Don't call other methods while spline not initialized. */ - void init_spline(const Vector3* controls, index_type count, EvaluationMode m) { SplineBase::init_spline(controls, count, m);} - void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point) { SplineBase::init_cyclic_spline(controls, count, m, cyclic_point);} + void init_spline(const Vector3* controls, index_type count, EvaluationMode m, float orientation = 0) { SplineBase::init_spline(controls, count, m, orientation);} + void init_cyclic_spline(const Vector3* controls, index_type count, EvaluationMode m, index_type cyclic_point, float orientation = 0) { SplineBase::init_cyclic_spline(controls, count, m, cyclic_point, orientation);} /** Initializes lengths with SplineBase::SegLength method. */ void initLengths(); From f9bc6c62923176d2f4997a1b7d0e4d0622121208 Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 30 Jan 2026 14:15:27 +0100 Subject: [PATCH 41/42] fix(Core/Player): Prevent player gaining client control when charmed (#24539) Co-authored-by: ariel- Co-authored-by: Treeston --- src/server/game/Entities/Player/Player.cpp | 14 +++++++++++++- src/server/game/Entities/Unit/Unit.cpp | 4 ++++ src/server/game/Entities/Unit/UnitDefines.h | 4 ++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a57c06478..ff26ef061 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -12843,9 +12843,21 @@ void Player::ResurectUsingRequestData() void Player::SetClientControl(Unit* target, bool allowMove, bool packetOnly /*= false*/) { + ASSERT(target); + // refuse to grant control if target has UNIT_STATE_CHARMED + if (target->HasUnitState(UNIT_STATE_CHARMED) && (GetGUID() != target->GetCharmerGUID())) + { + LOG_ERROR("entities.player", "Player '{}' attempt to client control '{}', which is charmed by GUID {}", GetName(), target->GetName(), target->GetCharmerGUID().ToString()); + return; + } + + // still affected by some aura that shouldn't allow control, only allow on last such aura to be removed + if (target->HasUnitState(UNIT_STATE_FLEEING | UNIT_STATE_CONFUSED)) + allowMove = false; + WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size() + 1); data << target->GetPackGUID(); - data << uint8((allowMove && !target->HasUnitState(UNIT_STATE_FLEEING | UNIT_STATE_CONFUSED)) ? 1 : 0); + data << uint8(allowMove ? 1 : 0); SendDirectMessage(&data); // We want to set the packet only diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 591488e89..f87548d18 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11125,6 +11125,8 @@ void Unit::SetCharm(Unit* charm, bool apply) } else { + charm->ClearUnitState(UNIT_STATE_CHARMED); + if (IsPlayer()) { if (!RemoveGuidValue(UNIT_FIELD_CHARM, charm->GetGUID())) @@ -18798,6 +18800,8 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au else if (IsPlayer()) RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + AddUnitState(UNIT_STATE_CHARMED); + if (Creature* creature = ToCreature()) creature->RefreshSwimmingFlag(); diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index af52a9891..dc80e1fba 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -169,7 +169,7 @@ enum UnitState { UNIT_STATE_DIED = 0x00000001, // player has fake death aura UNIT_STATE_MELEE_ATTACKING = 0x00000002, // player is melee attacking someone - //UNIT_STATE_MELEE_ATTACK_BY = 0x00000004, // player is melee attack by someone + UNIT_STATE_CHARMED = 0x00000004, // having any kind of charm aura on self UNIT_STATE_STUNNED = 0x00000008, UNIT_STATE_ROAMING = 0x00000010, UNIT_STATE_CHASE = 0x00000020, @@ -201,7 +201,7 @@ enum UnitState UNIT_STATE_NO_COMBAT_MOVEMENT = 0x40000000, // should not be changed outside the core and should be placed at the end UNIT_STATE_LOGOUT_TIMER = 0x80000000, // Unit is logging out - UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE + UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_CHARMED | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE | UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED | UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING | UNIT_STATE_POSSESSED | UNIT_STATE_CHARGING | UNIT_STATE_JUMPING | UNIT_STATE_MOVE | UNIT_STATE_ROTATING From 6c32ea2e5e5beed19f091f7f9c029d30a689c54a Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 30 Jan 2026 10:20:21 -0300 Subject: [PATCH 42/42] refactor(Scripts/EoE): Update creature register methods (#24556) --- .../Nexus/EyeOfEternity/boss_malygos.cpp | 2205 ++++++++--------- .../Nexus/EyeOfEternity/eye_of_eternity.h | 11 +- 2 files changed, 1059 insertions(+), 1157 deletions(-) diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 813349aea..d77608b47 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -188,1278 +188,1189 @@ enum MalygosLightOverrides LIGHT_OBSCURE_ARCANE_RUNES = 1825, }; -class boss_malygos : public CreatureScript +struct boss_malygos : public ScriptedAI { -public: - boss_malygos() : CreatureScript("boss_malygos") { } - - CreatureAI* GetAI(Creature* pCreature) const override + boss_malygos(Creature* c) : ScriptedAI(c), summons(me) { - return GetEyeOfEternityAI(pCreature); + pInstance = me->GetInstanceScript(); } - struct boss_malygosAI : public ScriptedAI + InstanceScript* pInstance; + EventMap events; + SummonList summons; + + uint32 timer1, timer2; + uint8 IntroCounter; + bool bLockHealthCheck; + + void InitializeAI() override { - boss_malygosAI(Creature* c) : ScriptedAI(c), summons(me) + me->SetDisableGravity(true); + Reset(); + } + + void Reset() override + { + events.Reset(); + events.SetPhase(PHASE_NONE); + summons.DespawnAll(); + + timer1 = MalygosIntroIntervals[4]; + timer2 = INTRO_MOVEMENT_INTERVAL; + IntroCounter = 0; + bLockHealthCheck = false; + + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); + me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); + + me->SetAnimTier(AnimTier::Fly); + + if (pInstance) { - pInstance = me->GetInstanceScript(); + pInstance->SetData(DATA_ENCOUNTER_STATUS, NOT_STARTED); + pInstance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_YOU_DONT_HAVE_AN_ENTERNITY_EVENT); + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE) + { + switch (id) + { + case MI_POINT_INTRO_SIDE_0: + case MI_POINT_INTRO_SIDE_1: + case MI_POINT_INTRO_SIDE_2: + case MI_POINT_INTRO_SIDE_3: + { + float angle = me->GetOrientation(); + float dist = 75.0f; + if (Creature* c = me->SummonCreature(NPC_PORTAL, me->GetPositionX() + cos(angle) * dist, me->GetPositionY() + std::sin(angle) * dist, me->GetPositionZ(), FourSidesPos[id].GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 13000)) + me->CastSpell(c, SPELL_PORTAL_BEAM, false); + timer2 = INTRO_MOVEMENT_INTERVAL - 10000; + } + break; + + case MI_POINT_INTRO_CENTER_AIR: + events.RescheduleEvent(EVENT_INTRO_LAND, 0ms, 1); + break; + case MI_POINT_VORTEX_CENTER: + if (Creature* c = me->SummonCreature(NPC_WORLD_TRIGGER_LAOI, CenterPos, TEMPSUMMON_TIMED_DESPAWN, 15000)) + c->CastSpell(c, SPELL_VORTEX_VISUAL, true); + events.RescheduleEvent(EVENT_START_VORTEX_REAL, 1s, 1); + break; + case MI_POINT_CENTER_GROUND_PH_2: + events.RescheduleEvent(EVENT_START_PHASE_2_FLY_UP, 0ms, 1); + break; + case MI_POINT_CIRCLE_OUTSIDE_PH_2: + events.RescheduleEvent(EVENT_RESUME_FLYING_CIRCLES_PH_2, 0ms, 1); + break; + case MI_POINT_SURGE_OF_POWER_CENTER: + events.RescheduleEvent(EVENT_SURGE_OF_POWER_WARNING, 0ms, 1); + break; + case MI_POINT_INTRO_LAND: + me->SetDisableGravity(false); + events.RescheduleEvent(EVENT_START_FIGHT, 0ms, 1); + break; + case MI_POINT_VORTEX_TAKEOFF: + events.RescheduleEvent(EVENT_VORTEX_FLY_TO_CENTER, 0ms, 1); + break; + case MI_POINT_VORTEX_LAND: + me->SetDisableGravity(false); + events.RescheduleEvent(EVENT_VORTEX_LAND_1, 0ms, 1); + break; + case MI_POINT_CENTER_AIR_PH_2: + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_ARCANE_RUNES, 5s); + break; + case MI_POINT_PH_3_FIGHT_POSITION: + events.RescheduleEvent(EVENT_START_PHASE_3, 6s, 1); + break; + } + } + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_POWER_SPARK_MALYGOS_BUFF) + { + if (!bLockHealthCheck) + { + Talk(SAY_BUFFED_BY_SPARK); + } + else + me->RemoveAura(SPELL_POWER_SPARK_MALYGOS_BUFF); + } + } + + void JustEngagedWith(Unit* /*who*/) override + { + events.Reset(); + DoZoneInCombat(); + + Talk(SAY_PHASE_1); + + events.RescheduleEvent(EVENT_INTRO_MOVE_CENTER, 0ms, 1); + if (pInstance) + pInstance->SetData(DATA_ENCOUNTER_STATUS, IN_PROGRESS); + } + + void AttackStart(Unit* victim) override + { + if (!victim) + return; + + if (me->GetVictim() && me->GetVictim()->GetGUID() == victim->GetGUID() && !me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED)) + { + if (!me->GetGuidValue(UNIT_FIELD_TARGET)) + me->SetTarget(victim->GetGUID()); + } + else if (me->Attack(victim, true)) + { + if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED)) + me->GetMotionMaster()->MoveChase(victim); + else + me->SetTarget(); + } + } + + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (damage >= me->GetHealth() && !me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) // allow dying only in phase 3! + { + damage = 0; + return; } - InstanceScript* pInstance; - EventMap events; - SummonList summons; - - uint32 timer1, timer2; - uint8 IntroCounter; - bool bLockHealthCheck; - - void InitializeAI() override + if (!bLockHealthCheck && me->HealthBelowPctDamaged(50, damage)) { - me->SetDisableGravity(true); - Reset(); + bLockHealthCheck = true; + events.RescheduleEvent(EVENT_START_PHASE_2, 0ms, 1); } + } - void Reset() override + void UpdateAI(uint32 diff) override + { + HandleIntroSpeech(diff); + + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) { - events.Reset(); - events.SetPhase(PHASE_NONE); + case 0: + break; + case EVENT_BERSERK: + me->CastSpell(me, SPELL_BERSERK, true); + Talk(EMOTE_BERSERK); + + break; + case EVENT_INTRO_MOVE_CENTER: + { + if (pInstance) + pInstance->SetData(DATA_SET_IRIS_INACTIVE, 0); summons.DespawnAll(); - - timer1 = MalygosIntroIntervals[4]; - timer2 = INTRO_MOVEMENT_INTERVAL; - IntroCounter = 0; - bLockHealthCheck = false; - - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); - me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); - - me->SetAnimTier(AnimTier::Fly); - + me->InterruptNonMeleeSpells(true); + me->RemoveAllAuras(); + float angle = CenterPos.GetAngle(me); + float x = CenterPos.GetPositionX() + cos(angle) * 35.0f; + float y = CenterPos.GetPositionY() + std::sin(angle) * 35.0f; + float z = FourSidesPos[0].GetPositionZ(); + me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + me->GetMotionMaster()->MovePoint(MI_POINT_INTRO_CENTER_AIR, x, y, z); + break; + } + case EVENT_INTRO_LAND: + { + me->GetMotionMaster()->MovePoint(MI_POINT_INTRO_LAND, me->GetPositionX(), me->GetPositionY(), CenterPos.GetPositionZ(), FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Ground); + break; + } + case EVENT_START_FIGHT: + { if (pInstance) { - pInstance->SetData(DATA_ENCOUNTER_STATUS, NOT_STARTED); - pInstance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_YOU_DONT_HAVE_AN_ENTERNITY_EVENT); + pInstance->SetData(DATA_HIDE_IRIS_AND_PORTAL, 0); + pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_YOU_DONT_HAVE_AN_ENTERNITY_EVENT); } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type == POINT_MOTION_TYPE) + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); + if (Unit* target = me->SelectNearestTarget(250.0f)) { - switch (id) - { - case MI_POINT_INTRO_SIDE_0: - case MI_POINT_INTRO_SIDE_1: - case MI_POINT_INTRO_SIDE_2: - case MI_POINT_INTRO_SIDE_3: - { - float angle = me->GetOrientation(); - float dist = 75.0f; - if (Creature* c = me->SummonCreature(NPC_PORTAL, me->GetPositionX() + cos(angle) * dist, me->GetPositionY() + std::sin(angle) * dist, me->GetPositionZ(), FourSidesPos[id].GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 13000)) - me->CastSpell(c, SPELL_PORTAL_BEAM, false); - timer2 = INTRO_MOVEMENT_INTERVAL - 10000; - } - break; - - case MI_POINT_INTRO_CENTER_AIR: - events.RescheduleEvent(EVENT_INTRO_LAND, 0ms, 1); - break; - case MI_POINT_VORTEX_CENTER: - if (Creature* c = me->SummonCreature(NPC_WORLD_TRIGGER_LAOI, CenterPos, TEMPSUMMON_TIMED_DESPAWN, 15000)) - c->CastSpell(c, SPELL_VORTEX_VISUAL, true); - events.RescheduleEvent(EVENT_START_VORTEX_REAL, 1s, 1); - break; - case MI_POINT_CENTER_GROUND_PH_2: - events.RescheduleEvent(EVENT_START_PHASE_2_FLY_UP, 0ms, 1); - break; - case MI_POINT_CIRCLE_OUTSIDE_PH_2: - events.RescheduleEvent(EVENT_RESUME_FLYING_CIRCLES_PH_2, 0ms, 1); - break; - case MI_POINT_SURGE_OF_POWER_CENTER: - events.RescheduleEvent(EVENT_SURGE_OF_POWER_WARNING, 0ms, 1); - break; - case MI_POINT_INTRO_LAND: - me->SetDisableGravity(false); - events.RescheduleEvent(EVENT_START_FIGHT, 0ms, 1); - break; - case MI_POINT_VORTEX_TAKEOFF: - events.RescheduleEvent(EVENT_VORTEX_FLY_TO_CENTER, 0ms, 1); - break; - case MI_POINT_VORTEX_LAND: - me->SetDisableGravity(false); - events.RescheduleEvent(EVENT_VORTEX_LAND_1, 0ms, 1); - break; - case MI_POINT_CENTER_AIR_PH_2: - me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_ARCANE_RUNES, 5s); - break; - case MI_POINT_PH_3_FIGHT_POSITION: - events.RescheduleEvent(EVENT_START_PHASE_3, 6s, 1); - break; - } + AttackStart(target); + me->GetMotionMaster()->MoveChase(target); } - } - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + events.SetPhase(PHASE_ONE); + events.RescheduleEvent(EVENT_BERSERK, 10min, 0); + events.RescheduleEvent(EVENT_SPELL_ARCANE_BREATH, 9s, 12s, 1); + events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 2s, 5s, 1); + events.RescheduleEvent(EVENT_SUMMON_POWER_SPARK, 10s, 15s, 1); + events.RescheduleEvent(EVENT_START_VORTEX_0, 30s, 1); + break; + } + case EVENT_SPELL_ARCANE_BREATH: + me->CastSpell(me->GetVictim(), SPELL_ARCANE_BREATH, false); + events.Repeat(12s, 15s); + break; + case EVENT_SPELL_ARCANE_STORM: + me->CastCustomSpell(SPELL_ARCANE_STORM, SPELLVALUE_MAX_TARGETS, DUNGEON_MODE(5, 12), me, true); + events.Repeat(10s, 15s); + break; + case EVENT_SUMMON_POWER_SPARK: { - if (spell->Id == SPELL_POWER_SPARK_MALYGOS_BUFF) + uint8 random = urand(0, 3); + if (Creature* c = me->SummonCreature(NPC_PORTAL, FourSidesPos[random], TEMPSUMMON_TIMED_DESPAWN, 6000)) + c->CastSpell(c, SPELL_PORTAL_BEAM, false); + if (Creature* c = me->SummonCreature(NPC_POWER_SPARK, FourSidesPos[random], TEMPSUMMON_MANUAL_DESPAWN, 0)) { - if (!bLockHealthCheck) - { - Talk(SAY_BUFFED_BY_SPARK); - } - else - me->RemoveAura(SPELL_POWER_SPARK_MALYGOS_BUFF); - } - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.Reset(); - DoZoneInCombat(); - - Talk(SAY_PHASE_1); - - events.RescheduleEvent(EVENT_INTRO_MOVE_CENTER, 0ms, 1); - if (pInstance) - pInstance->SetData(DATA_ENCOUNTER_STATUS, IN_PROGRESS); - } - - void AttackStart(Unit* victim) override - { - if (!victim) - return; - - if (me->GetVictim() && me->GetVictim()->GetGUID() == victim->GetGUID() && !me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED)) - { - if (!me->GetGuidValue(UNIT_FIELD_TARGET)) - me->SetTarget(victim->GetGUID()); - } - else if (me->Attack(victim, true)) - { - if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED)) - me->GetMotionMaster()->MoveChase(victim); - else - me->SetTarget(); - } - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth() && !me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) // allow dying only in phase 3! - { - damage = 0; - return; + c->AI()->DoAction(1); + c->AI()->Talk(EMOTE_POWER_SPARK); } - if (!bLockHealthCheck && me->HealthBelowPctDamaged(50, damage)) - { - bLockHealthCheck = true; - events.RescheduleEvent(EVENT_START_PHASE_2, 0ms, 1); - } + events.Repeat(20s, 30s); } - - void UpdateAI(uint32 diff) override + break; + case EVENT_START_VORTEX_0: { - HandleIntroSpeech(diff); + bLockHealthCheck = true; + Talk(SAY_MAGIC_BLAST); + EntryCheckPredicate pred(NPC_POWER_SPARK); + summons.DoAction(2, pred); // stop following + me->SetUnitFlag(UNIT_FLAG_PACIFIED); - if (!UpdateVictim()) - return; + me->SendMeleeAttackStop(me->GetVictim()); + me->SetTarget(); - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - Talk(EMOTE_BERSERK); - - break; - case EVENT_INTRO_MOVE_CENTER: - { - if (pInstance) - pInstance->SetData(DATA_SET_IRIS_INACTIVE, 0); - summons.DespawnAll(); - me->InterruptNonMeleeSpells(true); - me->RemoveAllAuras(); - float angle = CenterPos.GetAngle(me); - float x = CenterPos.GetPositionX() + cos(angle) * 35.0f; - float y = CenterPos.GetPositionY() + std::sin(angle) * 35.0f; - float z = FourSidesPos[0].GetPositionZ(); - me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - me->GetMotionMaster()->MovePoint(MI_POINT_INTRO_CENTER_AIR, x, y, z); - break; - } - case EVENT_INTRO_LAND: - { - me->GetMotionMaster()->MovePoint(MI_POINT_INTRO_LAND, me->GetPositionX(), me->GetPositionY(), CenterPos.GetPositionZ(), FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Ground); - break; - } - case EVENT_START_FIGHT: - { - if (pInstance) - { - pInstance->SetData(DATA_HIDE_IRIS_AND_PORTAL, 0); - pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_YOU_DONT_HAVE_AN_ENTERNITY_EVENT); - } - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); - if (Unit* target = me->SelectNearestTarget(250.0f)) - { - AttackStart(target); - me->GetMotionMaster()->MoveChase(target); - } - - events.SetPhase(PHASE_ONE); - events.RescheduleEvent(EVENT_BERSERK, 10min, 0); - events.RescheduleEvent(EVENT_SPELL_ARCANE_BREATH, 9s, 12s, 1); - events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 2s, 5s, 1); - events.RescheduleEvent(EVENT_SUMMON_POWER_SPARK, 10s, 15s, 1); - events.RescheduleEvent(EVENT_START_VORTEX_0, 30s, 1); - break; - } - case EVENT_SPELL_ARCANE_BREATH: - me->CastSpell(me->GetVictim(), SPELL_ARCANE_BREATH, false); - events.Repeat(12s, 15s); - break; - case EVENT_SPELL_ARCANE_STORM: - me->CastCustomSpell(SPELL_ARCANE_STORM, SPELLVALUE_MAX_TARGETS, DUNGEON_MODE(5, 12), me, true); - events.Repeat(10s, 15s); - break; - case EVENT_SUMMON_POWER_SPARK: - { - uint8 random = urand(0, 3); - if (Creature* c = me->SummonCreature(NPC_PORTAL, FourSidesPos[random], TEMPSUMMON_TIMED_DESPAWN, 6000)) - c->CastSpell(c, SPELL_PORTAL_BEAM, false); - if (Creature* c = me->SummonCreature(NPC_POWER_SPARK, FourSidesPos[random], TEMPSUMMON_MANUAL_DESPAWN, 0)) - { - c->AI()->DoAction(1); - c->AI()->Talk(EMOTE_POWER_SPARK); - } - - events.Repeat(20s, 30s); - } - break; - case EVENT_START_VORTEX_0: - { - bLockHealthCheck = true; - Talk(SAY_MAGIC_BLAST); - EntryCheckPredicate pred(NPC_POWER_SPARK); - summons.DoAction(2, pred); // stop following - me->SetUnitFlag(UNIT_FLAG_PACIFIED); - - me->SendMeleeAttackStop(me->GetVictim()); - me->SetTarget(); - - me->GetMotionMaster()->MoveIdle(); - me->StopMoving(); - me->SetDisableGravity(true); - me->GetMotionMaster()->MovePoint(MI_POINT_VORTEX_TAKEOFF, me->GetPositionX(), me->GetPositionY(), CenterPos.GetPositionZ() + 20.0f, FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Fly); - - events.DelayEvents(25s, 1); // don't delay berserk (group 0) - } - break; - case EVENT_VORTEX_FLY_TO_CENTER: - me->GetMotionMaster()->MovePoint(MI_POINT_VORTEX_CENTER, CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() + 20.0f); - break; - case EVENT_START_VORTEX_REAL: - { - me->SendMeleeAttackStop(me->GetVictim()); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_CUSTOM_SPELL_01); - me->HandleEmoteCommand(EMOTE_STATE_CUSTOM_SPELL_01); - - Position pos; - float angle = (me->GetOrientation() >= M_PI / 4 ? me->GetOrientation() - M_PI / 4 : 7 * M_PI / 4 + me->GetOrientation()); - pos.m_positionX = CenterPos.GetPositionX() + cos(angle) * 40.0f; - pos.m_positionY = CenterPos.GetPositionY() + std::sin(angle) * 40.0f; - pos.m_positionZ = CenterPos.GetPositionZ() + 20.0f; - pos.SetOrientation(pos.GetAngle(&CenterPos)); - - if (Creature* vp = me->SummonCreature(NPC_WORLD_TRIGGER_LAOI, pos, TEMPSUMMON_TIMED_DESPAWN, 14000)) - { - vp->SetDisableGravity(true); - - Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.IsEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player* pPlayer = i->GetSource()) - { - if (!pPlayer->IsAlive() || pPlayer->IsGameMaster()) - continue; - - Position plrpos; - float playerAngle = CenterPos.GetAngle(pPlayer); - plrpos.m_positionX = CenterPos.GetPositionX() + cos(playerAngle) * 5.0f; - plrpos.m_positionY = CenterPos.GetPositionY() + std::sin(playerAngle) * 5.0f; - plrpos.m_positionZ = CenterPos.GetPositionZ() + 18.0f; - plrpos.SetOrientation(plrpos.GetAngle(&CenterPos)); - - if (Creature* c = me->SummonCreature(NPC_VORTEX, plrpos, TEMPSUMMON_TIMED_DESPAWN, 15000)) - { - pPlayer->CastSpell(pPlayer, SPELL_FREEZE_ANIM, true); - pPlayer->CastSpell(c, SPELL_VORTEX_CONTROL_VEHICLE, true); - if (!pPlayer->GetVehicle()) // didn't work somehow, try again with a different way, if fails - break - { - pPlayer->EnterVehicle(c, 0); - if (!pPlayer->GetVehicle()) - continue; - } - //pPlayer->ClearUnitState(UNIT_STATE_ONVEHICLE); - - Movement::MoveSplineInit init(pPlayer); // TODO: has to be removed and handled with vehicle exit and vehicle enter code - init.MoveTo(CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ()); - init.SetFacing(pPlayer->GetOrientation()); - init.SetTransportExit(); - init.Launch(); - - pPlayer->SetUnitMovementFlags(MOVEMENTFLAG_NONE); - pPlayer->SetDisableGravity(true); - - WorldPacket data(SMSG_SPLINE_MOVE_UNROOT, 8); - data << pPlayer->GetPackGUID(); - pPlayer->SendMessageToSet(&data, true); - - sScriptMgr->AnticheatSetUnderACKmount(pPlayer); - - pPlayer->SetGuidValue(PLAYER_FARSIGHT, vp->GetGUID()); - c->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - } - } - } - - events.RescheduleEvent(EVENT_VORTEX_LAND_0, 11s, 1); - break; - } - case EVENT_VORTEX_LAND_0: - me->GetMotionMaster()->MovePoint(MI_POINT_VORTEX_LAND, CenterPos, FORCED_MOVEMENT_RUN, 0.f, true, true, AnimTier::Ground); - - break; - case EVENT_VORTEX_LAND_1: - { - bLockHealthCheck = false; - EntryCheckPredicate pred(NPC_POWER_SPARK); - summons.DoAction(1, pred); // resume following - me->RemoveUnitFlag(UNIT_FLAG_PACIFIED); - if (Unit* target = me->GetVictim()) - { - AttackStart(target); - me->GetMotionMaster()->MoveChase(target); - } - events.RescheduleEvent(EVENT_START_VORTEX_0, 60s, 1); - break; - } - case EVENT_START_PHASE_2: - events.SetPhase(PHASE_TWO); - Talk(SAY_END_P1); - me->SetUnitFlag(UNIT_FLAG_PACIFIED); - me->SendMeleeAttackStop(); - me->SetTarget(); - me->GetMotionMaster()->MoveIdle(); - me->DisableSpline(); - me->GetMotionMaster()->MovePoint(MI_POINT_CENTER_GROUND_PH_2, CenterPos); - events.CancelEventGroup(1); // don't cancel berserk (group 0) - break; - case EVENT_START_PHASE_2_FLY_UP: - { - me->SendMeleeAttackStop(me->GetVictim()); - me->GetMotionMaster()->MoveIdle(); - me->DisableSpline(); - me->SetDisableGravity(true); - me->GetMotionMaster()->MovePoint(MI_POINT_CENTER_AIR_PH_2, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 32.0f, FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Fly); - events.RescheduleEvent(EVENT_START_PHASE_2_MOVE_TO_SIDE, 22s + 500ms, 1); - break; - } - case EVENT_START_PHASE_2_MOVE_TO_SIDE: - Talk(SAY_PHASE_2); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); - me->GetMotionMaster()->MovePoint(MI_POINT_CIRCLE_OUTSIDE_PH_2, Phase2NorthPos); - events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 12s, 15s, 1); - events.RescheduleEvent(EVENT_SPELL_ARCANE_OVERLOAD, 8s, 1); - events.RescheduleEvent(EVENT_MOVE_TO_SURGE_OF_POWER, 55s, 1); - events.RescheduleEvent(EVENT_CHECK_TRASH_DEAD, 3s, 1); - - for (int i = 0; i < MAX_NEXUS_LORDS; i++) - { - float dist = 22.0f; - float angle = M_PI / 2 + ((float)i / MAX_NEXUS_LORDS) * 2 * M_PI; - if (Creature* disk = me->SummonCreature(NPC_HOVER_DISK, CenterPos.GetPositionX() + cos(angle) * dist, CenterPos.GetPositionY() + std::sin(angle) * dist, CenterPos.GetPositionZ() + 30.0f, 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) - if (Creature* c = me->SummonCreature(NPC_NEXUS_LORD, *disk, TEMPSUMMON_MANUAL_DESPAWN, 0)) - { - c->EnterVehicle(disk, 0); - disk->AI()->DoAction(1); // start moving - } - } - for (int i = 0; i < MAX_SCIONS_OF_ETERNITY; i++) - { - float dist = 30.0f; - float angle = 0.0f + ((float)i / MAX_SCIONS_OF_ETERNITY) * 2 * M_PI; - if (Creature* disk = me->SummonCreature(NPC_HOVER_DISK, CenterPos.GetPositionX() + cos(angle) * dist, CenterPos.GetPositionY() + std::sin(angle) * dist, CenterPos.GetPositionZ() + 30.0f, 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) - if (Creature* c = me->SummonCreature(NPC_SCION_OF_ETERNITY, *disk, TEMPSUMMON_MANUAL_DESPAWN, 0)) - { - c->EnterVehicle(disk, 0); - disk->AI()->DoAction(1); // start moving - } - } - - break; - case EVENT_SPELL_ARCANE_OVERLOAD: - { - me->GetMotionMaster()->MoveIdle(); - me->StopMoving(); - float dist = urand(5, 30); - float angle = rand_norm() * 2 * M_PI; - float posx = CenterPos.GetPositionX() + cos(angle) * dist; - float posy = CenterPos.GetPositionY() + std::sin(angle) * dist; - me->SetFacingTo(me->GetAngle(posx, posy)); - me->CastSpell(posx, posy, CenterPos.GetPositionZ() + 1.5f, SPELL_ARCANE_OVERLOAD, true); - events.Repeat(15s); - events.RescheduleEvent(EVENT_RESUME_FLYING_CIRCLES_PH_2, 3s, 1); - } - break; - case EVENT_RESUME_FLYING_CIRCLES_PH_2: - { - float angle = CenterPos.GetAngle(me); - float dist = Phase2NorthPos.GetExactDist2d(&CenterPos); - float newangle = angle + 0.5f; - if (newangle >= 2 * M_PI) newangle -= 2 * M_PI; - me->GetMotionMaster()->MovePoint(MI_POINT_CIRCLE_OUTSIDE_PH_2, CenterPos.GetPositionX() + cos(newangle)*dist, CenterPos.GetPositionY() + std::sin(newangle)*dist, Phase2NorthPos.GetPositionZ()); - } - break; - case EVENT_MOVE_TO_SURGE_OF_POWER: - { - Talk(SAY_DEEP_BREATH); - float angle = CenterPos.GetAngle(me); - me->GetMotionMaster()->MoveIdle(); - me->StopMoving(); - me->GetMotionMaster()->MovePoint(MI_POINT_SURGE_OF_POWER_CENTER, CenterPos.GetPositionX() + cos(angle) * 10.0f, CenterPos.GetPositionY() + std::sin(angle) * 10.0f, Phase2NorthPos.GetPositionZ()); - events.CancelEventGroup(1); // everything beside berserk - } - break; - case EVENT_SURGE_OF_POWER_WARNING: - Talk(EMOTE_SURGE_OF_POWER_WARNING_P2); - events.RescheduleEvent(EVENT_SPELL_SURGE_OF_POWER, 1500ms, 1); - break; - case EVENT_SPELL_SURGE_OF_POWER: - if (Creature* c = me->SummonCreature(NPC_SURGE_OF_POWER, CenterPos, TEMPSUMMON_TIMED_DESPAWN, 10000)) - me->CastSpell(c, SPELL_SURGE_OF_POWER, false); - Talk(SAY_SURGE_OF_POWER); - events.RescheduleEvent(EVENT_CLEAR_TARGET, 10s, 1); - events.RescheduleEvent(EVENT_RESUME_FLYING_CIRCLES_PH_2, 10s, 1); - events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 17s, 25s, 1); - events.RescheduleEvent(EVENT_SPELL_ARCANE_OVERLOAD, 16s, 1); - events.RescheduleEvent(EVENT_MOVE_TO_SURGE_OF_POWER, 65s, 1); - events.RescheduleEvent(EVENT_CHECK_TRASH_DEAD, 3s, 1); - break; - case EVENT_CLEAR_TARGET: - me->SendMeleeAttackStop(); - me->SetTarget(); - break; - case EVENT_CHECK_TRASH_DEAD: - { - if (me->FindNearestCreature(NPC_SCION_OF_ETERNITY, 250.0f, true) || me->FindNearestCreature(NPC_NEXUS_LORD, 250.0f, true)) - events.Repeat(3s); - else - { - me->SendMeleeAttackStop(); - me->SetTarget(); - events.CancelEventGroup(1); - summons.DespawnAll(); - // start phase 3 - Talk(SAY_END_P2); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - me->StopMoving(); - me->GetMotionMaster()->MovePoint(MI_POINT_START_PH_3, CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() + 70.0f); - events.RescheduleEvent(EVENT_LIGHT_DIMENSION_CHANGE, 1s, 1); - events.RescheduleEvent(EVENT_DESTROY_PLATFORM_0, 10s, 1); - } - } - break; - case EVENT_LIGHT_DIMENSION_CHANGE: - me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_CHANGE_DIMENSIONS, 2s); - break; - case EVENT_DESTROY_PLATFORM_0: - if (Creature* c = me->SummonCreature(NPC_WORLD_TRIGGER_LAOI, CenterPos, TEMPSUMMON_TIMED_DESPAWN, 3000)) - { - c->SetFaction(me->GetFaction()); - c->CastSpell(c, SPELL_DESTROY_PLATFORM_VISUAL, true); - c->CastSpell(c, SPELL_DESTROY_PLATFORM_EFFECT, false); - } - me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_OBSCURE_SPACE, 1s); - events.RescheduleEvent(EVENT_MOVE_TO_PHASE_3_POSITION, 2s, 1); - break; - case EVENT_MOVE_TO_PHASE_3_POSITION: - { - me->SendMeleeAttackStop(me->GetVictim()); - me->GetMotionMaster()->MovePoint(MI_POINT_PH_3_FIGHT_POSITION, CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() - 5.0f, FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Fly); - - me->GetThreatMgr().ClearAllThreat(); // players on vehicle are unattackable -> leads to EnterEvadeMode() because target is not acceptable! - - // mount players: - Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.IsEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player* player = i->GetSource()) - { - sScriptMgr->AnticheatSetUnderACKmount(player); - - if (!player->IsAlive() || player->IsGameMaster()) - continue; - - player->CastSpell(player, SPELL_SUMMON_RED_DRAGON_BUDDY, true); - } - - events.RescheduleEvent(EVENT_SAY_PHASE_3_INTRO, 3s, 1); - } - break; - case EVENT_SAY_PHASE_3_INTRO: - Talk(SAY_INTRO_PHASE_3); - break; - case EVENT_START_PHASE_3: - events.SetPhase(PHASE_THREE); - me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_OBSCURE_ARCANE_RUNES, 1s); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetUnitFlag(UNIT_FLAG_PACIFIED | UNIT_FLAG_DISABLE_MOVE); - if (Unit* target = me->GetVictim()) - AttackStart(target); - events.RescheduleEvent(EVENT_SPELL_ARCANE_PULSE, 0ms, 1); - events.RescheduleEvent(EVENT_SPELL_STATIC_FIELD, 1s, 4s, 1); - events.RescheduleEvent(EVENT_SPELL_PH3_SURGE_OF_POWER, 4s, 7s, 1); - events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 12s, 15s, 1); - break; - case EVENT_SPELL_ARCANE_PULSE: - me->CastSpell(me, SPELL_ARCANE_PULSE, true); - events.Repeat(3s); - break; - case EVENT_SPELL_STATIC_FIELD: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200.0f, false)) - { - me->SetFacingToObject(target); - me->CastSpell(target, SPELL_STATIC_FIELD_MAIN, true); - } - events.Repeat(12s); - break; - case EVENT_SPELL_PH3_SURGE_OF_POWER: - me->CastSpell((Unit*)nullptr, SPELL_PH3_SURGE_OF_POWER, false); - events.Repeat(7s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - if (pInstance) - { - pInstance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, NPC_MALYGOS, 1); - pInstance->SetData(DATA_ENCOUNTER_STATUS, DONE); - } - } - - void KilledUnit(Unit* victim) override - { - if (victim && victim->GetGUID() == me->GetGUID()) - return; - - if (events.IsInPhase(PHASE_ONE)) - Talk(SAY_SLAY_P1); - else if (events.IsInPhase(PHASE_TWO)) - Talk(SAY_SLAY_P2); - else - Talk(SAY_SLAY_P3); - } - - void JustSummoned(Creature* summon) override - { - if (!summon) - return; - summons.Summon(summon); - switch (summon->GetEntry()) - { - case NPC_ARCANE_OVERLOAD: - summon->CastSpell(summon, SPELL_ARCANE_OVERLOAD_DMG, true); - summon->DespawnOrUnsummon(45s); - break; - case NPC_STATIC_FIELD: - summon->DespawnOrUnsummon(20s); - break; - } - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - - void EnterEvadeMode(EvadeReason why) override - { + me->GetMotionMaster()->MoveIdle(); + me->StopMoving(); me->SetDisableGravity(true); - me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_GET_DEFAULT_FOR_MAP, 1s); + me->GetMotionMaster()->MovePoint(MI_POINT_VORTEX_TAKEOFF, me->GetPositionX(), me->GetPositionY(), CenterPos.GetPositionZ() + 20.0f, FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Fly); - me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); - ScriptedAI::EnterEvadeMode(why); + events.DelayEvents(25s, 1); // don't delay berserk (group 0) + } + break; + case EVENT_VORTEX_FLY_TO_CENTER: + me->GetMotionMaster()->MovePoint(MI_POINT_VORTEX_CENTER, CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() + 20.0f); + break; + case EVENT_START_VORTEX_REAL: + { + me->SendMeleeAttackStop(me->GetVictim()); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_CUSTOM_SPELL_01); + me->HandleEmoteCommand(EMOTE_STATE_CUSTOM_SPELL_01); + + Position pos; + float angle = (me->GetOrientation() >= M_PI / 4 ? me->GetOrientation() - M_PI / 4 : 7 * M_PI / 4 + me->GetOrientation()); + pos.m_positionX = CenterPos.GetPositionX() + cos(angle) * 40.0f; + pos.m_positionY = CenterPos.GetPositionY() + std::sin(angle) * 40.0f; + pos.m_positionZ = CenterPos.GetPositionZ() + 20.0f; + pos.SetOrientation(pos.GetAngle(&CenterPos)); + + if (Creature* vp = me->SummonCreature(NPC_WORLD_TRIGGER_LAOI, pos, TEMPSUMMON_TIMED_DESPAWN, 14000)) + { + vp->SetDisableGravity(true); + + Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); + if (!PlayerList.IsEmpty()) + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + if (Player* pPlayer = i->GetSource()) + { + if (!pPlayer->IsAlive() || pPlayer->IsGameMaster()) + continue; + + Position plrpos; + float playerAngle = CenterPos.GetAngle(pPlayer); + plrpos.m_positionX = CenterPos.GetPositionX() + cos(playerAngle) * 5.0f; + plrpos.m_positionY = CenterPos.GetPositionY() + std::sin(playerAngle) * 5.0f; + plrpos.m_positionZ = CenterPos.GetPositionZ() + 18.0f; + plrpos.SetOrientation(plrpos.GetAngle(&CenterPos)); + + if (Creature* c = me->SummonCreature(NPC_VORTEX, plrpos, TEMPSUMMON_TIMED_DESPAWN, 15000)) + { + pPlayer->CastSpell(pPlayer, SPELL_FREEZE_ANIM, true); + pPlayer->CastSpell(c, SPELL_VORTEX_CONTROL_VEHICLE, true); + if (!pPlayer->GetVehicle()) // didn't work somehow, try again with a different way, if fails - break + { + pPlayer->EnterVehicle(c, 0); + if (!pPlayer->GetVehicle()) + continue; + } + //pPlayer->ClearUnitState(UNIT_STATE_ONVEHICLE); + + Movement::MoveSplineInit init(pPlayer); // TODO: has to be removed and handled with vehicle exit and vehicle enter code + init.MoveTo(CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ()); + init.SetFacing(pPlayer->GetOrientation()); + init.SetTransportExit(); + init.Launch(); + + pPlayer->SetUnitMovementFlags(MOVEMENTFLAG_NONE); + pPlayer->SetDisableGravity(true); + + WorldPacket data(SMSG_SPLINE_MOVE_UNROOT, 8); + data << pPlayer->GetPackGUID(); + pPlayer->SendMessageToSet(&data, true); + + sScriptMgr->AnticheatSetUnderACKmount(pPlayer); + + pPlayer->SetGuidValue(PLAYER_FARSIGHT, vp->GetGUID()); + c->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } + } + } + + events.RescheduleEvent(EVENT_VORTEX_LAND_0, 11s, 1); + break; + } + case EVENT_VORTEX_LAND_0: + me->GetMotionMaster()->MovePoint(MI_POINT_VORTEX_LAND, CenterPos, FORCED_MOVEMENT_RUN, 0.f, true, true, AnimTier::Ground); + + break; + case EVENT_VORTEX_LAND_1: + { + bLockHealthCheck = false; + EntryCheckPredicate pred(NPC_POWER_SPARK); + summons.DoAction(1, pred); // resume following + me->RemoveUnitFlag(UNIT_FLAG_PACIFIED); + if (Unit* target = me->GetVictim()) + { + AttackStart(target); + me->GetMotionMaster()->MoveChase(target); + } + events.RescheduleEvent(EVENT_START_VORTEX_0, 60s, 1); + break; + } + case EVENT_START_PHASE_2: + events.SetPhase(PHASE_TWO); + Talk(SAY_END_P1); + me->SetUnitFlag(UNIT_FLAG_PACIFIED); + me->SendMeleeAttackStop(); + me->SetTarget(); + me->GetMotionMaster()->MoveIdle(); + me->DisableSpline(); + me->GetMotionMaster()->MovePoint(MI_POINT_CENTER_GROUND_PH_2, CenterPos); + events.CancelEventGroup(1); // don't cancel berserk (group 0) + break; + case EVENT_START_PHASE_2_FLY_UP: + { + me->SendMeleeAttackStop(me->GetVictim()); + me->GetMotionMaster()->MoveIdle(); + me->DisableSpline(); + me->SetDisableGravity(true); + me->GetMotionMaster()->MovePoint(MI_POINT_CENTER_AIR_PH_2, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 32.0f, FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Fly); + events.RescheduleEvent(EVENT_START_PHASE_2_MOVE_TO_SIDE, 22s + 500ms, 1); + break; + } + case EVENT_START_PHASE_2_MOVE_TO_SIDE: + Talk(SAY_PHASE_2); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); + me->GetMotionMaster()->MovePoint(MI_POINT_CIRCLE_OUTSIDE_PH_2, Phase2NorthPos); + events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 12s, 15s, 1); + events.RescheduleEvent(EVENT_SPELL_ARCANE_OVERLOAD, 8s, 1); + events.RescheduleEvent(EVENT_MOVE_TO_SURGE_OF_POWER, 55s, 1); + events.RescheduleEvent(EVENT_CHECK_TRASH_DEAD, 3s, 1); + + for (int i = 0; i < MAX_NEXUS_LORDS; i++) + { + float dist = 22.0f; + float angle = M_PI / 2 + ((float)i / MAX_NEXUS_LORDS) * 2 * M_PI; + if (Creature* disk = me->SummonCreature(NPC_HOVER_DISK, CenterPos.GetPositionX() + cos(angle) * dist, CenterPos.GetPositionY() + std::sin(angle) * dist, CenterPos.GetPositionZ() + 30.0f, 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) + if (Creature* c = me->SummonCreature(NPC_NEXUS_LORD, *disk, TEMPSUMMON_MANUAL_DESPAWN, 0)) + { + c->EnterVehicle(disk, 0); + disk->AI()->DoAction(1); // start moving + } + } + for (int i = 0; i < MAX_SCIONS_OF_ETERNITY; i++) + { + float dist = 30.0f; + float angle = 0.0f + ((float)i / MAX_SCIONS_OF_ETERNITY) * 2 * M_PI; + if (Creature* disk = me->SummonCreature(NPC_HOVER_DISK, CenterPos.GetPositionX() + cos(angle) * dist, CenterPos.GetPositionY() + std::sin(angle) * dist, CenterPos.GetPositionZ() + 30.0f, 0.0f, TEMPSUMMON_MANUAL_DESPAWN, 0)) + if (Creature* c = me->SummonCreature(NPC_SCION_OF_ETERNITY, *disk, TEMPSUMMON_MANUAL_DESPAWN, 0)) + { + c->EnterVehicle(disk, 0); + disk->AI()->DoAction(1); // start moving + } + } + + break; + case EVENT_SPELL_ARCANE_OVERLOAD: + { + me->GetMotionMaster()->MoveIdle(); + me->StopMoving(); + float dist = urand(5, 30); + float angle = rand_norm() * 2 * M_PI; + float posx = CenterPos.GetPositionX() + cos(angle) * dist; + float posy = CenterPos.GetPositionY() + std::sin(angle) * dist; + me->SetFacingTo(me->GetAngle(posx, posy)); + me->CastSpell(posx, posy, CenterPos.GetPositionZ() + 1.5f, SPELL_ARCANE_OVERLOAD, true); + events.Repeat(15s); + events.RescheduleEvent(EVENT_RESUME_FLYING_CIRCLES_PH_2, 3s, 1); + } + break; + case EVENT_RESUME_FLYING_CIRCLES_PH_2: + { + float angle = CenterPos.GetAngle(me); + float dist = Phase2NorthPos.GetExactDist2d(&CenterPos); + float newangle = angle + 0.5f; + if (newangle >= 2 * M_PI) newangle -= 2 * M_PI; + me->GetMotionMaster()->MovePoint(MI_POINT_CIRCLE_OUTSIDE_PH_2, CenterPos.GetPositionX() + cos(newangle) * dist, CenterPos.GetPositionY() + std::sin(newangle) * dist, Phase2NorthPos.GetPositionZ()); + } + break; + case EVENT_MOVE_TO_SURGE_OF_POWER: + { + Talk(SAY_DEEP_BREATH); + float angle = CenterPos.GetAngle(me); + me->GetMotionMaster()->MoveIdle(); + me->StopMoving(); + me->GetMotionMaster()->MovePoint(MI_POINT_SURGE_OF_POWER_CENTER, CenterPos.GetPositionX() + cos(angle) * 10.0f, CenterPos.GetPositionY() + std::sin(angle) * 10.0f, Phase2NorthPos.GetPositionZ()); + events.CancelEventGroup(1); // everything beside berserk + } + break; + case EVENT_SURGE_OF_POWER_WARNING: + Talk(EMOTE_SURGE_OF_POWER_WARNING_P2); + events.RescheduleEvent(EVENT_SPELL_SURGE_OF_POWER, 1500ms, 1); + break; + case EVENT_SPELL_SURGE_OF_POWER: + if (Creature* c = me->SummonCreature(NPC_SURGE_OF_POWER, CenterPos, TEMPSUMMON_TIMED_DESPAWN, 10000)) + me->CastSpell(c, SPELL_SURGE_OF_POWER, false); + Talk(SAY_SURGE_OF_POWER); + events.RescheduleEvent(EVENT_CLEAR_TARGET, 10s, 1); + events.RescheduleEvent(EVENT_RESUME_FLYING_CIRCLES_PH_2, 10s, 1); + events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 17s, 25s, 1); + events.RescheduleEvent(EVENT_SPELL_ARCANE_OVERLOAD, 16s, 1); + events.RescheduleEvent(EVENT_MOVE_TO_SURGE_OF_POWER, 65s, 1); + events.RescheduleEvent(EVENT_CHECK_TRASH_DEAD, 3s, 1); + break; + case EVENT_CLEAR_TARGET: + me->SendMeleeAttackStop(); + me->SetTarget(); + break; + case EVENT_CHECK_TRASH_DEAD: + { + if (me->FindNearestCreature(NPC_SCION_OF_ETERNITY, 250.0f, true) || me->FindNearestCreature(NPC_NEXUS_LORD, 250.0f, true)) + events.Repeat(3s); + else + { + me->SendMeleeAttackStop(); + me->SetTarget(); + events.CancelEventGroup(1); + summons.DespawnAll(); + // start phase 3 + Talk(SAY_END_P2); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + me->StopMoving(); + me->GetMotionMaster()->MovePoint(MI_POINT_START_PH_3, CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() + 70.0f); + events.RescheduleEvent(EVENT_LIGHT_DIMENSION_CHANGE, 1s, 1); + events.RescheduleEvent(EVENT_DESTROY_PLATFORM_0, 10s, 1); + } + } + break; + case EVENT_LIGHT_DIMENSION_CHANGE: + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_CHANGE_DIMENSIONS, 2s); + break; + case EVENT_DESTROY_PLATFORM_0: + if (Creature* c = me->SummonCreature(NPC_WORLD_TRIGGER_LAOI, CenterPos, TEMPSUMMON_TIMED_DESPAWN, 3000)) + { + c->SetFaction(me->GetFaction()); + c->CastSpell(c, SPELL_DESTROY_PLATFORM_VISUAL, true); + c->CastSpell(c, SPELL_DESTROY_PLATFORM_EFFECT, false); + } + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_OBSCURE_SPACE, 1s); + events.RescheduleEvent(EVENT_MOVE_TO_PHASE_3_POSITION, 2s, 1); + break; + case EVENT_MOVE_TO_PHASE_3_POSITION: + { + me->SendMeleeAttackStop(me->GetVictim()); + me->GetMotionMaster()->MovePoint(MI_POINT_PH_3_FIGHT_POSITION, CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() - 5.0f, FORCED_MOVEMENT_RUN, 0.f, 0.f, true, true, MOTION_SLOT_ACTIVE, AnimTier::Fly); + + me->GetThreatMgr().ClearAllThreat(); // players on vehicle are unattackable -> leads to EnterEvadeMode() because target is not acceptable! + + // mount players: + Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); + if (!PlayerList.IsEmpty()) + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + if (Player* player = i->GetSource()) + { + sScriptMgr->AnticheatSetUnderACKmount(player); + + if (!player->IsAlive() || player->IsGameMaster()) + continue; + + player->CastSpell(player, SPELL_SUMMON_RED_DRAGON_BUDDY, true); + } + + events.RescheduleEvent(EVENT_SAY_PHASE_3_INTRO, 3s, 1); + } + break; + case EVENT_SAY_PHASE_3_INTRO: + Talk(SAY_INTRO_PHASE_3); + break; + case EVENT_START_PHASE_3: + events.SetPhase(PHASE_THREE); + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_OBSCURE_ARCANE_RUNES, 1s); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetUnitFlag(UNIT_FLAG_PACIFIED | UNIT_FLAG_DISABLE_MOVE); + if (Unit* target = me->GetVictim()) + AttackStart(target); + events.RescheduleEvent(EVENT_SPELL_ARCANE_PULSE, 0ms, 1); + events.RescheduleEvent(EVENT_SPELL_STATIC_FIELD, 1s, 4s, 1); + events.RescheduleEvent(EVENT_SPELL_PH3_SURGE_OF_POWER, 4s, 7s, 1); + events.RescheduleEvent(EVENT_SPELL_ARCANE_STORM, 12s, 15s, 1); + break; + case EVENT_SPELL_ARCANE_PULSE: + me->CastSpell(me, SPELL_ARCANE_PULSE, true); + events.Repeat(3s); + break; + case EVENT_SPELL_STATIC_FIELD: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200.0f, false)) + { + me->SetFacingToObject(target); + me->CastSpell(target, SPELL_STATIC_FIELD_MAIN, true); + } + events.Repeat(12s); + break; + case EVENT_SPELL_PH3_SURGE_OF_POWER: + me->CastSpell((Unit*)nullptr, SPELL_PH3_SURGE_OF_POWER, false); + events.Repeat(7s); + break; } - void HandleIntroSpeech(const uint32 diff) - { - if (me->IsInCombat() || me->isDead()) - return; + DoMeleeAttackIfReady(); + } - // speech timer - if (timer1 <= diff) + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + if (pInstance) + { + pInstance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, NPC_MALYGOS, 1); + pInstance->SetData(DATA_ENCOUNTER_STATUS, DONE); + } + } + + void KilledUnit(Unit* victim) override + { + if (victim && victim->GetGUID() == me->GetGUID()) + return; + + if (events.IsInPhase(PHASE_ONE)) + Talk(SAY_SLAY_P1); + else if (events.IsInPhase(PHASE_TWO)) + Talk(SAY_SLAY_P2); + else + Talk(SAY_SLAY_P3); + } + + void JustSummoned(Creature* summon) override + { + if (!summon) + return; + summons.Summon(summon); + switch (summon->GetEntry()) + { + case NPC_ARCANE_OVERLOAD: + summon->CastSpell(summon, SPELL_ARCANE_OVERLOAD_DMG, true); + summon->DespawnOrUnsummon(45s); + break; + case NPC_STATIC_FIELD: + summon->DespawnOrUnsummon(20s); + break; + } + } + + void MoveInLineOfSight(Unit* /*who*/) override {} + + void EnterEvadeMode(EvadeReason why) override + { + me->SetDisableGravity(true); + me->GetMap()->SetZoneOverrideLight(AREA_EYE_OF_ETERNITY, LIGHT_GET_DEFAULT_FOR_MAP, 1s); + + me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); + ScriptedAI::EnterEvadeMode(why); + } + + void HandleIntroSpeech(const uint32 diff) + { + if (me->IsInCombat() || me->isDead()) + return; + + // speech timer + if (timer1 <= diff) + { + Talk(SAY_INTRO); + timer1 = MalygosIntroIntervals[IntroCounter]; + if (++IntroCounter >= 5) + IntroCounter = 0; + } + else + timer1 -= diff; + + // movement timer + if (timer2) + { + if (timer2 <= diff) { - Talk(SAY_INTRO); - timer1 = MalygosIntroIntervals[IntroCounter]; - if (++IntroCounter >= 5) - IntroCounter = 0; + timer2 = 0; + uint32 tmp = urand(0, 3); + me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + me->GetMotionMaster()->MovePoint(MI_POINT_INTRO_SIDE_0 + tmp, FourSidesPos[tmp]); } else - timer1 -= diff; - - // movement timer - if (timer2) - { - if (timer2 <= diff) - { - timer2 = 0; - uint32 tmp = urand(0, 3); - me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - me->GetMotionMaster()->MovePoint(MI_POINT_INTRO_SIDE_0 + tmp, FourSidesPos[tmp]); - } - else - timer2 -= diff; - } + timer2 -= diff; } - }; + } }; #define VORTEX_DEFAULT_DIFF 250 #define VORTEX_TRAVEL_TIME 3000 //#define VORTEX_RADIUS 25.0f -class npc_vortex_ride : public CreatureScript +struct npc_vortex_ride : public VehicleAI { -public: - npc_vortex_ride() : CreatureScript("npc_vortex_ride") { } - - CreatureAI* GetAI(Creature* pCreature) const override + npc_vortex_ride(Creature* pCreature) : VehicleAI(pCreature) { - return GetEyeOfEternityAI (pCreature); + VORTEX_RADIUS = urand(22, 28); + float h = urand(15, 30); + float angle = CenterPos.GetAngle(me); + Position pos; + pos.m_positionX = CenterPos.GetPositionX() + VORTEX_RADIUS * cos(angle); + pos.m_positionY = CenterPos.GetPositionY() + VORTEX_RADIUS * std::sin(angle); + pos.m_positionZ = CenterPos.GetPositionZ() + h; + pos.SetOrientation(pos.GetAngle(&CenterPos)); + me->SetPosition(pos); + timer = 0; + despawnTimer = 9500; + bUpdatedFlying = false; } - struct npc_vortex_rideAI : public VehicleAI + uint32 timer; + uint32 despawnTimer; + bool bUpdatedFlying; + float VORTEX_RADIUS; + + void PassengerBoarded(Unit* pass, int8 /*seat*/, bool apply) override { - npc_vortex_rideAI(Creature* pCreature) : VehicleAI(pCreature) + if (pass && !apply && pass->IsPlayer()) + { + Player* plr = pass->ToPlayer(); + float speed = plr->GetDistance(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()) / (1.0f * 0.001f); + plr->SetDisableGravity(false); // packet only would lead to issues elsewhere + plr->GetMotionMaster()->MoveCharge(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), speed); + plr->RemoveAura(SPELL_FREEZE_ANIM); + plr->SetGuidValue(PLAYER_FARSIGHT, ObjectGuid::Empty); + + sScriptMgr->AnticheatSetCanFlybyServer(plr, false); + sScriptMgr->AnticheatSetUnderACKmount(plr); + } + } + + void UpdateAI(uint32 diff) override + { + /* here: if player has some aura that should make him exit vehicle (eg. ice block) -> exit + or make it another way (dunno how) */ + + if (despawnTimer <= diff) + { + despawnTimer = 0; + me->UpdatePosition(CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() + 18.0f, 0.0f, true); + me->StopMovingOnCurrentPos(); + if (Vehicle* vehicle = me->GetVehicleKit()) + vehicle->RemoveAllPassengers(); + me->DespawnOrUnsummon(); + return; + } + else + despawnTimer -= diff; + + if (timer <= diff) { - VORTEX_RADIUS = urand(22, 28); - float h = urand(15, 30); float angle = CenterPos.GetAngle(me); - Position pos; - pos.m_positionX = CenterPos.GetPositionX() + VORTEX_RADIUS * cos(angle); - pos.m_positionY = CenterPos.GetPositionY() + VORTEX_RADIUS * std::sin(angle); - pos.m_positionZ = CenterPos.GetPositionZ() + h; - pos.SetOrientation(pos.GetAngle(&CenterPos)); - me->SetPosition(pos); - timer = 0; - despawnTimer = 9500; - bUpdatedFlying = false; - } - - uint32 timer; - uint32 despawnTimer; - bool bUpdatedFlying; - float VORTEX_RADIUS; - - void PassengerBoarded(Unit* pass, int8 /*seat*/, bool apply) override - { - if (pass && !apply && pass->IsPlayer()) + float newangle = angle + 2 * M_PI / ((float)VORTEX_TRAVEL_TIME / VORTEX_DEFAULT_DIFF); + if (newangle >= 2 * M_PI) + newangle -= 2 * M_PI; + float newx = CenterPos.GetPositionX() + VORTEX_RADIUS * cos(newangle); + float newy = CenterPos.GetPositionY() + VORTEX_RADIUS * std::sin(newangle); + float arcangle = me->GetAngle(newx, newy); + float dist = 2 * me->GetDistance2d(newx, newy); + if (me->GetVehicleKit()) if (Unit* pass = me->GetVehicleKit()->GetPassenger(0)) if (Player* plr = pass->ToPlayer()) { - Player* plr = pass->ToPlayer(); - float speed = plr->GetDistance(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()) / (1.0f * 0.001f); - plr->SetDisableGravity(false); // packet only would lead to issues elsewhere - plr->GetMotionMaster()->MoveCharge(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), speed); - plr->RemoveAura(SPELL_FREEZE_ANIM); - plr->SetGuidValue(PLAYER_FARSIGHT, ObjectGuid::Empty); + if (!bUpdatedFlying && timer) + { + bUpdatedFlying = true; + plr->SetDisableGravity(true); + } - sScriptMgr->AnticheatSetCanFlybyServer(plr, false); - sScriptMgr->AnticheatSetUnderACKmount(plr); + plr->SendMonsterMove(me->GetPositionX() + dist * cos(arcangle), me->GetPositionY() + dist * std::sin(arcangle), me->GetPositionZ(), VORTEX_DEFAULT_DIFF * 2, SPLINEFLAG_FLYING); + me->Relocate(newx, newy); } + + timer = (diff - timer <= VORTEX_DEFAULT_DIFF) ? VORTEX_DEFAULT_DIFF - (diff - timer) : 0; } + else + timer -= diff; + } - void UpdateAI(uint32 diff) override - { - /* here: if player has some aura that should make him exit vehicle (eg. ice block) -> exit - or make it another way (dunno how) */ - - if (despawnTimer <= diff) - { - despawnTimer = 0; - me->UpdatePosition(CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() + 18.0f, 0.0f, true); - me->StopMovingOnCurrentPos(); - if (Vehicle* vehicle = me->GetVehicleKit()) - vehicle->RemoveAllPassengers(); - me->DespawnOrUnsummon(); - return; - } - else - despawnTimer -= diff; - - if (timer <= diff) - { - float angle = CenterPos.GetAngle(me); - float newangle = angle + 2 * M_PI / ((float)VORTEX_TRAVEL_TIME / VORTEX_DEFAULT_DIFF); - if (newangle >= 2 * M_PI) - newangle -= 2 * M_PI; - float newx = CenterPos.GetPositionX() + VORTEX_RADIUS * cos(newangle); - float newy = CenterPos.GetPositionY() + VORTEX_RADIUS * std::sin(newangle); - float arcangle = me->GetAngle(newx, newy); - float dist = 2 * me->GetDistance2d(newx, newy); - if (me->GetVehicleKit()) if (Unit* pass = me->GetVehicleKit()->GetPassenger(0)) if (Player* plr = pass->ToPlayer()) - { - if (!bUpdatedFlying && timer) - { - bUpdatedFlying = true; - plr->SetDisableGravity(true); - } - - plr->SendMonsterMove(me->GetPositionX() + dist * cos(arcangle), me->GetPositionY() + dist * std::sin(arcangle), me->GetPositionZ(), VORTEX_DEFAULT_DIFF * 2, SPLINEFLAG_FLYING); - me->Relocate(newx, newy); - } - - timer = (diff - timer <= VORTEX_DEFAULT_DIFF) ? VORTEX_DEFAULT_DIFF - (diff - timer) : 0; - } - else - timer -= diff; - } - - void AttackStart(Unit* /*who*/) override {} - void MoveInLineOfSight(Unit* /*who*/) override {} - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override { damage = 0; } - }; + void AttackStart(Unit* /*who*/) override {} + void MoveInLineOfSight(Unit* /*who*/) override {} + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override { damage = 0; } }; -class npc_power_spark : public CreatureScript +struct npc_power_spark : public NullCreatureAI { -public: - npc_power_spark() : CreatureScript("npc_power_spark") { } - - CreatureAI* GetAI(Creature* pCreature) const override + npc_power_spark(Creature* pCreature) : NullCreatureAI(pCreature) { - return GetEyeOfEternityAI(pCreature); + pInstance = me->GetInstanceScript(); + me->CastSpell(me, SPELL_POWER_SPARK_VISUAL, false); + CheckTimer = 1000; + MoveTimer = 0; } - struct npc_power_sparkAI : public NullCreatureAI + InstanceScript* pInstance; + uint16 CheckTimer; + uint16 MoveTimer; + + void DoAction(int32 param) override { - npc_power_sparkAI(Creature* pCreature) : NullCreatureAI(pCreature) + switch (param) { - pInstance = me->GetInstanceScript(); - me->CastSpell(me, SPELL_POWER_SPARK_VISUAL, false); - CheckTimer = 1000; + case 1: + MoveTimer = 1; + break; + case 2: MoveTimer = 0; + me->GetMotionMaster()->MoveIdle(); + me->DisableSpline(); + me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 0.05f, FORCED_MOVEMENT_NONE, 7.0f); + break; } + } - InstanceScript* pInstance; - uint16 CheckTimer; - uint16 MoveTimer; - - void DoAction(int32 param) override + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (damage >= me->GetHealth()) { - switch (param) + damage = 0; + if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) { - case 1: - MoveTimer = 1; - break; - case 2: - MoveTimer = 0; - me->GetMotionMaster()->MoveIdle(); - me->DisableSpline(); - me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 0.05f, FORCED_MOVEMENT_NONE, 7.0f); - break; + MoveTimer = 0; + me->GetMotionMaster()->MoveIdle(); + me->DisableSpline(); + me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), CenterPos.GetPositionZ(), FORCED_MOVEMENT_NONE, 100.0f); + me->ReplaceAllUnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->RemoveAura(SPELL_POWER_SPARK_VISUAL); + me->CastSpell(me, SPELL_POWER_SPARK_GROUND_BUFF, true); + me->DespawnOrUnsummon(60s); } } + } - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + void UpdateAI(uint32 diff) override + { + if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (CheckTimer <= diff) { - if (damage >= me->GetHealth()) - { - damage = 0; - if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) - { - MoveTimer = 0; - me->GetMotionMaster()->MoveIdle(); - me->DisableSpline(); - me->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), CenterPos.GetPositionZ(), FORCED_MOVEMENT_NONE, 100.0f); - me->ReplaceAllUnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - me->RemoveAura(SPELL_POWER_SPARK_VISUAL); - me->CastSpell(me, SPELL_POWER_SPARK_GROUND_BUFF, true); - me->DespawnOrUnsummon(60s); - } - } + if (pInstance) + if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_MALYGOS_GUID))) + if (me->IsWithinDist3d(c, 12.0f)) + { + me->CastSpell(c, SPELL_POWER_SPARK_MALYGOS_BUFF, true); + me->DespawnOrUnsummon(); + return; + } + CheckTimer = 1000; } + else + CheckTimer -= diff; - void UpdateAI(uint32 diff) override + if (MoveTimer) { - if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) - return; - - if (CheckTimer <= diff) + if (MoveTimer <= diff) { if (pInstance) if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_MALYGOS_GUID))) - if (me->IsWithinDist3d(c, 12.0f)) - { - me->CastSpell(c, SPELL_POWER_SPARK_MALYGOS_BUFF, true); - me->DespawnOrUnsummon(); - return; - } - CheckTimer = 1000; + me->GetMotionMaster()->MovePoint(0, *c); + MoveTimer = 2000; } else - CheckTimer -= diff; - - if (MoveTimer) - { - if (MoveTimer <= diff) - { - if (pInstance) - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_MALYGOS_GUID))) - me->GetMotionMaster()->MovePoint(0, *c); - MoveTimer = 2000; - } - else - MoveTimer -= diff; - } + MoveTimer -= diff; } - }; + } }; -class npc_nexus_lord : public CreatureScript +struct npc_nexus_lord : public ScriptedAI { -public: - npc_nexus_lord() : CreatureScript("npc_nexus_lord") { } - - CreatureAI* GetAI(Creature* pCreature) const override + npc_nexus_lord(Creature* pCreature) : ScriptedAI(pCreature) { - return GetEyeOfEternityAI(pCreature); + me->SetReactState(REACT_PASSIVE); + pInstance = me->GetInstanceScript(); + timer = 0; + events.Reset(); + events.RescheduleEvent(EVENT_TELEPORT_VISUAL, 0ms); } - struct npc_nexus_lordAI : public ScriptedAI + InstanceScript* pInstance; + EventMap events; + uint16 timer; + + void JustEngagedWith(Unit* /*who*/) override { - npc_nexus_lordAI(Creature* pCreature) : ScriptedAI(pCreature) - { - me->SetReactState(REACT_PASSIVE); - pInstance = me->GetInstanceScript(); - timer = 0; - events.Reset(); - events.RescheduleEvent(EVENT_TELEPORT_VISUAL, 0ms); - } + DoZoneInCombat(); + events.Reset(); + events.RescheduleEvent(EVENT_NEXUS_LORD_ARCANE_SHOCK, 3s, 10s); + events.RescheduleEvent(EVENT_NEXUS_LORD_HASTE, 8s, 14s); + } - InstanceScript* pInstance; - EventMap events; - uint16 timer; + void AttackStart(Unit* victim) override + { + if (victim && me->Attack(victim, true)) + me->GetMotionMaster()->MoveIdle(); + } - void JustEngagedWith(Unit* /*who*/) override - { - DoZoneInCombat(); - events.Reset(); - events.RescheduleEvent(EVENT_NEXUS_LORD_ARCANE_SHOCK, 3s, 10s); - events.RescheduleEvent(EVENT_NEXUS_LORD_HASTE, 8s, 14s); - } - - void AttackStart(Unit* victim) override - { - if (victim && me->Attack(victim, true)) - me->GetMotionMaster()->MoveIdle(); - } - - void UpdateAI(uint32 diff) override - { - if (UpdateVictim()) - if (Unit* victim = me->GetVictim()) + void UpdateAI(uint32 diff) override + { + if (UpdateVictim()) + if (Unit* victim = me->GetVictim()) + { + if (timer <= diff) { - if (timer <= diff) + if (!victim->IsWithinMeleeRange(me)) { - if (!victim->IsWithinMeleeRange(me)) + float x, y, z; + victim->GetClosePoint(x, y, z, 0.0f, 1.5f, me->GetAngle(victim)); + if (Unit* v = me->GetVehicleBase()) + v->GetMotionMaster()->MovePoint(0, x, y, z); + } + timer = 1000; + } + else + timer -= diff; + } + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case 0: + break; + case EVENT_TELEPORT_VISUAL: + me->CastSpell(me, SPELL_TELEPORT_VISUAL, true); + break; + case EVENT_NEXUS_LORD_ARCANE_SHOCK: + if (Unit* victim = me->GetVictim()) + me->CastSpell(victim, SPELL_ARCANE_SHOCK); + events.Repeat(10s, 15s); + break; + case EVENT_NEXUS_LORD_HASTE: + me->CastSpell(me, SPELL_HASTE); + events.Repeat(20s, 30s); + break; + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + if (Vehicle* v = me->GetVehicle()) + v->RemoveAllPassengers(); + } +}; + +struct npc_scion_of_eternity : public ScriptedAI +{ + npc_scion_of_eternity(Creature* pCreature) : ScriptedAI(pCreature) + { + me->SetReactState(REACT_PASSIVE); + pInstance = me->GetInstanceScript(); + events.Reset(); + events.RescheduleEvent(EVENT_TELEPORT_VISUAL, 0ms); + events.RescheduleEvent(EVENT_SCION_OF_ETERNITY_ARCANE_BARRAGE, 20s, 25s); + } + + InstanceScript* pInstance; + EventMap events; + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case 0: + break; + case EVENT_TELEPORT_VISUAL: + me->CastSpell(me, SPELL_TELEPORT_VISUAL, true); + break; + case EVENT_SCION_OF_ETERNITY_ARCANE_BARRAGE: + { + GuidVector guids; + Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); + if (!PlayerList.IsEmpty()) + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + if (Player* pPlayer = i->GetSource()) + { + if (pPlayer->IsAlive()) { - float x, y, z; - victim->GetClosePoint(x, y, z, 0.0f, 1.5f, me->GetAngle(victim)); - if (Unit* v = me->GetVehicleBase()) - v->GetMotionMaster()->MovePoint(0, x, y, z); + if (!pPlayer->GetVehicle()) + { + guids.push_back(pPlayer->GetGUID()); + } } - timer = 1000; } - else - timer -= diff; - } + if (!guids.empty()) + if (Player* plr = ObjectAccessor::GetPlayer(*me, guids.at(urand(0, guids.size() - 1)))) + me->CastSpell(plr, SPELL_SCION_ARCANE_BARRAGE); - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_TELEPORT_VISUAL: - me->CastSpell(me, SPELL_TELEPORT_VISUAL, true); - break; - case EVENT_NEXUS_LORD_ARCANE_SHOCK: - if (Unit* victim = me->GetVictim()) - me->CastSpell(victim, SPELL_ARCANE_SHOCK); - events.Repeat(10s, 15s); - break; - case EVENT_NEXUS_LORD_HASTE: - me->CastSpell(me, SPELL_HASTE); - events.Repeat(20s, 30s); - break; - } - - DoMeleeAttackIfReady(); + events.Repeat(5s, 8s); } - - void JustDied(Unit* /*killer*/) override - { - if (Vehicle* v = me->GetVehicle()) - v->RemoveAllPassengers(); + break; } - }; -}; - -class npc_scion_of_eternity : public CreatureScript -{ -public: - npc_scion_of_eternity() : CreatureScript("npc_scion_of_eternity") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetEyeOfEternityAI(pCreature); } - struct npc_scion_of_eternityAI : public ScriptedAI + void JustDied(Unit* killer) override { - npc_scion_of_eternityAI(Creature* pCreature) : ScriptedAI(pCreature) - { - me->SetReactState(REACT_PASSIVE); - pInstance = me->GetInstanceScript(); - events.Reset(); - events.RescheduleEvent(EVENT_TELEPORT_VISUAL, 0ms); - events.RescheduleEvent(EVENT_SCION_OF_ETERNITY_ARCANE_BARRAGE, 20s, 25s); - } + if (Vehicle* v = me->GetVehicle()) + v->RemoveAllPassengers(); - InstanceScript* pInstance; - EventMap events; - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_TELEPORT_VISUAL: - me->CastSpell(me, SPELL_TELEPORT_VISUAL, true); - break; - case EVENT_SCION_OF_ETERNITY_ARCANE_BARRAGE: - { - GuidVector guids; - Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.IsEmpty()) - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player* pPlayer = i->GetSource()) - { - if (pPlayer->IsAlive()) - { - if (!pPlayer->GetVehicle()) - { - guids.push_back(pPlayer->GetGUID()); - } - } - } - if (!guids.empty()) - if (Player* plr = ObjectAccessor::GetPlayer(*me, guids.at(urand(0, guids.size() - 1)))) - me->CastSpell(plr, SPELL_SCION_ARCANE_BARRAGE); - - events.Repeat(5s, 8s); - } - break; - } - } - - void JustDied(Unit* killer) override - { - if (Vehicle* v = me->GetVehicle()) - v->RemoveAllPassengers(); - - if (killer) - if (Player* player = killer->GetCharmerOrOwnerPlayerOrPlayerItself()) - player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS, 1, 0, me); - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - void AttackStart(Unit* /*who*/) override {} - }; -}; - -class npc_hover_disk : public CreatureScript -{ -public: - npc_hover_disk() : CreatureScript("npc_hover_disk") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetEyeOfEternityAI(pCreature); + if (killer) + if (Player* player = killer->GetCharmerOrOwnerPlayerOrPlayerItself()) + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS, 1, 0, me); } - struct npc_hover_diskAI : public VehicleAI + void MoveInLineOfSight(Unit* /*who*/) override {} + void AttackStart(Unit* /*who*/) override {} +}; + +struct npc_hover_disk : public VehicleAI +{ + npc_hover_disk(Creature* pCreature) : VehicleAI(pCreature) { - npc_hover_diskAI(Creature* pCreature) : VehicleAI(pCreature) - { - pInstance = me->GetInstanceScript(); - events.Reset(); - } + pInstance = me->GetInstanceScript(); + events.Reset(); + } - InstanceScript* pInstance; - EventMap events; + InstanceScript* pInstance; + EventMap events; - void PassengerBoarded(Unit* who, int8 /*seat*/, bool apply) override + void PassengerBoarded(Unit* who, int8 /*seat*/, bool apply) override + { + events.Reset(); + if (!who) + return; + if (apply) { - events.Reset(); - if (!who) - return; - if (apply) + if (who->IsPlayer()) { - if (who->IsPlayer()) - { - who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_ARCANE_OVERLOAD_DMG, true); - who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SURGE_OF_POWER_DMG, true); - me->SetSpeed(MOVE_RUN, 1.5f); - me->SetSpeed(MOVE_FLIGHT, 1.5f); - } - else if (who->GetEntry() == NPC_NEXUS_LORD) - { - me->SetSpeed(MOVE_RUN, 1.5f); - me->SetSpeed(MOVE_FLIGHT, 1.5f); - } - else - { - me->SetSpeed(MOVE_RUN, 0.6f); - me->SetSpeed(MOVE_FLIGHT, 0.6f); - } - - who->SetFacingTo(me->GetOrientation()); - me->SetCanFly(true); + who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_ARCANE_OVERLOAD_DMG, true); + who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SURGE_OF_POWER_DMG, true); + me->SetSpeed(MOVE_RUN, 1.5f); + me->SetSpeed(MOVE_FLIGHT, 1.5f); + } + else if (who->GetEntry() == NPC_NEXUS_LORD) + { + me->SetSpeed(MOVE_RUN, 1.5f); + me->SetSpeed(MOVE_FLIGHT, 1.5f); } else { - me->GetMotionMaster()->MoveIdle(); - me->DisableSpline(); - me->SetCanFly(false); - me->GetMotionMaster()->MoveLand(0, me->GetPositionX(), me->GetPositionY(), 267.24f, 10.0f); - - if (who->IsPlayer()) - { - who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_ARCANE_OVERLOAD_DMG, false); - who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SURGE_OF_POWER_DMG, false); - } + me->SetSpeed(MOVE_RUN, 0.6f); + me->SetSpeed(MOVE_FLIGHT, 0.6f); } - } - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - return; - - switch (id) - { - case MI_POINT_SCION: - events.RescheduleEvent(EVENT_DISK_MOVE_NEXT_POINT, 0ms); - break; - case MI_POINT_NEXUS_LORD: - if (me->GetPositionZ() > CenterPos.GetPositionZ() + 2.0f) - events.RescheduleEvent(EVENT_DISK_MOVE_NEXT_POINT, 0ms); - else if (Vehicle* v = me->GetVehicleKit()) - if (Unit* pass = v->GetPassenger(0)) - if (Creature* c = pass->ToCreature()) - { - c->SetReactState(REACT_AGGRESSIVE); - if (Player* plr = c->SelectNearestPlayer(100.0f)) - c->AI()->AttackStart(plr); - } - break; - } - } - - void DoAction(int32 param) override - { - switch (param) - { - case 1: // move to next point - if (Vehicle* v = me->GetVehicleKit()) - if (Unit* pass = v->GetPassenger(0)) - switch (pass->GetEntry()) - { - case NPC_NEXUS_LORD: - { - float angle = CenterPos.GetAngle(me); - float newangle = angle - 0.5f; - if (newangle < 0.0f) newangle += 2 * M_PI; - float newz = me->GetPositionZ() - 4.0f; - if (newz < CenterPos.GetPositionZ()) newz = CenterPos.GetPositionZ(); - me->GetMotionMaster()->MovePoint(MI_POINT_NEXUS_LORD, CenterPos.GetPositionX() + cos(newangle) * 22.0f, CenterPos.GetPositionY() + std::sin(newangle) * 22.0f, newz); - } - break; - case NPC_SCION_OF_ETERNITY: - { - float angle = CenterPos.GetAngle(me); - float newangle = angle - 0.3f; - if (newangle < 0.0f) newangle += 2 * M_PI; - float newz = me->GetPositionZ() - 2.0f; - if (newz < CenterPos.GetPositionZ() + 20.0f) newz = CenterPos.GetPositionZ() + 20.0f; - me->GetMotionMaster()->MovePoint(MI_POINT_SCION, CenterPos.GetPositionX() + cos(newangle) * 30.0f, CenterPos.GetPositionY() + std::sin(newangle) * 30.0f, newz); - } - break; - } - break; - } - } - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_DISK_MOVE_NEXT_POINT: - DoAction(1); - break; - } - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - void AttackStart(Unit* /*who*/) override {} - }; -}; - -class npc_alexstrasza : public CreatureScript -{ -public: - npc_alexstrasza() : CreatureScript("npc_alexstrasza") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetEyeOfEternityAI(pCreature); - } - - struct npc_alexstraszaAI : public ScriptedAI - { - npc_alexstraszaAI(Creature* pCreature) : ScriptedAI(pCreature) - { - events.Reset(); - events.ScheduleEvent(1, 9s); + who->SetFacingTo(me->GetOrientation()); me->SetCanFly(true); - me->SetDisableGravity(true); } - - EventMap events; - - void UpdateAI(uint32 diff) override + else { - events.Update(diff); - switch (events.ExecuteEvent()) + me->GetMotionMaster()->MoveIdle(); + me->DisableSpline(); + me->SetCanFly(false); + me->GetMotionMaster()->MoveLand(0, me->GetPositionX(), me->GetPositionY(), 267.24f, 10.0f); + + if (who->IsPlayer()) { - case 0: - break; - case 1: - me->CastSpell(773.98f, 1285.97f, 266.254f, SPELL_ALEXSTRASZA_GIFT, true); - if (GameObject* chest = me->SummonGameObject(ALEXSTRASZA_GIFT, 773.98f, 1285.97f, 266.254f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) - { - chest->SetLootRecipient(me->GetMap()); - } - - if (GameObject* heart = me->SummonGameObject(HEART_OF_MAGIC, 773.98f, 1275.97f, 266.254f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) - { - heart->SetLootRecipient(me->GetMap()); - } - - Talk(SAY_ALEXSTRASZA_ONE); - events.RescheduleEvent(2, 6s); - break; - case 2: - Talk(SAY_ALEXSTRASZA_TWO); - events.RescheduleEvent(3, 5s); - break; - case 3: - Talk(SAY_ALEXSTRASZA_THREE); - events.RescheduleEvent(4, 22s); - break; - case 4: - Talk(SAY_ALEXSTRASZA_FOUR); - break; + who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_ARCANE_OVERLOAD_DMG, false); + who->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SURGE_OF_POWER_DMG, false); } } - - void MoveInLineOfSight(Unit* /*who*/) override {} - void AttackStart(Unit* /*who*/) override {} - }; -}; - -class npc_eoe_wyrmrest_skytalon : public CreatureScript -{ -public: - npc_eoe_wyrmrest_skytalon() : CreatureScript("npc_eoe_wyrmrest_skytalon") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetEyeOfEternityAI(pCreature); } - struct npc_eoe_wyrmrest_skytalonAI : public VehicleAI + void MovementInform(uint32 type, uint32 id) override { - npc_eoe_wyrmrest_skytalonAI(Creature* pCreature) : VehicleAI(pCreature) { } + if (type != POINT_MOTION_TYPE) + return; - void IsSummonedBy(WorldObject* summoner) override + switch (id) { - me->SetDisableGravity(true); - if (summoner && summoner->IsPlayer()) - { - ObjectGuid summonerGUID = summoner->GetGUID(); - me->m_Events.AddEventAtOffset([summonerGUID, this] { - if (Player* rider = ObjectAccessor::GetPlayer(*me, summonerGUID)) - DoCast(rider, SPELL_RIDE_RED_DRAGON, true); - }, 2s); - } + case MI_POINT_SCION: + events.RescheduleEvent(EVENT_DISK_MOVE_NEXT_POINT, 0ms); + break; + case MI_POINT_NEXUS_LORD: + if (me->GetPositionZ() > CenterPos.GetPositionZ() + 2.0f) + events.RescheduleEvent(EVENT_DISK_MOVE_NEXT_POINT, 0ms); + else if (Vehicle* v = me->GetVehicleKit()) + if (Unit* pass = v->GetPassenger(0)) + if (Creature* c = pass->ToCreature()) + { + c->SetReactState(REACT_AGGRESSIVE); + if (Player* plr = c->SelectNearestPlayer(100.0f)) + c->AI()->AttackStart(plr); + } + break; } + } - void PassengerBoarded(Unit* pass, int8 /*seat*/, bool apply) override + void DoAction(int32 param) override + { + switch (param) { - if (apply) - { - me->SetDisableGravity(false); - me->SendMovementFlagUpdate(); - } - else if (pass && pass->IsPlayer() && me->IsAlive()) - { - me->SetDisplayId(11686); // prevents nasty falling animation at despawn - me->DespawnOrUnsummon(1ms); - } + case 1: // move to next point + if (Vehicle* v = me->GetVehicleKit()) + if (Unit* pass = v->GetPassenger(0)) + switch (pass->GetEntry()) + { + case NPC_NEXUS_LORD: + { + float angle = CenterPos.GetAngle(me); + float newangle = angle - 0.5f; + if (newangle < 0.0f) newangle += 2 * M_PI; + float newz = me->GetPositionZ() - 4.0f; + if (newz < CenterPos.GetPositionZ()) newz = CenterPos.GetPositionZ(); + me->GetMotionMaster()->MovePoint(MI_POINT_NEXUS_LORD, CenterPos.GetPositionX() + cos(newangle) * 22.0f, CenterPos.GetPositionY() + std::sin(newangle) * 22.0f, newz); + } + break; + case NPC_SCION_OF_ETERNITY: + { + float angle = CenterPos.GetAngle(me); + float newangle = angle - 0.3f; + if (newangle < 0.0f) newangle += 2 * M_PI; + float newz = me->GetPositionZ() - 2.0f; + if (newz < CenterPos.GetPositionZ() + 20.0f) newz = CenterPos.GetPositionZ() + 20.0f; + me->GetMotionMaster()->MovePoint(MI_POINT_SCION, CenterPos.GetPositionX() + cos(newangle) * 30.0f, CenterPos.GetPositionY() + std::sin(newangle) * 30.0f, newz); + } + break; + } + break; } + } - void JustDied(Unit* /*killer*/) override + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + switch (events.ExecuteEvent()) + { + case 0: + break; + case EVENT_DISK_MOVE_NEXT_POINT: + DoAction(1); + break; + } + } + + void MoveInLineOfSight(Unit* /*who*/) override {} + void AttackStart(Unit* /*who*/) override {} +}; + +struct npc_alexstrasza : public ScriptedAI +{ + npc_alexstrasza(Creature* pCreature) : ScriptedAI(pCreature) + { + events.Reset(); + events.ScheduleEvent(1, 9s); + me->SetCanFly(true); + me->SetDisableGravity(true); + } + + EventMap events; + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + switch (events.ExecuteEvent()) + { + case 0: + break; + case 1: + me->CastSpell(773.98f, 1285.97f, 266.254f, SPELL_ALEXSTRASZA_GIFT, true); + if (GameObject* chest = me->SummonGameObject(ALEXSTRASZA_GIFT, 773.98f, 1285.97f, 266.254f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + { + chest->SetLootRecipient(me->GetMap()); + } + + if (GameObject* heart = me->SummonGameObject(HEART_OF_MAGIC, 773.98f, 1275.97f, 266.254f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + { + heart->SetLootRecipient(me->GetMap()); + } + + Talk(SAY_ALEXSTRASZA_ONE); + events.RescheduleEvent(2, 6s); + break; + case 2: + Talk(SAY_ALEXSTRASZA_TWO); + events.RescheduleEvent(3, 5s); + break; + case 3: + Talk(SAY_ALEXSTRASZA_THREE); + events.RescheduleEvent(4, 22s); + break; + case 4: + Talk(SAY_ALEXSTRASZA_FOUR); + break; + } + } + + void MoveInLineOfSight(Unit* /*who*/) override {} + void AttackStart(Unit* /*who*/) override {} +}; + +struct npc_eoe_wyrmrest_skytalon : public VehicleAI +{ + npc_eoe_wyrmrest_skytalon(Creature* creature) : VehicleAI(creature) { } + + void IsSummonedBy(WorldObject* summoner) override + { + if (summoner && summoner->IsPlayer()) + { + ObjectGuid summonerGUID = summoner->GetGUID(); + me->m_Events.AddEventAtOffset([summonerGUID, this] { + if (Player* rider = ObjectAccessor::GetPlayer(*me, summonerGUID)) + DoCast(rider, SPELL_RIDE_RED_DRAGON, true); + }, 2s); + } + } + + void PassengerBoarded(Unit* pass, int8 /*seat*/, bool apply) override + { + if (apply) + { + me->SetDisableGravity(false); + me->SendMovementFlagUpdate(); + } + else if (pass && pass->IsPlayer() && me->IsAlive()) { me->SetDisplayId(11686); // prevents nasty falling animation at despawn me->DespawnOrUnsummon(1ms); } - }; + } + + void JustDied(Unit* /*killer*/) override + { + me->SetDisplayId(11686); // prevents nasty falling animation at despawn + me->DespawnOrUnsummon(1ms); + } }; class go_the_focusing_iris : public GameObjectScript @@ -1567,15 +1478,15 @@ class spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger : public SpellScript void AddSC_boss_malygos() { - new boss_malygos(); - new npc_power_spark(); - new npc_vortex_ride(); - new npc_alexstrasza(); + RegisterEoECreatureAI(boss_malygos); + RegisterEoECreatureAI(npc_power_spark); + RegisterEoECreatureAI(npc_vortex_ride); + RegisterEoECreatureAI(npc_alexstrasza); new go_the_focusing_iris(); - new npc_nexus_lord(); - new npc_scion_of_eternity(); - new npc_hover_disk(); - new npc_eoe_wyrmrest_skytalon(); + RegisterEoECreatureAI(npc_nexus_lord); + RegisterEoECreatureAI(npc_scion_of_eternity); + RegisterEoECreatureAI(npc_hover_disk); + RegisterEoECreatureAI(npc_eoe_wyrmrest_skytalon); RegisterSpellScript(spell_wyrmrest_skytalon_summon_red_dragon_buddy); RegisterSpellScript(spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger); diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h index cfdac7ee6..77a4f7c46 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h @@ -105,20 +105,11 @@ const Position Phase2NorthPos = {837.22f, 1301.676f, 296.10f, M_PI}; const uint32 MalygosIntroIntervals[] = {18000, 19000, 21000, 18000, 15000}; -class EoEDrakeEnterVehicleEvent : public BasicEvent -{ -public: - EoEDrakeEnterVehicleEvent(Creature& owner, ObjectGuid playerGUID) : _owner(owner), _playerGUID(playerGUID) { } - bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override; -private: - Creature& _owner; - ObjectGuid _playerGUID; -}; - template inline AI* GetEyeOfEternityAI(T* obj) { return GetInstanceAI(obj, EyeOfEternityScriptName); } +#define RegisterEoECreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetEyeOfEternityAI) #endif