25 Commits
Author SHA1 Message Date
luis bfe79dc2d0 exiles horde 2026-09-22 18:02:44 -03:00
luis 429ecdbe18 Exiles reach horde 2026-09-22 18:02:11 -03:00
luis 29e1d475f2 LFGList: stop blacklisting live Midnight activities 2026-09-22 14:06:05 -03:00
luis 609aee127d db assign 2026-09-22 13:14:40 -03:00
luis 7d1bc1237a Midnight campaign quest 2026-09-22 13:14:07 -03:00
Céfiro_Caecias 5cb9dee306 DB/World: Gameobjects Translates 2026-09-22 13:13:24 -03:00
luis 3b52d609b1 typo 2026-09-22 12:26:23 -03:00
luis 4995d2ad14 prev commit sql 2026-09-22 11:05:33 -03:00
luis b76377791b typo fix 2026-09-22 10:55:19 -03:00
luis 2f83245d87 Core/Professions: Export missing game symbols and wire crafting procs 2026-09-22 10:49:20 -03:00
luis bed3e0ff04 Core/Professions: Apply Dragonflight crafting reagents and quality
The craft packet never reached the spell, and the reagent, quality and bonus indexes were never built, so every recipe ignored the chosen materials. Crafts now consume the sent reagents, pick the quality item, and apply missive bonus trees instead of rolling random stats.
2026-09-22 10:28:51 -03:00
luis ca9f547035 Scripts/Raids: Script Amirdrassil bosses and Sennarth 2026-09-22 10:17:53 -03:00
luis 5b021fc840 Scripts/ExilesReach: Keep the sparring jump ahead of aggro and hold Gor'groth's sacrifice 2026-09-22 10:09:47 -03:00
luis 9cded4b858 Scripts/ExilesReach: Play retail conversations and script Gor'groth and Ravnyr 2026-09-22 10:06:50 -03:00
luis 53a7b0bbe9 exiles 2026-09-22 09:48:04 -03:00
luis e0ec5ac309 Scripts/ExilesReach: Hook scene triggers and bind quest scripts 2026-09-22 09:47:33 -03:00
luis 5f9987f63c db assign 2026-09-22 09:07:31 -03:00
luis 238bc1541d class spells 2026-09-22 08:59:58 -03:00
Céfiro_Caecias deb42069d1 DB/World: Fixes 2026-09-21 20:10:37 -03:00
SistemasCPU 0cca4fcaf3 Core/Followers: Updated behaviours and scaling 2026-09-22 00:07:27 +02:00
luis c2c22d88e8 db misc 2026-09-21 14:54:37 -03:00
luis 4f387f1a3b base for arakara 2026-09-21 14:49:21 -03:00
luis f26a91fccf Dungeon followers 2026-09-21 12:17:44 -03:00
luis 1a4902da04 Core/Random: Add random distribution that minimizes long good and bad luck streaks 2026-09-21 12:03:48 -03:00
luis 4f942bbfe2 The stonevault 2026-09-21 12:02:38 -03:00
80 changed files with 41239 additions and 694 deletions
@@ -0,0 +1,108 @@
-- AZSUNA: Fix chain break between "Down to Azsuna" and "Into the Fray"
UPDATE quest_template_addon SET NextQuestID = 38834 WHERE ID = 41220;
UPDATE quest_template_addon SET PrevQuestID = 41220 WHERE ID = 38834;
-- STORMHEIM: Fix Alliance breadcrumb chain "A Royal Summons" -> "Making the Rounds"
UPDATE quest_template_addon SET NextQuestID = 38206 WHERE ID = 38035;
-- STORMHEIM: Fix Horde breadcrumb chain "The Warchief Beckons" -> "Havi's Test"
UPDATE quest_template_addon SET NextQuestID = 38331 WHERE ID = 38307;
-- STORMHEIM: Fix dragon chain - both 38413 and 38414 lead to 39652, so PrevQuest must allow either
UPDATE quest_template_addon SET PrevQuestID = 0 WHERE ID = 39652;
REPLACE INTO `conversation_actors` (`ConversationId`, `ConversationActorId`, `Idx`, `CreatureId`, `CreatureDisplayInfoId`, `NoActorObject`, `ActivePlayerObject`, `VerifiedBuild`) VALUES
(25770, 99466, 0, 224736, 119519, 0, 0, 69875),
(25772, 99466, 0, 224736, 119519, 0, 0, 69875),
(25814, 94742, 0, 213119, 119519, 0, 0, 69875), -- Full: 0x2041E14B80D01FC000B16A000031380C Creature/0 R4216/S45418 Map: 2652 (The Stonevault) Entry: 213119 (Void Speaker Eirich) Low: 3225612
(25767, 99466, 0, 224736, 119519, 0, 0, 69875),
(25771, 99466, 0, 224736, 119519, 0, 0, 69875),
(25775, 99466, 0, 224736, 119519, 0, 0, 69875),
(25773, 99466, 0, 224736, 119519, 0, 0, 69875);
UPDATE `conversation_actors` SET `VerifiedBuild`=69875 WHERE (`Idx`=0 AND `ConversationId` IN (25768,25769));
UPDATE `conversation_line_template` SET `VerifiedBuild`=69875 WHERE `Id` IN (70042, 70044, 70040, 70039, 70204, 70203, 70038, 70037, 70036, 70043, 70041, 70049, 70048, 70046, 70045);
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25814;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25775;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25773;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25772;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25771;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25770;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25769;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25768;
UPDATE `conversation_template` SET `VerifiedBuild`=69875 WHERE `Id`=25767;
DELETE FROM `world_quest_template` WHERE `QuestID` IN (52463 /*Haegol the Hammer*/, 50513 /*Tainted Guardian*/, 65102 /*Fish Eyes*/, 55889 /*Champion Kyx'zhul the Deepspeaker*/, 54572 /*Darbel Montrose*/, 58221 /*Gettin' Even*/, 64717 /*Materials of Creation*/, 51512 /*Bilefang Mother*/, 70172 /*Mountain Mysteries*/, 51824 /*You're Grounded*/, 58460 /*Marshals of Madness*/, 56397 /*CK-9 Micro-Oppression Unit*/, 49056 /*Pilfer*/, 61148 /*AHHH! Real Monsters!*/, 51406 /*The Lord's Hunt*/, 52045 /*Boarder Patrol*/, 65262 /*Save the Vombata*/, 50499 /*Gwugnug*/);
INSERT INTO `world_quest_template` (`QuestID`, `Duration`, `VariableID`, `Value`) VALUES
(52463, 86400, 15763, 1), -- Haegol the Hammer
(50513, 86400, 14807, 1), -- Tainted Guardian
(65102, 86400, 21265, 1), -- Fish Eyes
(55889, 259200, 17587, 1), -- Champion Kyx'zhul the Deepspeaker
(54572, 86400, 16866, 1), -- Darbel Montrose
(58221, 86400, 18290, 1), -- Gettin' Even
(64717, 86400, 21348, 2), -- Materials of Creation
(51512, 86400, 15256, 1), -- Bilefang Mother
(70172, 302400, 22456, 1), -- Mountain Mysteries
(51824, 86400, 15528, 1), -- You're Grounded
(58460, 86400, 18540, 3), -- Marshals of Madness
(56397, 86400, 17788, 5), -- CK-9 Micro-Oppression Unit
(49056, 86400, 14322, 1), -- Pilfer
(61148, 86400, 19408, 2), -- AHHH! Real Monsters!
(51406, 86400, 15766, 1), -- The Lord's Hunt
(52045, 86400, 15621, 1), -- Boarder Patrol
(65262, 86400, 21383, 1), -- Save the Vombata
(50499, 86400, 14796, 1); -- Gwugnug
UPDATE `spawn_tracking_template` SET `VerifiedBuild`=69875 WHERE `SpawnTrackingId` IN (1978804, 1890492, 1989444, 1864180, 1922790, 1997407, 1994265, 1960887, 1838831, 1989140, 1989145, 1888621, 1989132, 1964065, 3262739, 3275039, 3274804, 3275034, 3274764, 2853599, 3258080, 3257755, 3144987, 3102636, 2914859, 3162881, 2196338, 1824267, 2026887, 1870387, 1867398, 1867410, 1792406, 1751724, 1738299, 1603576, 1767633, 1727469, 1429886, 1500761, 1486549, 1484068);
UPDATE `spawn_tracking_template` SET `MapId`=1642, `VerifiedBuild`=69875 WHERE `SpawnTrackingId`=1578973; -- Map: 1642 (Zandalar) - Phase: 0
UPDATE `spawn_tracking_template` SET `MapId`=1643, `VerifiedBuild`=69875 WHERE `SpawnTrackingId`=1483872; -- Map: 1643 (Kul Tiras) - Phase: 0
REPLACE INTO `spell_target_position` (`ID`, `EffectIndex`, `OrderIndex`, `MapID`, `PositionX`, `PositionY`, `PositionZ`, `VerifiedBuild`) VALUES
(346210, 0, 0, 2222, 4617.888671875, 7026.46533203125, 4826.462890625, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 1, 2222, 4607.80615234375, 7010.3359375, 4827.0068359375, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 2, 2222, 4624.658203125, 7014.18701171875, 4826.96875, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 3, 2222, 4665.35302734375, 7028.07666015625, 4827.82666015625, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 4, 2222, 4670.82568359375, 7007.93115234375, 4831.5107421875, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 5, 2222, 4579.4697265625, 7012.05810546875, 4827.4619140625, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 6, 2222, 4610.82373046875, 6970.57080078125, 4838.31787109375, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 7, 2222, 4611.87646484375, 7015.9013671875, 4826.7978515625, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 8, 2222, 4581.7568359375, 6991.1083984375, 4828.62353515625, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 9, 2222, 4630.5537109375, 7015.6220703125, 4826.8974609375, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 10, 2222, 4603.8916015625, 6979.06640625, 4833.4638671875, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 11, 2222, 4622.50341796875, 7009.1640625, 4827.22412109375, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 12, 2222, 4658.17529296875, 7012.29736328125, 4831.14111328125, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 13, 2222, 4620.7958984375, 7026.10888671875, 4826.4951171875, 69875), -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
(346210, 0, 14, 2222, 4590.6630859375, 6999.40234375, 4828.29150390625, 69875); -- Spell: 346210 (Molten Eruption) Effect 0: 32 (SPELL_EFFECT_TRIGGER_MISSILE)
DELETE FROM `creature_template_difficulty` WHERE (`Entry`=237776 AND `DifficultyID`=205);
INSERT INTO `creature_template_difficulty` (`Entry`, `DifficultyID`, `HealthScalingExpansion`, `HealthModifier`, `ManaModifier`, `CreatureDifficultyID`, `TypeFlags`, `TypeFlags2`, `TypeFlags3`) VALUES
(237776, 205, 11, 3, 1, 316716, 0x0, 0, 0); -- Mecha-Scrapper
DELETE FROM `creature_template_difficulty` WHERE (`Entry`=237776 AND `DifficultyID`=205);
INSERT INTO `creature_template_difficulty` (`Entry`, `DifficultyID`, `LevelScalingDeltaMin`, `LevelScalingDeltaMax`, `ContentTuningID`, `StaticFlags1`, `StaticFlags2`, `StaticFlags3`, `StaticFlags4`, `StaticFlags5`, `StaticFlags6`, `StaticFlags7`, `StaticFlags8`, `VerifiedBuild`) VALUES
(237776, 205, 0, 0, 391, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 69875); -- 237776 (Mecha-Scrapper) -
INSERT INTO `creature` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnDifficulties`, `PhaseId`, `PhaseGroup`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `MovementType`, `npcflag`, `unit_flags`, `unit_flags2`, `unit_flags3`, `VerifiedBuild`) VALUES
(900133, 264418, 0, 15969, 16079, '0', '0', 0, 0, 0, 8331.859375, -4907.92041015625, 91.51644134521484375, 5.476967811584472656, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 69497), -- Penny (Area: The Bazaar - Difficulty: 0) CreateObject1
(900134, 256563, 0, 15969, 16088, '0', '0', 0, 0, 0, 8334.8603515625, -4110.10595703125, 44.21979904174804687, 5.380497455596923828, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 68974), -- -Unknown- (Area: 16088 - Difficulty: 0) CreateObject1 (Auras: 1269392 - 1269392, 1263773 - 1263773, 421177 - 421177, 1260011 - 1260011, 421175 - 421175)
(900135, 266434, 0, 15969, 16079, '0', '0', 0, 0, 0, 8785.796875, -4459.45654296875, 29.78347587585449218, 1.880817055702209472, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 68974), -- -Unknown- (Area: 16079 - Difficulty: 0) CreateObject1 (Auras: 1250294 - 1250294)
(900136, 265582, 0, 15969, 16079, '0', '0', 0, 0, 0, 8863.4150390625, -4678.1787109375, 76.85699462890625, 4.171336650848388671, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 68974), -- -Unknown- (Area: 16079 - Difficulty: 0) CreateObject1
(900137, 263598, 0, 15969, 16079, '0', '0', 0, 0, 0, 8860.12890625, -4689.8681640625, 76.85699462890625, 5.51227569580078125, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 68974), -- -Unknown- (Area: 16079 - Difficulty: 0) CreateObject1
(900138, 263604, 0, 15969, 16079, '0', '0', 0, 0, 0, 8858.0693359375, -4691.61474609375, 76.85699462890625, 5.881916999816894531, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 68974), -- -Unknown- (Area: 16079 - Difficulty: 0) CreateObject1
(900139, 263601, 0, 15969, 16079, '0', '0', 0, 0, 0, 8862.560546875, -4688.6162109375, 76.85699462890625, 4.870819568634033203, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 68974), -- -Unknown- (Area: 16079 - Difficulty: 0) CreateObject1
(900140, 267869, 0, 15969, 0, '0', '0', 0, 0, 0, 8664.6845703125, -4437.46728515625, 71.44916534423828125, 3.940832376480102539, 120, 0, 0, 0, NULL, NULL, NULL, NULL, 68974); -- -Unknown- (Area: 0 - Difficulty: 0) CreateObject1 (Auras: 179665 - 179665)
@@ -0,0 +1,251 @@
-- =====
-- Fixes
-- =====
-- NPC Possessed Vrykul (103529)
UPDATE `npc_spellclick_spells` SET `spell_id`=46598 WHERE `npc_entry`=103529 AND `spell_id`=206469;
-- NPC Theramore Combat Dummy (4952)
UPDATE `creature_template_addon` SET `PvPFlags`=0 WHERE `entry`=4952;
-- NPC Training Dummy (60197)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=60197);
-- Misc: Invalid Areatrigger
UPDATE `creature_addon` SET `auras`='' WHERE `auras`='212102';
UPDATE `creature_addon` SET `auras`='288179' WHERE `guid`=444249;
UPDATE `creature_addon` SET `auras`='42459' WHERE `guid`=443776;
UPDATE `creature_addon` SET `auras`='259919 288179' WHERE `guid`=443768;
UPDATE `creature_addon` SET `auras`='170528' WHERE `guid`=444341;
UPDATE `creature_addon` SET `auras`='288178' WHERE `guid`=444236;
UPDATE `creature_addon` SET `auras`='288178' WHERE `guid`=444237;
UPDATE `creature_addon` SET `auras`='144465' WHERE `guid`=443729;
UPDATE `creature_addon` SET `auras`='123168' WHERE `guid`=344263;
UPDATE `creature_template_addon` SET `auras`='288178' WHERE `entry`=142686;
UPDATE `creature_template_addon` SET `auras`='144465' WHERE `entry`=142725;
UPDATE `creature_template_addon` SET `auras`='288178' WHERE `entry`=142684;
UPDATE `creature_template_addon` SET `auras`='170528' WHERE `entry`=142683;
UPDATE `creature_template_addon` SET `auras`='259919 288179' WHERE `entry`=141615;
UPDATE `creature_template_addon` SET `auras`='42459' WHERE `entry`=142423;
UPDATE `creature_template_addon` SET `auras`='288014 288179' WHERE `entry`=141620;
UPDATE `creature_template_addon` SET `auras`='389215' WHERE `entry`=196805;
UPDATE `creature_template_addon` SET `auras`='465756' WHERE `entry`=231436;
UPDATE `creature_template_addon` SET `auras`='203761 67236' WHERE `entry`=114873;
UPDATE `creature_template_addon` SET `auras`='123168' WHERE `entry`=102303;
UPDATE `creature_template_addon` SET `auras`='223531 214336' WHERE `entry`=109954;
UPDATE `creature_template_addon` SET `auras`='214336' WHERE `entry`=109954;
UPDATE `creature_addon` SET `auras`='' WHERE `guid` IN (443699,444335,443681,443670,340845);
UPDATE `creature_template_addon` SET `auras`='' WHERE `entry` IN (142440,142436,142435,142508,182696,181680,185172,189930,106873,110944);
DELETE FROM `disables` WHERE `sourceType`=0 AND `entry` IN (287904,287906,287908,287911,287953,287954,287959,287960,287961,287962,287966,389216,362331,398620,212102,214074,222299,222401,222412,222423,228771,223526,223532,219489,223531);
INSERT INTO `disables` (`sourceType`, `entry`, `flags`, `params_0`, `params_1`, `comment`) VALUES
(0, 287904, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15745 Create Properties'),
(0, 287906, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15747 Create Properties'),
(0, 287908, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15750 Create Properties'),
(0, 287911, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15753 Create Properties'),
(0, 287953, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15777 Create Properties'),
(0, 287954, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15778 Create Properties'),
(0, 287959, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15783 Create Properties'),
(0, 287960, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15784 Create Properties'),
(0, 287961, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15785 Create Properties'),
(0, 287962, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15786 Create Properties'),
(0, 287966, 8, '0', '0', 'Spell Area Trigger - Invalid Areatrigger 15790 Create Properties'),
(0, 389216, 8, '0', '0', 'Spell Conjured Goods Stolen Area Trigger [DNT] - Invalid Areatrigger 26684 Create Properties'),
(0, 362331, 8, '0', '0', 'Spell Stasis - Invalid Areatrigger 24255 Create Properties'),
(0, 398620, 8, '0', '0', 'Spell Stasis - Invalid Areatrigger 24255 Create Properties'),
(0, 212102, 8, '0', '0', 'Spell Arcane Font Area Aura - Invalid Areatrigger 6946 Create Properties'),
(0, 214074, 8, '0', '0', 'Spell Aluneth Charge Whispers Half-Hour Timer - Invalid Areatrigger 6946 Create Properties'),
(0, 222299, 8, '0', '0', 'Spell Aluneth Dungeon Whispers Two-Hour Timer - Invalid Areatrigger 6946 Create Properties'),
(0, 222401, 8, '0', '0', 'Spell Aluneth Dalaran Whispers Control - Invalid Areatrigger 6946 Create Properties'),
(0, 222412, 8, '0', '0', 'Spell Aluneth Dalaran Whispers Four-Hour Timer - Invalid Areatrigger 6946 Create Properties'),
(0, 222423, 8, '0', '0', 'Spell Aluneth General Whispers One-Hour Timer - Invalid Areatrigger 6946 Create Properties'),
(0, 228771, 8, '0', '0', 'Spell Waylay Citizens - Invalid Areatrigger 8902 Create Properties'),
(0, 223526, 8, '0', '0', 'Spell Guardian Thor\'el - Level 3 - Invalid Areatrigger 8325 Create Properties'),
(0, 223532, 8, '0', '0', 'Spell Lieutenant Strathmar - Level 3 - Invalid Areatrigger 8335 Create Properties'),
(0, 219489, 8, '0', '0', 'Spell Illusions Explode Area Trigger - Invalid Areatrigger 7872 Create Properties'),
(0, 223531, 8, '0', '0', 'Spell Magister Phaedris - Level 3 - Invalid Areatrigger 8333 Create Properties');
-- ======================
-- Stormwind Calibrations
-- ======================
-- Clean
DELETE FROM `creature` WHERE `guid` IN (671121,631635,671104,364873,364874,595458,595456,364866,671094,595478,671044,671070,113132,196497,196498,196499,671178,9796,671168,595413);
DELETE FROM `creature_addon` WHERE `guid` IN (671121,631635,671104,364873,364874,595458,595456,364866,671094,595478,671044,671070,113132,196497,196498,196499,671178,9796,671168,595413);
UPDATE `creature` SET `areaId`=5390 WHERE `guid`=364864;
UPDATE `creature` SET `zoneId`=1519, `areaId`=4411 WHERE `guid` IN (605468,475063,475064,475065,475066,475062);
DELETE FROM `gameobject` WHERE `guid` IN (212103,144285,67725);
DELETE FROM `gameobject_addon` WHERE `guid` IN (212103,144285,67725);
-- NPC [DNT] Kill Credit (200001)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=200001);
DELETE FROM `creature` WHERE `id`=200001;
UPDATE `creature_template` SET `AIName`='' WHERE `entry`=200001;
UPDATE `creature_template_addon` SET `SheathState`=0 WHERE `entry`=200001;
UPDATE `creature_template_difficulty` SET `StaticFlags1`=536871168 WHERE `Entry`=200001;
DELETE FROM `creature_text` WHERE `CreatureID`=200001;
DELETE FROM `smart_scripts` WHERE `entryorguid`=200001 AND `source_type`=0;
-- NPC Plump Turkey Bunny (32819)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=32819);
DELETE FROM `creature` WHERE `id`=32819;
UPDATE `creature_template_addon` SET `SheathState`=0 WHERE `entry`=32819;
UPDATE `creature_template_difficulty` SET `StaticFlags1`=536871168 WHERE `Entry`=32819;
DELETE FROM `game_event_creature` WHERE `guid`=223831 AND `eventEntry`=41;
-- NPC Rat (4075)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=4075 AND `zoneid`=1519);
DELETE FROM `creature` WHERE `id`=4075 AND `zoneid`=1519;
-- NPC Squirrel (1412)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=1412 AND `zoneid`=1519);
DELETE FROM `creature` WHERE `id`=1412 AND `zoneid`=1519;
-- NPC Rabbit (61080)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=61080);
DELETE FROM `creature` WHERE `id`=61080;
-- NPC Squirrel (61081)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=61081);
DELETE FROM `creature` WHERE `id`=61081;
-- NPC Super Shellkhan Gang (198147)
DELETE FROM `creature_addon` WHERE `guid` IN (SELECT `guid` FROM `creature` WHERE `id`=198147);
DELETE FROM `creature` WHERE `id`=198147;
UPDATE `creature_template_addon` SET `SheathState`=0 WHERE `entry`=198147;
-- GO Trail of Gore (205162)
DELETE FROM `gameobject_addon` WHERE `guid` IN (SELECT `guid` FROM `gameobject` WHERE `id`=205162);
DELETE FROM `gameobject` WHERE `id`=205162;
-- GO Trail of Gore (205163)
DELETE FROM `gameobject_addon` WHERE `guid` IN (SELECT `guid` FROM `gameobject` WHERE `id`=205163);
DELETE FROM `gameobject` WHERE `id`=205163;
-- GO Worm Mound (204281)
DELETE FROM `gameobject_addon` WHERE `guid` IN (SELECT `guid` FROM `gameobject` WHERE `id`=204281);
DELETE FROM `gameobject` WHERE `id`=204281;
-- NPC ELM General Purpose Bunny (23837)
UPDATE `creature_template` SET `flags_extra`=128 WHERE `entry`=23837;
UPDATE `creature_template_addon` SET `SheathState`=0 WHERE `entry`=23837;
UPDATE `creature_template_difficulty` SET `StaticFlags1`=536871168 WHERE `Entry`=23837;
-- Event Darkmoon Faire (3)
DELETE FROM `game_event_gameobject` WHERE `guid` IN (113115,113116,113117,113118,113124,113126,113127,113128);
INSERT INTO `game_event_gameobject` (`eventEntry`, `guid`) VALUES
(3, 113115),
(3, 113116),
(3, 113117),
(3, 113118),
(3, 113124),
(3, 113126),
(3, 113127),
(3, 113128);
-- Event Fireworks Spectacular (72)
DELETE FROM `game_event_gameobject` WHERE `guid` IN (111674,111673,111672,111671,111670,111669,111668,111667,111666,111665,111664,111663,111662,111661,162197,111660,111659);
INSERT INTO `game_event_gameobject` (`eventEntry`, `guid`) VALUES
(72, 111674),
(72, 111673),
(72, 111672),
(72, 111671),
(72, 111670),
(72, 111669),
(72, 111668),
(72, 111667),
(72, 111666),
(72, 111665),
(72, 111664),
(72, 111663),
(72, 111662),
(72, 111661),
(72, 162197),
(72, 111660),
(72, 111659);
-- Event Midsummer Fire Festival (1)
DELETE FROM `game_event_creature` WHERE `guid` IN (222734,222779,222785,222804,222860,222865,222872,222874,222882,222891,222895,222898,222952,222969,222973,222982,223005,223049,223051,223080,223085,223095,223121,223145,223147,223173,223185,223191,223193,223201,223229,223231,223233,223279,223282,223299,223328,223340,223363,223386,223389,223392,223432,223435,223440,223496,226696,226909,226923,226926,226933,226938,226947,227007,227024);
INSERT INTO `game_event_creature` (`eventEntry`, `guid`) VALUES
(1, 222734),
(1, 222779),
(1, 222785),
(1, 222804),
(1, 222860),
(1, 222865),
(1, 222872),
(1, 222874),
(1, 222882),
(1, 222891),
(1, 222895),
(1, 222898),
(1, 222952),
(1, 222969),
(1, 222973),
(1, 222982),
(1, 223005),
(1, 223049),
(1, 223051),
(1, 223080),
(1, 223085),
(1, 223095),
(1, 223121),
(1, 223145),
(1, 223147),
(1, 223173),
(1, 223185),
(1, 223191),
(1, 223193),
(1, 223201),
(1, 223229),
(1, 223231),
(1, 223233),
(1, 223279),
(1, 223282),
(1, 223299),
(1, 223328),
(1, 223340),
(1, 223363),
(1, 223386),
(1, 223389),
(1, 223392),
(1, 223432),
(1, 223435),
(1, 223440),
(1, 223496),
(1, 226696),
(1, 226909),
(1, 226923),
(1, 226926),
(1, 226933),
(1, 226938),
(1, 226947),
(1, 227007),
(1, 227024);
@@ -0,0 +1,31 @@
DELETE FROM `areatrigger_create_properties` WHERE (`Id`=35958 AND `IsCustom`=0);
INSERT INTO `areatrigger_create_properties` (`Id`, `IsCustom`, `AreaTriggerId`, `IsAreatriggerCustom`, `Flags`, `MoveCurveId`, `ScaleCurveId`, `MorphCurveId`, `FacingCurveId`, `AnimId`, `AnimKitId`, `DecalPropertiesId`, `SpellForVisuals`, `PositionalSoundKitId`, `TimeToTargetScale`, `Speed`, `Shape`, `ShapeData0`, `ShapeData1`, `ShapeData2`, `ShapeData3`, `ShapeData4`, `ShapeData5`, `ShapeData6`, `ShapeData7`, `ScriptName`, `VerifiedBuild`) VALUES
(35958, 0, 3678, 0, 0, 0, 0, 0, 0, -1, 0, 0, 132950, 0, 20000, 0, 4, 15, 15, 7, 7, 3, 3, 0, 0, 'at_hun_legion_flare', 69875); -- SpellForVisuals: 132950 (Flare)
UPDATE `areatrigger_create_properties` SET `ScriptName`='at_hun_legion_flare' WHERE `Id`=510;
DELETE FROM `spell_script_names` WHERE `ScriptName`='spell_hun_kindling_flare';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(132950, 'spell_hun_kindling_flare');
DELETE FROM `spell_script_names` WHERE `ScriptName`='spell_hun_take_aim';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(257045, 'spell_hun_take_aim');
DELETE FROM `spell_proc` WHERE `SpellId` IN (1301406);
INSERT INTO `spell_proc` (`SpellId`,`SchoolMask`,`SpellFamilyName`,`SpellFamilyMask0`,`SpellFamilyMask1`,`SpellFamilyMask2`,`SpellFamilyMask3`,`ProcFlags`,`ProcFlags2`,`SpellTypeMask`,`SpellPhaseMask`,`HitMask`,`AttributesMask`,`DisableEffectsMask`,`ProcsPerMinute`,`Chance`,`Cooldown`,`Charges`) VALUES
(1301406,0x00,9,0x00000000,0x80000000,0x00000000,0x00000000,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0,0,0,0); -- Tactical Reload
DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_hun_quick_draw', 'spell_hun_quick_draw_speed');
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(459794, 'spell_hun_quick_draw'),
(1279347, 'spell_hun_quick_draw_speed');
DELETE FROM `spell_proc` WHERE `SpellId` IN (459794);
INSERT INTO `spell_proc` (`SpellId`,`SchoolMask`,`SpellFamilyName`,`SpellFamilyMask0`,`SpellFamilyMask1`,`SpellFamilyMask2`,`SpellFamilyMask3`,`ProcFlags`,`ProcFlags2`,`SpellTypeMask`,`SpellPhaseMask`,`HitMask`,`AttributesMask`,`DisableEffectsMask`,`ProcsPerMinute`,`Chance`,`Cooldown`,`Charges`) VALUES
(459794,0x00,9,0x00020000,0x00000000,0x00000000,0x00000000,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0,0,0,0); -- Quick Draw
DELETE FROM `spell_script_names` WHERE `ScriptName`='spell_hun_harriers_cry';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(466904, 'spell_hun_harriers_cry');
@@ -0,0 +1,156 @@
INSERT IGNORE INTO `creature_questender` (`id`, `quest`, `VerifiedBuild`)
SELECT steward.`entry`, quest.`ID`, 69875
FROM (SELECT 233063 AS `entry` UNION ALL SELECT 233708) AS steward
INNER JOIN `quest_template` AS quest ON quest.`ID` IN (91863, 94210);
-- Keep existing addon rows; only wire the tutorial predecessors.
INSERT INTO `quest_template_addon` (`ID`, `PrevQuestID`)
SELECT 93057, 0 FROM `quest_template` WHERE `ID` = 93057
ON DUPLICATE KEY UPDATE `PrevQuestID` = VALUES(`PrevQuestID`);
INSERT INTO `quest_template_addon` (`ID`, `PrevQuestID`)
SELECT 91863, 0 FROM `quest_template` WHERE `ID` = 91863
ON DUPLICATE KEY UPDATE `PrevQuestID` = VALUES(`PrevQuestID`);
INSERT INTO `quest_template_addon` (`ID`, `PrevQuestID`)
SELECT 94455, 0 FROM `quest_template` WHERE `ID` = 94455
ON DUPLICATE KEY UPDATE `PrevQuestID` = VALUES(`PrevQuestID`);
INSERT INTO `quest_template_addon` (`ID`, `PrevQuestID`)
SELECT 91968,
IF(EXISTS(SELECT 1 FROM `quest_template` WHERE `ID` = 94455), 94455, 91863)
FROM `quest_template` WHERE `ID` = 91968
ON DUPLICATE KEY UPDATE `PrevQuestID` = VALUES(`PrevQuestID`);
INSERT INTO `quest_template_addon` (`ID`, `PrevQuestID`)
SELECT 91969, 91968 FROM `quest_template` WHERE `ID` = 91969
ON DUPLICATE KEY UPDATE `PrevQuestID` = VALUES(`PrevQuestID`);
INSERT INTO `quest_template_addon` (`ID`, `PrevQuestID`)
SELECT 94210, 91969 FROM `quest_template` WHERE `ID` = 94210
ON DUPLICATE KEY UPDATE `PrevQuestID` = VALUES(`PrevQuestID`);
UPDATE `creature_template`
SET `npcflag` = `npcflag` | 3,
`ScriptName` = 'npc_housing_steward'
WHERE `entry` IN (233063, 233708);
DELETE FROM `creature_queststarter`
WHERE `id` IN (233063, 233708)
AND `quest` IN (91863, 91968, 91969, 93057, 94455, 94210);
-- Exile's Reach: bind the C++ scripts. Without these rows the quest scripts never run,
-- so the ship, beach, escorts and scenes do not advance.
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ship_166573_156280' WHERE `entry` IN (166573, 156280);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ship_training_dummy' WHERE `entry` = 160737;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ship_166583_160664' WHERE `entry` IN (166583, 160664);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_shore_166782_156626' WHERE `entry` IN (166782, 156626);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_shore_injured_buddie' WHERE `entry` IN (166786, 156612, 166791, 156610, 166796, 156609);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_shore_other_two_injured' WHERE `entry` IN (166784, 166800, 149917, 156622);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_abandoned_camp_166854_156607' WHERE `entry` IN (166854, 156607);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_abandoned_camp_166906_156651' WHERE `entry` IN (166906, 156651);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_abandoned_camp_175030_175031' WHERE `entry` IN (175030, 175031);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_jrokgar_161265' WHERE `entry` = 161265;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_quilboar_briarpatch_166824_156662' WHERE `entry` IN (166824, 156662);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_quilboar_briarpatch_166996_154327' WHERE `entry` IN (166996, 154327);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_geolord_grekog_151091' WHERE `entry` = 151091;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_quilboars_150237_150238' WHERE `entry` IN (150237, 150238);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ogre_overseer_156676' WHERE `entry` = 156676;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_plains_167019_149899' WHERE `entry` IN (167019, 149899);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_plains_167905_156526' WHERE `entry` IN (167905, 156526);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_plains_167915_156749' WHERE `entry` IN (167915, 156749);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_plains_156736_156716' WHERE `entry` IN (156736, 156716);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_plains_156267' WHERE `entry` = 156267;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_plains_157114' WHERE `entry` = 157114;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ogre_ruins_167145_156807' WHERE `entry` IN (167145, 156807);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ogre_ruins_162817' WHERE `entry` = 162817;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ogre_ruins_167126_167128_156799' WHERE `entry` IN (167126, 167128, 156799);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_hruns_barrow_156900' WHERE `entry` = 156900;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_hruns_barrow_167244_160964' WHERE `entry` IN (167244, 160964);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_hruns_barrow_156929_167254' WHERE `entry` IN (156929, 167254);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_ogre_ruins_156833_167219_156859_167307' WHERE `entry` IN (156833, 167219, 156859, 167307);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_harpy_roost_167298_153211' WHERE `entry` IN (167298, 153211);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_harpy_roost_155197_167301_155199_167300' WHERE `entry` IN (155197, 167301, 155199, 167300);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_harpy_roost_153964' WHERE `entry` = 153964;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_harpy_roost_152843_152571_152998' WHERE `entry` IN (152843, 152571, 152998);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_bridge_167596_156941' WHERE `entry` IN (167596, 156941);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_bridge_167598' WHERE `entry` = 167598;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_bridge_156943' WHERE `entry` = 156943;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_bridge_prisoners' WHERE `entry` IN (167599, 167597, 156944, 156942);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_killclaw_153266' WHERE `entry` = 153266;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_citadel_153239_153242_154103' WHERE `entry` IN (153239, 153242, 154103);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_citadel_153581_153582_153583' WHERE `entry` IN (153581, 153582, 153583);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_citadel_153580' WHERE `entry` = 153580;
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_citadel_156954_167646' WHERE `entry` IN (156954, 167646);
UPDATE `creature_template` SET `ScriptName` = 'npc_exiles_reach_darkmaul_citadel_161306' WHERE `entry` = 161306;
UPDATE `creature_template` SET `ScriptName` = 'boss_tunk' WHERE `entry` = 157300;
UPDATE `gameobject_template` SET `ScriptName` = 'go_campfire_339769' WHERE `entry` = 339769;
UPDATE `gameobject_template` SET `ScriptName` = 'go_exiles_reach_darkmaul_plains_326716_326717_326718_326719' WHERE `entry` IN (326716, 326717, 326718, 326719);
UPDATE `gameobject_template` SET `ScriptName` = 'go_exiles_reach_hruns_barrow_339568_350796' WHERE `entry` IN (339568, 350796);
UPDATE `gameobject_template` SET `ScriptName` = 'go_exiles_reach_harpy_roost_350803_327146' WHERE `entry` IN (350803, 327146);
UPDATE `gameobject_template` SET `ScriptName` = 'go_exiles_reach_catapult_351477_326651' WHERE `entry` IN (351477, 326651);
UPDATE `gameobject_template` SET `ScriptName` = 'go_exiles_reach_ogre_runestone_351476_339865' WHERE `entry` IN (351476, 339865);
UPDATE `instance_template` SET `script` = 'instance_darkmaul_citadel' WHERE `map` = 2236;
INSERT INTO `instance_template` (`map`, `parent`, `script`, `insideResurrection`)
SELECT 2236, 0, 'instance_darkmaul_citadel', 0 FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `instance_template` WHERE `map` = 2236);
DELETE FROM `spell_script_names` WHERE `ScriptName` IN (
'spell_necrotic_ritual_325406',
'spell_exiles_reach_scene_spells',
'spell_exiles_reach_stormwind_teleport_299565',
'spell_exiles_reach_orgrimmar_teleport_326311',
'spell_trampling_charge_210131',
'spell_ogre_transformation_313583',
'spell_swooping_lunge_317559',
'spell_re_sizing_313269',
'spell_re_sizing_305716',
'spell_resizer_hit_305742',
'spell_re_sizing_325346',
'spell_riding_giant_boar_305068',
'spell_ride_vehicle_173426',
'spell_giant_boar_charge_321627',
'spell_giant_boar_ping_vehicle_305559',
'spell_around_front_door_299483',
'spell_around_cooking_area_299486'
);
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(325406, 'spell_necrotic_ritual_325406'),
(321690, 'spell_exiles_reach_scene_spells'),
(321691, 'spell_exiles_reach_scene_spells'),
(321692, 'spell_exiles_reach_scene_spells'),
(321693, 'spell_exiles_reach_scene_spells'),
(312605, 'spell_exiles_reach_scene_spells'),
(299565, 'spell_exiles_reach_stormwind_teleport_299565'),
(326311, 'spell_exiles_reach_orgrimmar_teleport_326311'),
(210131, 'spell_trampling_charge_210131'),
(313583, 'spell_ogre_transformation_313583'),
(317559, 'spell_swooping_lunge_317559'),
(313269, 'spell_re_sizing_313269'),
(305716, 'spell_re_sizing_305716'),
(305742, 'spell_resizer_hit_305742'),
(325346, 'spell_re_sizing_325346'),
(305068, 'spell_riding_giant_boar_305068'),
(173426, 'spell_ride_vehicle_173426'),
(321627, 'spell_giant_boar_charge_321627'),
(305559, 'spell_giant_boar_ping_vehicle_305559'),
(299483, 'spell_around_front_door_299483'),
(299486, 'spell_around_cooking_area_299486');
DELETE FROM `item_script_names` WHERE `Id` = 168410;
INSERT INTO `item_script_names` (`Id`, `ScriptName`) VALUES (168410, 'item_first_aid_kit');
@@ -0,0 +1,131 @@
UPDATE `creature_template` SET `ScriptName` = 'boss_gorgroth' WHERE `entry` = 156814;
UPDATE `creature_template` SET `ScriptName` = 'boss_ravnyr' WHERE `entry` = 156501;
DELETE FROM `creature_text` WHERE `CreatureID` IN (156814, 156501);
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `SoundPlayType`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(156814, 0, 0, 'Ravnyr will destroy you all!', 14, 0, 100, 0, 0, 0, 0, 0, 0, 'Gor''groth - Aggro'),
(156814, 1, 0, 'We... will be... the final sacrifice!', 14, 0, 100, 0, 0, 0, 0, 0, 0, 'Gor''groth - Sacrifice'),
(156501, 0, 0, 'Foolish ogre! You have given me nowhere near enough power!', 14, 0, 100, 0, 0, 0, 0, 0, 0, 'Ravnyr - Aggro'),
(156501, 1, 0, 'Pathetic mortals. Your deaths will nourish me!', 14, 0, 100, 0, 0, 0, 0, 0, 0, 'Ravnyr - Slay');
-- Amirdrassil, Vault of the Incarnates (Sennarth) and De Other Side script bindings.
-- Without ScriptName the creature AI never runs. Without instance_template.script the encounter state is not saved.
UPDATE `creature_template` SET `ScriptName` = 'boss_gnarlroot' WHERE `entry` = 209333;
UPDATE `creature_template` SET `ScriptName` = 'boss_igira_the_cruel' WHERE `entry` = 200926;
UPDATE `creature_template` SET `ScriptName` = 'boss_volcoross' WHERE `entry` = 208478;
UPDATE `creature_template` SET `ScriptName` = 'boss_urctos' WHERE `entry` = 208363;
UPDATE `creature_template` SET `ScriptName` = 'boss_aerwynn' WHERE `entry` = 208365;
UPDATE `creature_template` SET `ScriptName` = 'boss_pip' WHERE `entry` = 208367;
UPDATE `creature_template` SET `ScriptName` = 'boss_larodar_keeper_of_the_flame' WHERE `entry` = 208445;
UPDATE `creature_template` SET `ScriptName` = 'boss_nymue_weaver_of_the_cycle' WHERE `entry` = 206172;
UPDATE `creature_template` SET `ScriptName` = 'boss_smolderon' WHERE `entry` = 200927;
UPDATE `creature_template` SET `ScriptName` = 'boss_tindral_sageswift_seer_of_the_flame' WHERE `entry` = 209090;
UPDATE `creature_template` SET `ScriptName` = 'boss_fyrakk_the_blazing' WHERE `entry` = 204931;
UPDATE `instance_template` SET `script` = 'instance_amirdrassil_the_dreams_hope' WHERE `map` = 2549;
INSERT INTO `instance_template` (`map`, `parent`, `script`, `insideResurrection`)
SELECT 2549, 0, 'instance_amirdrassil_the_dreams_hope', 0 FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `instance_template` WHERE `map` = 2549);
UPDATE `creature_template` SET `ScriptName` = 'boss_sennarth' WHERE `entry` = 187967;
UPDATE `instance_template` SET `script` = 'instance_vault_of_the_incarnates' WHERE `map` = 2522;
INSERT INTO `instance_template` (`map`, `parent`, `script`, `insideResurrection`)
SELECT 2522, 0, 'instance_vault_of_the_incarnates', 0 FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `instance_template` WHERE `map` = 2522);
UPDATE `creature_template` SET `ScriptName` = 'boss_hakkar_the_soulflayer_os' WHERE `entry` = 164558;
UPDATE `creature_template` SET `ScriptName` = 'boss_the_manastorms' WHERE `entry` IN (164556, 164555);
UPDATE `creature_template` SET `ScriptName` = 'boss_dealer_xyexa' WHERE `entry` = 164450;
UPDATE `creature_template` SET `ScriptName` = 'boss_muehzala' WHERE `entry` = 166608;
UPDATE `instance_template` SET `script` = 'instance_de_other_side' WHERE `map` = 2291;
INSERT INTO `instance_template` (`map`, `parent`, `script`, `insideResurrection`)
SELECT 2291, 0, 'instance_de_other_side', 0 FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `instance_template` WHERE `map` = 2291);
REPLACE INTO `areatrigger_create_properties_orbit` (`AreaTriggerCreatePropertiesId`, `IsCustom`, `ExtraTimeForBlending`, `CircleRadius`, `BlendFromRadius`, `InitialAngle`, `ZOffset`, `CounterClockwise`, `CanLoop`, `VerifiedBuild`) VALUES
(31874, 0, 0, 15, 14.99997234344482421, 1.714424490928649902, 0, 0, 0, 69875); -- Spell: 433766 (Eye of the Swarm)
INSERT IGNORE INTO `conversation_actors` (`ConversationId`, `ConversationActorId`, `Idx`, `CreatureId`, `CreatureDisplayInfoId`, `NoActorObject`, `ActivePlayerObject`, `VerifiedBuild`) VALUES
(15668, 76253, 0, 162804, 115723, 0, 0, 69875), -- Full: 0x203B1915C09EFD000420300000326EA9 Creature/0 R3782/S270384 Map: 2222 (The Shadowlands) Entry: 162804 (Ve'nari) Low: 3305129
(26371, 96026, 0, 215405, 115723, 0, 0, 69875),
(15664, 76253, 0, 162804, 95004, 0, 0, 69875),
(15663, 76253, 0, 162804, 95004, 0, 0, 69875),
(15662, 76253, 0, 162804, 95004, 0, 0, 69875),
(15507, 76253, 0, 0, 0, 0, 0, 69875), -- Full: 0x0
(15665, 76253, 0, 162804, 95004, 0, 0, 69875),
(16869, 73604, 0, 175559, 101866, 0, 0, 69875);
DELETE FROM `conversation_line_template` WHERE `Id`=42683;
INSERT INTO `conversation_line_template` (`Id`, `UiCameraID`, `ActorIdx`, `Flags`, `ChatType`, `VerifiedBuild`) VALUES
(42683, 1588, 0, 0, 0, 69875);
DELETE FROM `conversation_template` WHERE `Id` IN (25181, 26371, 15507, 15665, 15664, 15663, 15662, 15668);
INSERT INTO `conversation_template` (`Id`, `FirstLineID`, `TextureKitId`, `VerifiedBuild`) VALUES
(25181, 68556, 0, 69875),
(26371, 71951, 0, 69875),
(15507, 38903, 0, 69875),
(15665, 39300, 0, 69875),
(15664, 39299, 0, 69875),
(15663, 39298, 0, 69875),
(15662, 39297, 0, 69875),
(15668, 39303, 0, 69875);
UPDATE `conversation_template` SET `FirstLineID`=42683 WHERE `Id`=16869;
DELETE FROM `world_quest_template` WHERE `QuestID` IN (58981 /*Hardened Hive*/, 54601 /*Ragebeak*/, 64974 /*Clean Up, Automa Aisle*/, 51467 /*Hyo'gi*/, 64854 /*Trappings of Success*/, 82964 /*Hillhelm Family: Uncle's Eye*/, 82963 /*Hillhelm Family: Lil Piggy*/, 51588 /*Familiar Foes*/, 56386 /*Elderspawn of Nalaada*/, 51461 /*Deathcap*/, 52200 /*Turtle Tactics*/, 55825 /*Bounty: A Few Fathom Rays*/, 65252 /*Stomp It Now*/, 58990 /*Titanus Egg*/, 65177 /*Fruit of the Bloom*/, 82995 /*Nerubian Device*/, 82966 /*Hillhelm Family: Unusual Potato*/, 51385 /*A Supply of Stingers*/, 82961 /*Hillhelm Family: The Chicken*/);
INSERT INTO `world_quest_template` (`QuestID`, `Duration`, `VariableID`, `Value`) VALUES
(58981, 86400, 18721, 1), -- Hardened Hive
(54601, 86400, 16889, 1), -- Ragebeak
(64974, 86400, 21351, 1), -- Clean Up, Automa Aisle
(51467, 86400, 15239, 1), -- Hyo'gi
(64854, 86400, 21241, 1), -- Trappings of Success
(82964, 604800, 23011, 1), -- Hillhelm Family: Uncle's Eye
(82963, 604800, 23011, 1), -- Hillhelm Family: Lil Piggy
(51588, 86400, 15490, 1), -- Familiar Foes
(56386, 86400, 17787, 6), -- Elderspawn of Nalaada
(51461, 86400, 15237, 1), -- Deathcap
(52200, 86400, 15660, 1), -- Turtle Tactics
(55825, 86400, 17520, 1), -- Bounty: A Few Fathom Rays
(65252, 86400, 21377, 1), -- Stomp It Now
(58990, 86400, 18722, 1), -- Titanus Egg
(65177, 86400, 21353, 1), -- Fruit of the Bloom
(82995, 604800, 23011, 1), -- Nerubian Device
(82966, 604800, 23011, 1), -- Hillhelm Family: Unusual Potato
(51385, 86400, 15206, 1), -- A Supply of Stingers
(82961, 604800, 23011, 1); -- Hillhelm Family: The Chicken
DELETE FROM `creature_template_difficulty` WHERE (`DifficultyID`=205 AND `Entry` IN (224552,218324,217533,216413,216293,216336,253060));
INSERT INTO `creature_template_difficulty` (`Entry`, `DifficultyID`, `HealthScalingExpansion`, `HealthModifier`, `ManaModifier`, `CreatureDifficultyID`, `TypeFlags`, `TypeFlags2`, `TypeFlags3`) VALUES
(224552, 205, 10, 1500, 1, 275475, 0x0, 67108928, 0), -- Rasha'nan
(218324, 205, 10, 10, 1, 267828, 0x200048, 2147483648, 0), -- Nakt
(217533, 205, 10, 12, 1, 267026, 0x200048, 2147483648, 0), -- Atik
(216413, 205, 10, 1, 1, 265899, 0x60000010, 6, 0), -- [DNT] Spawner
(216293, 205, 10, 4.5, 1, 265779, 0x200048, 0, 0), -- Trilling Attendant
(216336, 205, 10, 1.279999971389770507, 1, 265822, 0x200048, 0, 0), -- Ravenous Crawler
(253060, 205, 11, 1, 1, 333916, 0x0, 0, 0); -- El-ahrairah
DELETE FROM `creature_template_difficulty` WHERE (`DifficultyID`=205 AND `Entry` IN (253060,216293,218324,217533,224552,216413,216336));
INSERT INTO `creature_template_difficulty` (`Entry`, `DifficultyID`, `LevelScalingDeltaMin`, `LevelScalingDeltaMax`, `ContentTuningID`, `StaticFlags1`, `StaticFlags2`, `StaticFlags3`, `StaticFlags4`, `StaticFlags5`, `StaticFlags6`, `StaticFlags7`, `StaticFlags8`, `VerifiedBuild`) VALUES
(253060, 205, 0, 0, 181, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 69875), -- 253060 (El-ahrairah) - Sessile
(216293, 205, 0, 0, 2893, 0x10000000, 0x0, 0x0, 0x2000000, 0x0, 0x0, 0x0, 0x0, 69875), -- 216293 (Trilling Attendant) - CanSwim - HideInCombatLog
(218324, 205, 1, 1, 2893, 0x10000100, 0x0, 0x0, 0x2000000, 0x0, 0x0, 0x0, 0x0, 69875), -- 218324 (Nakt) - Sessile, CanSwim - HideInCombatLog
(217533, 205, 1, 1, 2893, 0x10000100, 0x0, 0x0, 0x2000000, 0x0, 0x0, 0x0, 0x0, 69875), -- 217533 (Atik) - Sessile, CanSwim - HideInCombatLog
(224552, 205, 3, 3, 2893, 0x30000000, 0x0, 0x4, 0x0, 0x1, 0x0, 0x0, 0x0, 69875), -- 224552 (Rasha'nan) - CanSwim, Floating - DoNotFadeIn - UntargetableByClient
(216413, 205, 0, 0, 2893, 0x100, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 69875), -- 216413 ([DNT] Spawner) - Sessile - UntargetableByClient
(216336, 205, 0, 0, 2893, 0x10000000, 0x0, 0x4, 0x2000000, 0x0, 0x0, 0x0, 0x0, 69875); -- 216336 (Ravenous Crawler) - CanSwim - DoNotFadeIn - HideInCombatLog
UPDATE `creature_model_info` SET `BoundingRadius`=3.728271484375, `CombatReach`=3 WHERE `DisplayID`=114552;
UPDATE `creature_model_info` SET `BoundingRadius`=0.310000002384185791, `CombatReach`=1 WHERE `DisplayID`=24107;
UPDATE `creature_model_info` SET `BoundingRadius`=1.256006956100463867, `CombatReach`=1 WHERE `DisplayID`=96826;
UPDATE `creature_template_model` SET `CreatureDisplayID`=11686 WHERE (`Idx`=1 AND `CreatureID` IN (179114,247012,180358,180347,70528,177525)) OR (`Idx`=0 AND `CreatureID` IN (43499,216413));
UPDATE `creature_template_model` SET `CreatureDisplayID`=27823 WHERE (`Idx`=1 AND `CreatureID` IN (230874,253060,172631,180633));
File diff suppressed because one or more lines are too long
@@ -0,0 +1,763 @@
INSERT INTO `quest_template_addon` (`ID`) VALUES
(88920), (88923), (88925), (88937), (88927), (88922), (88938), (88939), (88941)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 88923, `ExclusiveGroup` = 0 WHERE `ID` = 88920;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88920, `NextQuestID` = 88925, `ExclusiveGroup` = 0 WHERE `ID` = 88923;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88923, `NextQuestID` = 88937, `ExclusiveGroup` = 0 WHERE `ID` = 88925;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88925, `NextQuestID` = 88927, `ExclusiveGroup` = 0 WHERE `ID` = 88937;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88937, `NextQuestID` = 88922, `ExclusiveGroup` = 0 WHERE `ID` = 88927;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88927, `NextQuestID` = 88938, `ExclusiveGroup` = 0 WHERE `ID` = 88922;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88922, `NextQuestID` = 88939, `ExclusiveGroup` = 0 WHERE `ID` = 88938;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88938, `NextQuestID` = 88941, `ExclusiveGroup` = 0 WHERE `ID` = 88939;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88939, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 88941;
-- ---------------------------------------------------------------------------
-- Legacy of the Amani (12.1 capital campaign). Wowhead / warcraft.wiki.gg:
-- 92897 (Adventure Guide breadcrumb) -> 92895 -> 92899 -> 92900 -> 92901 ->
-- 92904 -> 92907 -> 92955 -> (92957 + 92958) -> 92952 -> (92953 + 92951) ->
-- 92954 -> 93010 -> 93011 -> 93012
-- 92897 can be skipped. 92957/92958 and 92953/92951 are both required.
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(92897), (92895), (92899), (92900), (92901), (92904), (92907), (92955),
(92957), (92958), (92952), (92953), (92951), (92954), (93010), (93011), (93012)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92895, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 92895 WHERE `ID` = 92897;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92899, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 0 WHERE `ID` = 92895;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92895, `NextQuestID` = 92900, `ExclusiveGroup` = 0 WHERE `ID` = 92899;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92899, `NextQuestID` = 92901, `ExclusiveGroup` = 0 WHERE `ID` = 92900;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92900, `NextQuestID` = 92904, `ExclusiveGroup` = 0 WHERE `ID` = 92901;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92901, `NextQuestID` = 92907, `ExclusiveGroup` = 0 WHERE `ID` = 92904;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92904, `NextQuestID` = 92955, `ExclusiveGroup` = 0 WHERE `ID` = 92907;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92907, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 92955;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92955, `NextQuestID` = 92952, `ExclusiveGroup` = -92957 WHERE `ID` IN (92957, 92958);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 92952;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92952, `NextQuestID` = 92954, `ExclusiveGroup` = -92953 WHERE `ID` IN (92953, 92951);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 93010, `ExclusiveGroup` = 0 WHERE `ID` = 92954;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92954, `NextQuestID` = 93011, `ExclusiveGroup` = 0 WHERE `ID` = 93010;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93010, `NextQuestID` = 93012, `ExclusiveGroup` = 0 WHERE `ID` = 93011;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93011, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 93012;
-- 253640 Orweyna is in Silvermoon (Wowhead; accept line for 92895).
-- 241795 Orweyna is in Harandar (Wowhead; turn-in for 92895 / start 92899).
-- npcflag 1 = gossip, 2 = questgiver.
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3 WHERE `entry` IN (253640, 241795);
DELETE FROM `creature_queststarter` WHERE (`id` = 253640 AND `quest` = 92895) OR (`id` = 241795 AND `quest` = 92899);
INSERT INTO `creature_queststarter` (`id`, `quest`, `VerifiedBuild`) VALUES
(253640, 92895, 69497), -- Hagar's Invitation offered by Orweyna in Silvermoon
(241795, 92899, 69497); -- History Lesson offered by Orweyna in Harandar
DELETE FROM `creature_questender` WHERE (`id` = 253640 AND `quest` = 92897) OR (`id` = 241795 AND `quest` IN (92895, 92899));
INSERT INTO `creature_questender` (`id`, `quest`, `VerifiedBuild`) VALUES
(253640, 92897, 69497), -- The Preparations Are Complete turned in to Orweyna in Silvermoon
(241795, 92895, 69497), -- Hagar's Invitation turned in to Orweyna in Harandar
(241795, 92899, 69497); -- History Lesson turned in to Orweyna in Harandar
-- Midnight leveling campaign. Quest IDs and order come from Wowhead storyline
-- pages plus warcraft.wiki.gg. TBC Eversong/Ghostlands scripts stay for Chromie Time.
-- Parallel steps use ExclusiveGroup < 0 (all required). The two Paved in Ash IDs
-- (86735/86736) are faction variants (ExclusiveGroup > 0). Bonus / sojourner
-- quests stay unlinked. No invented IDs or spawns.
--
-- After Eversong, Zul'Aman / Harandar / Arator's Journey can be done in any
-- order. Voidstorm is last on live; that three-path gate is not encoded here
-- (quest_template_addon has one PrevQuestID).
-- ---------------------------------------------------------------------------
-- Intro: The Light's Summons (Wowhead 91281)
-- 91281 -> 88719 -> 86769 -> (86770 + 89271 + 86780) -> (86805 + 89012)
-- -> 86806 -> 86807 -> (91274 + 86834) -> (86811 + 86848) -> 86849 -> 86850
-- -> 86852. Bonus Clear the Decks / Light Show / Protecting the Flank stay off.
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(91281), (88719), (86769), (86770), (89271), (86780), (86805), (89012),
(86806), (86807), (91274), (86834), (86811), (86848), (86849), (86850),
(86852)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 88719, `ExclusiveGroup` = 0 WHERE `ID` = 91281;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91281, `NextQuestID` = 86769, `ExclusiveGroup` = 0 WHERE `ID` = 88719;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88719, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86769;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86769, `NextQuestID` = 0, `ExclusiveGroup` = -86770 WHERE `ID` IN (86770, 89271, 86780);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86770, `NextQuestID` = 86806, `ExclusiveGroup` = -86805 WHERE `ID` IN (86805, 89012);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86805, `NextQuestID` = 86807, `ExclusiveGroup` = 0 WHERE `ID` = 86806;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86806, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86807;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86807, `NextQuestID` = 0, `ExclusiveGroup` = -86834 WHERE `ID` IN (86834, 91274);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86834, `NextQuestID` = 86849, `ExclusiveGroup` = -86811 WHERE `ID` IN (86811, 86848);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86850, `ExclusiveGroup` = 0 WHERE `ID` = 86849;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86849, `NextQuestID` = 86852, `ExclusiveGroup` = 0 WHERE `ID` = 86850;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86850, `NextQuestID` = 86733, `ExclusiveGroup` = 0 WHERE `ID` = 86852;
-- ---------------------------------------------------------------------------
-- Eversong: Whispers in the Twilight (Wowhead 86733)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86733), (86734), (86735), (86736), (86737), (86738), (86739), (86740),
(86741), (86742), (86743), (86744), (86745)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86852, `NextQuestID` = 86734, `ExclusiveGroup` = 0 WHERE `ID` = 86733;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86733, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86734;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86734, `NextQuestID` = 86737, `ExclusiveGroup` = 86735 WHERE `ID` IN (86735, 86736);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86737;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86737, `NextQuestID` = 86741, `ExclusiveGroup` = -86738 WHERE `ID` IN (86738, 86739, 86740);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86741;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86741, `NextQuestID` = 86744, `ExclusiveGroup` = -86742 WHERE `ID` IN (86742, 86743);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86745, `ExclusiveGroup` = 0 WHERE `ID` = 86744;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86744, `NextQuestID` = 86621, `ExclusiveGroup` = 0 WHERE `ID` = 86745;
-- ---------------------------------------------------------------------------
-- Eversong: Shadowfall (Wowhead 86621). Tranquillien chapter, not Outland
-- Shadowmoon. 86625 The Clue in the Inn is not on the wiki campaign list.
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86621), (86623), (86624), (90907), (86622), (86626), (86632), (90509),
(90493), (90494), (86781), (86634), (86633), (86635), (86636)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86745, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86621;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86621, `NextQuestID` = 90907, `ExclusiveGroup` = -86623 WHERE `ID` IN (86623, 86624);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86622, `ExclusiveGroup` = 0 WHERE `ID` = 90907;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90907, `NextQuestID` = 86626, `ExclusiveGroup` = 0 WHERE `ID` = 86622;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86622, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86626;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86626, `NextQuestID` = 90494, `ExclusiveGroup` = -86632 WHERE `ID` IN (86632, 90509, 90493);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86781, `ExclusiveGroup` = 0 WHERE `ID` = 90494;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90494, `NextQuestID` = 86634, `ExclusiveGroup` = 0 WHERE `ID` = 86781;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86781, `NextQuestID` = 86633, `ExclusiveGroup` = 0 WHERE `ID` = 86634;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86634, `NextQuestID` = 86635, `ExclusiveGroup` = 0 WHERE `ID` = 86633;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86633, `NextQuestID` = 86636, `ExclusiveGroup` = 0 WHERE `ID` = 86635;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86635, `NextQuestID` = 86637, `ExclusiveGroup` = 0 WHERE `ID` = 86636;
-- ---------------------------------------------------------------------------
-- Eversong: Ripple Effects (Wowhead 86637). Skip 86645 Hidden [DNT].
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86637), (86638), (86639), (86640), (86641), (86642), (86643), (86644),
(86646), (86647), (86648), (86649), (86650)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86636, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86637;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86637, `NextQuestID` = 86640, `ExclusiveGroup` = -86638 WHERE `ID` IN (86638, 86639);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86640;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86640, `NextQuestID` = 86643, `ExclusiveGroup` = -86641 WHERE `ID` IN (86641, 86642);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86644, `ExclusiveGroup` = 0 WHERE `ID` = 86643;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86643, `NextQuestID` = 86646, `ExclusiveGroup` = 0 WHERE `ID` = 86644;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86644, `NextQuestID` = 86647, `ExclusiveGroup` = 0 WHERE `ID` = 86646;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86646, `NextQuestID` = 86648, `ExclusiveGroup` = 0 WHERE `ID` = 86647;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86647, `NextQuestID` = 86649, `ExclusiveGroup` = 0 WHERE `ID` = 86648;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86648, `NextQuestID` = 86650, `ExclusiveGroup` = 0 WHERE `ID` = 86649;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86649, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86650;
-- ---------------------------------------------------------------------------
-- Zul'Aman: Dis Was Our Land (Wowhead 86708)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86708), (86710), (90749), (86868), (86711), (86719), (86717), (86716),
(86721), (86712), (86718), (86715), (86720), (86722), (86723), (86652)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86710, `ExclusiveGroup` = 0 WHERE `ID` = 86708;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86708, `NextQuestID` = 90749, `ExclusiveGroup` = 0 WHERE `ID` = 86710;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86710, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 90749;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90749, `NextQuestID` = 0, `ExclusiveGroup` = -86868 WHERE `ID` IN (86868, 86711);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86868, `NextQuestID` = 0, `ExclusiveGroup` = -86719 WHERE `ID` IN (86719, 86717);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86719, `NextQuestID` = 0, `ExclusiveGroup` = -86716 WHERE `ID` IN (86716, 86721);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86716, `NextQuestID` = 86720, `ExclusiveGroup` = -86712 WHERE `ID` IN (86712, 86718, 86715);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86722, `ExclusiveGroup` = 0 WHERE `ID` = 86720;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86720, `NextQuestID` = 86723, `ExclusiveGroup` = 0 WHERE `ID` = 86722;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86722, `NextQuestID` = 86652, `ExclusiveGroup` = 0 WHERE `ID` = 86723;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86723, `NextQuestID` = 86653, `ExclusiveGroup` = 0 WHERE `ID` = 86652;
-- ---------------------------------------------------------------------------
-- Zul'Aman: Path of de Hash'ey (Wowhead 86653)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86653), (86655), (89334), (86654), (86656), (86809), (86657), (86658),
(86660), (86659), (92084), (86661), (86808), (86663), (86664), (86665),
(90772), (86666)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86652, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86653;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86653, `NextQuestID` = 86656, `ExclusiveGroup` = -86655 WHERE `ID` IN (86655, 89334, 86654);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86809, `ExclusiveGroup` = 0 WHERE `ID` = 86656;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86656, `NextQuestID` = 86657, `ExclusiveGroup` = 0 WHERE `ID` = 86809;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86809, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86657;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86657, `NextQuestID` = 86659, `ExclusiveGroup` = -86658 WHERE `ID` IN (86658, 86660);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92084, `ExclusiveGroup` = 0 WHERE `ID` = 86659;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86659, `NextQuestID` = 86661, `ExclusiveGroup` = 0 WHERE `ID` = 92084;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92084, `NextQuestID` = 86808, `ExclusiveGroup` = 0 WHERE `ID` = 86661;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86661, `NextQuestID` = 86663, `ExclusiveGroup` = 0 WHERE `ID` = 86808;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86808, `NextQuestID` = 86664, `ExclusiveGroup` = 0 WHERE `ID` = 86663;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86663, `NextQuestID` = 86665, `ExclusiveGroup` = 0 WHERE `ID` = 86664;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86664, `NextQuestID` = 90772, `ExclusiveGroup` = 0 WHERE `ID` = 86665;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86665, `NextQuestID` = 86666, `ExclusiveGroup` = 0 WHERE `ID` = 90772;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90772, `NextQuestID` = 86681, `ExclusiveGroup` = 0 WHERE `ID` = 86666;
-- ---------------------------------------------------------------------------
-- Zul'Aman: Where War Slumbers (Wowhead 86681)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86681), (86682), (91958)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86666, `NextQuestID` = 86682, `ExclusiveGroup` = 0 WHERE `ID` = 86681;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86681, `NextQuestID` = 91958, `ExclusiveGroup` = 0 WHERE `ID` = 86682;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86682, `NextQuestID` = 86683, `ExclusiveGroup` = 0 WHERE `ID` = 91958;
-- ---------------------------------------------------------------------------
-- Zul'Aman: De Amani Never Die (Wowhead 86683)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86683), (86684), (86687), (86685), (86686), (91001), (86692), (86693),
(91062), (91087)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91958, `NextQuestID` = 86684, `ExclusiveGroup` = 0 WHERE `ID` = 86683;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86683, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86684;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86684, `NextQuestID` = 91001, `ExclusiveGroup` = -86687 WHERE `ID` IN (86687, 86685, 86686);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86692, `ExclusiveGroup` = 0 WHERE `ID` = 91001;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91001, `NextQuestID` = 86693, `ExclusiveGroup` = 0 WHERE `ID` = 86692;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86692, `NextQuestID` = 91062, `ExclusiveGroup` = 0 WHERE `ID` = 86693;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86693, `NextQuestID` = 91087, `ExclusiveGroup` = 0 WHERE `ID` = 91062;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91062, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 91087;
-- ---------------------------------------------------------------------------
-- Harandar: Of Caves and Cradles (Wowhead 86899). 89402 is the zone breadcrumb.
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(89402), (86899), (86900), (86901), (86929), (86907), (86911), (90094),
(90095), (86912), (86913), (86914), (86956), (86910), (86973), (86942),
(89034), (86944)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86899, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 86899 WHERE `ID` = 89402;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86900, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 0 WHERE `ID` = 86899;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86899, `NextQuestID` = 86901, `ExclusiveGroup` = 0 WHERE `ID` = 86900;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86900, `NextQuestID` = 86929, `ExclusiveGroup` = 0 WHERE `ID` = 86901;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86901, `NextQuestID` = 86907, `ExclusiveGroup` = 0 WHERE `ID` = 86929;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86929, `NextQuestID` = 86911, `ExclusiveGroup` = 0 WHERE `ID` = 86907;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86907, `NextQuestID` = 90094, `ExclusiveGroup` = 0 WHERE `ID` = 86911;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86911, `NextQuestID` = 90095, `ExclusiveGroup` = 0 WHERE `ID` = 90094;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90094, `NextQuestID` = 86912, `ExclusiveGroup` = 0 WHERE `ID` = 90095;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90095, `NextQuestID` = 86913, `ExclusiveGroup` = 0 WHERE `ID` = 86912;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86912, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86913;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86913, `NextQuestID` = 86910, `ExclusiveGroup` = -86914 WHERE `ID` IN (86914, 86956);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86910;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86910, `NextQuestID` = 86944, `ExclusiveGroup` = -86973 WHERE `ID` IN (86973, 86942, 89034);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86930, `ExclusiveGroup` = 0 WHERE `ID` = 86944;
-- ---------------------------------------------------------------------------
-- Harandar: Call of the Goddess (Wowhead 86930)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86930), (86864), (86836), (86855), (86851), (86856), (86857), (86858),
(86861), (86860), (86859), (86862), (86865), (86866), (94677), (86882),
(86867), (86881), (86880), (86877), (86890)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86944, `NextQuestID` = 86864, `ExclusiveGroup` = 0 WHERE `ID` = 86930;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86930, `NextQuestID` = 86836, `ExclusiveGroup` = 0 WHERE `ID` = 86864;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86864, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86836;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86836, `NextQuestID` = 86856, `ExclusiveGroup` = -86855 WHERE `ID` IN (86855, 86851);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86857, `ExclusiveGroup` = 0 WHERE `ID` = 86856;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86856, `NextQuestID` = 86858, `ExclusiveGroup` = 0 WHERE `ID` = 86857;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86857, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86858;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86858, `NextQuestID` = 86862, `ExclusiveGroup` = -86861 WHERE `ID` IN (86861, 86860, 86859);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86865, `ExclusiveGroup` = 0 WHERE `ID` = 86862;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86862, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86865;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86865, `NextQuestID` = 86882, `ExclusiveGroup` = -86866 WHERE `ID` IN (86866, 94677);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86867, `ExclusiveGroup` = 0 WHERE `ID` = 86882;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86882, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86867;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86867, `NextQuestID` = 86890, `ExclusiveGroup` = -86881 WHERE `ID` IN (86881, 86880, 86877);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86883, `ExclusiveGroup` = 0 WHERE `ID` = 86890;
-- ---------------------------------------------------------------------------
-- Harandar: Emergence (Wowhead 86883)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86883), (86885), (86884), (86887), (86891), (86892), (86894), (86896),
(86897), (86898), (91084)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86890, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86883;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86883, `NextQuestID` = 0, `ExclusiveGroup` = -86885 WHERE `ID` IN (86885, 86884);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86885, `NextQuestID` = 86892, `ExclusiveGroup` = -86887 WHERE `ID` IN (86887, 86891);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86892;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86892, `NextQuestID` = 86897, `ExclusiveGroup` = -86894 WHERE `ID` IN (86894, 86896);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86898, `ExclusiveGroup` = 0 WHERE `ID` = 86897;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86897, `NextQuestID` = 91084, `ExclusiveGroup` = 0 WHERE `ID` = 86898;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86898, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 91084;
-- ---------------------------------------------------------------------------
-- Arator: The Path of Light (Wowhead 86837). 89193 is the chapter breadcrumb.
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(89193), (86837), (86838), (86839), (86840), (86841), (86842), (86843),
(86844), (92136), (86902), (86845), (91000), (86846), (89338)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86837, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 86837 WHERE `ID` = 89193;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86838, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 0 WHERE `ID` = 86837;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86837, `NextQuestID` = 86839, `ExclusiveGroup` = 0 WHERE `ID` = 86838;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86838, `NextQuestID` = 86840, `ExclusiveGroup` = 0 WHERE `ID` = 86839;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86839, `NextQuestID` = 86841, `ExclusiveGroup` = 0 WHERE `ID` = 86840;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86840, `NextQuestID` = 86842, `ExclusiveGroup` = 0 WHERE `ID` = 86841;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86841, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86842;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86842, `NextQuestID` = 92136, `ExclusiveGroup` = -86843 WHERE `ID` IN (86843, 86844);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86902, `ExclusiveGroup` = 0 WHERE `ID` = 92136;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92136, `NextQuestID` = 86845, `ExclusiveGroup` = 0 WHERE `ID` = 86902;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86902, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86845;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86845, `NextQuestID` = 89338, `ExclusiveGroup` = -91000 WHERE `ID` IN (91000, 86846);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86822, `ExclusiveGroup` = 0 WHERE `ID` = 89338;
-- ---------------------------------------------------------------------------
-- Arator: Regrets of the Past (Wowhead 86822)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86822), (86823), (86824), (86825), (91391), (86827), (86826), (91842),
(86828), (86831), (86830), (86829), (91726), (86832), (86833), (86903),
(91787)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 89338, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86822;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86822, `NextQuestID` = 91391, `ExclusiveGroup` = -86823 WHERE `ID` IN (86823, 86824, 86825);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 91391;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91391, `NextQuestID` = 86828, `ExclusiveGroup` = -86827 WHERE `ID` IN (86827, 86826, 91842);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86828;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86828, `NextQuestID` = 86829, `ExclusiveGroup` = -86831 WHERE `ID` IN (86831, 86830);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 91726, `ExclusiveGroup` = 0 WHERE `ID` = 86829;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86829, `NextQuestID` = 86832, `ExclusiveGroup` = 0 WHERE `ID` = 91726;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91726, `NextQuestID` = 86833, `ExclusiveGroup` = 0 WHERE `ID` = 86832;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86832, `NextQuestID` = 86903, `ExclusiveGroup` = 0 WHERE `ID` = 86833;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86833, `NextQuestID` = 91787, `ExclusiveGroup` = 0 WHERE `ID` = 86903;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86903, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 91787;
-- ---------------------------------------------------------------------------
-- Voidstorm: Into the Abyss (Wowhead 86543). 89388 is the zone breadcrumb.
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(89388), (92061), (86543), (86549), (86558), (86557), (86559), (86562),
(86561), (86565)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92061, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 92061 WHERE `ID` = 89388;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86543, `ExclusiveGroup` = 0, `BreadcrumbForQuestId` = 0 WHERE `ID` = 92061;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92061, `NextQuestID` = 86549, `ExclusiveGroup` = 0 WHERE `ID` = 86543;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86543, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86549;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86549, `NextQuestID` = 86559, `ExclusiveGroup` = -86558 WHERE `ID` IN (86558, 86557);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86559;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86559, `NextQuestID` = 86565, `ExclusiveGroup` = -86562 WHERE `ID` IN (86562, 86561);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86536, `ExclusiveGroup` = 0 WHERE `ID` = 86565;
-- ---------------------------------------------------------------------------
-- Voidstorm: The Night's Veil (Wowhead 86536)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86536), (86531), (86530), (86528), (86538), (86537), (86539), (86540),
(86541), (88768), (86542), (89249), (86544), (86545)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86565, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86536;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86536, `NextQuestID` = 0, `ExclusiveGroup` = -86531 WHERE `ID` IN (86531, 86530, 86528);
UPDATE `quest_template_addon` SET `PrevQuestID` = 86531, `NextQuestID` = 86539, `ExclusiveGroup` = -86538 WHERE `ID` IN (86538, 86537);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86539;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86539, `NextQuestID` = 86542, `ExclusiveGroup` = -86540 WHERE `ID` IN (86540, 86541, 88768);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 89249, `ExclusiveGroup` = 0 WHERE `ID` = 86542;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86542, `NextQuestID` = 86544, `ExclusiveGroup` = 0 WHERE `ID` = 89249;
UPDATE `quest_template_addon` SET `PrevQuestID` = 89249, `NextQuestID` = 86545, `ExclusiveGroup` = 0 WHERE `ID` = 86544;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86544, `NextQuestID` = 86509, `ExclusiveGroup` = 0 WHERE `ID` = 86545;
-- ---------------------------------------------------------------------------
-- Voidstorm: Dawn of Reckoning (Wowhead 86509)
-- ---------------------------------------------------------------------------
INSERT INTO `quest_template_addon` (`ID`) VALUES
(86509), (86510), (90571), (86511), (86512), (86513), (86514), (86516),
(86517), (86515), (86518), (86519), (86520), (86521), (86522), (95276)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86545, `NextQuestID` = 86510, `ExclusiveGroup` = 0 WHERE `ID` = 86509;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86509, `NextQuestID` = 90571, `ExclusiveGroup` = 0 WHERE `ID` = 86510;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86510, `NextQuestID` = 86511, `ExclusiveGroup` = 0 WHERE `ID` = 90571;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90571, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86511;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86511, `NextQuestID` = 86514, `ExclusiveGroup` = -86512 WHERE `ID` IN (86512, 86513);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 86514;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86514, `NextQuestID` = 86518, `ExclusiveGroup` = -86516 WHERE `ID` IN (86516, 86517, 86515);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 86519, `ExclusiveGroup` = 0 WHERE `ID` = 86518;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86518, `NextQuestID` = 86520, `ExclusiveGroup` = 0 WHERE `ID` = 86519;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86519, `NextQuestID` = 86521, `ExclusiveGroup` = 0 WHERE `ID` = 86520;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86520, `NextQuestID` = 86522, `ExclusiveGroup` = 0 WHERE `ID` = 86521;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86521, `NextQuestID` = 95276, `ExclusiveGroup` = 0 WHERE `ID` = 86522;
UPDATE `quest_template_addon` SET `PrevQuestID` = 86522, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 95276;
-- Harandar chapter 1 conversations already implemented in C++.
UPDATE `conversation_template` SET `ScriptName` = 'conversation_the_root_cause_open_portal' WHERE `Id` = 28444;
UPDATE `conversation_template` SET `ScriptName` = 'conversation_to_harathir_follow_orweyna' WHERE `Id` = 29720;
UPDATE `conversation_template` SET `ScriptName` = 'conversation_to_harathir_talk_to_kupaal' WHERE `Id` = 29598;
-- Missing Midnight content after 2026_09_22_15.
-- 1) Bind Blinding Vale, Nexus-Point Xenas, and Voidscar Arena. The C++ already
-- exists; ScriptName and instance_template.script were never set, so the AIs
-- never attached. NPC entries match the enums next to each Register* call.
-- 2) Wire the max-level War of Light and Shadow plus the remaining Curse of
-- Ula'tek chapters. IDs and order come from Wowhead storyline lists plus
-- warcraft.wiki.gg parallels. Sojourner / Snake Charmed stay unlinked.
-- No invented IDs or spawns.
-- ===========================================================================
-- Blinding Vale (map 2859)
-- ===========================================================================
UPDATE `creature_template` SET `ScriptName` = 'boss_kezkitt' WHERE `entry` = 243029;
UPDATE `creature_template` SET `ScriptName` = 'boss_meittik' WHERE `entry` = 243028;
UPDATE `creature_template` SET `ScriptName` = 'boss_lekshi' WHERE `entry` = 243030;
UPDATE `creature_template` SET `ScriptName` = 'npc_lightblossom' WHERE `entry` = 244528;
UPDATE `creature_template` SET `ScriptName` = 'boss_ikuzz' WHERE `entry` = 244887;
UPDATE `creature_template` SET `ScriptName` = 'npc_bloodthorn_roots' WHERE `entry` = 253571;
UPDATE `creature_template` SET `ScriptName` = 'boss_lightwarden_ruia' WHERE `entry` = 245912;
UPDATE `creature_template` SET `ScriptName` = 'boss_ziekket' WHERE `entry` = 247676;
UPDATE `creature_template` SET `ScriptName` = 'npc_lightspawn_lasher' WHERE `entry` = 247755;
UPDATE `instance_template` SET `script` = 'instance_blindingvale' WHERE `map` = 2859;
INSERT INTO `instance_template` (`map`, `parent`, `script`, `insideResurrection`)
SELECT 2859, 0, 'instance_blindingvale', 0 FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `instance_template` WHERE `map` = 2859);
-- ===========================================================================
-- Nexus-Point Xenas (map 2915)
-- ===========================================================================
UPDATE `creature_template` SET `ScriptName` = 'boss_kasreth' WHERE `entry` = 241539;
UPDATE `creature_template` SET `ScriptName` = 'boss_nysarra' WHERE `entry` = 241542;
UPDATE `creature_template` SET `ScriptName` = 'boss_lothraxion' WHERE `entry` = 241546;
UPDATE `creature_template` SET `ScriptName` = 'npc_lingering_image' WHERE `entry` = 241642;
UPDATE `creature_template` SET `ScriptName` = 'npc_fractured_image' WHERE `entry` = 251568;
UPDATE `creature_template` SET `ScriptName` = 'npc_corespark_pylon' WHERE `entry` = 254485;
UPDATE `creature_template` SET `ScriptName` = 'npc_coresparks' WHERE `entry` = 252938;
UPDATE `creature_template` SET `ScriptName` = 'npc_circuit_seer' WHERE `entry` = 248373;
UPDATE `creature_template` SET `ScriptName` = 'npc_umbra_weaver' WHERE `entry` = 248708;
UPDATE `creature_template` SET `ScriptName` = 'npc_manacite_worker' WHERE `entry` = 249096;
UPDATE `creature_template` SET `ScriptName` = 'npc_flux_engineer' WHERE `entry` = 241647;
UPDATE `instance_template` SET `script` = 'instance_nexuspointxenas' WHERE `map` = 2915;
INSERT INTO `instance_template` (`map`, `parent`, `script`, `insideResurrection`)
SELECT 2915, 0, 'instance_nexuspointxenas', 0 FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `instance_template` WHERE `map` = 2915);
-- ===========================================================================
-- Voidscar Arena (map 2923)
-- ===========================================================================
UPDATE `creature_template` SET `ScriptName` = 'boss_atroxus' WHERE `entry` = 239008;
UPDATE `creature_template` SET `ScriptName` = 'boss_aegyra_the_unyielding' WHERE `entry` = 267545;
UPDATE `creature_template` SET `ScriptName` = 'npc_champions_spear' WHERE `entry` = 267550;
UPDATE `creature_template` SET `ScriptName` = 'boss_brutok' WHERE `entry` = 244309;
UPDATE `creature_template` SET `ScriptName` = 'boss_brutal_overseer' WHERE `entry` = 252053;
UPDATE `creature_template` SET `ScriptName` = 'boss_devouring_brutalizer' WHERE `entry` = 268184;
UPDATE `creature_template` SET `ScriptName` = 'boss_watchful_harrower' WHERE `entry` = 245950;
UPDATE `creature_template` SET `ScriptName` = 'boss_tazrah' WHERE `entry` = 238887;
UPDATE `creature_template` SET `ScriptName` = 'boss_charonus' WHERE `entry` = 239167;
UPDATE `creature_template` SET `ScriptName` = 'npc_targeting_stalker' WHERE `entry` = 255000;
UPDATE `creature_template` SET `ScriptName` = 'npc_gravitic_orb' WHERE `entry` = 255001;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_ethereal_shade' WHERE `entry` = 254677;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_voidminder' WHERE `entry` = 244708;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_longtooth_tuskarr' WHERE `entry` = 243985;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_sycophantic_tarasek' WHERE `entry` = 243983;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_dominated_brawler' WHERE `entry` = 238883;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_protective_turtle' WHERE `entry` = 249603;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_kilivore_screamer' WHERE `entry` = 243766;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_chitigoth' WHERE `entry` = 244260;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_toxic_creeper' WHERE `entry` = 239070;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_abducted_drakonid' WHERE `entry` = 249461;
UPDATE `creature_template` SET `ScriptName` = 'npc_voidscar_raging_raptor' WHERE `entry` = 249608;
UPDATE `instance_template` SET `script` = 'instance_voidscar_arena' WHERE `map` = 2923;
INSERT INTO `instance_template` (`map`, `parent`, `script`, `insideResurrection`)
SELECT 2923, 0, 'instance_voidscar_arena', 0 FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM `instance_template` WHERE `map` = 2923);
-- ===========================================================================
-- Max-level: Foothold (Wowhead 90777). Follows The Last Push (95276).
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(90777), (88696), (88697), (88698), (88699), (91417), (88700), (88701),
(88702), (91426), (88703), (88704), (88705), (88706), (94957)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `NextQuestID` = 90777 WHERE `ID` = 95276;
UPDATE `quest_template_addon` SET `PrevQuestID` = 95276, `NextQuestID` = 88696, `ExclusiveGroup` = 0 WHERE `ID` = 90777;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90777, `NextQuestID` = 88697, `ExclusiveGroup` = 0 WHERE `ID` = 88696;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88696, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 88697;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88697, `NextQuestID` = 91417, `ExclusiveGroup` = -88698 WHERE `ID` IN (88698, 88699);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 88700, `ExclusiveGroup` = 0 WHERE `ID` = 91417;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91417, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 88700;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88700, `NextQuestID` = 91426, `ExclusiveGroup` = -88701 WHERE `ID` IN (88701, 88702);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 88703, `ExclusiveGroup` = 0 WHERE `ID` = 91426;
UPDATE `quest_template_addon` SET `PrevQuestID` = 91426, `NextQuestID` = 88704, `ExclusiveGroup` = 0 WHERE `ID` = 88703;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88703, `NextQuestID` = 88705, `ExclusiveGroup` = 0 WHERE `ID` = 88704;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88704, `NextQuestID` = 88706, `ExclusiveGroup` = 0 WHERE `ID` = 88705;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88705, `NextQuestID` = 94957, `ExclusiveGroup` = 0 WHERE `ID` = 88706;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88706, `NextQuestID` = 90690, `ExclusiveGroup` = 0 WHERE `ID` = 94957;
-- ===========================================================================
-- Max-level: The Voidspire (Wowhead 90690)
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(90690), (88709), (90724), (92520)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 94957, `NextQuestID` = 88709, `ExclusiveGroup` = 0 WHERE `ID` = 90690;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90690, `NextQuestID` = 90724, `ExclusiveGroup` = 0 WHERE `ID` = 88709;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88709, `NextQuestID` = 92520, `ExclusiveGroup` = 0 WHERE `ID` = 90724;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90724, `NextQuestID` = 88920, `ExclusiveGroup` = 0 WHERE `ID` = 92520;
-- Gathering of the Elves (88920-88941) is already chained in 2026_09_22_14.
-- After Voidspire it is the next max-level chapter.
UPDATE `quest_template_addon` SET `PrevQuestID` = 92520 WHERE `ID` = 88920;
UPDATE `quest_template_addon` SET `NextQuestID` = 90746 WHERE `ID` = 88941;
-- ===========================================================================
-- Max-level: The Battle of the Bridge / The Darkwell / Dawn of a New Well
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(90746), (88769), (90748), (88710), (92689), (90876), (90871), (90861),
(90862), (90867)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88941, `NextQuestID` = 88769, `ExclusiveGroup` = 0 WHERE `ID` = 90746;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90746, `NextQuestID` = 90748, `ExclusiveGroup` = 0 WHERE `ID` = 88769;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88769, `NextQuestID` = 88710, `ExclusiveGroup` = 0 WHERE `ID` = 90748;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90748, `NextQuestID` = 92689, `ExclusiveGroup` = 0 WHERE `ID` = 88710;
UPDATE `quest_template_addon` SET `PrevQuestID` = 88710, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 92689;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92689, `NextQuestID` = 90861, `ExclusiveGroup` = -90876 WHERE `ID` IN (90876, 90871);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 90862, `ExclusiveGroup` = 0 WHERE `ID` = 90861;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90861, `NextQuestID` = 90867, `ExclusiveGroup` = 0 WHERE `ID` = 90862;
UPDATE `quest_template_addon` SET `PrevQuestID` = 90862, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 90867;
-- ===========================================================================
-- Curse of Ula'tek: An Island of Fangs (Wowhead 98218). Follows Dead End 93012.
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(98218), (92916), (92917), (92919), (93265), (92921), (93263), (93266),
(92920), (92924), (95804), (93019), (95564), (93018), (93022), (93023),
(93024)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `NextQuestID` = 98218 WHERE `ID` = 93012;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93012, `NextQuestID` = 92916, `ExclusiveGroup` = 0 WHERE `ID` = 98218;
UPDATE `quest_template_addon` SET `PrevQuestID` = 98218, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 92916;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92916, `NextQuestID` = 93265, `ExclusiveGroup` = -92917 WHERE `ID` IN (92917, 92919);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92921, `ExclusiveGroup` = 0 WHERE `ID` = 93265;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93265, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 92921;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92921, `NextQuestID` = 92920, `ExclusiveGroup` = -93263 WHERE `ID` IN (93263, 93266);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92924, `ExclusiveGroup` = 0 WHERE `ID` = 92920;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92920, `NextQuestID` = 95804, `ExclusiveGroup` = 0 WHERE `ID` = 92924;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92924, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 95804;
UPDATE `quest_template_addon` SET `PrevQuestID` = 95804, `NextQuestID` = 0, `ExclusiveGroup` = -93019 WHERE `ID` IN (93019, 95564);
UPDATE `quest_template_addon` SET `PrevQuestID` = 93019, `NextQuestID` = 93023, `ExclusiveGroup` = -93018 WHERE `ID` IN (93018, 93022);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 93024, `ExclusiveGroup` = 0 WHERE `ID` = 93023;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93023, `NextQuestID` = 93454, `ExclusiveGroup` = 0 WHERE `ID` = 93024;
-- ===========================================================================
-- Curse of Ula'tek: Ghosts of the Past (Wowhead 93454)
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(93454), (92925), (92927), (92928), (92929), (92930)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93024, `NextQuestID` = 92925, `ExclusiveGroup` = 0 WHERE `ID` = 93454;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93454, `NextQuestID` = 92927, `ExclusiveGroup` = 0 WHERE `ID` = 92925;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92925, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 92927;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92927, `NextQuestID` = 92930, `ExclusiveGroup` = -92928 WHERE `ID` IN (92928, 92929);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92931, `ExclusiveGroup` = 0 WHERE `ID` = 92930;
-- ===========================================================================
-- Curse of Ula'tek: Original Sin (Wowhead 92931)
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(92931), (92932), (92933), (92938), (93063), (93064), (92934), (92935),
(92936), (92937)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92930, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 92931;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92931, `NextQuestID` = 0, `ExclusiveGroup` = -92932 WHERE `ID` IN (92932, 92933);
UPDATE `quest_template_addon` SET `PrevQuestID` = 92932, `NextQuestID` = 93064, `ExclusiveGroup` = -92938 WHERE `ID` IN (92938, 93063);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 93064;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93064, `NextQuestID` = 92936, `ExclusiveGroup` = -92934 WHERE `ID` IN (92934, 92935);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 92937, `ExclusiveGroup` = 0 WHERE `ID` = 92936;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92936, `NextQuestID` = 93417, `ExclusiveGroup` = 0 WHERE `ID` = 92937;
-- ===========================================================================
-- Curse of Ula'tek: The Battle for Atal'Utek (Wowhead 93417)
-- Extra Vaults helper steps on some wiki lists are not on the Wowhead series.
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(93417), (93419), (93418), (93420)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 92937, `NextQuestID` = 93419, `ExclusiveGroup` = 0 WHERE `ID` = 93417;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93417, `NextQuestID` = 93418, `ExclusiveGroup` = 0 WHERE `ID` = 93419;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93419, `NextQuestID` = 93420, `ExclusiveGroup` = 0 WHERE `ID` = 93418;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93418, `NextQuestID` = 95973, `ExclusiveGroup` = 0 WHERE `ID` = 93420;
-- ===========================================================================
-- Curse of Ula'tek: The Call of the Void (Wowhead 95973)
-- ===========================================================================
INSERT INTO `quest_template_addon` (`ID`) VALUES
(95973), (94519), (94520), (94521), (94522), (94523), (94524), (94525),
(94526), (94527), (94528), (94529), (94530), (94531)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 93420, `NextQuestID` = 94519, `ExclusiveGroup` = 0 WHERE `ID` = 95973;
UPDATE `quest_template_addon` SET `PrevQuestID` = 95973, `NextQuestID` = 94520, `ExclusiveGroup` = 0 WHERE `ID` = 94519;
UPDATE `quest_template_addon` SET `PrevQuestID` = 94519, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 94520;
UPDATE `quest_template_addon` SET `PrevQuestID` = 94520, `NextQuestID` = 0, `ExclusiveGroup` = -94521 WHERE `ID` IN (94521, 94522, 94523);
UPDATE `quest_template_addon` SET `PrevQuestID` = 94521, `NextQuestID` = 94526, `ExclusiveGroup` = -94524 WHERE `ID` IN (94524, 94525);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 94527, `ExclusiveGroup` = 0 WHERE `ID` = 94526;
UPDATE `quest_template_addon` SET `PrevQuestID` = 94526, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 94527;
UPDATE `quest_template_addon` SET `PrevQuestID` = 94527, `NextQuestID` = 94530, `ExclusiveGroup` = -94528 WHERE `ID` IN (94528, 94529);
UPDATE `quest_template_addon` SET `PrevQuestID` = 0, `NextQuestID` = 94531, `ExclusiveGroup` = 0 WHERE `ID` = 94530;
UPDATE `quest_template_addon` SET `PrevQuestID` = 94530, `NextQuestID` = 0, `ExclusiveGroup` = 0 WHERE `ID` = 94531;
-- ---------------------------------------------------------------------------
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_image_of_lady_liadrin_midnight' WHERE `entry` = 241677;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_liadrin_lights_last_stand' WHERE `entry` = 241575;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_arator_wayward_magister' WHERE `entry` = 236716;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_umbric_tranquillien' WHERE `entry` = 236087;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_lord_antenorian_tranquillien' WHERE `entry` = 245004;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_lorthemar_sanctum_of_light' WHERE `entry` IN (235787, 245704);
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_contact_silvermoon_midnight' WHERE `entry` IN (241050, 239685);
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_liadrin_gates_of_zulaman' WHERE `entry` = 236436;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_zuljarra_midnight' WHERE `entry` IN (236126, 240186, 240934);
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_zuljan_midnight' WHERE `entry` = 249211;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_orweyna_fairbreeze_midnight' WHERE `entry` = 236704;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_follow_orweyna_harandar' WHERE `entry` = 237209;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_kupaal_harandar' WHERE `entry` = 237866;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_halduron_harandar_council' WHERE `entry` = 237345;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_umbric_voidstorm' WHERE `entry` IN (235386, 235405);
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_lothraxion_voidstorm' WHERE `entry` IN (235606, 260236);
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_liadrin_feeding_the_flame' WHERE `entry` = 247414;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_umbric_clarity_of_purpose' WHERE `entry` = 242722;
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_alonsus_faol_arator' WHERE `entry` IN (237565, 240747);
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_arator_journey' WHERE `entry` IN (244644, 240267, 239827, 254884);
UPDATE `creature_template` SET `npcflag` = `npcflag` | 3, `ScriptName` = 'npc_shandris_children_of_the_stars' WHERE `entry` = 240334;
-- ---------------------------------------------------------------------------
-- Generic talk-to / stay-awhile / speak-with (Wowhead objective NPCs)
-- ---------------------------------------------------------------------------
UPDATE `creature_template` SET `npcflag` = `npcflag` | 1, `ScriptName` = 'npc_midnight_objective_talk' WHERE `entry` IN
(237211, 236657, 236789, 236959, 236964, 243091, 237343, 237865, 237210,
247408, 247410, 247411, 239405, 236149, 239457, 239406, 242568, 244840,
240582, 240777, 239630, 239673, 239639, 239664, 242435, 240936, 240940,
236583, 252821, 243882, 243958, 252348, 237857, 237837, 253343, 239623,
242608, 242606, 244867, 242614, 242613, 242607, 242609, 242615, 242611,
246863, 238157, 260106, 243760, 245886, 245697, 252294, 253508, 240335,
253613, 250853, 248629, 237483, 240539, 240928, 236903, 242748);
-- ---------------------------------------------------------------------------
-- Generic arrive / meet / reach / portal credits (Wowhead objective NPCs)
-- ---------------------------------------------------------------------------
UPDATE `creature_template` SET `ScriptName` = 'npc_midnight_objective_proximity' WHERE `entry` IN
(239653, 242375, 240573, 258837, 240587, 239527, 238613, 240048, 240448,
244433, 241042, 253687, 247020, 243995, 241610, 239973, 244261, 251871,
252596, 246476, 252309, 252347, 236140, 245606, 235395, 241126, 239600,
246899, 244383, 240555);
-- ---------------------------------------------------------------------------
-- Questgivers (Wowhead named NPCs on the chapter-start quests)
-- ---------------------------------------------------------------------------
DELETE FROM `creature_queststarter` WHERE
(`id` = 241677 AND `quest` IN (91281, 88719))
OR (`id` = 236704 AND `quest` IN (86899, 89402))
OR (`id` = 237565 AND `quest` IN (89193, 86837))
OR (`id` = 244644 AND `quest` = 86837)
OR (`id` = 235386 AND `quest` = 89388)
OR (`id` = 235405 AND `quest` = 86543)
OR (`id` = 236716 AND `quest` = 86621)
OR (`id` = 247414 AND `quest` = 90777)
OR (`id` = 236436 AND `quest` = 86708)
OR (`id` = 236126 AND `quest` = 86653)
OR (`id` = 240267 AND `quest` = 88920)
OR (`id` = 240747 AND `quest` = 86822)
OR (`id` = 235787 AND `quest` IN (86733, 86734));
INSERT INTO `creature_queststarter` (`id`, `quest`, `VerifiedBuild`) VALUES
(241677, 91281, 69875), -- Midnight offered by Image of Lady Liadrin
(241677, 88719, 69875), -- A Voice from the Light
(236704, 86899, 69875), -- The Root Cause offered by Orweyna
(236704, 89402, 69875), -- Harandar breadcrumb
(237565, 89193, 69875), -- Arator offered by Alonsus Faol
(237565, 86837, 69875), -- Meet at the Sunwell
(244644, 86837, 69875), -- Meet at the Sunwell (Arator)
(235386, 89388, 69875), -- Voidstorm offered by Umbric
(235405, 86543, 69875), -- Magisters' Terrace: Homecoming
(236716, 86621, 69875), -- The Wayward Magister offered by Arator
(247414, 90777, 69875), -- Feeding the Flame offered by Liadrin
(236436, 86708, 69875), -- The Gates of Zul'Aman offered by Liadrin
(236126, 86653, 69875), -- The Path of the Amani offered by Zul'jarra
(240267, 88920, 69875), -- The Kaldorei offered by Arator
(240747, 86822, 69875), -- One Final Relic offered by Alonsus Faol
(235787, 86733, 69875), -- Silvermoon Negotiations offered by Lor'themar
(235787, 86734, 69875); -- Diplomacy
DELETE FROM `creature_questender` WHERE
(`id` = 241677 AND `quest` = 91281)
OR (`id` = 236704 AND `quest` IN (89402, 86899))
OR (`id` = 237565 AND `quest` = 89193)
OR (`id` = 244644 AND `quest` = 86837)
OR (`id` = 235386 AND `quest` = 89388)
OR (`id` = 235405 AND `quest` = 86543)
OR (`id` = 236087 AND `quest` = 86621)
OR (`id` = 247414 AND `quest` = 90777)
OR (`id` = 236436 AND `quest` = 86708)
OR (`id` = 236126 AND `quest` = 86653)
OR (`id` = 240267 AND `quest` = 88920)
OR (`id` = 240747 AND `quest` = 86822)
OR (`id` = 235787 AND `quest` IN (86733, 86734, 86650));
INSERT INTO `creature_questender` (`id`, `quest`, `VerifiedBuild`) VALUES
(241677, 91281, 69875),
(236704, 89402, 69875),
(236704, 86899, 69875),
(237565, 89193, 69875),
(244644, 86837, 69875),
(235386, 89388, 69875),
(235405, 86543, 69875),
(236087, 86621, 69875),
(247414, 90777, 69875),
(236436, 86708, 69875),
(236126, 86653, 69875),
(240267, 88920, 69875),
(240747, 86822, 69875),
(235787, 86733, 69875),
(235787, 86734, 69875),
(235787, 86650, 69875);
@@ -0,0 +1,31 @@
UPDATE `creature_template` SET `npcflag` = `npcflag` | 2 WHERE `entry` IN (156280, 166573, 160664, 166583);
DELETE FROM `creature_queststarter` WHERE (`id` = 156280 AND `quest` IN (56775, 58208))
OR (`id` = 160664 AND `quest` = 58209)
OR (`id` = 166573 AND `quest` IN (59926, 59928))
OR (`id` = 166583 AND `quest` = 59927);
INSERT INTO `creature_queststarter` (`id`, `quest`, `VerifiedBuild`) VALUES
(156280, 56775, 69875),
(156280, 58208, 69875),
(160664, 58209, 69875),
(166573, 59926, 69875),
(166573, 59928, 69875),
(166583, 59927, 69875);
DELETE FROM `creature_questender` WHERE (`id` = 156280 AND `quest` IN (56775, 58208))
OR (`id` = 160664 AND `quest` = 58209)
OR (`id` = 166573 AND `quest` IN (59926, 59928))
OR (`id` = 166583 AND `quest` = 59927);
INSERT INTO `creature_questender` (`id`, `quest`, `VerifiedBuild`) VALUES
(156280, 56775, 69875),
(156280, 58208, 69875),
(160664, 58209, 69875),
(166573, 59926, 69875),
(166573, 59928, 69875),
(166583, 59927, 69875);
INSERT INTO `quest_template_addon` (`ID`) VALUES (56775), (59926)
ON DUPLICATE KEY UPDATE `ID` = `ID`;
UPDATE `quest_template_addon` SET `PrevQuestID` = 0 WHERE `ID` IN (56775, 59926);
+53
View File
@@ -18,6 +18,7 @@
#include "Random.h"
#include "Errors.h"
#include "SFMTRand.h"
#include <boost/math/tools/roots.hpp>
#include <memory>
#include <random>
@@ -89,3 +90,55 @@ uint32 urandweighted(size_t count, double const* chances)
std::discrete_distribution<uint32> dd(chances, chances + count);
return dd(engine);
}
namespace
{
struct PseudoRandomDistributionChanceTable : std::array<float, 10000>
{
PseudoRandomDistributionChanceTable()
{
(*this)[0] = 0.0f;
for (std::size_t i = 1; i < size(); ++i)
(*this)[i] = NormalizePseudoRandomDistributionChance(i * 0.0001f);
}
static float NormalizePseudoRandomDistributionChance(float chance)
{
std::uintptr_t iterationLimit = 10;
return boost::math::tools::newton_raphson_iterate([target = 1.0f / chance](float p) -> std::pair<float, float>
{
// (value, derivative) pairs
std::pair<float, float> chain = { 1.0f, 0.0f };
std::pair<float, float> result = { 1.0f - target, 0.0f };
uint32 cap = uint32(std::ceil(1.0f / p));
for (uint32 i = 1; i < cap; ++i)
{
float chanceToFail = 1 - p * i;
chain.second = chain.second * chanceToFail - chain.first * i;
chain.first = chain.first * chanceToFail;
result.first += chain.first;
result.second += chain.second;
}
return result;
}, chance * chance, 0.0f, chance, 6, iterationLimit);
}
} const ChanceCache;
}
bool roll_chance(float chance, PseudoRandomDistributionState& state)
{
std::ptrdiff_t chanceIndex = std::ptrdiff_t(std::round(chance * 100.0f));
if (chanceIndex < 0)
return false;
if (chanceIndex >= std::ssize(ChanceCache) || state.AccumulateChance(ChanceCache[chanceIndex]) > rand_norm())
{
state.Reset();
return true;
}
return false;
}
+13 -1
View File
@@ -84,4 +84,16 @@ public:
result_type operator()() const { return rand32(); }
};
#endif // Random_h__
struct PseudoRandomDistributionState
{
float AccumulateChance(float progress) { return _progress += progress; }
void Reset() { _progress = 0; }
private:
float _progress = 0.0f;
};
/* Return true if a random roll fits in the specified chance (range 0-100) using pseudo random distribution that minimizes long good/bad luck streaks. */
TC_COMMON_API bool roll_chance(float chance, PseudoRandomDistributionState& state);
#endif // TRINITYCORE_RANDOM_H
+504 -138
View File
@@ -17,17 +17,146 @@
#include "Crafting.h"
#include "Containers.h"
#include "Log.h"
#include "DB2Stores.h"
#include "DB2Structure.h"
#include "Item.h"
#include "ItemBonusMgr.h"
#include "ItemEnchantmentMgr.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "SharedDefines.h"
#include "Spell.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "CraftingPacketsCommon.h"
#include "Random.h"
#include "SpellPackets.h"
#include "Player.h"
#include "UpdateFields.h"
#include <cmath>
#include <unordered_set>
namespace
{
struct PendingBonusTree
{
uint32 TreeId = 0;
ItemContext Context = ItemContext::Trade_Skill;
bool ChoosesStats = false;
};
int32 ReagentItemId(WorldPackets::Spells::SpellCraftingReagent const& reagent)
{
if (reagent.Reagent.ItemID && *reagent.Reagent.ItemID > 0)
return *reagent.Reagent.ItemID;
return 0;
}
int32 ReagentCurrencyId(WorldPackets::Spells::SpellCraftingReagent const& reagent)
{
if (reagent.Reagent.CurrencyID && *reagent.Reagent.CurrencyID > 0)
return *reagent.Reagent.CurrencyID;
return 0;
}
ItemContext ContextForReagent(ModifiedCraftingReagentItemEntry const* reagentItem)
{
if (!reagentItem || reagentItem->ItemContextOffset <= 0)
return ItemContext::Trade_Skill;
uint32 shifted = uint32(ItemContext::Trade_Skill) + uint32(reagentItem->ItemContextOffset);
if (shifted > 255)
return ItemContext::Trade_Skill;
return ItemContext(shifted);
}
bool MatchItemReagent(uint32 itemId, std::vector<ModifiedCraftingCategoryEntry const*> const& categories, ModifiedCraftingReagentItemEntry const*& reagentItem, ModifiedCraftingCategoryEntry const*& category)
{
for (ModifiedCraftingCategoryEntry const* candidate : categories)
{
for (ModifiedCraftingReagentItemEntry const* candidateItem : sDB2Manager.GetMCRReagentItemByCategory(candidate->ID))
{
for (ItemSparseEntry const* sparse : sDB2Manager.GetItemSparseByMCRReagentItem(candidateItem->ID))
{
if (sparse->ID != itemId)
continue;
reagentItem = candidateItem;
category = candidate;
return true;
}
}
}
return false;
}
bool MatchCurrencyReagent(uint32 currencyId, std::vector<ModifiedCraftingCategoryEntry const*> const& categories, ModifiedCraftingCategoryEntry const*& category, CraftingReagentQualityEntry const*& quality)
{
for (ModifiedCraftingCategoryEntry const* candidate : categories)
{
for (CraftingReagentQualityEntry const* candidateQuality : sCraftingReagentQualityStore)
{
if (candidateQuality->CurrencyTypesID != int32(currencyId))
continue;
if (candidateQuality->ModifiedCraftingCategoryID != int32(candidate->ID))
continue;
category = candidate;
quality = candidateQuality;
return true;
}
}
return false;
}
struct CraftingQualityMatch
{
int32 CraftingQualityID = 0;
uint32 Tier = 0;
float NextPercentage = 0.0f;
};
CraftingQualityMatch MatchCraftingQuality(int32 craftingDifficultyID, float skillPercent)
{
CraftingQualityMatch match;
float bestPercentage = -1.0f;
bool foundNext = false;
for (CraftingDifficultyQualityEntry const* row : sCraftingDifficultyQualityStore)
{
if (row->CraftingDifficultyID != craftingDifficultyID)
continue;
if (row->QualityPercentage <= skillPercent && row->QualityPercentage >= bestPercentage)
{
bestPercentage = row->QualityPercentage;
match.CraftingQualityID = row->CraftingQualityID;
if (CraftingQualityEntry const* quality = sCraftingQualityStore.LookupEntry(row->CraftingQualityID))
match.Tier = uint32(std::max(0, quality->QualityTier));
else
match.Tier = 0;
}
if (row->QualityPercentage > skillPercent && (!foundNext || row->QualityPercentage < match.NextPercentage))
{
match.NextPercentage = row->QualityPercentage;
foundNext = true;
}
}
if (!foundNext)
match.NextPercentage = 0.0f;
return match;
}
}
SpellCastResult Crafting::DoCraft(uint32 craftingDataId)
{
@@ -37,193 +166,437 @@ SpellCastResult Crafting::DoCraft(uint32 craftingDataId)
if (!_craftingData || spellSlots.empty())
return SpellCastResult::SPELL_FAILED_ERROR;
std::unordered_map<uint32 /*itemId*/, uint32 /*quantity*/> usedReagentsToDestroy;
std::vector<uint32> bonusTreeIds;
std::unordered_map<uint32, uint32> usedItems;
std::unordered_map<uint32, uint32> usedCurrencies;
std::vector<PendingBonusTree> bonusTrees;
bool statsChosenByReagent = false;
float difficulty = float(_craftingData->CraftingDifficulty);
float totalWeight = 0.0f;
float weightedEffect = 0.0f;
if (_craftingData->ItemBonusTreeID)
bonusTreeIds.push_back(_craftingData->ItemBonusTreeID);
if (_craftingData->ItemBonusTreeID > 0)
bonusTrees.push_back({ uint32(_craftingData->ItemBonusTreeID), ItemContext::Trade_Skill, false });
uint32 craftingDifficulty = _craftingData->CraftingDifficulty;
// List all reagent "slots" for this craft, with ReagentSlotID & ReagentCount
for (ModifiedCraftingSpellSlotEntry const* spellSlot : spellSlots)
{
// If no ReagentCount used by spell why even bother ?
if (spellSlot->ReagentCount < 1)
int32 requiredRaw = _recraft ? spellSlot->ReagentReCraftCount : spellSlot->ReagentCount;
if (requiredRaw < 1)
continue;
uint32 requiredCount = uint32(requiredRaw);
ModifiedCraftingReagentSlotEntry const* reagentSlot = sModifiedCraftingReagentSlotStore.LookupEntry(spellSlot->ModifiedCraftingReagentSlotID);
uint32 reagentCountForSlot = 0;
if (!reagentSlot)
continue;
auto providedReagentItr = _spell->m_targets.ModifiedCraftingReagents.find(spellSlot->Slot);
if (providedReagentItr != _spell->m_targets.ModifiedCraftingReagents.end())
auto providedReagentItr = _spell->m_targets.ModifiedCraftingReagents.find(uint32(spellSlot->Slot));
if (providedReagentItr == _spell->m_targets.ModifiedCraftingReagents.end())
{
if (reagentSlot->ReagentType == MCR_REAGENT_TYPE_REQUIRED)
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
continue;
}
uint32 providedCount = 0;
std::unordered_map<uint32, uint32> slotItems;
std::unordered_map<uint32, uint32> slotCurrencies;
for (WorldPackets::Spells::SpellCraftingReagent const& provided : providedReagentItr->second)
{
if (provided.Quantity <= 0)
return SpellCastResult::SPELL_FAILED_ERROR;
if (int32 itemId = ReagentItemId(provided))
{
slotItems[uint32(itemId)] += uint32(provided.Quantity);
if (!_player->HasItemCount(uint32(itemId), usedItems[uint32(itemId)] + slotItems[uint32(itemId)]))
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
providedCount += uint32(provided.Quantity);
}
else if (int32 currencyId = ReagentCurrencyId(provided))
{
slotCurrencies[uint32(currencyId)] += uint32(provided.Quantity);
if (!_player->HasCurrency(uint32(currencyId), usedCurrencies[uint32(currencyId)] + slotCurrencies[uint32(currencyId)]))
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
providedCount += uint32(provided.Quantity);
}
else
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
}
if (providedCount < requiredCount)
{
for (WorldPackets::Spells::SpellCraftingReagent const& providedReagentForSlot : providedReagentItr->second)
{
if (_player->HasItemCount(providedReagentForSlot.Slot, providedReagentForSlot.Quantity))
{
reagentCountForSlot += providedReagentForSlot.Quantity;
usedReagentsToDestroy[providedReagentForSlot.Slot] = providedReagentForSlot.Quantity;
if (CraftingReagentQualityEntry const* reagentQuality = sDB2Manager.GetCraftingReagentQualityByItem(providedReagentForSlot.Slot))
craftingDifficulty += reagentQuality->MaxDifficultyAdjustment;
}
}
}
if (reagentCountForSlot < uint32(spellSlot->ReagentCount))
{
// If we don't have enough Reagent for this slot and it is required, craft failed
if (reagentSlot->ReagentType == MCR_REAGENT_TYPE_REQUIRED)
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
// Still not enough but the reagent is optional, just skip
continue;
}
std::vector<ModifiedCraftingCategoryEntry const*> categories = sDB2Manager.GetMCRCategoryByReagentSlot(spellSlot->ModifiedCraftingReagentSlotID);
bool hasOneMatchingItem = false;
bool slotWeightAdded = false;
for (ModifiedCraftingCategoryEntry const* category : categories)
for (WorldPackets::Spells::SpellCraftingReagent const& provided : providedReagentItr->second)
{
std::vector<ModifiedCraftingReagentItemEntry const*> reagentItems = sDB2Manager.GetMCRReagentItemByCategory(category->ID);
ModifiedCraftingReagentItemEntry const* reagentItem = nullptr;
ModifiedCraftingCategoryEntry const* category = nullptr;
CraftingReagentQualityEntry const* quality = nullptr;
uint32 quantity = uint32(provided.Quantity);
for (ModifiedCraftingReagentItemEntry const* reagentItem : reagentItems)
if (int32 itemId = ReagentItemId(provided))
{
std::set<ItemSparseEntry const*> requiredItems = sDB2Manager.GetItemSparseByMCRReagentItem(reagentItem->ID);
if (!MatchItemReagent(uint32(itemId), categories, reagentItem, category))
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
for (WorldPackets::Spells::SpellCraftingReagent const& providedReagent : providedReagentItr->second)
quality = sDB2Manager.GetCraftingReagentQualityByItem(uint32(itemId));
usedItems[uint32(itemId)] += quantity;
}
else if (int32 currencyId = ReagentCurrencyId(provided))
{
if (!MatchCurrencyReagent(uint32(currencyId), categories, category, quality))
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
usedCurrencies[uint32(currencyId)] += quantity;
}
if (quality)
difficulty += quality->MaxDifficultyAdjustment * float(quantity) / float(requiredCount);
if (category)
{
if (!slotWeightAdded)
{
bool hasItem = std::count_if(std::cbegin(requiredItems), std::cend(requiredItems), [providedReagent](ItemSparseEntry const* itemSparse) {
return itemSparse->ID == uint32(providedReagent.Slot);
});
if (hasItem)
bonusTreeIds.push_back(reagentItem->ItemBonusTreeID);
hasOneMatchingItem = hasOneMatchingItem || hasItem;
totalWeight += category->MatQualityWeight * float(requiredCount);
slotWeightAdded = true;
}
float effect = quality ? quality->ReagentEffectPct / 100.0f : 0.0f;
weightedEffect += category->MatQualityWeight * float(quantity) * effect;
}
if (reagentItem)
{
if (reagentItem->ItemBonusTreeID > 0)
bonusTrees.push_back({ uint32(reagentItem->ItemBonusTreeID), ContextForReagent(reagentItem), (reagentItem->Flags & 1) != 0 });
if (reagentItem->Flags & 1)
statsChosenByReagent = true;
}
}
// If any item provided do not match, probably a cheater, return
if (!hasOneMatchingItem)
return SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS;
}
uint32 skillLevel = GetSkillLevelForCraft();
uint32 difficultyPercentMatched = round(float(skillLevel) / float(craftingDifficulty) * 100.f);
uint32 skillLevel = _player->GetSkillValue(GetSkillIdForSpell(_spell->GetSpellInfo()->Id));
uint32 reagentSkill = 0;
float baseDifficulty = float(_craftingData->CraftingDifficulty);
CraftingDifficultyEntry const* craftingDifficultyEntry = sCraftingDifficultyStore.LookupEntry(_craftingData->CraftingDifficultyID);
if (craftingDifficultyEntry)
{
float bonusPercent = _recraft ? craftingDifficultyEntry->ReCraftSkillBonusPercent : craftingDifficultyEntry->CraftSkillBonusPercent;
if (totalWeight > 0.0f && bonusPercent > 0.0f)
{
float maximumReagentBonus = std::round(baseDifficulty * bonusPercent / 100.0f);
reagentSkill = uint32(std::max(0L, std::lround(maximumReagentBonus * weightedEffect / totalWeight)));
skillLevel += reagentSkill;
}
}
uint32 qualityTier = sDB2Manager.GetCraftingQualityTierByDifficultyPercent(_craftingData->CraftingDifficultyID, difficultyPercentMatched);
SkillLineAbilityEntry const* skillAbility = nullptr;
SkillLineAbilityMapBounds abilityBounds = sSpellMgr->GetSkillLineAbilityMapBounds(_spell->GetSpellInfo()->Id);
for (SkillLineAbilityMap::const_iterator ability = abilityBounds.first; ability != abilityBounds.second; ++ability)
{
if (!skillAbility)
skillAbility = ability->second;
if (ability->second->SkillupSkillLineID)
{
skillAbility = ability->second;
break;
}
}
ProfessionEffectTotals traitEffects;
auto visitSkillLineChain = [](uint32 skillLineId, auto&& visitor)
{
for (uint32 step = 0; skillLineId && step < 8; ++step)
{
if (visitor(skillLineId))
return true;
SkillLineEntry const* skillLine = sSkillLineStore.LookupEntry(skillLineId);
uint32 parent = skillLine ? skillLine->ParentSkillLineID : 0;
if (!parent || parent == skillLineId)
break;
skillLineId = parent;
}
return false;
};
if (skillAbility)
{
std::unordered_set<uint32> skillLines;
auto addChain = [&](uint32 skillLineId)
{
visitSkillLineChain(skillLineId, [&](uint32 line)
{
return !skillLines.insert(line).second;
});
};
addChain(skillAbility->SkillLine);
addChain(skillAbility->SkillupSkillLineID);
auto applyTraitEntry = [&](UF::TraitEntry const& entry)
{
int32 rank = entry.Rank + entry.GrantedRanks + entry.BonusRanks;
if (rank <= 0 || entry.TraitNodeEntryID <= 0)
return;
TraitNodeEntryEntry const* nodeEntry = sTraitNodeEntryStore.LookupEntry(entry.TraitNodeEntryID);
if (!nodeEntry || nodeEntry->TraitDefinitionID <= 0)
return;
sDB2Manager.AccumulateProfessionTraitEffects(nodeEntry->TraitDefinitionID, rank, traitEffects);
};
for (auto const& [_, traitConfig] : _player->m_activePlayerData->TraitConfigs)
{
if (TraitConfigType(*traitConfig.value.Type) != TraitConfigType::Profession)
continue;
int32 configSkillLine = traitConfig.value.SkillLineID;
if (configSkillLine <= 0 || !skillLines.contains(uint32(configSkillLine)))
continue;
for (UF::TraitEntry const& entry : traitConfig.value.Entries)
applyTraitEntry(entry);
for (UF::TraitSubTreeCache const& subTree : traitConfig.value.SubTrees)
for (UF::TraitEntry const& entry : subTree.Entries)
applyTraitEntry(entry);
}
}
if (traitEffects.Skill > 0.0f)
skillLevel += uint32(std::lround(traitEffects.Skill));
difficulty += traitEffects.DifficultyDelta;
if (difficulty < 1.0f)
difficulty = 1.0f;
// Client profession UI scale shared by Dragonflight, The War Within and Midnight: 1100 rating is 100%.
// This is not a spell id.
constexpr float ProfessionRatingScale = 1100.0f;
bool inspired = false;
float critBonusSkill = 0.0f;
if (traitEffects.InspirationRating > 0.0f && roll_chance(traitEffects.InspirationRating / ProfessionRatingScale * 100.0f))
{
inspired = true;
float bonusPercent = _craftingData->InspirationSkillBonusPercent;
if (bonusPercent <= 0.0f && craftingDifficultyEntry)
bonusPercent = craftingDifficultyEntry->InspirationSkillBonusPercent;
critBonusSkill = difficulty * bonusPercent / 100.0f + traitEffects.InspirationBonusSkill;
if (critBonusSkill > 0.0f)
skillLevel += uint32(std::lround(critBonusSkill));
}
auto skillPercent = [&]()
{
return float(skillLevel) / difficulty * 100.0f;
};
uint32 concentrationCurrency = 0;
if (skillAbility)
{
auto findCurrency = [&](uint32 skillLineId)
{
return visitSkillLineChain(skillLineId, [&](uint32 line)
{
concentrationCurrency = sDB2Manager.GetConcentrationCurrencyForSkillLine(line);
return concentrationCurrency != 0;
});
};
if (!findCurrency(skillAbility->SkillLine))
findCurrency(skillAbility->SkillupSkillLineID);
}
int32 concentrationSpent = 0;
bool concentrationApplied = false;
CraftingQualityMatch quality = MatchCraftingQuality(_craftingData->CraftingDifficultyID, skillPercent());
if (_spell->m_targets.ApplyConcentration && quality.NextPercentage > skillPercent())
{
bool haveCurves = craftingDifficultyEntry
&& craftingDifficultyEntry->ConcentrationSkillCurveID > 0
&& craftingDifficultyEntry->ConcentrationDifficultyCurveID > 0;
float skillFactor = 0.0f;
float difficultyFactor = 0.0f;
if (haveCurves)
{
skillFactor = sDB2Manager.GetCurveValueAt(uint32(craftingDifficultyEntry->ConcentrationSkillCurveID), skillPercent());
difficultyFactor = sDB2Manager.GetCurveValueAt(uint32(craftingDifficultyEntry->ConcentrationDifficultyCurveID), difficulty);
}
float reduction = traitEffects.ConcentrationCostReductionPercent;
if (reduction < 0.0f)
reduction = 0.0f;
else if (reduction > 100.0f)
reduction = 100.0f;
int32 cost = 0;
if (skillFactor > 0.0f && difficultyFactor > 0.0f)
cost = int32(std::lround(skillFactor * difficultyFactor * (1.0f - reduction / 100.0f)));
if (!haveCurves || !concentrationCurrency || cost <= 0)
{
TC_LOG_DEBUG("spells", "Spell {} crafted without concentration because the profession currency or cost curve is missing.", _spell->GetSpellInfo()->Id);
}
else
{
uint32 reserved = 0;
if (auto reservedCurrency = usedCurrencies.find(concentrationCurrency); reservedCurrency != usedCurrencies.end())
reserved = reservedCurrency->second;
if (!_player->HasCurrency(concentrationCurrency, reserved + uint32(cost)))
return SpellCastResult::SPELL_FAILED_NOT_ENOUGH_CURRENCY;
skillLevel = std::max(skillLevel, uint32(std::ceil(difficulty * quality.NextPercentage / 100.0f)));
concentrationSpent = cost;
concentrationApplied = true;
quality = MatchCraftingQuality(_craftingData->CraftingDifficultyID, skillPercent());
}
}
uint32 qualityTier = quality.Tier;
uint32 craftedItemId = GetCraftedItemIdForQuality(qualityTier);
// If we didn't found anything, we have a problem. Return and don't take reagent from player.
if (!craftedItemId)
return SpellCastResult::SPELL_FAILED_ERROR;
ItemContext context = _spell->GetSpellInfo()->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) ? ItemContext::Trade_Skill : ItemContext::NONE;
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(craftedItemId);
std::vector<int32> bonusLists = ItemBonusMgr::GetBonusListsForItem(craftedItemId, context, _player);
std::unordered_set<int32> seenBonuses(bonusLists.begin(), bonusLists.end());
std::vector<int32> bonusLists;
for (PendingBonusTree const& tree : bonusTrees)
{
std::vector<int32> contextBonusLists = ItemBonusMgr::GetBonusListsForItem(craftedItemId, context, _player);
bonusLists.insert(std::end(bonusLists), std::begin(contextBonusLists), std::end(contextBonusLists));
for (int32 bonusListId : ItemBonusMgr::GetBonusListsForTree(tree.TreeId, itemTemplate, { tree.Context }, _player))
{
if (seenBonuses.insert(bonusListId).second)
bonusLists.push_back(bonusListId);
}
if (tree.ChoosesStats)
statsChosenByReagent = true;
}
for (int32 bonusTreeID : bonusTreeIds)
uint32 craftCount = 1;
uint32 multicraftExtra = 0;
if (itemTemplate && itemTemplate->GetMaxStackSize() > 1 && traitEffects.MulticraftRating > 0.0f
&& roll_chance(traitEffects.MulticraftRating / ProfessionRatingScale * 100.0f))
{
std::vector<int32> treeBonusList = ItemBonusMgr::GetAllBonusListsForTree(bonusTreeID);
bonusLists.insert(std::end(bonusLists), std::begin(treeBonusList), std::end(treeBonusList));
float minExtra = 0.0f;
float maxExtra = 0.0f;
bool haveRange = false;
if (skillAbility)
{
auto findRange = [&](uint32 skillLineId)
{
return visitSkillLineChain(skillLineId, [&](uint32 line)
{
haveRange = sDB2Manager.GetMulticraftExtraQuantityRange(line, minExtra, maxExtra);
return haveRange;
});
};
if (!findRange(skillAbility->SkillLine))
findRange(skillAbility->SkillupSkillLineID);
}
if (haveRange)
{
maxExtra += traitEffects.MulticraftExtraPercent;
if (maxExtra < minExtra)
maxExtra = minExtra;
float rolled = frand(minExtra, maxExtra);
multicraftExtra = std::max<uint32>(1, uint32(std::lround(float(craftCount) * rolled / 100.0f)));
}
else
multicraftExtra = 1;
}
uint32 totalCount = craftCount + multicraftExtra;
ItemPosCountVec dest;
uint32 no_space = 0;
InventoryResult msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, craftedItemId, 1, &no_space);
uint32 noSpace = 0;
InventoryResult msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, craftedItemId, totalCount, &noSpace);
if (msg != EQUIP_ERR_OK)
{
_player->SendEquipError(msg, nullptr, nullptr, craftedItemId);
return SpellCastResult::SPELL_FAILED_DONT_REPORT;
}
if (Item* createdItem = _player->StoreNewItem(dest, craftedItemId, true, GenerateItemRandomBonusListId(craftedItemId), GuidSet(), context, &bonusLists))
{
InitCraftingStatModifier(createdItem);
Item* createdItem = _player->StoreNewItem(dest, craftedItemId, true, GenerateItemRandomBonusListId(craftedItemId), GuidSet(), context, &bonusLists);
if (!createdItem)
return SpellCastResult::SPELL_FAILED_ERROR;
if (createdItem->GetTemplate()->HasSignature())
createdItem->SetCreator(_player->GetGUID());
InitCraftingStatModifier(createdItem, statsChosenByReagent);
_player->SendNewItem(createdItem, 1, true, true);
}
if (createdItem->GetTemplate()->HasSignature())
createdItem->SetCreator(_player->GetGUID());
// Item has been crafted, remove reagent from player
for (auto const& [itemId, itemCount] : usedReagentsToDestroy)
if (concentrationApplied)
usedCurrencies[concentrationCurrency] += uint32(concentrationSpent);
for (auto const& [itemId, itemCount] : usedItems)
_player->DestroyItemCount(itemId, itemCount, true);
for (auto const& [currencyId, currencyCount] : usedCurrencies)
_player->ModifyCurrency(currencyId, -int32(currencyCount), CurrencyGainSource::Spell, CurrencyDestroyReason::Spell);
float progress = float(skillLevel) / difficulty;
if (progress < 0.0f)
progress = 0.0f;
else if (progress > 1.0f)
progress = 1.0f;
WorldPackets::Crafting::CraftingData craftingData;
craftingData.CraftingQualityID = quality.CraftingQualityID;
craftingData.QualityProgress = progress;
craftingData.SkillLineAbilityID = skillAbility ? int32(skillAbility->ID) : 0;
craftingData.CraftingDataID = int32(_craftingData->ID);
craftingData.Multicraft = int32(multicraftExtra);
craftingData.SkillFromReagents = int32(reagentSkill);
craftingData.Skill = int32(skillLevel);
craftingData.CritBonusSkill = inspired ? int32(std::lround(critBonusSkill)) : 0;
craftingData.IsCrit = inspired;
craftingData.IsRecraft = _recraft;
craftingData.ItemGUID = createdItem->GetGUID();
craftingData.Quantity = int32(totalCount);
craftingData.NewItem.Initialize(createdItem);
craftingData.ConcentrationCurrencyID = concentrationApplied ? int32(concentrationCurrency) : 0;
craftingData.ConcentrationSpent = concentrationSpent;
craftingData.ApplyConcentration = concentrationApplied;
_player->SendNewItem(createdItem, totalCount, true, true, false, 0, &craftingData);
_player->UpdateCraftSkill(_spell->GetSpellInfo());
return SpellCastResult::SPELL_CAST_OK;
}
uint32 Crafting::GetSkillLevelForCraft()
{
uint32 skillId = GetSkillIdForSpell(_spell->GetSpellInfo()->Id);
uint32 skillValue = _player->GetSkillValue(skillId);
uint32 totalReagentWeigth = CalculateTotalReagentWeights();
CraftingDifficultyEntry const* craftingDifficultyEntry = sCraftingDifficultyStore.LookupEntry(_craftingData->CraftingDifficultyID);
if (!craftingDifficultyEntry)
return skillValue;
float maximumReagentBonus = round(float(_craftingData->CraftingDifficulty) * float(craftingDifficultyEntry->CraftSkillBonusPercent) / 100.f);
for (ModifiedCraftingSpellSlotEntry const* spellSlot : sDB2Manager.GetMCRSpellSlotBySpell(_spell->GetSpellInfo()->Id))
{
for (ModifiedCraftingCategoryEntry const* category : sDB2Manager.GetMCRCategoryByReagentSlot(spellSlot->ModifiedCraftingReagentSlotID))
{
auto providedReagentItr = _spell->m_targets.ModifiedCraftingReagents.find(spellSlot->Slot);
if (providedReagentItr != _spell->m_targets.ModifiedCraftingReagents.end())
{
for (WorldPackets::Spells::SpellCraftingReagent const& providedReagentForSlot : providedReagentItr->second)
{
if (CraftingReagentQualityEntry const* reagentQuality = sDB2Manager.GetCraftingReagentQualityByItem(providedReagentForSlot.Slot))
{
float reagentWeight = float(category->MatQualityWeight) * float(providedReagentForSlot.Quantity) / 2.0f * float(reagentQuality->OrderIndex);
uint32 addedSkillByReagent = round(maximumReagentBonus * reagentWeight / float(totalReagentWeigth));
skillValue += addedSkillByReagent;
}
}
}
}
}
return skillValue;
}
uint32 Crafting::GetSkillIdForSpell(uint32 spellId)
uint32 Crafting::GetSkillIdForSpell(uint32 spellId) const
{
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
if (_spell_idx->second->SkillupSkillLineID)
return _spell_idx->second->SkillupSkillLineID;
for (SkillLineAbilityMap::const_iterator spellIndex = bounds.first; spellIndex != bounds.second; ++spellIndex)
if (spellIndex->second->SkillupSkillLineID)
return spellIndex->second->SkillupSkillLineID;
return 0;
}
uint32 Crafting::CalculateTotalReagentWeights()
void Crafting::InitCraftingStatModifier(Item* item, bool statsChosenByReagent)
{
uint32 totalWeight = 0;
if (statsChosenByReagent)
return;
for (ModifiedCraftingSpellSlotEntry const* spellSlot : sDB2Manager.GetMCRSpellSlotBySpell(_spell->GetSpellInfo()->Id))
for (ModifiedCraftingCategoryEntry const* category : sDB2Manager.GetMCRCategoryByReagentSlot(spellSlot->ModifiedCraftingReagentSlotID))
totalWeight += category->MatQualityWeight * spellSlot->ReagentCount;
return totalWeight;
}
// Todo : We currently only set random stats, but crafter
// can define stats using a "Missive" item
void Crafting::InitCraftingStatModifier(Item* item)
{
std::vector<uint32> allowedStats = {
ITEM_MOD_MASTERY_RATING,
ITEM_MOD_HASTE_RATING,
@@ -242,20 +615,13 @@ void Crafting::InitCraftingStatModifier(Item* item)
}
}
uint32 Crafting::GetCraftedItemIdForQuality(uint32 qualityTier)
uint32 Crafting::GetCraftedItemIdForQuality(uint32 qualityTier) const
{
// If this is a simple item (no quality), we have the id
uint32 newItemId = _craftingData->CraftedItemID;
uint32 craftedItemId = _craftingData->CraftedItemID > 0 ? uint32(_craftingData->CraftedItemID) : 0;
std::vector<uint32> craftedItemList = sDB2Manager.GetCraftingDataItemIDByCraftingData(_craftingData->ID);
if (craftedItemList.empty() || qualityTier == 0)
return craftedItemId;
// If it's a reagent with quality, get id from CraftingDataItem
if (!newItemId)
{
std::vector<uint32> craftedItemList = sDB2Manager.GetCraftingDataItemIDByCraftingData(_craftingData->ID);
if (craftedItemList.size() < qualityTier)
return 0;
newItemId = craftedItemList[qualityTier - 1];
}
return newItemId;
uint32 index = std::min(qualityTier, uint32(craftedItemList.size())) - 1;
return craftedItemList[index] ? craftedItemList[index] : craftedItemId;
}
+8 -8
View File
@@ -31,22 +31,22 @@ enum SpellCastResult : int32;
class TC_GAME_API Crafting
{
public:
Crafting(Player* player, Spell* spell) :
_player(player), _spell(spell), _craftingData(nullptr) { }
Crafting(Player* player, Spell* spell, bool recraft = false) :
_player(player), _spell(spell), _craftingData(nullptr), _recraft(recraft) {
}
SpellCastResult DoCraft(uint32 craftingDataId);
SpellCastResult DoCraft(uint32 craftingDataId);
private:
uint32 GetSkillLevelForCraft();
uint32 GetSkillIdForSpell(uint32 spellId);
uint32 CalculateTotalReagentWeights();
void InitCraftingStatModifier(Item* item);
uint32 GetCraftedItemIdForQuality(uint32 qualityTier);
uint32 GetSkillIdForSpell(uint32 spellId) const;
uint32 GetCraftedItemIdForQuality(uint32 qualityTier) const;
void InitCraftingStatModifier(Item* item, bool statsChosenByReagent);
Player* _player;
Spell* _spell;
CraftingDataEntry const* _craftingData;
bool _recraft;
};
#endif
+462 -6
View File
@@ -37,6 +37,12 @@
#include <numeric>
#include <cctype>
#include <cmath>
#include <limits>
//WowCommunity
#include "DB2FileSystemSource.h"
#include <cstring>
#include <type_traits>
//WowCommunity
DB2Storage<AchievementEntry> sAchievementStore("Achievement.db2", &AchievementLoadInfo::Instance);
DB2Storage<Achievement_CategoryEntry> sAchievementCategoryStore("Achievement_Category.db2", &AchievementCategoryLoadInfo::Instance);
@@ -726,6 +732,322 @@ namespace
std::unordered_map<uint32, std::set<ItemSparseEntry const*>> _ItemSparseByMCRReagentItem;
std::unordered_map<uint32, std::vector<uint32>> _CraftingDataItemIDByCraftingData;
std::unordered_map<uint32, CraftingReagentQualityEntry const*> _CraftingReagentQualityByItem;
//WowCommunity
struct ProfessionEffectValue
{
int32 Type = 0;
float Amount = 0.0f;
};
std::unordered_map<uint32, std::vector<ProfessionEffectValue>> _professionEffectsByTraitDefinition;
std::unordered_map<uint32, uint32> _concentrationCurrencyBySkillLine;
std::unordered_map<uint32, std::pair<float, float>> _multicraftExtraPercentBySkillLine;
// Client files only. These tables have no hotfix statement, so a missing or
// mismatched db2 must not be added to the fatal load error list.
void LoadProfessionProcTables(std::string const& path)
{
#pragma pack(push, 1)
struct ProfessionRecord
{
uint32 ID;
int32 SkillLineID;
int32 ProfessionEnumValue;
float ResourcefulEffectLow;
float ResourcefulEffectHigh;
float MinCraftExtraQtyPct;
float MaxCraftExtraQtyPct;
float GatherTimeIncreasePct;
int32 ActionTypeEnumValue;
float RecraftModSkillGainPct;
int32 PublicOrderCapacityCurrencyID;
};
struct ProfessionEffectRecord
{
uint32 ID;
int32 ProfessionEffectTypeEnumID;
float Amount;
int32 ModifiedCraftingReagentSlotID;
};
struct ProfessionTraitRecord
{
uint32 ID;
int32 TraitDefinitionID;
};
struct ProfessionTraitXEffectRecord
{
uint32 ID;
int32 ProfessionTraitID;
int32 ProfessionEffectID;
int32 Unused;
};
struct ProfessionExpansionRecord
{
uint32 ID;
int32 SkillLineID;
int32 RecraftSpellID;
int32 ConcentrationCurrencyID;
int32 ExpansionID;
int32 SpecResetSpellID;
int32 SpecResetCurrencyID;
int32 ProfessionID;
};
#pragma pack(pop)
static_assert(sizeof(ProfessionRecord) == 44);
static_assert(sizeof(ProfessionEffectRecord) == 16);
static_assert(sizeof(ProfessionTraitRecord) == 8);
static_assert(sizeof(ProfessionTraitXEffectRecord) == 16);
static_assert(sizeof(ProfessionExpansionRecord) == 32);
// Layout hashes are the 12.1.0.69875 WoWDBDefs LAYOUT ids.
static const DB2MetaField ProfessionFields[11] =
{
{.Type = FT_INT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_FLOAT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_FLOAT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_FLOAT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_FLOAT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_FLOAT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_FLOAT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
};
static const DB2Meta ProfessionMeta =
{
.FileDataId = 4508544,
.IndexField = 0,
.ParentIndexField = -1,
.FieldCount = 11,
.FileFieldCount = 11,
.LayoutHash = 0x86FE926D,
.Fields = ProfessionFields
};
static const DB2FieldMeta ProfessionLoadFields[11] =
{
{.IsSigned = false, .Type = FT_INT, .Name = "ID" },
{.IsSigned = true, .Type = FT_INT, .Name = "SkillLineID" },
{.IsSigned = true, .Type = FT_INT, .Name = "ProfessionEnumValue" },
{.IsSigned = false, .Type = FT_FLOAT, .Name = "ResourcefulEffectLow" },
{.IsSigned = false, .Type = FT_FLOAT, .Name = "ResourcefulEffectHigh" },
{.IsSigned = false, .Type = FT_FLOAT, .Name = "MinCraftExtraQtyPct" },
{.IsSigned = false, .Type = FT_FLOAT, .Name = "MaxCraftExtraQtyPct" },
{.IsSigned = false, .Type = FT_FLOAT, .Name = "GatherTimeIncreasePct" },
{.IsSigned = true, .Type = FT_INT, .Name = "ActionTypeEnumValue" },
{.IsSigned = false, .Type = FT_FLOAT, .Name = "RecraftModSkillGainPct" },
{.IsSigned = true, .Type = FT_INT, .Name = "PublicOrderCapacityCurrencyID" },
};
static const DB2FileLoadInfo ProfessionLoadInfo{ ProfessionLoadFields, 11, &ProfessionMeta };
static const DB2MetaField ProfessionEffectFields[3] =
{
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_FLOAT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
};
static const DB2Meta ProfessionEffectMeta =
{
.FileDataId = 4505297,
.IndexField = -1,
.ParentIndexField = -1,
.FieldCount = 3,
.FileFieldCount = 3,
.LayoutHash = 0x04C16FA9,
.Fields = ProfessionEffectFields
};
static const DB2FieldMeta ProfessionEffectLoadFields[4] =
{
{.IsSigned = false, .Type = FT_INT, .Name = "ID" },
{.IsSigned = true, .Type = FT_INT, .Name = "ProfessionEffectTypeEnumID" },
{.IsSigned = false, .Type = FT_FLOAT, .Name = "Amount" },
{.IsSigned = true, .Type = FT_INT, .Name = "ModifiedCraftingReagentSlotID" },
};
static const DB2FileLoadInfo ProfessionEffectLoadInfo{ ProfessionEffectLoadFields, 4, &ProfessionEffectMeta };
static const DB2MetaField ProfessionTraitFields[2] =
{
{.Type = FT_INT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
};
static const DB2Meta ProfessionTraitMeta =
{
.FileDataId = 4505298,
.IndexField = 0,
.ParentIndexField = -1,
.FieldCount = 2,
.FileFieldCount = 2,
.LayoutHash = 0x306455FE,
.Fields = ProfessionTraitFields
};
static const DB2FieldMeta ProfessionTraitLoadFields[2] =
{
{.IsSigned = false, .Type = FT_INT, .Name = "ID" },
{.IsSigned = true, .Type = FT_INT, .Name = "TraitDefinitionID" },
};
static const DB2FileLoadInfo ProfessionTraitLoadInfo{ ProfessionTraitLoadFields, 2, &ProfessionTraitMeta };
static const DB2MetaField ProfessionTraitXEffectFields[4] =
{
{.Type = FT_INT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
};
static const DB2Meta ProfessionTraitXEffectMeta =
{
.FileDataId = 4505494,
.IndexField = 0,
.ParentIndexField = 1,
.FieldCount = 4,
.FileFieldCount = 4,
.LayoutHash = 0x97A0A286,
.Fields = ProfessionTraitXEffectFields
};
static const DB2FieldMeta ProfessionTraitXEffectLoadFields[4] =
{
{.IsSigned = false, .Type = FT_INT, .Name = "ID" },
{.IsSigned = true, .Type = FT_INT, .Name = "ProfessionTraitID" },
{.IsSigned = true, .Type = FT_INT, .Name = "ProfessionEffectID" },
{.IsSigned = true, .Type = FT_INT, .Name = "Field_10_0_0_44649_003" },
};
static const DB2FileLoadInfo ProfessionTraitXEffectLoadInfo{ ProfessionTraitXEffectLoadFields, 4, &ProfessionTraitXEffectMeta };
static const DB2MetaField ProfessionExpansionFields[8] =
{
{.Type = FT_INT, .ArraySize = 1, .IsSigned = false },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
{.Type = FT_INT, .ArraySize = 1, .IsSigned = true },
};
static const DB2Meta ProfessionExpansionMeta =
{
.FileDataId = 5662322,
.IndexField = 0,
.ParentIndexField = 7,
.FieldCount = 8,
.FileFieldCount = 7,
.LayoutHash = 0x6DABCB98,
.Fields = ProfessionExpansionFields
};
static const DB2FieldMeta ProfessionExpansionLoadFields[8] =
{
{.IsSigned = false, .Type = FT_INT, .Name = "ID" },
{.IsSigned = true, .Type = FT_INT, .Name = "SkillLineID" },
{.IsSigned = true, .Type = FT_INT, .Name = "RecraftSpellID" },
{.IsSigned = true, .Type = FT_INT, .Name = "ConcentrationCurrencyID" },
{.IsSigned = true, .Type = FT_INT, .Name = "ExpansionID" },
{.IsSigned = true, .Type = FT_INT, .Name = "SpecResetSpellID" },
{.IsSigned = true, .Type = FT_INT, .Name = "SpecResetCurrencyID" },
{.IsSigned = true, .Type = FT_INT, .Name = "ProfessionID" },
};
static const DB2FileLoadInfo ProfessionExpansionLoadInfo{ ProfessionExpansionLoadFields, 8, &ProfessionExpansionMeta };
auto loadRecords = [&](char const* fileName, DB2FileLoadInfo const* loadInfo, auto& out)
{
using Record = typename std::decay_t<decltype(out)>::value_type;
try
{
DB2FileLoader loader;
DB2FileSystemSource source(path + fileName);
loader.Load(&source, loadInfo);
uint32 indexSize = 0;
char** index = nullptr;
char* data = loader.AutoProduceData(indexSize, index);
loader.AutoProduceRecordCopies(indexSize, index, data);
if (loadInfo->Meta->GetRecordSize() != sizeof(Record))
{
delete[] data;
delete[] index;
TC_LOG_ERROR("server.loading", "Profession table {} record size {} does not match C++ size {}", fileName, loadInfo->Meta->GetRecordSize(), sizeof(Record));
return;
}
for (uint32 i = 0; i < indexSize; ++i)
{
if (!index[i])
continue;
Record record;
std::memcpy(&record, index[i], sizeof(Record));
out.push_back(record);
}
delete[] data;
delete[] index;
TC_LOG_INFO("server.loading", ">> Loaded {} rows from {}", out.size(), fileName);
}
catch (std::exception const& error)
{
TC_LOG_ERROR("server.loading", "Profession table {} was not loaded ({}). Concentration, inspiration and multicraft stay off for data from this file.", fileName, error.what());
}
};
std::vector<ProfessionRecord> professions;
std::vector<ProfessionEffectRecord> effects;
std::vector<ProfessionTraitRecord> traits;
std::vector<ProfessionTraitXEffectRecord> links;
std::vector<ProfessionExpansionRecord> expansions;
loadRecords("Profession.db2", &ProfessionLoadInfo, professions);
loadRecords("ProfessionEffect.db2", &ProfessionEffectLoadInfo, effects);
loadRecords("ProfessionTrait.db2", &ProfessionTraitLoadInfo, traits);
loadRecords("ProfessionTraitXEffect.db2", &ProfessionTraitXEffectLoadInfo, links);
loadRecords("ProfessionExpansion.db2", &ProfessionExpansionLoadInfo, expansions);
if (professions.empty() || effects.empty() || traits.empty() || links.empty() || expansions.empty())
{
TC_LOG_ERROR("server.loading", "Profession proc tables are incomplete. Concentration, inspiration and multicraft stay disabled.");
return;
}
std::unordered_map<uint32, ProfessionEffectValue> effectsById;
for (ProfessionEffectRecord const& effect : effects)
effectsById[effect.ID] = { effect.ProfessionEffectTypeEnumID, effect.Amount };
std::unordered_map<uint32, uint32> traitDefinitionById;
for (ProfessionTraitRecord const& trait : traits)
if (trait.TraitDefinitionID > 0)
traitDefinitionById[trait.ID] = uint32(trait.TraitDefinitionID);
for (ProfessionTraitXEffectRecord const& link : links)
{
if (link.ProfessionTraitID <= 0 || link.ProfessionEffectID <= 0)
continue;
auto trait = traitDefinitionById.find(uint32(link.ProfessionTraitID));
auto effect = effectsById.find(uint32(link.ProfessionEffectID));
if (trait == traitDefinitionById.end() || effect == effectsById.end())
continue;
_professionEffectsByTraitDefinition[trait->second].push_back(effect->second);
}
for (ProfessionRecord const& profession : professions)
{
if (profession.SkillLineID <= 0)
continue;
if (profession.MinCraftExtraQtyPct > 0.0f || profession.MaxCraftExtraQtyPct > 0.0f)
_multicraftExtraPercentBySkillLine[uint32(profession.SkillLineID)] = { profession.MinCraftExtraQtyPct, profession.MaxCraftExtraQtyPct };
}
for (ProfessionExpansionRecord const& expansion : expansions)
if (expansion.SkillLineID > 0 && expansion.ConcentrationCurrencyID > 0)
_concentrationCurrencyBySkillLine[uint32(expansion.SkillLineID)] = uint32(expansion.ConcentrationCurrencyID);
}
//WowCommunity
}
static void LoadDB2(std::bitset<TOTAL_LOCALES>& availableDb2Locales, std::vector<std::string>& errlist, StorageMap& stores, DB2StorageBase* storage, std::string const& db2Path,
@@ -1377,6 +1699,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
LOAD_DB2(sItemLogicalCostStore);
LOAD_DB2(sTrophyStore);
LOAD_DB2(sManagedWorldStateBuffStore);
LoadProfessionProcTables(db2Path + localeNames[defaultLocale] + '/');
//WowCommunity
// error checks
@@ -1866,6 +2189,61 @@ void DB2Manager::IndexLoadedStores()
if (sSkillLineStore.LookupEntry(entry->SkillID))
_skillRaceClassInfoBySkill.insert(SkillRaceClassInfoContainer::value_type(entry->SkillID, entry));
for (ModifiedCraftingSpellSlotEntry const* slot : sModifiedCraftingSpellSlotStore)
if (slot->SpellID > 0)
_MCRSpellSlotBySpell[uint32(slot->SpellID)].push_back(slot);
for (MCRSlotXMCRCategoryEntry const* link : sMCRSlotXMCRCategoryStore)
if (ModifiedCraftingCategoryEntry const* category = sModifiedCraftingCategoryStore.LookupEntry(link->ModifiedCraftingCategoryID))
_MCRCategoryByReagentSlot[uint32(link->ModifiedCraftingReagentSlotID)].push_back(category);
for (ModifiedCraftingReagentItemEntry const* reagentItem : sModifiedCraftingReagentItemStore)
if (reagentItem->ModifiedCraftingCategoryID > 0)
_MCRReagentItemByCategory[uint32(reagentItem->ModifiedCraftingCategoryID)].push_back(reagentItem);
for (ItemSparseEntry const* sparse : sItemSparseStore)
if (sparse->ModifiedCraftingReagentItemID > 0)
_ItemSparseByMCRReagentItem[uint32(sparse->ModifiedCraftingReagentItemID)].insert(sparse);
for (CraftingReagentQualityEntry const* quality : sCraftingReagentQualityStore)
if (quality->ItemID > 0)
_CraftingReagentQualityByItem[uint32(quality->ItemID)] = quality;
struct CraftingQualityItem
{
int32 Order = 0;
uint32 RowId = 0;
uint32 ItemId = 0;
};
std::unordered_map<uint32, std::vector<CraftingQualityItem>> craftingItemsByData;
for (CraftingDataItemQualityEntry const* row : sCraftingDataItemQualityStore)
{
if (row->ItemID <= 0 || row->CraftingDataID <= 0)
continue;
int32 order = std::numeric_limits<int32>::max();
if (CraftingReagentQualityEntry const* quality = Trinity::Containers::MapGetValuePtr(_CraftingReagentQualityByItem, uint32(row->ItemID)))
order = quality->OrderIndex;
craftingItemsByData[uint32(row->CraftingDataID)].push_back({ order, row->ID, uint32(row->ItemID) });
}
for (auto& [craftingDataId, rows] : craftingItemsByData)
{
std::ranges::sort(rows, [](CraftingQualityItem const& left, CraftingQualityItem const& right)
{
if (left.Order != right.Order)
return left.Order < right.Order;
return left.RowId < right.RowId;
});
std::vector<uint32>& itemIds = _CraftingDataItemIDByCraftingData[craftingDataId];
itemIds.reserve(rows.size());
for (CraftingQualityItem const& row : rows)
itemIds.push_back(row.ItemId);
}
for (SoulbindConduitRankEntry const* soulbindConduitRank : sSoulbindConduitRankStore)
_soulbindConduitRanks[{ soulbindConduitRank->SoulbindConduitID, soulbindConduitRank->RankIndex }] = soulbindConduitRank;
@@ -3856,32 +4234,47 @@ DB2Manager::DelvesSeasonXSpellContainer const* DB2Manager::GetDelvesSeasonSpells
std::vector<ModifiedCraftingSpellSlotEntry const*> DB2Manager::GetMCRSpellSlotBySpell(uint32 spellId)
{
return _MCRSpellSlotBySpell[spellId];
if (std::vector<ModifiedCraftingSpellSlotEntry const*> const* slots = Trinity::Containers::MapGetValuePtr(_MCRSpellSlotBySpell, spellId))
return *slots;
return {};
}
std::vector<ModifiedCraftingCategoryEntry const*> DB2Manager::GetMCRCategoryByReagentSlot(uint32 reagentSlotID)
{
return _MCRCategoryByReagentSlot[reagentSlotID];
if (std::vector<ModifiedCraftingCategoryEntry const*> const* categories = Trinity::Containers::MapGetValuePtr(_MCRCategoryByReagentSlot, reagentSlotID))
return *categories;
return {};
}
std::vector<ModifiedCraftingReagentItemEntry const*> DB2Manager::GetMCRReagentItemByCategory(uint32 categoryID)
{
return _MCRReagentItemByCategory[categoryID];
if (std::vector<ModifiedCraftingReagentItemEntry const*> const* reagentItems = Trinity::Containers::MapGetValuePtr(_MCRReagentItemByCategory, categoryID))
return *reagentItems;
return {};
}
std::set<ItemSparseEntry const*> DB2Manager::GetItemSparseByMCRReagentItem(uint32 reagentItemID)
{
return _ItemSparseByMCRReagentItem[reagentItemID];
if (std::set<ItemSparseEntry const*> const* items = Trinity::Containers::MapGetValuePtr(_ItemSparseByMCRReagentItem, reagentItemID))
return *items;
return {};
}
std::vector<uint32> DB2Manager::GetCraftingDataItemIDByCraftingData(uint32 craftingDataID)
{
return _CraftingDataItemIDByCraftingData[craftingDataID];
if (std::vector<uint32> const* itemIds = Trinity::Containers::MapGetValuePtr(_CraftingDataItemIDByCraftingData, craftingDataID))
return *itemIds;
return {};
}
CraftingReagentQualityEntry const* DB2Manager::GetCraftingReagentQualityByItem(uint32 itemID)
{
return _CraftingReagentQualityByItem[itemID];
return Trinity::Containers::MapGetValuePtr(_CraftingReagentQualityByItem, itemID);
}
uint32 DB2Manager::GetCraftingQualityTierByDifficultyPercent(int32 craftingDifficultyID, uint32 difficultyPercent)
@@ -3900,6 +4293,69 @@ uint32 DB2Manager::GetCraftingQualityTierByDifficultyPercent(int32 craftingDiffi
return 0;
}
void DB2Manager::AccumulateProfessionTraitEffects(int32 traitDefinitionId, int32 rank, ProfessionEffectTotals& totals) const
{
if (traitDefinitionId <= 0 || rank <= 0)
return;
auto itr = _professionEffectsByTraitDefinition.find(uint32(traitDefinitionId));
if (itr == _professionEffectsByTraitDefinition.end())
return;
// ProfessionEffectType.EnumID, build 12.1.0.69875. Amount is per rank.
for (ProfessionEffectValue const& effect : itr->second)
{
float value = effect.Amount * float(rank);
switch (effect.Type)
{
case 0: // Skill
totals.Skill += value;
break;
case 1: // Inspiration rating
totals.InspirationRating += value;
break;
case 7: // Multicraft rating
totals.MulticraftRating += value;
break;
case 18: // additional items crafted with Multicraft, percent
totals.MulticraftExtraPercent += value;
break;
case 20: // bonus Crafting Skill from Inspiration
totals.InspirationBonusSkill += value;
break;
case 22: // Decrease Difficulty
totals.DifficultyDelta -= value;
break;
case 23: // Increase Difficulty
totals.DifficultyDelta += value;
break;
case 27: // reduce concentration cost, percent
totals.ConcentrationCostReductionPercent += value;
break;
default:
break;
}
}
}
uint32 DB2Manager::GetConcentrationCurrencyForSkillLine(uint32 skillLineId) const
{
auto itr = _concentrationCurrencyBySkillLine.find(skillLineId);
return itr != _concentrationCurrencyBySkillLine.end() ? itr->second : 0;
}
bool DB2Manager::GetMulticraftExtraQuantityRange(uint32 skillLineId, float& minPercent, float& maxPercent) const
{
auto itr = _multicraftExtraPercentBySkillLine.find(skillLineId);
if (itr == _multicraftExtraPercentBySkillLine.end())
return false;
minPercent = itr->second.first;
maxPercent = itr->second.second;
return true;
}
uint32 DB2Manager::GetConduitForItem(uint32 itemId) const
{
auto itr = _conduitsByItem.find(itemId);
+17
View File
@@ -541,6 +541,20 @@ TC_GAME_API extern TaxiPathNodesByPath sTaxiPathNod
static bool Compare(structure const* left, structure const* right); \
};
// Sum of ProfessionEffect rows reached through the player's profession trait config.
// Amounts are already multiplied by rank. Effect types are ProfessionEffectType.EnumID.
struct ProfessionEffectTotals
{
float Skill = 0.0f;
float InspirationRating = 0.0f;
float MulticraftRating = 0.0f;
float MulticraftExtraPercent = 0.0f;
float InspirationBonusSkill = 0.0f;
float ConcentrationCostReductionPercent = 0.0f;
float DifficultyDelta = 0.0f;
};
class TC_GAME_API DB2Manager
{
public:
@@ -737,6 +751,9 @@ public:
std::vector<uint32> GetCraftingDataItemIDByCraftingData(uint32 craftingDataID);
CraftingReagentQualityEntry const* GetCraftingReagentQualityByItem(uint32 itemID);
uint32 GetCraftingQualityTierByDifficultyPercent(int32 craftingDifficultyID, uint32 difficultyPercent);
void AccumulateProfessionTraitEffects(int32 traitDefinitionId, int32 rank, ProfessionEffectTotals& totals) const;
uint32 GetConcentrationCurrencyForSkillLine(uint32 skillLineId) const;
bool GetMulticraftExtraQuantityRange(uint32 skillLineId, float& minPercent, float& maxPercent) const;
std::vector<RenownRewardsEntry const*> const* GetRenownRewards(int32 covenantId, int32 level) const;
std::vector<BountyEntry const*> const* GetBountiesForBountySet(int32 bountySetId) const;
@@ -584,6 +584,8 @@ void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons)
{
Player* plrg = itr.GetSource();
ObjectGuid pguid = plrg->GetGUID();
uint8 memberRoles = (pguid == guid) ? roles : grp->GetLfgRoles(pguid);
SetRoles(pguid, memberRoles);
plrg->GetSession()->SendLfgUpdateStatus(updateData, true);
SetState(pguid, LFG_STATE_ROLECHECK);
SetTicket(pguid, ticket);
@@ -1383,6 +1385,12 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false*
{
TC_LOG_DEBUG("lfg.teleport", "Player {} is being teleported out. Current Map {} - Expected Map {}",
player->GetName(), player->GetMapId(), uint32(dungeon->map));
// In a follower dungeon, clean up the companion party before the player leaves the map
// so the client doesn't keep "Unknown" members in the group frame.
if (group)
group->DespawnFollowerModeBots(player);
if (player->GetMapId() == uint32(dungeon->map))
player->TeleportToBGEntryPoint();
+30 -12
View File
@@ -427,19 +427,8 @@ int32 GetAzeriteUnlockBonusList(uint16 azeriteUnlockMappingSetId, uint16 minItem
return 0;
}
std::vector<int32> GetBonusListsForItem(uint32 itemId, ItemBonusGenerationParams const& params, Player const* player)
void AppendItemLevelSelectorBonuses(ItemTemplate const* itemTemplate, uint32 itemLevelSelectorId, std::vector<int32>& bonusListIDs)
{
std::vector<int32> bonusListIDs;
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
if (!itemTemplate)
return bonusListIDs;
uint32 itemLevelSelectorId = 0;
for (auto [_, itemBonusTreeId] : Trinity::Containers::MapEqualRange(_itemToBonusTree, itemId))
ApplyBonusTreeHelper(itemTemplate, itemBonusTreeId, params, 0, &itemLevelSelectorId, &bonusListIDs, player);
if (ItemLevelSelectorEntry const* selector = sItemLevelSelectorStore.LookupEntry(itemLevelSelectorId))
{
int16 delta = int16(selector->MinItemLevel) - int16(itemTemplate->GetBaseItemLevel());
@@ -470,6 +459,35 @@ std::vector<int32> GetBonusListsForItem(uint32 itemId, ItemBonusGenerationParams
bonusListIDs.push_back(azeriteUnlockBonusListId);
}
}
std::vector<int32> GetBonusListsForItem(uint32 itemId, ItemBonusGenerationParams const& params, Player const* player)
{
std::vector<int32> bonusListIDs;
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
if (!itemTemplate)
return bonusListIDs;
uint32 itemLevelSelectorId = 0;
for (auto [_, itemBonusTreeId] : Trinity::Containers::MapEqualRange(_itemToBonusTree, itemId))
ApplyBonusTreeHelper(itemTemplate, itemBonusTreeId, params, 0, &itemLevelSelectorId, &bonusListIDs, player);
AppendItemLevelSelectorBonuses(itemTemplate, itemLevelSelectorId, bonusListIDs);
return bonusListIDs;
}
std::vector<int32> GetBonusListsForTree(uint32 itemBonusTreeId, ItemTemplate const* itemTemplate, ItemBonusGenerationParams const& params, Player const* player)
{
std::vector<int32> bonusListIDs;
if (!itemBonusTreeId || !itemTemplate)
return bonusListIDs;
uint32 itemLevelSelectorId = 0;
ApplyBonusTreeHelper(itemTemplate, itemBonusTreeId, params, 0, &itemLevelSelectorId, &bonusListIDs, player);
AppendItemLevelSelectorBonuses(itemTemplate, itemLevelSelectorId, bonusListIDs);
return bonusListIDs;
}
@@ -23,6 +23,7 @@
#include <span>
#include <vector>
class ItemTemplate;
class Player;
struct ItemBonusEntry;
struct MapDifficultyEntry;
@@ -48,6 +49,7 @@ struct ItemBonusGenerationParams
Optional<int32> PvpTier;
};
TC_GAME_API std::vector<int32> GetBonusListsForTree(uint32 itemBonusTreeId, ItemTemplate const* itemTemplate, ItemBonusGenerationParams const& params, Player const* player = nullptr);
TC_GAME_API std::vector<int32> GetBonusListsForItem(uint32 itemId, ItemBonusGenerationParams const& params, Player const* player = nullptr);
TC_GAME_API std::vector<int32> GetAllBonusListsForTree(uint32 itemBonusTreeId);
}
@@ -21,7 +21,7 @@
#include "ObjectGuid.h"
#include <atomic>
class ObjectGuidGenerator
class TC_GAME_API ObjectGuidGenerator
{
public:
explicit ObjectGuidGenerator(HighGuid high, ObjectGuid::LowType start = UI64LIT(1)) : _nextGuid(start), _high(high) { }
+3 -1
View File
@@ -14021,7 +14021,7 @@ void Player::SendItemPassives()
SendDirectMessage(sendItemPassives.Write());
}
void Player::SendNewItem(Item* item, uint32 quantity, bool pushed, bool created, bool broadcast /*= false*/, uint32 dungeonEncounterId /*= 0*/)
void Player::SendNewItem(Item* item, uint32 quantity, bool pushed, bool created, bool broadcast /*= false*/, uint32 dungeonEncounterId /*= 0*/, WorldPackets::Crafting::CraftingData const* craftingData /*= nullptr*/)
{
if (!item) // prevent crash
return;
@@ -14047,6 +14047,8 @@ void Player::SendNewItem(Item* item, uint32 quantity, bool pushed, bool created,
packet.BattlePetLevel = item->GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL);
packet.ItemGUID = item->GetGUID();
if (craftingData)
packet.CraftingData = *craftingData;
packet.Pushed = pushed;
packet.ChatNotifyType = WorldPackets::Item::ItemPushResult::DISPLAY_TYPE_NORMAL;
+1 -1
View File
@@ -1724,7 +1724,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
bool IsUseEquipedWeapon(bool mainhand) const;
bool IsTwoHandUsed() const;
bool IsUsingTwoHandedWeaponInOneHand() const;
void SendNewItem(Item* item, uint32 quantity, bool pushed, bool created, bool broadcast = false, uint32 dungeonEncounterId = 0);
void SendNewItem(Item* item, uint32 quantity, bool pushed, bool created, bool broadcast = false, uint32 dungeonEncounterId = 0, WorldPackets::Crafting::CraftingData const* craftingData = nullptr);
bool BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uint32 item, uint32 count, uint8 bag, uint8 slot);
Optional<SellResult> CanSellItemToVendor(Item const* item, uint32 amount) const;
Optional<SellResult> SellItemToVendor(Item* item, uint32 amount);
@@ -112,6 +112,10 @@ void SceneMgr::OnSceneTrigger(uint32 sceneInstanceID, std::string const& trigger
SceneTemplate const* sceneTemplate = GetSceneTemplateFromInstanceId(sceneInstanceID);
sScriptMgr->OnSceneTrigger(GetPlayer(), sceneInstanceID, sceneTemplate, triggerName);
// Exile's Reach reacts to scene triggers ("Teleport", "Big Kill Credit", ...) from a player script.
// Scene template scripts are optional and do not receive those events.
sScriptMgr->OnSceneTriggerEvent(GetPlayer(), sceneInstanceID, triggerName);
}
void SceneMgr::OnSceneCancel(uint32 sceneInstanceID)
+22 -3
View File
@@ -581,10 +581,19 @@ bool Group::RemoveMember(ObjectGuid guid, RemoveMethod method /*= GROUP_REMOVEME
Player* player = ObjectAccessor::FindConnectedPlayer(guid);
if (player)
{
for (GroupReference const& itr : GetMembers())
// Snapshot member GUIDs first: aura removal below can re-enter group code
// (script hooks, follower bot cleanup) and mutate m_memberSlots, which would
// leave this loop walking a dead list.
std::vector<ObjectGuid> memberGuids;
memberGuids.reserve(m_memberSlots.size());
for (MemberSlot const& slot : m_memberSlots)
if (slot.guid != guid && slot.guid.IsPlayer())
memberGuids.push_back(slot.guid);
for (ObjectGuid const& memberGuid : memberGuids)
{
Player* groupMember = itr.GetSource();
if (groupMember->GetGUID() == guid)
Player* groupMember = ObjectAccessor::FindConnectedPlayer(memberGuid);
if (!groupMember)
continue;
groupMember->RemoveAllGroupBuffsFromCaster(guid);
@@ -2163,6 +2172,10 @@ void Group::DespawnFollowerModeBots(Player* player)
if (!player)
return;
// Clean up LFG data so the player doesn't keep a stale follower dungeon state.
if (isLFGGroup())
sLFGMgr->RemoveGroupData(GetGUID());
uint32 playerMembers = 0;
for (MemberSlot const& slot : m_memberSlots)
if (slot.guid.IsPlayer())
@@ -2202,6 +2215,12 @@ void Group::DespawnFollowerModeBots(Player* player)
}
}
// NOTE: never call RemoveMember() here. This runs inside
// GroupScript::OnRemoveMember during an in-flight RemoveMember(); a nested
// removal can Disband()/delete `this` and leave the outer call running on a
// dead object. The outer RemoveMember (or the OnMapChanged LFG script)
// removes the last real player and disbands the group by itself.
SendUpdate();
}
+18 -1
View File
@@ -329,7 +329,24 @@ void WorldSession::SendLfgUpdateStatus(lfg::LfgUpdateData const& updateData, boo
{
return sLFGMgr->GetLFGDungeonEntry(dungeonId);
});
lfgUpdateStatus.RequestedRoles = sLFGMgr->GetRoles(_player->GetGUID());
uint8 requestedRoles = sLFGMgr->GetRoles(_player->GetGUID());
if (requestedRoles == 0)
{
if (Group* group = _player->GetGroup())
requestedRoles = group->GetLfgRoles(_player->GetGUID());
if (requestedRoles == 0)
{
uint32 roleForGroup = _player->GetRoleForGroup();
if (roleForGroup == ROLE_TANK)
requestedRoles = lfg::PLAYER_ROLE_TANK;
else if (roleForGroup == ROLE_HEALER)
requestedRoles = lfg::PLAYER_ROLE_HEALER;
else
requestedRoles = lfg::PLAYER_ROLE_DAMAGE;
}
}
lfgUpdateStatus.RequestedRoles = requestedRoles;
//lfgUpdateStatus.SuspendedPlayers;
lfgUpdateStatus.IsParty = party;
lfgUpdateStatus.NotifyUI = true;
+23 -16
View File
@@ -889,10 +889,15 @@ void WorldSession::HandleLFGListInviteResponse(WorldPackets::LFGList::LFGListInv
namespace
{
// GroupFinderActivity.ExpansionID of the expansion that has not shipped yet. Not a named client constant -
// read straight out of the data: the 205 activities the 12.1 captures blacklist with reason 1 are exactly
// the ExpansionID == 11 rows of GroupFinderActivity, and no reason-1 row has any other ExpansionID.
constexpr int32 EXPANSION_UNRELEASED_GROUP_FINDER = 11;
// GroupFinderActivity.ExpansionID of the expansion that has not shipped yet.
// The 69382/69404 captures (18-20 Aug 2026) blacklist reason 1 as exactly the 205 ExpansionID == 11
// rows, and 69273 (11 Aug) sends no reason 1 at all. That set is Midnight, not a future expansion:
// 12.1.0.69875 GroupFinderActivity names them Windrunner Spire, Maisara Caverns, Murder Row,
// Voidspire, March on Quel'Danas, the Midnight delves and the Midnight zones. Forwarding that
// reason-1 set tells a 69875 client the current expansion is still locked, which is the Premade
// Group Finder with no Midnight activities. 12 is the next id; this build's table has no
// ExpansionID 12 rows.
constexpr int32 EXPANSION_UNRELEASED_GROUP_FINDER = 12;
// SMSG_LFG_LIST_UPDATE_BLACKLIST content, extracted verbatim from the TARGET build 12.1.0.69382
// (C:/dumps/wpp_work/lfg_ref/69382_s69382_ws6_5A000E_{0,1}.bin, both 9100 byte and BYTE-IDENTICAL to each
@@ -913,8 +918,9 @@ namespace
// nothing more.
//
// What the reasons are, as far as the captures decide it:
// reason 1 - EXACT RULE, see below: ExpansionID == 11 (the unreleased expansion). 205 activities,
// and the set is byte-for-byte identical in all three captures that carry it.
// reason 1 - ExpansionID == 11, 205 activities, identical in the three captures that carry it.
// On 69875 those rows are the live Midnight catalog, so HandleRequestLFGListBlacklist
// does not forward reason 1. The snapshot below is left intact as the capture record.
// reason 18 - 143 rows in ALL FOUR captures, identical set: legacy content permanently off the group
// finder (MoP/WoD zones and the 10-man raid tiers - "The Jade Forest", "Mogu'shan Vaults
// (10 Heroic)"). Constant, therefore derivable, but the deriving field is not yet found.
@@ -1063,22 +1069,23 @@ void WorldSession::HandleRequestLFGListBlacklist(WorldPackets::LFGList::RequestL
sent.reserve(std::size(LFGListActivityBlacklist));
for (LFGListBlacklistRow const& row : LFGListActivityBlacklist)
{
// The 205 reason-1 rows are every ExpansionID 11 activity. That was "unreleased" on the
// 69382 capture and is the live Midnight list on 69875 (see EXPANSION_UNRELEASED_GROUP_FINDER).
// Sending them makes the client drop Maisara, Windrunner Spire, Voidspire and the rest.
if (row.Reason == 1)
continue;
WorldPackets::LFGList::LFGListBlacklistEntry& entry = packet.Entries.emplace_back();
entry.ActivityID = row.ActivityID;
entry.Reason = row.Reason;
sent.insert(row.ActivityID);
}
// Reason 1 is the one rule the captures settle exactly, so it is DERIVED and not trusted to the snapshot
// above. In all three 12.1 captures that carry reason 1 (69382 x1, 69404 x2 - different build, different
// faction, different character) the reason-1 set is EXACTLY the set of GroupFinderActivity rows with
// ExpansionID == 11, 205 of 205, no row missing and no row extra. 69273 carries no reason 1 at all, which
// fits: expansion 11 is the unreleased one and its activities entered the client data later.
// Deriving it matters because the alternative rots on a schedule. Every build adds ExpansionID 11
// activities, retail blacklists each of them on sight, and a frozen list of 205 ids would keep answering
// with yesterday's set - the exact failure that put the 12.0.7 table into a 12.1 branch in the first
// place. Rows already present in the snapshot are left alone so the observed 1137 keep their observed
// order and no activity is ever sent twice (the retail payload has no duplicate ids either).
// Reason 1 stays derived, but for the expansion that is not in this client yet. The August captures
// used 11 because those rows were still locked; 69875 ships them. New GroupFinderActivity rows with
// ExpansionID == EXPANSION_UNRELEASED_GROUP_FINDER are appended so a later DB2 does not start
// offering the next expansion's activities the day they appear in the file. Snapshot rows already
// copied above are left alone, and no activity is sent twice.
for (GroupFinderActivityEntry const* activity : sGroupFinderActivityStore)
{
if (activity->ExpansionID != EXPANSION_UNRELEASED_GROUP_FINDER || sent.count(activity->ID))
+14 -15
View File
@@ -2185,6 +2185,11 @@ void ScriptMgr::OnMovieComplete(Player* player, uint32 movieId)
FOREACH_SCRIPT(PlayerScript)->OnMovieComplete(player, movieId);
}
void ScriptMgr::OnSceneTriggerEvent(Player* player, uint32 sceneInstanceId, std::string event)
{
FOREACH_SCRIPT(PlayerScript)->OnSceneTriggerEvent(player, sceneInstanceId, std::move(event));
}
void ScriptMgr::OnPlayerChoiceResponse(WorldObject* object, Player* player, PlayerChoice const* choice, PlayerChoiceResponse const* response, uint16 clientIdentifier)
{
ASSERT(choice);
@@ -3299,22 +3304,16 @@ void ScriptMgr::OnQuestAbandon(Player* player, const Quest* quest)
void WowCommunity::PhaseShift(Player* player, std::vector<uint32> phaseIDs, bool add)
{
for (uint32 phaseID : phaseIDs)
{
if (add)
{
if (!player->GetPhaseShift().HasPhase(phaseID))
PhasingHandler::AddPhase(player, phaseID, true);
}
else
{
if (player->GetPhaseShift().HasPhase(phaseID))
PhasingHandler::RemovePhase(player, phaseID, true);
}
}
// Exile's Reach calls this every second. A false condition used to RemovePhase,
// which stripped phases the world database had already applied and hid the quest
// givers for the rest of the island. Only additions run here. A caller that must
// drop a phase uses PhasingHandler::RemovePhase itself.
if (!player || !add)
return;
PhasingHandler::SendToPlayer(player);
player->UpdateObjectVisibility(true);
for (uint32 phaseID : phaseIDs)
if (!player->GetPhaseShift().HasPhase(phaseID))
PhasingHandler::AddPhase(player, phaseID, true);
}
void ScriptMgr::OnPlayerWorldObjectUpdate(Player* player, uint32 diff)
@@ -254,8 +254,8 @@ namespace WorldPackets
MissileTrajectoryRequest MissileTrajectory;
Optional<MovementInfo> MoveUpdate;
std::vector<SpellWeight> Weight;
Array<SpellCraftingReagent, 6> CraftingReagents;
Array<SpellCraftingReagent, 6> RemovedReagents;
Array<SpellCraftingReagent, 16> CraftingReagents;
Array<SpellCraftingReagent, 16> RemovedReagents;
Array<SpellExtraCurrencyCost, 5 /*MAX_ITEM_EXT_COST_CURRENCIES*/> ExtraCurrencyCosts;
Optional<uint64> CraftingOrderID;
uint8 CraftingCastFlags = 0; // 1 = ApplyConcentration
+1 -1
View File
@@ -1894,7 +1894,7 @@ void OpcodeTable::InitializeServerOpcodes()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_APPLICANT_LIST_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_APPLICATION_STATUS_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_APPLY_TO_GROUP_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_CENSORED_ACTIVE_ENTRY_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_CENSORED_ACTIVE_ENTRY_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_JOIN_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_SEARCH_RESULTS, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_LFG_LIST_SEARCH_RESULTS_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
+15
View File
@@ -140,6 +140,21 @@ SpellCastTargets::SpellCastTargets(Unit* caster, WorldPackets::Spells::SpellCast
SetPitch(spellCastRequest.MissileTrajectory.Pitch);
SetSpeed(spellCastRequest.MissileTrajectory.Speed);
for (WorldPackets::Spells::SpellCraftingReagent const& reagent : spellCastRequest.CraftingReagents)
{
if (reagent.Quantity <= 0)
continue;
bool hasItem = reagent.Reagent.ItemID && *reagent.Reagent.ItemID > 0;
bool hasCurrency = reagent.Reagent.CurrencyID && *reagent.Reagent.CurrencyID > 0;
if (!hasItem && !hasCurrency)
continue;
ModifiedCraftingReagents[uint32(reagent.Slot)].push_back(reagent);
}
ApplyConcentration = (spellCastRequest.CraftingCastFlags & 1) != 0;
Update(caster);
}
+1
View File
@@ -452,6 +452,7 @@ public:
//WowCommunity
std::unordered_map<uint32 /*SpellSlot*/, std::vector<WorldPackets::Spells::SpellCraftingReagent>> ModifiedCraftingReagents;
bool ApplyConcentration = false; // SpellCastRequest.CraftingCastFlags bit 0 (value 1)
//WowCommunity
private:
+36 -1
View File
@@ -97,6 +97,7 @@
#include "GarrisonMgr.h"
#include "GroupMgr.h"
#include "LFG.h"
#include <unordered_map>
//WowCommunity
NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EFFECTS] =
@@ -2903,7 +2904,7 @@ void Spell::EffectCraftEnchant()
return;
}
Crafting craftingInstance = Crafting(player, this);
Crafting craftingInstance = Crafting(player, this, true);
SpellCastResult result = craftingInstance.DoCraft(effectInfo->MiscValue);
if (result != SpellCastResult::SPELL_CAST_OK)
@@ -7998,6 +7999,15 @@ void Spell::EffectCraftItem()
Player* player = unitTarget->ToPlayer();
if (!sDB2Manager.GetMCRSpellSlotBySpell(m_spellInfo->Id).empty())
{
Crafting crafting(player, this, false);
SpellCastResult result = crafting.DoCraft(uint32(effectInfo->MiscValue));
if (result != SPELL_CAST_OK)
SendCastResult(result);
return;
}
uint32 craftingID = effectInfo->MiscValue;
CraftingDataEntry const* craftingData = sCraftingDataStore.LookupEntry(craftingID);
@@ -8044,8 +8054,33 @@ void Spell::EffectCraftLoot()
if (!treasureId)
return;
std::unordered_map<uint32, uint32> usedItems;
for (auto const& [slot, reagents] : m_targets.ModifiedCraftingReagents)
{
for (WorldPackets::Spells::SpellCraftingReagent const& reagent : reagents)
{
if (!reagent.Reagent.ItemID || *reagent.Reagent.ItemID <= 0 || reagent.Quantity <= 0)
continue;
uint32 itemId = uint32(*reagent.Reagent.ItemID);
uint32 quantity = usedItems[itemId] + uint32(reagent.Quantity);
if (!player->HasItemCount(itemId, quantity))
{
SendCastResult(SpellCastResult::SPELL_FAILED_NEED_MORE_ITEMS);
return;
}
usedItems[itemId] = quantity;
}
}
ItemContext context = m_spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) ? ItemContext::Trade_Skill : ItemContext::NONE;
player->AutoStoreLoot(treasureId, LootTemplates_Spell, context, false, false, true);
for (auto const& [itemId, quantity] : usedItems)
player->DestroyItemCount(itemId, quantity, true);
player->UpdateCraftSkill(m_spellInfo);
}
void Spell::EffectApplyAreaAura()
@@ -16,7 +16,8 @@ uint32 const EncounterCount = 9;
enum AmirdrassilDataTypes
{
// Encounters
DATA_GNARLROOT = 1,
// Encounters ? 0-based. EncounterCount is 9, so Fyrakk must stay index 8.
DATA_GNARLROOT = 0,
DATA_IGIRA_THE_CRUEL,
DATA_VOLCOROSS,
DATA_COUNCIL_OF_DREAMS,
@@ -1,32 +1,404 @@
/*
* This file is part of Algeth Team
* Council of Dreams ? Urctos (208363), Aerwynn (208365), Pip (208367).
*
* One journal encounter. A member who would die while another is still standing
* is downed and reborn after 12 seconds (418187 Rebirth). The encounter is
* complete only when all three are down together.
*
* Body spells confirmed on Wowhead:
* Urctos: 420946 Barreling Charge, 421020 Agonizing Claws, 420525 Blind Rage.
* Aerwynn: 420937 Relentless Barrage, 421292 Constricting Thicket, 420856 Poisonous Javelin.
* Pip's creature page listed no spells. The remaining council spells from the
* Urctos/Aerwynn pages are cast by Pip: 418720 Polymorph Bomb, 421029 Song of the Dragon,
* 421032 Captivating Finale.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum CouncilSpells
{
SPELL_COUNCIL_REBIRTH = 418187,
SPELL_BARRELING_CHARGE = 420946,
SPELL_AGONIZING_CLAWS = 421020,
SPELL_BLIND_RAGE = 420525,
SPELL_RELENTLESS_BARRAGE = 420937,
SPELL_CONSTRICTING_THICKET = 421292,
SPELL_POISONOUS_JAVELIN = 420856,
SPELL_POLYMORPH_BOMB = 418720,
SPELL_SONG_OF_THE_DRAGON = 421029,
SPELL_CAPTIVATING_FINALE = 421032
};
enum amirdrassilEvents
enum CouncilEvents
{
EVENT_COUNCIL_REBIRTH = 1,
EVENT_COUNCIL_SPELL_1,
EVENT_COUNCIL_SPELL_2,
EVENT_COUNCIL_SPELL_3
};
static constexpr float CouncilSearchRange = 150.0f;
struct council_ai : public ScriptedAI
{
council_ai(Creature* creature) : ScriptedAI(creature), _downed(false), _finishing(false), _linking(false), _evading(false) {}
void Reset() override
{
_events.Reset();
_downed = false;
_finishing = false;
me->SetReactState(REACT_AGGRESSIVE);
me->SetControlled(false, UNIT_STATE_ROOT);
}
void JustEngagedWith(Unit* who) override
{
if (InstanceScript* instance = me->GetInstanceScript())
{
if (instance->GetBossState(DATA_COUNCIL_OF_DREAMS) == DONE)
{
EnterEvadeMode(EvadeReason::Other);
return;
}
if (instance->GetBossState(DATA_COUNCIL_OF_DREAMS) != IN_PROGRESS)
{
instance->SetBossState(DATA_COUNCIL_OF_DREAMS, IN_PROGRESS);
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
}
else
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
}
PullSiblings(who);
ScheduleCouncilEvents();
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo*/) override
{
uint64 health = me->GetHealth();
if (_finishing || damage < health)
return;
if (!AnySiblingStanding())
{
_finishing = true;
return;
}
damage = health > 1 ? uint32(health - 1) : 0;
if (_downed)
return;
_downed = true;
_events.Reset();
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->InterruptNonMeleeSpells(false);
me->SetControlled(true, UNIT_STATE_ROOT);
_events.ScheduleEvent(EVENT_COUNCIL_REBIRTH, 12s);
CastRebirthFromSibling();
}
void JustDied(Unit* /*killer*/) override
{
if (InstanceScript* instance = me->GetInstanceScript())
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (AnySiblingStanding())
return;
if (InstanceScript* instance = me->GetInstanceScript())
if (instance->GetBossState(DATA_COUNCIL_OF_DREAMS) != DONE)
instance->SetBossState(DATA_COUNCIL_OF_DREAMS, DONE);
ForEachSibling([](council_ai* ai) { ai->ForceFinish(); });
}
void EnterEvadeMode(EvadeReason why) override
{
if (_evading)
return;
_evading = true;
if (InstanceScript* instance = me->GetInstanceScript())
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (instance->GetBossState(DATA_COUNCIL_OF_DREAMS) == IN_PROGRESS)
instance->SetBossState(DATA_COUNCIL_OF_DREAMS, FAIL);
}
ForEachSibling([why](council_ai* ai)
{
if (ai->me->IsEngaged())
ai->EnterEvadeMode(why);
});
ScriptedAI::EnterEvadeMode(why);
_evading = false;
}
void UpdateAI(uint32 diff) override
{
if (_finishing)
return;
// A downed member has dropped its victim, but still has to finish Rebirth.
if (!_downed && !UpdateVictim())
return;
_events.Update(diff);
if (_downed)
{
while (uint32 eventId = _events.ExecuteEvent())
{
if (eventId == EVENT_COUNCIL_REBIRTH)
CompleteRebirth();
}
return;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = _events.ExecuteEvent())
{
ExecuteCouncilEvent(eventId);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
void ForceFinish()
{
if (!me->IsAlive() || _finishing)
return;
_finishing = true;
_downed = false;
me->SetControlled(false, UNIT_STATE_ROOT);
me->KillSelf();
}
protected:
virtual void ScheduleCouncilEvents() = 0;
virtual void ExecuteCouncilEvent(uint32 eventId) = 0;
EventMap _events;
private:
bool SiblingStanding(uint32 entry) const
{
Creature* sibling = me->FindNearestCreature(entry, CouncilSearchRange, true);
if (!sibling)
return false;
if (council_ai* ai = dynamic_cast<council_ai*>(sibling->AI()))
return !ai->_downed;
return true;
}
bool AnySiblingStanding() const
{
return SiblingStanding(SiblingEntryA()) || SiblingStanding(SiblingEntryB());
}
virtual uint32 SiblingEntryA() const = 0;
virtual uint32 SiblingEntryB() const = 0;
void PullSiblings(Unit* who)
{
if (_linking)
return;
_linking = true;
for (uint32 entry : { SiblingEntryA(), SiblingEntryB() })
{
if (Creature* sibling = me->FindNearestCreature(entry, CouncilSearchRange, true))
if (CreatureAI* ai = sibling->AI())
if (!sibling->IsEngaged())
ai->AttackStart(who);
}
_linking = false;
}
template<typename Callback>
void ForEachSibling(Callback&& callback)
{
for (uint32 entry : { SiblingEntryA(), SiblingEntryB() })
{
if (Creature* sibling = me->FindNearestCreature(entry, CouncilSearchRange, true))
if (council_ai* ai = dynamic_cast<council_ai*>(sibling->AI()))
callback(ai);
}
}
void CastRebirthFromSibling()
{
for (uint32 entry : { SiblingEntryA(), SiblingEntryB() })
{
Creature* sibling = me->FindNearestCreature(entry, CouncilSearchRange, true);
if (!sibling)
continue;
if (council_ai* ai = dynamic_cast<council_ai*>(sibling->AI()))
if (ai->_downed)
continue;
sibling->CastSpell(me, SPELL_COUNCIL_REBIRTH, false);
return;
}
}
void CompleteRebirth()
{
if (!_downed)
return;
if (!AnySiblingStanding())
{
ForceFinish();
return;
}
_downed = false;
me->SetControlled(false, UNIT_STATE_ROOT);
me->SetReactState(REACT_AGGRESSIVE);
me->SetFullHealth();
_events.Reset();
ScheduleCouncilEvents();
if (Unit* victim = me->GetVictim())
AttackStart(victim);
else if (Player* player = me->SelectNearestPlayer(50.0f))
AttackStart(player);
}
bool _downed;
bool _finishing;
bool _linking;
bool _evading;
};
struct boss_urctos : public council_ai
{
boss_urctos(Creature* creature) : council_ai(creature) {}
uint32 SiblingEntryA() const override { return BOSS_AEWYN; }
uint32 SiblingEntryB() const override { return BOSS_PIP; }
void ScheduleCouncilEvents() override
{
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_1, 6s);
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_2, 12s);
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_3, 22s);
}
void ExecuteCouncilEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_COUNCIL_SPELL_1:
DoCastVictim(SPELL_BARRELING_CHARGE);
_events.Repeat(18s);
break;
case EVENT_COUNCIL_SPELL_2:
DoCastVictim(SPELL_AGONIZING_CLAWS);
_events.Repeat(14s);
break;
case EVENT_COUNCIL_SPELL_3:
DoCastSelf(SPELL_BLIND_RAGE);
_events.Repeat(32s);
break;
default:
break;
}
}
};
struct boss_aerwynn : public council_ai
{
boss_aerwynn(Creature* creature) : council_ai(creature) {}
uint32 SiblingEntryA() const override { return BOSS_URCTOS; }
uint32 SiblingEntryB() const override { return BOSS_PIP; }
void ScheduleCouncilEvents() override
{
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_1, 8s);
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_2, 14s);
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_3, 20s);
}
void ExecuteCouncilEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_COUNCIL_SPELL_1:
DoCastSelf(SPELL_RELENTLESS_BARRAGE);
_events.Repeat(24s);
break;
case EVENT_COUNCIL_SPELL_2:
DoCastSelf(SPELL_CONSTRICTING_THICKET);
_events.Repeat(28s);
break;
case EVENT_COUNCIL_SPELL_3:
DoCastVictim(SPELL_POISONOUS_JAVELIN);
_events.Repeat(16s);
break;
default:
break;
}
}
};
struct boss_pip : public council_ai
{
boss_pip(Creature* creature) : council_ai(creature) {}
uint32 SiblingEntryA() const override { return BOSS_URCTOS; }
uint32 SiblingEntryB() const override { return BOSS_AEWYN; }
void ScheduleCouncilEvents() override
{
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_1, 10s);
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_2, 18s);
_events.ScheduleEvent(EVENT_COUNCIL_SPELL_3, 28s);
}
void ExecuteCouncilEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_COUNCIL_SPELL_1:
DoCastSelf(SPELL_POLYMORPH_BOMB);
_events.Repeat(26s);
break;
case EVENT_COUNCIL_SPELL_2:
DoCastSelf(SPELL_SONG_OF_THE_DRAGON);
_events.Repeat(34s);
break;
case EVENT_COUNCIL_SPELL_3:
DoCastSelf(SPELL_CAPTIVATING_FINALE);
_events.Repeat(40s);
break;
default:
break;
}
}
};
void AddSC_boss_council_of_dreams()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_urctos);
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_aerwynn);
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_pip);
}
@@ -1,32 +1,177 @@
/*
* This file is part of Algeth Team
*
* Fyrakk the Blazing (204931) ? Amirdrassil.
* Phase 1: 426368 Darkflame Cleave, 419506 Firestorm, 417455 Dream Rend.
* Phase 2 (70%): 422524 Shadowflame Devastation, 422837 Apocalypse Roar, 417443 Fyr'alath's Mark.
* Phase 3 (40%): 422518 Greater Firestorm, 425492 Infernal Maw, 428400 Exploding Core.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum FyrakkSpells
{
SPELL_DARKFLAME_CLEAVE = 426368,
SPELL_FIRESTORM = 419506,
SPELL_DREAM_REND = 417455,
SPELL_SHADOWFLAME_DEVASTATION = 422524,
SPELL_APOCALYPSE_ROAR = 422837,
SPELL_FYRALATHS_MARK = 417443,
SPELL_GREATER_FIRESTORM = 422518,
SPELL_INFERNAL_MAW = 425492,
SPELL_EXPLODING_CORE = 428400
};
enum amirdrassilEvents
enum FyrakkEvents
{
EVENT_DARKFLAME_CLEAVE = 1,
EVENT_FIRESTORM,
EVENT_DREAM_REND,
EVENT_SHADOWFLAME_DEVASTATION,
EVENT_APOCALYPSE_ROAR,
EVENT_FYRALATHS_MARK,
EVENT_GREATER_FIRESTORM,
EVENT_INFERNAL_MAW,
EVENT_EXPLODING_CORE
};
// 204931 - Fyrakk the Blazing
struct boss_fyrakk_the_blazing : public BossAI
{
boss_fyrakk_the_blazing(Creature* creature) : BossAI(creature, DATA_FYRAKK_THE_BLAZING), _phase(1) {}
void Reset() override
{
BossAI::Reset();
_phase = 1;
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_FYRAKK_THE_BLAZING) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
SchedulePhaseOne();
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_FYRAKK_THE_BLAZING) != DONE)
instance->SetBossState(DATA_FYRAKK_THE_BLAZING, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (_phase == 1 && me->HealthBelowPct(70))
{
_phase = 2;
events.Reset();
SchedulePhaseTwo();
}
if (_phase == 2 && me->HealthBelowPct(40))
{
_phase = 3;
events.Reset();
SchedulePhaseThree();
}
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_DARKFLAME_CLEAVE:
DoCastVictim(SPELL_DARKFLAME_CLEAVE);
events.Repeat(16s);
break;
case EVENT_FIRESTORM:
DoCastSelf(SPELL_FIRESTORM);
events.Repeat(28s);
break;
case EVENT_DREAM_REND:
DoCastVictim(SPELL_DREAM_REND);
events.Repeat(22s);
break;
case EVENT_SHADOWFLAME_DEVASTATION:
DoCastSelf(SPELL_SHADOWFLAME_DEVASTATION);
events.Repeat(30s);
break;
case EVENT_APOCALYPSE_ROAR:
DoCastSelf(SPELL_APOCALYPSE_ROAR);
events.Repeat(24s);
break;
case EVENT_FYRALATHS_MARK:
DoCastVictim(SPELL_FYRALATHS_MARK);
events.Repeat(20s);
break;
case EVENT_GREATER_FIRESTORM:
DoCastSelf(SPELL_GREATER_FIRESTORM);
events.Repeat(32s);
break;
case EVENT_INFERNAL_MAW:
DoCastVictim(SPELL_INFERNAL_MAW);
events.Repeat(18s);
break;
case EVENT_EXPLODING_CORE:
DoCastSelf(SPELL_EXPLODING_CORE);
events.Repeat(26s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
void SchedulePhaseOne()
{
events.ScheduleEvent(EVENT_DARKFLAME_CLEAVE, 6s);
events.ScheduleEvent(EVENT_DREAM_REND, 12s);
events.ScheduleEvent(EVENT_FIRESTORM, 20s);
}
void SchedulePhaseTwo()
{
events.ScheduleEvent(EVENT_FYRALATHS_MARK, 4s);
events.ScheduleEvent(EVENT_APOCALYPSE_ROAR, 10s);
events.ScheduleEvent(EVENT_SHADOWFLAME_DEVASTATION, 18s);
}
void SchedulePhaseThree()
{
events.ScheduleEvent(EVENT_INFERNAL_MAW, 4s);
events.ScheduleEvent(EVENT_EXPLODING_CORE, 10s);
events.ScheduleEvent(EVENT_GREATER_FIRESTORM, 18s);
}
uint8 _phase;
};
void AddSC_boss_fyrakk_the_blazing()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_fyrakk_the_blazing);
}
@@ -1,32 +1,113 @@
/*
* This file is part of Algeth Team
*
* Gnarlroot (209333) ? Amirdrassil.
* Spell ids taken from the retail encounter (cast spells, not their damage twins):
* 422026 Tortured Scream, 424352 Dreadfire Barrage, 421971 Controlled Burn,
* 421898 Flaming Pestilence, 421013 Doom Cultivation.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum GnarlrootSpells
{
SPELL_TORTURED_SCREAM = 422026,
SPELL_DREADFIRE_BARRAGE = 424352,
SPELL_CONTROLLED_BURN = 421971,
SPELL_FLAMING_PESTILENCE = 421898,
SPELL_DOOM_CULTIVATION = 421013
};
enum amirdrassilEvents
enum GnarlrootEvents
{
EVENT_TORTURED_SCREAM = 1,
EVENT_DREADFIRE_BARRAGE,
EVENT_CONTROLLED_BURN,
EVENT_FLAMING_PESTILENCE,
EVENT_DOOM_CULTIVATION
};
// 209333 - Gnarlroot
struct boss_gnarlroot : public BossAI
{
boss_gnarlroot(Creature* creature) : BossAI(creature, DATA_GNARLROOT) {}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_GNARLROOT) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
events.ScheduleEvent(EVENT_TORTURED_SCREAM, 6s);
events.ScheduleEvent(EVENT_DREADFIRE_BARRAGE, 12s);
events.ScheduleEvent(EVENT_CONTROLLED_BURN, 18s);
events.ScheduleEvent(EVENT_FLAMING_PESTILENCE, 24s);
events.ScheduleEvent(EVENT_DOOM_CULTIVATION, 32s);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_GNARLROOT) != DONE)
instance->SetBossState(DATA_GNARLROOT, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_TORTURED_SCREAM:
DoCastSelf(SPELL_TORTURED_SCREAM);
events.Repeat(22s);
break;
case EVENT_DREADFIRE_BARRAGE:
DoCastSelf(SPELL_DREADFIRE_BARRAGE);
events.Repeat(28s);
break;
case EVENT_CONTROLLED_BURN:
DoCastVictim(SPELL_CONTROLLED_BURN);
events.Repeat(24s);
break;
case EVENT_FLAMING_PESTILENCE:
DoCastSelf(SPELL_FLAMING_PESTILENCE);
events.Repeat(30s);
break;
case EVENT_DOOM_CULTIVATION:
DoCastSelf(SPELL_DOOM_CULTIVATION);
events.Repeat(40s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
};
void AddSC_boss_gnarlroot()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_gnarlroot);
}
@@ -1,32 +1,113 @@
/*
* This file is part of Algeth Team
*
* Igira the Cruel (200926) ? Amirdrassil.
* Spell ids taken from the retail encounter:
* 414425 Blistering Spear, 416998 Twisting Blade, 415624 Heart Stopper,
* 418533 Smashing Viscera, 422776 Marked for Torment.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum IgiraSpells
{
SPELL_BLISTERING_SPEAR = 414425,
SPELL_TWISTING_BLADE = 416998,
SPELL_HEART_STOPPER = 415624,
SPELL_SMASHING_VISCERA = 418533,
SPELL_MARKED_FOR_TORMENT = 422776
};
enum amirdrassilEvents
enum IgiraEvents
{
EVENT_BLISTERING_SPEAR = 1,
EVENT_TWISTING_BLADE,
EVENT_HEART_STOPPER,
EVENT_SMASHING_VISCERA,
EVENT_MARKED_FOR_TORMENT
};
// 200926 - Igira the Cruel
struct boss_igira_the_cruel : public BossAI
{
boss_igira_the_cruel(Creature* creature) : BossAI(creature, DATA_IGIRA_THE_CRUEL) {}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_IGIRA_THE_CRUEL) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
events.ScheduleEvent(EVENT_BLISTERING_SPEAR, 5s);
events.ScheduleEvent(EVENT_TWISTING_BLADE, 11s);
events.ScheduleEvent(EVENT_HEART_STOPPER, 16s);
events.ScheduleEvent(EVENT_SMASHING_VISCERA, 22s);
events.ScheduleEvent(EVENT_MARKED_FOR_TORMENT, 30s);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_IGIRA_THE_CRUEL) != DONE)
instance->SetBossState(DATA_IGIRA_THE_CRUEL, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_BLISTERING_SPEAR:
DoCastVictim(SPELL_BLISTERING_SPEAR);
events.Repeat(18s);
break;
case EVENT_TWISTING_BLADE:
DoCastVictim(SPELL_TWISTING_BLADE);
events.Repeat(21s);
break;
case EVENT_HEART_STOPPER:
DoCastVictim(SPELL_HEART_STOPPER);
events.Repeat(26s);
break;
case EVENT_SMASHING_VISCERA:
DoCastVictim(SPELL_SMASHING_VISCERA);
events.Repeat(32s);
break;
case EVENT_MARKED_FOR_TORMENT:
DoCastVictim(SPELL_MARKED_FOR_TORMENT);
events.Repeat(35s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
};
void AddSC_boss_igira_the_cruel()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_igira_the_cruel);
}
@@ -1,32 +1,137 @@
/*
* This file is part of Algeth Team
*
* Larodar, Keeper of the Flame (208445) ? Amirdrassil.
* Spell ids taken from the retail encounter:
* 418637 Furious Charge, 417634 Raging Inferno, 417610 Burning Ground,
* 418654 Scorching Roots. At 40%: 421998 Avatar of Ash, 421316 Consuming Flame.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum LarodarSpells
{
SPELL_FURIOUS_CHARGE = 418637,
SPELL_RAGING_INFERNO = 417634,
SPELL_BURNING_GROUND = 417610,
SPELL_SCORCHING_ROOTS = 418654,
SPELL_AVATAR_OF_ASH = 421998,
SPELL_CONSUMING_FLAME = 421316
};
enum amirdrassilEvents
enum LarodarEvents
{
EVENT_FURIOUS_CHARGE = 1,
EVENT_RAGING_INFERNO,
EVENT_BURNING_GROUND,
EVENT_SCORCHING_ROOTS,
EVENT_CONSUMING_FLAME
};
// 208445 - Larodar, Keeper of the Flame
struct boss_larodar_keeper_of_the_flame : public BossAI
{
boss_larodar_keeper_of_the_flame(Creature* creature) : BossAI(creature, DATA_LARODAR_KEEPER_OF_THE_FLAME), _phase(1) {}
void Reset() override
{
BossAI::Reset();
_phase = 1;
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_LARODAR_KEEPER_OF_THE_FLAME) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
SchedulePhaseOne();
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_LARODAR_KEEPER_OF_THE_FLAME) != DONE)
instance->SetBossState(DATA_LARODAR_KEEPER_OF_THE_FLAME, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (_phase == 1 && me->HealthBelowPct(40))
{
_phase = 2;
events.Reset();
DoCastSelf(SPELL_AVATAR_OF_ASH);
events.ScheduleEvent(EVENT_CONSUMING_FLAME, 8s);
events.ScheduleEvent(EVENT_FURIOUS_CHARGE, 14s);
events.ScheduleEvent(EVENT_SCORCHING_ROOTS, 20s);
}
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_FURIOUS_CHARGE:
DoCastVictim(SPELL_FURIOUS_CHARGE);
events.Repeat(22s);
break;
case EVENT_RAGING_INFERNO:
DoCastSelf(SPELL_RAGING_INFERNO);
events.Repeat(30s);
break;
case EVENT_BURNING_GROUND:
DoCastVictim(SPELL_BURNING_GROUND);
events.Repeat(18s);
break;
case EVENT_SCORCHING_ROOTS:
DoCastSelf(SPELL_SCORCHING_ROOTS);
events.Repeat(26s);
break;
case EVENT_CONSUMING_FLAME:
DoCastVictim(SPELL_CONSUMING_FLAME);
events.Repeat(16s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
void SchedulePhaseOne()
{
events.ScheduleEvent(EVENT_FURIOUS_CHARGE, 6s);
events.ScheduleEvent(EVENT_BURNING_GROUND, 12s);
events.ScheduleEvent(EVENT_SCORCHING_ROOTS, 18s);
events.ScheduleEvent(EVENT_RAGING_INFERNO, 26s);
}
uint8 _phase;
};
void AddSC_boss_larodar_keeper_of_the_flame()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_larodar_keeper_of_the_flame);
}
@@ -1,32 +1,113 @@
/*
* This file is part of Algeth Team
*
* Nymue, Weaver of the Cycle (206172) ? Amirdrassil.
* Spell ids taken from the retail encounter:
* 420846 Continuum, 429785 Impending Loom, 427722 Weaver's Burden,
* 425357 Surging Growth, 418423 Verdant Matrix.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum NymueSpells
{
SPELL_CONTINUUM = 420846,
SPELL_IMPENDING_LOOM = 429785,
SPELL_WEAVERS_BURDEN = 427722,
SPELL_SURGING_GROWTH = 425357,
SPELL_VERDANT_MATRIX = 418423
};
enum amirdrassilEvents
enum NymueEvents
{
EVENT_CONTINUUM = 1,
EVENT_IMPENDING_LOOM,
EVENT_WEAVERS_BURDEN,
EVENT_SURGING_GROWTH,
EVENT_VERDANT_MATRIX
};
void AddSC_boss_nyume_weaver_of_the_cycle()
// 206172 - Nymue, Weaver of the Cycle
struct boss_nymue_weaver_of_the_cycle : public BossAI
{
boss_nymue_weaver_of_the_cycle(Creature* creature) : BossAI(creature, DATA_NYMUE_WEAVER_OF_THE_CYCLE) {}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_NYMUE_WEAVER_OF_THE_CYCLE) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
events.ScheduleEvent(EVENT_WEAVERS_BURDEN, 6s);
events.ScheduleEvent(EVENT_SURGING_GROWTH, 12s);
events.ScheduleEvent(EVENT_VERDANT_MATRIX, 18s);
events.ScheduleEvent(EVENT_CONTINUUM, 24s);
events.ScheduleEvent(EVENT_IMPENDING_LOOM, 34s);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_NYMUE_WEAVER_OF_THE_CYCLE) != DONE)
instance->SetBossState(DATA_NYMUE_WEAVER_OF_THE_CYCLE, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CONTINUUM:
DoCastSelf(SPELL_CONTINUUM);
events.Repeat(36s);
break;
case EVENT_IMPENDING_LOOM:
DoCastSelf(SPELL_IMPENDING_LOOM);
events.Repeat(42s);
break;
case EVENT_WEAVERS_BURDEN:
DoCastVictim(SPELL_WEAVERS_BURDEN);
events.Repeat(22s);
break;
case EVENT_SURGING_GROWTH:
DoCastSelf(SPELL_SURGING_GROWTH);
events.Repeat(28s);
break;
case EVENT_VERDANT_MATRIX:
DoCastSelf(SPELL_VERDANT_MATRIX);
events.Repeat(32s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
};
void AddSC_boss_nymue_weaver_of_the_cycle()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_nymue_weaver_of_the_cycle);
}
@@ -1,32 +1,113 @@
/*
* This file is part of Algeth Team
*
* Smolderon (200927) ? Amirdrassil.
* Spell ids taken from the retail encounter:
* 421343 Brand of Damnation, 421455 Overheated, 422172 World in Flames,
* 422691 Lava Geysers, 426018 Seeking Inferno.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum SmolderonSpells
{
SPELL_BRAND_OF_DAMNATION = 421343,
SPELL_OVERHEATED = 421455,
SPELL_WORLD_IN_FLAMES = 422172,
SPELL_LAVA_GEYSERS = 422691,
SPELL_SEEKING_INFERNO = 426018
};
enum amirdrassilEvents
enum SmolderonEvents
{
EVENT_BRAND_OF_DAMNATION = 1,
EVENT_OVERHEATED,
EVENT_WORLD_IN_FLAMES,
EVENT_LAVA_GEYSERS,
EVENT_SEEKING_INFERNO
};
// 200927 - Smolderon
struct boss_smolderon : public BossAI
{
boss_smolderon(Creature* creature) : BossAI(creature, DATA_SMOLDERON) {}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_SMOLDERON) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
events.ScheduleEvent(EVENT_BRAND_OF_DAMNATION, 6s);
events.ScheduleEvent(EVENT_LAVA_GEYSERS, 12s);
events.ScheduleEvent(EVENT_SEEKING_INFERNO, 18s);
events.ScheduleEvent(EVENT_OVERHEATED, 26s);
events.ScheduleEvent(EVENT_WORLD_IN_FLAMES, 36s);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_SMOLDERON) != DONE)
instance->SetBossState(DATA_SMOLDERON, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_BRAND_OF_DAMNATION:
DoCastVictim(SPELL_BRAND_OF_DAMNATION);
events.Repeat(24s);
break;
case EVENT_OVERHEATED:
DoCastSelf(SPELL_OVERHEATED);
events.Repeat(40s);
break;
case EVENT_WORLD_IN_FLAMES:
DoCastSelf(SPELL_WORLD_IN_FLAMES);
events.Repeat(48s);
break;
case EVENT_LAVA_GEYSERS:
DoCastSelf(SPELL_LAVA_GEYSERS);
events.Repeat(28s);
break;
case EVENT_SEEKING_INFERNO:
DoCastVictim(SPELL_SEEKING_INFERNO);
events.Repeat(22s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
};
void AddSC_boss_smolderon()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_smolderon);
}
@@ -1,32 +1,140 @@
/*
* This file is part of Algeth Team
*
* Tindral Sageswift, Seer of the Flame (209090) ? Amirdrassil.
* Spell ids taken from the retail encounter:
* 424495 Mass Entanglement, 421398 Fire Beam, 424140 Supernova,
* 420236 Falling Star, 423266 Flaming Germination.
* Later phase: 420240 Sunflame.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum TindralSpells
{
SPELL_MASS_ENTANGLEMENT = 424495,
SPELL_FIRE_BEAM = 421398,
SPELL_SUPERNOVA = 424140,
SPELL_FALLING_STAR = 420236,
SPELL_FLAMING_GERMINATION = 423266,
SPELL_SUNFLAME = 420240
};
enum amirdrassilEvents
enum TindralEvents
{
EVENT_MASS_ENTANGLEMENT = 1,
EVENT_FIRE_BEAM,
EVENT_SUPERNOVA,
EVENT_FALLING_STAR,
EVENT_FLAMING_GERMINATION,
EVENT_SUNFLAME
};
// 209090 - Tindral Sageswift, Seer of the Flame
struct boss_tindral_sageswift_seer_of_the_flame : public BossAI
{
boss_tindral_sageswift_seer_of_the_flame(Creature* creature) : BossAI(creature, DATA_TINDRAL_SAGESWIFT_SEER_OF_THE_FLAME), _phase(1) {}
void Reset() override
{
BossAI::Reset();
_phase = 1;
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_TINDRAL_SAGESWIFT_SEER_OF_THE_FLAME) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
SchedulePhaseOne();
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_TINDRAL_SAGESWIFT_SEER_OF_THE_FLAME) != DONE)
instance->SetBossState(DATA_TINDRAL_SAGESWIFT_SEER_OF_THE_FLAME, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (_phase == 1 && me->HealthBelowPct(40))
{
_phase = 2;
events.ScheduleEvent(EVENT_SUNFLAME, 4s);
}
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_MASS_ENTANGLEMENT:
DoCastSelf(SPELL_MASS_ENTANGLEMENT);
events.Repeat(28s);
break;
case EVENT_FIRE_BEAM:
DoCastVictim(SPELL_FIRE_BEAM);
events.Repeat(18s);
break;
case EVENT_SUPERNOVA:
DoCastSelf(SPELL_SUPERNOVA);
events.Repeat(36s);
break;
case EVENT_FALLING_STAR:
DoCastSelf(SPELL_FALLING_STAR);
events.Repeat(22s);
break;
case EVENT_FLAMING_GERMINATION:
DoCastSelf(SPELL_FLAMING_GERMINATION);
events.Repeat(32s);
break;
case EVENT_SUNFLAME:
DoCastSelf(SPELL_SUNFLAME);
events.Repeat(20s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
void SchedulePhaseOne()
{
events.ScheduleEvent(EVENT_FIRE_BEAM, 6s);
events.ScheduleEvent(EVENT_FALLING_STAR, 12s);
events.ScheduleEvent(EVENT_MASS_ENTANGLEMENT, 18s);
events.ScheduleEvent(EVENT_FLAMING_GERMINATION, 24s);
events.ScheduleEvent(EVENT_SUPERNOVA, 34s);
}
uint8 _phase;
};
void AddSC_boss_tindral_sageswift_seer_of_the_flame()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_tindral_sageswift_seer_of_the_flame);
}
@@ -1,32 +1,113 @@
/*
* This file is part of Algeth Team
*
* Volcoross (208478) ? Amirdrassil.
* Spell ids taken from the retail encounter:
* 423117 Cataclysm Jaws, 420933 Flood of the Firelands, 421616 Volcanic Disgorge,
* 421672 Serpent's Fury, 420415 Scorchtail Crash.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "amirdrassil.h"
enum amirdrassilSpells
enum VolcorossSpells
{
SPELL_CATACLYSM_JAWS = 423117,
SPELL_FLOOD_OF_THE_FIRELANDS = 420933,
SPELL_VOLCANIC_DISGORGE = 421616,
SPELL_SERPENTS_FURY = 421672,
SPELL_SCORCHTAIL_CRASH = 420415
};
enum amirdrassilEvents
enum VolcorossEvents
{
EVENT_CATACLYSM_JAWS = 1,
EVENT_FLOOD_OF_THE_FIRELANDS,
EVENT_VOLCANIC_DISGORGE,
EVENT_SERPENTS_FURY,
EVENT_SCORCHTAIL_CRASH
};
// 208478 - Volcoross
struct boss_volcoross : public BossAI
{
boss_volcoross(Creature* creature) : BossAI(creature, DATA_VOLCOROSS) {}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_VOLCOROSS) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
events.ScheduleEvent(EVENT_CATACLYSM_JAWS, 6s);
events.ScheduleEvent(EVENT_SCORCHTAIL_CRASH, 12s);
events.ScheduleEvent(EVENT_VOLCANIC_DISGORGE, 18s);
events.ScheduleEvent(EVENT_SERPENTS_FURY, 24s);
events.ScheduleEvent(EVENT_FLOOD_OF_THE_FIRELANDS, 34s);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_VOLCOROSS) != DONE)
instance->SetBossState(DATA_VOLCOROSS, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CATACLYSM_JAWS:
DoCastVictim(SPELL_CATACLYSM_JAWS);
events.Repeat(20s);
break;
case EVENT_FLOOD_OF_THE_FIRELANDS:
DoCastSelf(SPELL_FLOOD_OF_THE_FIRELANDS);
events.Repeat(45s);
break;
case EVENT_VOLCANIC_DISGORGE:
DoCastSelf(SPELL_VOLCANIC_DISGORGE);
events.Repeat(28s);
break;
case EVENT_SERPENTS_FURY:
DoCastSelf(SPELL_SERPENTS_FURY);
events.Repeat(32s);
break;
case EVENT_SCORCHTAIL_CRASH:
DoCastVictim(SPELL_SCORCHTAIL_CRASH);
events.Repeat(24s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
};
void AddSC_boss_volcoross()
{
RegisterAmirdrassilTheDreamsHopeCreatureAI(boss_volcoross);
}
@@ -19,7 +19,6 @@ ObjectData const creatureData[] =
{ BOSS_SMOLDERON, DATA_SMOLDERON },
{ BOSS_TINDRAL_SAGESWIFT_SEER_OF_THE_FLAME, DATA_TINDRAL_SAGESWIFT_SEER_OF_THE_FLAME },
{ BOSS_FYRAKK_THE_BLAZING, DATA_FYRAKK_THE_BLAZING },
{ 0, 0 } // END
};
DungeonEncounterData const encounters[] =
@@ -48,7 +47,7 @@ enum AmirdrassilInstanceSpells
class instance_amirdrassil_the_dreams_hope : public InstanceMapScript
{
public:
instance_amirdrassil_the_dreams_hope() : InstanceMapScript(ATSCScriptName, 2549) { }
instance_amirdrassil_the_dreams_hope() : InstanceMapScript(ATSCScriptName, 2549) {}
struct instance_amirdrassil_the_dreams_hope_InstanceMapScript : public InstanceScript
{
@@ -56,6 +55,7 @@ public:
{
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadObjectData(creatureData, {});
LoadDungeonEncounterData(encounters);
}
};
@@ -1,32 +1,91 @@
/*
* Thordekk
*
*
*/
* Sennarth, The Cold Breath (187967) ? Vault of the Incarnates.
* The two spells already named in this file:
* 372539 Apex of Ice, 372051 Breath of Ice.
*/
#include "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "CellImpl.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "SpellScript.h"
#include "ScriptMgr.h"
#include "Vault_of_the_Incarnates.h"
enum SennarthSpells
{
SPELL_APEX_OF_ICE = 372539,
SPELL_BREATH_OF_ICE = 372051,
SPELL_APEX_OF_ICE = 372539,
SPELL_BREATH_OF_ICE = 372051
};
enum SennarthEvents
{
EVENT_APEX_OF_ICE = 1,
EVENT_BREATH_OF_ICE
};
// 187967 - Sennarth, The Cold Breath
struct boss_sennarth : public BossAI
{
boss_sennarth(Creature* creature) : BossAI(creature, DATA_SENNARTH) {}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (!instance || instance->GetBossState(DATA_SENNARTH) != IN_PROGRESS)
return;
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
events.ScheduleEvent(EVENT_BREATH_OF_ICE, 8s);
events.ScheduleEvent(EVENT_APEX_OF_ICE, 16s);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
}
void EnterEvadeMode(EvadeReason why) override
{
if (instance)
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
BossAI::EnterEvadeMode(why);
if (instance && instance->GetBossState(DATA_SENNARTH) != DONE)
instance->SetBossState(DATA_SENNARTH, FAIL);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_APEX_OF_ICE:
DoCastSelf(SPELL_APEX_OF_ICE);
events.Repeat(28s);
break;
case EVENT_BREATH_OF_ICE:
DoCastVictim(SPELL_BREATH_OF_ICE);
events.Repeat(18s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
};
//187967
void AddSC_boss_Sennarth()
{
RegisterCreatureAI(boss_sennarth);
}
@@ -22,6 +22,7 @@ enum VaultOfIncarnatesDataTypes
DATA_DATHEA,
DATA_OPALFANG,
DATA_EMBER_FIREPATH,
DATA_SENNARTH = 7,
};
enum VaultOfIncarnatesCreatures
@@ -74,8 +74,17 @@ void AddSC_boss_treemouth();
void AddSC_Boss_Gutshoot();
void AddSC_BrackenhideHollow_trash();
//AmirdrassilTheDreamsHope
// TODO
// Amirdrassil, the Dream's Hope
void AddSC_instance_amirdrassil_the_dreams_hope();
void AddSC_boss_gnarlroot();
void AddSC_boss_igira_the_cruel();
void AddSC_boss_volcoross();
void AddSC_boss_council_of_dreams();
void AddSC_boss_larodar_keeper_of_the_flame();
void AddSC_boss_nymue_weaver_of_the_cycle();
void AddSC_boss_smolderon();
void AddSC_boss_tindral_sageswift_seer_of_the_flame();
void AddSC_boss_fyrakk_the_blazing();
// Dawn Of The Infinite
void AddSC_instance_dawnoftheinfinite_df();
@@ -186,8 +195,17 @@ void AddDragonIslesScripts()
AddSC_Boss_Gutshoot();
AddSC_BrackenhideHollow_trash();
//AmirdrassilTheDreamsHope
// TODO
// Amirdrassil, the Dream's Hope
AddSC_instance_amirdrassil_the_dreams_hope();
AddSC_boss_gnarlroot();
AddSC_boss_igira_the_cruel();
AddSC_boss_volcoross();
AddSC_boss_council_of_dreams();
AddSC_boss_larodar_keeper_of_the_flame();
AddSC_boss_nymue_weaver_of_the_cycle();
AddSC_boss_smolderon();
AddSC_boss_tindral_sageswift_seer_of_the_flame();
AddSC_boss_fyrakk_the_blazing();
// Dawn Of The Infinite
AddSC_instance_dawnoftheinfinite_df();
@@ -0,0 +1,317 @@
/*
* Darkmaul Citadel ? Gor'groth (156814) and Ravnyr (156501).
*
* One encounter: Gor'groth fights until 40%, sacrifices himself and turns to
* stone, then Ravnyr is the journal boss (encounter 2326).
*
* Spell ids confirmed on Wowhead:
* 305913 Shadow Bolt (Exile's Reach ogre bolt)
* 308527 Unstable Shadows (1.5s cast, up to 3 motes, explode after 5s, 8 yards)
* 337047 Necrotic Breath (frontal cone, 40 Shadow damage)
* 305567 Wing Buffet (necrotic blast, 60 yards, knockback)
*/
#include "InstanceScript.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "darkmaul_citadel.h"
enum GorgrothSpells
{
SPELL_GORGROTH_SHADOW_BOLT = 305913,
SPELL_GORGROTH_UNSTABLE_SHADOWS = 308527,
SPELL_RAVNYR_NECROTIC_BREATH = 337047,
SPELL_RAVNYR_WING_BUFFET = 305567
};
enum GorgrothEvents
{
EVENT_SHADOW_BOLT = 1,
EVENT_UNSTABLE_SHADOWS,
EVENT_NECROTIC_BREATH,
EVENT_WING_BUFFET
};
enum GorgrothTexts
{
SAY_GORGROTH_AGGRO = 0,
SAY_GORGROTH_SACRIFICE = 1
};
enum RavnyrTexts
{
SAY_RAVNYR_AGGRO = 0,
SAY_RAVNYR_SLAY = 1
};
static constexpr float SacrificeHealthPct = 40.0f;
// 156814 - Gor'groth
struct boss_gorgroth : public ScriptedAI
{
boss_gorgroth(Creature* creature) : ScriptedAI(creature), _sacrificed(false), _evading(false) {}
void Reset() override
{
_events.Reset();
_sacrificed = false;
me->SetReactState(REACT_AGGRESSIVE);
me->SetImmuneToPC(false);
me->SetUninteractible(false);
me->SetControlled(false, UNIT_STATE_ROOT);
}
void JustEngagedWith(Unit* who) override
{
if (InstanceScript* instance = me->GetInstanceScript())
{
if (instance->GetBossState(DATA_RAVNYR) == DONE)
{
EnterEvadeMode(EvadeReason::Other);
return;
}
instance->SetBossState(DATA_RAVNYR, IN_PROGRESS);
}
Talk(SAY_GORGROTH_AGGRO);
_events.ScheduleEvent(EVENT_SHADOW_BOLT, 3s);
_events.ScheduleEvent(EVENT_UNSTABLE_SHADOWS, 12s);
if (who && !who->IsPlayer())
if (Player* player = me->SelectNearestPlayer(50.0f))
me->SetInCombatWith(player);
}
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo*/) override
{
if (_sacrificed)
{
damage = 0;
return;
}
if (!me->HealthBelowPctDamaged(SacrificeHealthPct, damage))
return;
_sacrificed = true;
damage = 0;
_events.Reset();
me->InterruptNonMeleeSpells(true);
Unit* victim = me->GetVictim();
if (!victim)
victim = attacker;
if (victim && !victim->IsPlayer())
victim = me->SelectNearestPlayer(80.0f);
me->AttackStop();
me->SetReactState(REACT_PASSIVE);
me->SetImmuneToPC(true);
me->SetUninteractible(true);
me->SetControlled(true, UNIT_STATE_ROOT);
uint64 threshold = me->CountPctFromMaxHealth(SacrificeHealthPct);
if (me->GetHealth() > threshold)
me->SetHealth(threshold);
// Drop combat without running home. EnterEvadeMode ignores this while Ravnyr is still up.
me->CombatStop(true);
Talk(SAY_GORGROTH_SACRIFICE);
RaiseRavnyr(victim);
}
void EnterEvadeMode(EvadeReason why) override
{
// Sacrifice ends combat. Only a Ravnyr wipe (boss state FAIL) may reset Gor'groth.
if (_sacrificed)
{
InstanceScript* instance = me->GetInstanceScript();
if (!instance || instance->GetBossState(DATA_RAVNYR) != FAIL)
return;
}
if (_evading)
return;
_evading = true;
_sacrificed = false;
me->SetControlled(false, UNIT_STATE_ROOT);
me->SetImmuneToPC(false);
me->SetUninteractible(false);
me->SetReactState(REACT_AGGRESSIVE);
if (Creature* ravnyr = me->FindNearestCreature(BOSS_RAVNYR, 150.0f, true))
if (ravnyr->IsInCombat())
ravnyr->AI()->EnterEvadeMode(why);
if (InstanceScript* instance = me->GetInstanceScript())
if (instance->GetBossState(DATA_RAVNYR) == IN_PROGRESS)
instance->SetBossState(DATA_RAVNYR, FAIL);
ScriptedAI::EnterEvadeMode(why);
_evading = false;
}
void UpdateAI(uint32 diff) override
{
if (_sacrificed || !UpdateVictim())
return;
_events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_SHADOW_BOLT:
DoCastVictim(SPELL_GORGROTH_SHADOW_BOLT);
_events.Repeat(6s);
break;
case EVENT_UNSTABLE_SHADOWS:
DoCast(SPELL_GORGROTH_UNSTABLE_SHADOWS);
_events.Repeat(22s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
void RaiseRavnyr(Unit* victim)
{
Creature* ravnyr = me->FindNearestCreature(BOSS_RAVNYR, 150.0f, true);
if (!ravnyr)
{
Position pos(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 6.0f, me->GetOrientation());
ravnyr = me->SummonCreature(BOSS_RAVNYR, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30s);
}
if (!ravnyr)
return;
ravnyr->SetImmuneToPC(false);
ravnyr->SetUninteractible(false);
ravnyr->SetReactState(REACT_AGGRESSIVE);
if (victim)
ravnyr->AI()->AttackStart(victim);
// Summoning Ravnyr calls BossAI::Reset, which clears IN_PROGRESS. Put the encounter back.
if (InstanceScript* instance = me->GetInstanceScript())
if (instance->GetBossState(DATA_RAVNYR) == NOT_STARTED)
instance->SetBossState(DATA_RAVNYR, IN_PROGRESS);
}
EventMap _events;
bool _sacrificed;
bool _evading;
};
// 156501 - Ravnyr
struct boss_ravnyr : public BossAI
{
boss_ravnyr(Creature* creature) : BossAI(creature, DATA_RAVNYR), _evading(false) {}
void Reset() override
{
BossAI::Reset();
me->SetReactState(REACT_PASSIVE);
me->SetImmuneToPC(true);
me->SetUninteractible(true);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
Talk(SAY_RAVNYR_AGGRO);
events.ScheduleEvent(EVENT_NECROTIC_BREATH, 8s);
events.ScheduleEvent(EVENT_WING_BUFFET, 18s);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer())
Talk(SAY_RAVNYR_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_JustDied();
if (Creature* gorgroth = instance->GetCreature(DATA_GORGROTH))
gorgroth->DespawnOrUnsummon(10s);
}
void EnterEvadeMode(EvadeReason why) override
{
if (_evading)
return;
_evading = true;
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (instance->GetBossState(DATA_RAVNYR) == IN_PROGRESS)
instance->SetBossState(DATA_RAVNYR, FAIL);
if (Creature* gorgroth = instance->GetCreature(DATA_GORGROTH))
if (gorgroth->IsAlive() && (gorgroth->IsInCombat() || gorgroth->IsImmuneToPC()))
gorgroth->AI()->EnterEvadeMode(why);
CreatureAI::EnterEvadeMode(why);
me->SetReactState(REACT_PASSIVE);
me->SetImmuneToPC(true);
me->SetUninteractible(true);
_evading = false;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_NECROTIC_BREATH:
DoCastVictim(SPELL_RAVNYR_NECROTIC_BREATH);
events.Repeat(16s);
break;
case EVENT_WING_BUFFET:
DoCast(SPELL_RAVNYR_WING_BUFFET);
events.Repeat(25s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
bool _evading;
};
void AddSC_boss_gorgroth_ravnyr()
{
RegisterDarkmaulCitadelCreatureAI(boss_gorgroth);
RegisterDarkmaulCitadelCreatureAI(boss_ravnyr);
}
@@ -579,6 +579,91 @@ enum ExileReachHelpers
CONVERSATION_REACHED_COOKING_PIT = 11408,
CONVERSATION_WAVED_TO_GORGROTH = 11512,
// Retail conversation ids (TrinityCore zone_exiles_reach). These replace scheduled creature_text Says.
CONVERSATION_WARMING_UP_ACCEPT = 12818,
CONVERSATION_WARMING_UP_COMPLETE = 12798,
CONVERSATION_STAND_PREFIGHT = 14422,
CONVERSATION_STAND_AGGRO = 14423,
CONVERSATION_STAND_JUMP = 14424,
CONVERSATION_MURLOC_MANIA_A = 12043,
CONVERSATION_MURLOC_MANIA_H = 14432,
CONVERSATION_BEACH_BJORN = 11685,
CONVERSATION_BEACH_AUSTIN = 12128,
CONVERSATION_BEACH_KEELA = 12127,
CONVERSATION_ESCORT_SURVIVOR_A = 12044,
CONVERSATION_ESCORT_SURVIVOR_H = 14437,
CONVERSATION_ESCORT_REACHED_CAMP = 12058,
CONVERSATION_COOKING_COMPLETE_A = 12863,
CONVERSATION_COOKING_COMPLETE_H = 14611,
CONVERSATION_NORTHBOUND_A = 12066,
CONVERSATION_NORTHBOUND_H = 14499,
CONVERSATION_BRIARPATCH_A = 12073,
CONVERSATION_BRIARPATCH_H = 14513,
CONVERSATION_GEOLORD_AGGRO = 13712,
CONVERSATION_QUILBOAR_COMPLETE_A = 12076,
CONVERSATION_QUILBOAR_COMPLETE_H = 14514,
CONVERSATION_RESIZE_COPTER_A = 12078,
CONVERSATION_RESIZE_COPTER_H = 14516,
CONVERSATION_RIDE_TO_RUINS_A = 12083,
CONVERSATION_RIDE_FROM_RUINS_A = 12084,
CONVERSATION_RIDE_TO_RUINS_H = 14517,
CONVERSATION_RIDE_FROM_RUINS_H = 14520,
CONVERSATION_RESIZING_ACCEPT = 12086,
CONVERSATION_RESIZING_REPORT = 12089,
CONVERSATION_BOAR_ACCEPT = 12090,
CONVERSATION_BOAR_HENRY = 15615,
CONVERSATION_BOAR_EXIT = 12092,
CONVERSATION_REDEATHER_ACCEPT = 14525,
CONVERSATION_REDEATHER_EXPLODE = 14526,
CONVERSATION_GRIMAXE_REDEATHER_SPAWN = 14527,
CONVERSATION_GRIMAXE_REDEATHER_COMPLETE = 15618,
CONVERSATION_ENHANCED_PREFIGHT = 13710,
CONVERSATION_ENHANCED_USE_SPELLS_AT_RANGE = 13630,
CONVERSATION_ENHANCED_READY = 14440,
CONVERSATION_ENHANCED_READY_WARRIOR = 14441,
CONVERSATION_ENHANCED_CHARGE_ZERO = 14444,
CONVERSATION_ENHANCED_SLAM = 14447,
CONVERSATION_ENHANCED_CHARGE_ONE = 14448,
CONVERSATION_ENHANCED_CHARGE_FINAL = 14449,
CONVERSATION_ENHANCED_HOLY_POWER_ONE = 14452,
CONVERSATION_ENHANCED_CRUSADER_ONE = 14453,
CONVERSATION_ENHANCED_CRUSADER_TWO = 14454,
CONVERSATION_ENHANCED_SHIELD_ONE = 14455,
CONVERSATION_ENHANCED_HOLY_POWER_TWO = 14456,
CONVERSATION_ENHANCED_SHIELD_TWO = 14457,
CONVERSATION_ENHANCED_CRUSADER_THREE = 14458,
CONVERSATION_ENHANCED_HOLY_POWER_THREE = 14459,
CONVERSATION_ENHANCED_SINISTER_ONE = 14486,
CONVERSATION_ENHANCED_ROGUE_ATTACKS = 14487,
CONVERSATION_ENHANCED_COMBO_THREE = 14488,
CONVERSATION_ENHANCED_EVISCERATE_THREE = 14489,
CONVERSATION_ENHANCED_SINISTER_TWO = 14490,
CONVERSATION_ENHANCED_COMBO_FOUR = 14491,
CONVERSATION_ENHANCED_EVISCERATE_FOUR = 14492,
CONVERSATION_ENHANCED_SINISTER_THREE = 14493,
CONVERSATION_ENHANCED_COMBO_FIVE = 14494,
CONVERSATION_ENHANCED_EVISCERATE_FAIL = 14495,
CONVERSATION_ENHANCED_SWP_CREDIT = 13892,
CONVERSATION_ENHANCED_SMITE = 14460,
CONVERSATION_ENHANCED_SWP_PRE = 14461,
CONVERSATION_ENHANCED_SWP_SOON = 14462,
CONVERSATION_ENHANCED_LB_FIRST = 13631,
CONVERSATION_ENHANCED_PRIMAL_FIRST = 13632,
CONVERSATION_ENHANCED_PRIMAL_CREDIT = 13633,
CONVERSATION_ENHANCED_LB_RANGE = 14475,
CONVERSATION_ENHANCED_FROSTBOLT = 13634,
CONVERSATION_ENHANCED_FIRE_BLAST_CREDIT = 13635,
CONVERSATION_ENHANCED_FROSTBOLT_CLOSE = 14476,
CONVERSATION_ENHANCED_FIRE_BLAST_NO_CREDIT = 14477,
CONVERSATION_ENHANCED_CORRUPTION_CREDIT = 13895,
CONVERSATION_ENHANCED_SHADOW_BOLT_WL = 14465,
CONVERSATION_ENHANCED_CORRUPTION_PRE = 14466,
CONVERSATION_ENHANCED_CORRUPTION_SOON = 14467,
CONVERSATION_ENHANCED_MOONFIRE_CREDIT = 13893,
CONVERSATION_ENHANCED_WRATH = 14471,
CONVERSATION_ENHANCED_MOONFIRE_PRE = 14472,
CONVERSATION_ENHANCED_MOONFIRE_SOON = 14473,
QUEST_OBJECTIVE_RIGHT_BENEATH_THEIR_EYES_REACHED_DARKMAUL_CITADEL_ENTRANCE = 16996,
QUEST_OBJECTIVE_RIGHT_BENEATH_THEIR_EYES_REACHED_COOKING_PIT = 17000,
QUEST_OBJECTIVE_RIGHT_BENEATH_THEIR_EYES_DANCING_AT_COOKING_PIT_A = 80052,
@@ -955,6 +1040,14 @@ private:
bool _fly;
};
inline void PlayExileConversation(Player* player, uint32 conversationId)
{
if (!player || !conversationId)
return;
player->PlayConversation(conversationId);
}
static void StartPrivateConversation(Player* player, uint32 conversationId, std::vector<Creature*> actors)
{
Conversation* conversation = Conversation::CreateConversation(conversationId, player, *player, player->GetGUID(), nullptr, false);
@@ -24,6 +24,7 @@
#include "Loot.h"
#include "exiles_reach.h"
#include <set>
class exiles_reach_abandoned_camp_playerscript : public PlayerScript
{
@@ -154,23 +155,14 @@ struct npc_exiles_reach_abandoned_camp_166854_156607 : public ScriptedAI
{
if (Creature* breka = player->GetSummonedCreatureByEntry(NPC_MURLOC_HIDEAWAY_BREKA))
{
breka->AI()->Talk(3, player);
// Won'sa!
me->AddDelayedEvent(3000, [this, player]() // time_delay
{
me->AI()->Talk(2, player); // So... hungry...
});
PlayExileConversation(player, CONVERSATION_ESCORT_REACHED_CAMP);
breka->GetMotionMaster()->MovePoint(0, -249.05f, -2492.35f, 17.957f, true);
metBrekaOrGarrick = true;
}
if (Creature* garrick = player->GetSummonedCreatureByEntry(NPC_MURLOC_HIDEAWAY_GARRICK))
{
garrick->AI()->Talk(3, player);
// Alaria!
PlayExileConversation(player, CONVERSATION_ESCORT_REACHED_CAMP);
garrick->GetMotionMaster()->MovePoint(0, -249.05f, -2492.35f, 17.957f, true);
metBrekaOrGarrick = true;
}
@@ -253,9 +245,9 @@ struct npc_exiles_reach_abandoned_camp_166906_156651 : public ScriptedAI
{
if (Creature* clone = player->SummonCreature(me->GetEntry(), me->GetPosition(), TEMPSUMMON_MANUAL_DESPAWN, 0s, 0U, 0U, player->GetGUID()))
{
clone->AI()->Talk(2, player);
// Recruit. Quilboar pose a far greater threat than murlocs. You must be ready to face them in battle.
// Recruit! Quilboar are far deadlier than murlocs. You need to be ready for the dangers ahead.
PlayExileConversation(player, CONVERSATION_ENHANCED_PREFIGHT);
uint32 readyConversation = player->GetClass() == CLASS_WARRIOR ? CONVERSATION_ENHANCED_READY_WARRIOR : CONVERSATION_ENHANCED_READY;
player->GetScheduler().Schedule(2s, [clone](TaskContext /*context*/)
{ clone->GetMotionMaster()->MovePoint(1, EnhancedCombatTacticFightingPositions[1], true); })
@@ -265,11 +257,9 @@ struct npc_exiles_reach_abandoned_camp_166906_156651 : public ScriptedAI
clone->GetMotionMaster()->MovePoint(2, EnhancedCombatTacticFightingPositions[2], true);
else
clone->GetMotionMaster()->MovePoint(3, EnhancedCombatTacticFightingPositions[3], true); })
.Schedule(12s, [clone, player](TaskContext /*context*/)
.Schedule(12s, [clone, player, readyConversation](TaskContext /*context*/)
{
clone->AI()->Talk(3, player);
// Your next lesson begins now!
// Alright, let's see what you know!
PlayExileConversation(player, readyConversation);
clone->SetFaction(14);
clone->SetLevel(player->GetLevel());
clone->SetTarget(player->GetGUID());
@@ -280,13 +270,13 @@ struct npc_exiles_reach_abandoned_camp_166906_156651 : public ScriptedAI
me->CastSpell(player, SPELL_UPDATE_PHASESHIFT, true);
}
void SpellHit(WorldObject* caster, SpellInfo const* /*spell*/) override
void SpellHit(WorldObject* caster, SpellInfo const* spell) override
{
// @TODO : make it class specific
Player* player = caster->ToPlayer();
if (!player)
if (!player || !spell)
return;
PlayEnhancedCombatConversation(player, spell->Id);
hitCounter++;
if (hitCounter == 3)
@@ -348,10 +338,115 @@ struct npc_exiles_reach_abandoned_camp_166906_156651 : public ScriptedAI
}
private:
void PlayEnhancedCombatConversation(Player* player, uint32 spellId)
{
if (player->GetQuestStatus(QUEST_ENHANCED_COMBAT_TACTICS) != QUEST_STATUS_INCOMPLETE &&
player->GetQuestStatus(QUEST_H_ENHANCED_COMBAT_TACTICS) != QUEST_STATUS_INCOMPLETE)
return;
uint32 conversationId = 0;
switch (player->GetClass())
{
case CLASS_WARRIOR:
if (spellId == 100)
conversationId = _chargeCasts++ == 0 ? CONVERSATION_ENHANCED_CHARGE_ZERO : (_chargeCasts == 2 ? CONVERSATION_ENHANCED_CHARGE_ONE : CONVERSATION_ENHANCED_CHARGE_FINAL);
else if (spellId == 1464)
conversationId = CONVERSATION_ENHANCED_SLAM;
break;
case CLASS_PALADIN:
if (spellId == 35395)
{
if (player->GetPower(POWER_HOLY_POWER) >= 3)
conversationId = CONVERSATION_ENHANCED_HOLY_POWER_THREE;
else if (_classCasts == 0)
conversationId = CONVERSATION_ENHANCED_CRUSADER_ONE;
else if (_classCasts == 1)
conversationId = CONVERSATION_ENHANCED_CRUSADER_TWO;
else
conversationId = CONVERSATION_ENHANCED_CRUSADER_THREE;
++_classCasts;
}
else if (spellId == 53600)
conversationId = _shieldCasts++ == 0 ? CONVERSATION_ENHANCED_SHIELD_ONE : CONVERSATION_ENHANCED_SHIELD_TWO;
break;
case CLASS_ROGUE:
if (spellId == 1752)
{
uint8 combo = static_cast<uint8>(player->GetPower(POWER_COMBO_POINTS));
if (combo >= 5)
conversationId = CONVERSATION_ENHANCED_COMBO_FIVE;
else if (combo >= 4)
conversationId = CONVERSATION_ENHANCED_COMBO_FOUR;
else if (combo >= 3)
conversationId = CONVERSATION_ENHANCED_COMBO_THREE;
else if (_classCasts == 0)
conversationId = CONVERSATION_ENHANCED_SINISTER_ONE;
else if (_classCasts == 1)
conversationId = CONVERSATION_ENHANCED_SINISTER_TWO;
else
conversationId = CONVERSATION_ENHANCED_SINISTER_THREE;
++_classCasts;
}
else if (spellId == 196819)
{
uint8 combo = static_cast<uint8>(player->GetPower(POWER_COMBO_POINTS));
if (combo < 3)
conversationId = CONVERSATION_ENHANCED_EVISCERATE_FAIL;
else if (combo < 4)
conversationId = CONVERSATION_ENHANCED_EVISCERATE_THREE;
else
conversationId = CONVERSATION_ENHANCED_EVISCERATE_FOUR;
}
break;
case CLASS_PRIEST:
if (spellId == 585)
conversationId = CONVERSATION_ENHANCED_SMITE;
else if (spellId == 589)
conversationId = _classCasts++ == 0 ? CONVERSATION_ENHANCED_SWP_PRE : CONVERSATION_ENHANCED_SWP_CREDIT;
break;
case CLASS_SHAMAN:
if (spellId == 188196)
conversationId = player->GetDistance(me) > 10.0f ? CONVERSATION_ENHANCED_LB_RANGE : CONVERSATION_ENHANCED_LB_FIRST;
else if (spellId == 73899)
conversationId = _classCasts++ == 0 ? CONVERSATION_ENHANCED_PRIMAL_FIRST : CONVERSATION_ENHANCED_PRIMAL_CREDIT;
break;
case CLASS_MAGE:
if (spellId == 116)
conversationId = player->GetDistance(me) < 8.0f ? CONVERSATION_ENHANCED_FROSTBOLT_CLOSE : CONVERSATION_ENHANCED_FROSTBOLT;
else if (spellId == 319836)
conversationId = _classCasts++ == 0 ? CONVERSATION_ENHANCED_FIRE_BLAST_NO_CREDIT : CONVERSATION_ENHANCED_FIRE_BLAST_CREDIT;
break;
case CLASS_WARLOCK:
if (spellId == 686)
conversationId = CONVERSATION_ENHANCED_SHADOW_BOLT_WL;
else if (spellId == 172)
conversationId = _classCasts++ == 0 ? CONVERSATION_ENHANCED_CORRUPTION_PRE : CONVERSATION_ENHANCED_CORRUPTION_CREDIT;
break;
case CLASS_DRUID:
if (spellId == 5176)
conversationId = CONVERSATION_ENHANCED_WRATH;
else if (spellId == 164812 || spellId == 8921)
conversationId = _classCasts++ == 0 ? CONVERSATION_ENHANCED_MOONFIRE_PRE : CONVERSATION_ENHANCED_MOONFIRE_CREDIT;
break;
default:
break;
}
if (!conversationId || _playedConversations.count(conversationId))
return;
_playedConversations.insert(conversationId);
PlayExileConversation(player, conversationId);
}
float orientation = 0.f;
uint32 timer = 1000;
int8 hitCounter = 0;
uint8 _chargeCasts = 0;
uint8 _classCasts = 0;
uint8 _shieldCasts = 0;
bool trainingQuestDone = false;
std::set<uint32> _playedConversations;
};
// Won'sa, Alaria (abandoned camp) (eaten)
@@ -365,17 +460,8 @@ struct npc_exiles_reach_abandoned_camp_175030_175031 : public ScriptedAI
{
if (TempSummon* tempBreka = player->SummonCreature(NPC_ABANDONED_CAMP_BREKA_WALKING, Position(-249.05f, -2492.35f, 17.957f, 0.466517f), TEMPSUMMON_TIMED_DESPAWN, 5min, 0U, 0U, player->GetGUID()))
{
tempBreka->AI()->Talk(0, player); // We'll head north to see what the others have found. Won'sa, rest and regain your strength before joining us.
tempBreka->FollowTarget(player);
tempBreka->SetSpeed(MOVE_RUN, player->GetSpeed(MOVE_RUN));
player->GetScheduler().Schedule(Milliseconds(10000), [this, player](TaskContext /*context*/)
{
me->AI()->Talk(0, player); // I be seein' ya soon, den. Ya daughter be right ta count on ya.
})
.Schedule(Milliseconds(16000), [this, tempBreka, player](TaskContext /*context*/)
{
tempBreka->AI()->Talk(1, player); // The Horde never abandons its soldiers. I will not abandon her.
});
}
}
@@ -383,14 +469,8 @@ struct npc_exiles_reach_abandoned_camp_175030_175031 : public ScriptedAI
{
if (TempSummon* tempGarrick = player->SummonCreature(NPC_ABANDONED_CAMP_GARRICK_WALKING, Position(-249.05f, -2492.35f, 17.957f, 0.466517f), TEMPSUMMON_TIMED_DESPAWN, 5min, 0U, 0U, player->GetGUID()))
{
tempGarrick->AI()->Talk(4, player); // We'll head north and rendezvous with the others. Alaria, recover your stamina before pushing forward.
tempGarrick->FollowTarget(player);
tempGarrick->SetSpeed(MOVE_RUN, player->GetSpeed(MOVE_RUN));
player->GetScheduler().Schedule(Milliseconds(10000), [tempGarrick, player](TaskContext /*context*/)
{
// I've never given up on any member of the Alliance. I'm not about to start by failing my son.
tempGarrick->Say(178620, player);
tempGarrick->PlayDirectSound(152746, player, 178620); });
}
}
}
@@ -0,0 +1,89 @@
/*
* Exile's Reach retail conversations.
* Quest accepts and completes that used to be scheduled creature Says
* play the same private conversation ids TrinityCore uses.
*/
#include "exiles_reach.h"
class exiles_reach_conversation_playerscript : public PlayerScript
{
public:
exiles_reach_conversation_playerscript() : PlayerScript("exiles_reach_conversation_playerscript") { }
void OnQuestStatusChange(Player* player, uint32 questId) override
{
if (player->GetMapId() != MAP_EXILES_REACH && player->GetMapId() != MAP_NPE)
return;
uint32 conversationId = 0;
switch (player->GetQuestStatus(questId))
{
case QUEST_STATUS_INCOMPLETE:
conversationId = ConversationOnAccept(questId);
break;
case QUEST_STATUS_COMPLETE:
conversationId = ConversationOnComplete(questId);
break;
default:
break;
}
PlayExileConversation(player, conversationId);
}
private:
static uint32 ConversationOnAccept(uint32 questId)
{
switch (questId)
{
case QUEST_WARMING_UP:
case QUEST_H_WARMING_UP:
return CONVERSATION_WARMING_UP_ACCEPT;
case QUEST_MURLOC_MANIA:
return CONVERSATION_MURLOC_MANIA_A;
case QUEST_H_MURLOC_MANIA:
return CONVERSATION_MURLOC_MANIA_H;
case QUEST_NORTHBOUND:
return CONVERSATION_NORTHBOUND_A;
case QUEST_H_NORTHBOUND:
return CONVERSATION_NORTHBOUND_H;
case QUEST_RIDE_OF_THE_SCIENTIFICALLY_ENHANCED_BOAR:
return CONVERSATION_BOAR_ACCEPT;
case QUEST_H_THE_REDEATHER:
return CONVERSATION_REDEATHER_ACCEPT;
default:
return 0;
}
}
static uint32 ConversationOnComplete(uint32 questId)
{
switch (questId)
{
case QUEST_WARMING_UP:
case QUEST_H_WARMING_UP:
return CONVERSATION_WARMING_UP_COMPLETE;
case QUEST_COOKING_MEAT:
return CONVERSATION_COOKING_COMPLETE_A;
case QUEST_H_COOKING_MEAT:
return CONVERSATION_COOKING_COMPLETE_H;
case QUEST_DOWN_WITH_THE_QUILBOAR:
return CONVERSATION_QUILBOAR_COMPLETE_A;
case QUEST_H_DOWN_WITH_THE_QUILBOAR:
return CONVERSATION_QUILBOAR_COMPLETE_H;
case QUEST_H_THE_REDEATHER:
return CONVERSATION_GRIMAXE_REDEATHER_COMPLETE;
case QUEST_RE_SIZING_THE_SITUATION:
case QUEST_H_RE_SIZING_THE_SITUATION:
return CONVERSATION_RESIZING_REPORT;
default:
return 0;
}
}
};
void AddSC_exiles_reach_conversations()
{
new exiles_reach_conversation_playerscript();
}
@@ -164,7 +164,7 @@ struct npc_exiles_reach_darkmaul_plains_167019_149899 : public ScriptedAI
scoutOmatic->SetObjectScale(0.2f);
scoutOmatic->SetUninteractible(true);
tempSpringstock->AI()->Talk(2, player); // I brought along a miniaturized Scout-o-Matic 5000! Let's just unshrink it...
PlayExileConversation(player, CONVERSATION_RESIZE_COPTER_A);
player->GetScheduler().Schedule(7s, [tempSpringstock, scoutOmatic, player](TaskContext /*context*/)
{ tempSpringstock->CastSpell(scoutOmatic, SPELL_RE_SIZING); });
@@ -181,7 +181,7 @@ struct npc_exiles_reach_darkmaul_plains_167019_149899 : public ScriptedAI
choppyBooster->SetObjectScale(0.2f);
choppyBooster->SetUninteractible(true);
tempFizzlepop->AI()->Talk(2, player); // I brought along a miniaturized Scout-o-Matic 5000! Let's just unshrink it...
PlayExileConversation(player, CONVERSATION_RESIZE_COPTER_H);
player->GetScheduler().Schedule(7s, [tempFizzlepop, choppyBooster, player](TaskContext /*context*/)
{ tempFizzlepop->CastSpell(choppyBooster, SPELL_RE_SIZING); });
@@ -200,10 +200,7 @@ struct npc_exiles_reach_darkmaul_plains_167019_149899 : public ScriptedAI
}
if (quest->GetQuestId() == QUEST_H_THE_REDEATHER)
{
me->AI()->Talk(4);
player->SummonCreature(NPC_DARKMAUL_PLAINS_CHOPPYBOOSTER_SLAUGHTER, CopterPositions[0], TEMPSUMMON_MANUAL_DESPAWN, 0s, 0U, 0U, player->GetGUID());
}
}
void MoveInLineOfSight(Unit* who) override
@@ -297,9 +294,9 @@ struct npc_exiles_reach_darkmaul_plains_167905_156526 : public VehicleAI
me->GetMotionMaster()->MovePoint(1, CopterPositions[0], true);
if (player->GetTeam() == ALLIANCE)
player->PlayConversation(12078);
player->PlayConversation(CONVERSATION_RIDE_FROM_RUINS_A);
else
player->PlayConversation(14520);
player->PlayConversation(CONVERSATION_RIDE_FROM_RUINS_H);
}
// We are at the start point again
@@ -335,9 +332,9 @@ struct npc_exiles_reach_darkmaul_plains_167905_156526 : public VehicleAI
me->GetMotionMaster()->MovePoint(1, CopterPositions[1], true);
if (player->GetTeam() == ALLIANCE)
player->PlayConversation(12083);
player->PlayConversation(CONVERSATION_RIDE_TO_RUINS_A);
else
player->PlayConversation(14517);
player->PlayConversation(CONVERSATION_RIDE_TO_RUINS_H);
}
}
}
@@ -381,7 +378,7 @@ struct npc_exiles_reach_darkmaul_plains_167915_156749 : public ScriptedAI
if (!player)
return;
player->PlayConversation(12086);
player->PlayConversation(CONVERSATION_RESIZING_ACCEPT);
me->FollowTarget(player);
}
@@ -602,8 +599,6 @@ public:
if (!caster || !target)
return;
Creature* creature = caster->ToCreature();
Creature* smallCopter = target->ToCreature();
if (!smallCopter)
return;
@@ -612,18 +607,6 @@ public:
smallCopter->CastSpell(smallCopter, SPELL_GROW_1);
smallCopter->PlayObjectSound(SOUND_COPTER_SIZING_1, smallCopter->GetGUID());
if (creature)
{
if (creature->GetEntry() == NPC_DARKMAUL_PLAINS_SPRINGSTOCK)
{
creature->AI()->Talk(3); // Whoops, too big! A slight adjustment, and...
}
if (creature->GetEntry() == NPC_DARKMAUL_PLAINS_FIZZLEPOP)
{
creature->AI()->Talk(1); // Okay, might have overdone it a bit. Hold on...
}
}
}
void PeriodicTick(AuraEffect const* /*aurEff*/)
@@ -693,30 +676,10 @@ public:
tickCount++;
}
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
Unit* caster = GetCaster();
if (!caster)
return;
if (Creature* creature = caster->ToCreature())
{
if (creature->GetEntry() == NPC_DARKMAUL_PLAINS_SPRINGSTOCK)
{
creature->AI()->Talk(4); // There! Just right. Give it a try!
}
if (creature->GetEntry() == NPC_DARKMAUL_PLAINS_FIZZLEPOP)
{
creature->AI()->Talk(2); // Perfect! It ain't pretty, but it gets the job done.
}
}
}
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_re_sizing_313269_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL);
OnEffectPeriodic += AuraEffectPeriodicFn(spell_re_sizing_313269_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
OnEffectRemove += AuraEffectRemoveFn(spell_re_sizing_313269_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
private:
@@ -772,6 +735,9 @@ public:
if (player->GetQuestStatus(QUEST_RE_SIZING_THE_SITUATION) || player->GetQuestStatus(QUEST_H_RE_SIZING_THE_SITUATION))
{
if (selectedFailedSpell == SPELL_RESIZER_HIT_EXPLODE && QuestAccepted(player, QUEST_H_THE_REDEATHER))
PlayExileConversation(player, CONVERSATION_REDEATHER_EXPLODE);
if (player->GetQuestObjectiveProgress(QUEST_RE_SIZING_THE_SITUATION, 0) < 2)
{
player->CastSpell(target, selectedFailedSpell, true);
@@ -164,9 +164,7 @@ struct npc_exiles_reach_shore_166782_156626 : public ScriptedAI
{
case QUEST_MURLOC_MANIA:
case QUEST_H_MURLOC_MANIA:
Talk(1);
// Recover our supplies, but don't fight too many murlocs at once. They're dangerous in packs.
// Murlocs are dangerous in large numbers.Take back our supplies, but avoid fighting too many of them at once.
// Conversations 12043 / 14432 are played from the quest-status script.
break;
case QUEST_H_EMERGENCY_FIRST_AID:
case QUEST_EMERGENCY_FIRST_AID:
@@ -238,9 +236,7 @@ struct npc_exiles_reach_shore_166782_156626 : public ScriptedAI
PhasingHandler::InheritPhaseShift(clone, player);
clone->FollowTarget(player);
clone->AI()->Talk(2, player);
// You three, search for more survivors. $n, you and I will push forward.
// You three, scour the island for any sign of the expedition. $N, you and I will scout ahead.
PlayExileConversation(player, player->IsInAlliance() ? CONVERSATION_ESCORT_SURVIVOR_A : CONVERSATION_ESCORT_SURVIVOR_H);
}
}
break;
@@ -319,8 +315,22 @@ struct npc_exiles_reach_shore_injured_buddie : public ScriptedAI
}
me->SetEmoteState(EMOTE_STATE_STAND);
player->KilledMonsterCredit(me->GetEntry());
Talk(0, player); // various text e.g: My thanks. We'll find the expedition yet.
player->KilledMonsterCredit(me->GetEntry());
uint32 beachConversation = 0;
switch (me->GetEntry())
{
case 156609: beachConversation = CONVERSATION_BEACH_BJORN; break;
case 156610: beachConversation = CONVERSATION_BEACH_AUSTIN; break;
case 156612: beachConversation = CONVERSATION_BEACH_KEELA; break;
default: break;
}
if (beachConversation)
PlayExileConversation(player, beachConversation);
else
Talk(0, player);
me->m_Events.AddEventAtOffset(new DelayedMoveEvent(player, me, pos, 0, false, false), 3s);
}
};
@@ -29,27 +29,75 @@ class exiles_reach_north_sea_playerscript : public PlayerScript
public:
exiles_reach_north_sea_playerscript() : PlayerScript("exiles_reach_north_sea_playerscript") {}
void GiveExileShipIntroQuest(Player* player)
{
uint32 const questId = player->IsInHorde() ? QUEST_H_WARMING_UP : QUEST_WARMING_UP;
if (player->GetQuestStatus(questId) != QUEST_STATUS_NONE)
return;
uint32 const laterQuests[] =
{
player->IsInHorde() ? QUEST_H_STAND_YOUR_GROUND : QUEST_STAND_YOUR_GROUND,
player->IsInHorde() ? QUEST_H_BRACE_FOR_IMPACT : QUEST_BRACE_FOR_IMPACT,
player->IsInHorde() ? QUEST_H_MURLOC_MANIA : QUEST_MURLOC_MANIA,
player->IsInHorde() ? QUEST_H_AN_END_TO_BEGINNINGS : QUEST_AN_END_TO_BEGINNINGS,
};
for (uint32 laterQuestId : laterQuests)
if (player->GetQuestStatus(laterQuestId) != QUEST_STATUS_NONE)
return;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest || !player->CanTakeQuest(quest, false) || !player->CanAddQuest(quest, false))
return;
player->AddQuestAndCheckCompletion(quest, nullptr);
}
void CheckExilesReachPhaseShift(Player* player)
{
// A new character has no quest yet. Hand out Warming Up before the phase
// update so the ship quest giver and the combat dummy stay visible.
GiveExileShipIntroQuest(player);
if (player->GetMapId() == MAP_EXILES_REACH)
{
switch (player->GetAreaId())
{
case AREA_NORTH_SEA_H:
{
WowCommunity::PhaseShift(player, { PHASE_GENERAL_SHIP, PHASE_HORDE_SHIP_15514 }, player->GetQuestStatus(QUEST_H_BRACE_FOR_IMPACT) || player->GetQuestStatus(QUEST_H_STAND_YOUR_GROUND));
// The general ship phase has to stay on. Gating it on Brace for Impact
// or Stand Your Ground hides Thrall / Breka from a character who has
// not taken a quest yet.
WowCommunity::PhaseShift(player, { PHASE_GENERAL_SHIP }, true);
if (player->IsInHorde())
{
WowCommunity::PhaseShift(player, { PHASE_ALLIANCE_SHIP_COLE, PHASE_ALLIANCE_SHIP_GARRICK_1, PHASE_ALLIANCE_SHIP_CREW_OUTSIDE }, true);
WowCommunity::PhaseShift(player, { PHASE_HORDE_SHIP_THROG }, !QuestAccepted(player, QUEST_H_STAND_YOUR_GROUND));
WowCommunity::PhaseShift(player, { PHASE_HORDE_SHIP_15287, PHASE_HORDE_SHIP_15516 }, player->GetQuestStatus(QUEST_H_BRACE_FOR_IMPACT));
// PhaseShift no longer removes phases. Drop the Alliance ship
// phases here so a Horde character does not keep Jaina's crew.
uint32 const allianceShipPhases[] =
{
PHASE_ALLIANCE_SHIP_COLE,
PHASE_ALLIANCE_SHIP_GARRICK_1,
PHASE_ALLIANCE_SHIP_CREW_OUTSIDE,
PHASE_ALLIANCE_SHIP_13619,
PHASE_ALLIANCE_SHIP_14350,
PHASE_ALLIANCE_SHIP_GARRICK_2,
PHASE_ALLIANCE_SHIP_CREW_INSIDE,
};
for (uint32 phaseId : allianceShipPhases)
if (player->GetPhaseShift().HasPhase(phaseId))
PhasingHandler::RemovePhase(player, phaseId, true);
WowCommunity::PhaseShift(player, { PHASE_HORDE_SHIP_15514 }, !QuestRewarded(player, QUEST_H_WARMING_UP));
WowCommunity::PhaseShift(player, { PHASE_HORDE_SHIP_THROG }, !QuestAccepted(player, QUEST_H_STAND_YOUR_GROUND) && !QuestCompleted(player, QUEST_H_BRACE_FOR_IMPACT));
WowCommunity::PhaseShift(player, { PHASE_HORDE_SHIP_15287 }, QuestRewarded(player, QUEST_H_WARMING_UP) && !QuestCompleted(player, QUEST_H_BRACE_FOR_IMPACT));
WowCommunity::PhaseShift(player, { PHASE_HORDE_SHIP_15516 }, QuestAcceptedOrCompleted(player, QUEST_H_BRACE_FOR_IMPACT));
}
}
break;
case AREA_NORTH_SEA_A:
{
WowCommunity::PhaseShift(player, { PHASE_GENERAL_SHIP }, true);
WowCommunity::PhaseShift(player, { PHASE_GENERAL_SHIP }, true);
if (player->IsInAlliance())
{
WowCommunity::PhaseShift(player, { PHASE_ALLIANCE_SHIP_COLE }, !QuestAccepted(player, QUEST_STAND_YOUR_GROUND) && !QuestCompleted(player, QUEST_BRACE_FOR_IMPACT));
@@ -119,12 +167,8 @@ struct npc_exiles_reach_ship_166573_156280 : public ScriptedAI
switch (quest->GetQuestId())
{
case QUEST_WARMING_UP:
// I need everyone in top form if we're to complete this mission. Show me what you can do!
Talk(1, player);
break;
case QUEST_H_WARMING_UP:
// We need to be ready for anything. Show me your skill in combat.
Talk(0, player);
// Conversation 12818 is played from the quest-status script.
break;
case QUEST_RIDE_OF_THE_SCIENTIFICALLY_ENHANCED_BOAR:
if (Creature* clone = player->SummonCreature(NPC_OGRE_RUINS_TORGOK_FIGHTER_A, FindSummonPosition(player, NPC_DARKMAUL_PLAINS_GARRICK), TEMPSUMMON_MANUAL_DESPAWN, 0s, 0U, 0U, player->GetGUID()))
@@ -138,12 +182,8 @@ struct npc_exiles_reach_ship_166573_156280 : public ScriptedAI
switch (quest->GetQuestId())
{
case QUEST_WARMING_UP:
// @TODO: walk inside and spawn it inside too and make phaseshift for it
Talk(3, player); // Private Cole will run you through the rest of the drills. I need to discuss this storm with the helmsman.
break;
case QUEST_H_WARMING_UP:
// @TODO: walk inside and spawn it inside too and make phaseshift for it
Talk(1, player); // Throg will spar with you for now. I must speak to the crew about the rain.
// Completion conversation 12798 is played from the quest-status script.
break;
case QUEST_BRACE_FOR_IMPACT:
player->TeleportTo(2175, -462.722f, -2620.544f, 0.472f, 0.760f);
@@ -191,21 +231,8 @@ struct npc_exiles_reach_ship_training_dummy : public ScriptedAI
if (!player)
return;
if (player->GetQuestStatus(QUEST_H_WARMING_UP))
{
if (Creature* breka = me->FindNearestCreature(NPC_SHIP_BREKA, 20.f, true))
breka->AI()->Talk(3, player); // Fine work. Next, we... rain? Our shaman said the skies would be clear.
if (player->GetQuestStatus(QUEST_H_WARMING_UP) || player->GetQuestStatus(QUEST_WARMING_UP))
player->CastSpell(player, SPELL_MEDIUM_FOG_AND_RAIN, true);
}
if (player->GetQuestStatus(QUEST_WARMING_UP))
{
if (Creature* garrick = me->FindNearestCreature(NPC_SHIP_GARRICK, 20.f, true))
garrick->AI()->Talk(2, player); // Good! Next... hm, that's odd. We weren't expecting rain...
player->CastSpell(player, SPELL_MEDIUM_FOG_AND_RAIN, true);
}
}
};
@@ -231,24 +258,22 @@ struct npc_exiles_reach_ship_166583_160664 : public ScriptedAI
if (Creature* clone = me->SummonPersonalClone(me->GetPosition(), TEMPSUMMON_MANUAL_DESPAWN, 0s, 0U, 0U, player))
{
clone->SetHealth(player->GetHealth());
clone->AI()->Talk(0, player);
// I'll take my position. Strike first, and strike hard!
// Let's move into sparring positions. I'll let you have the first strike.
PlayExileConversation(player, CONVERSATION_STAND_PREFIGHT);
player->GetScheduler().Schedule(Milliseconds(3000), [clone, player](TaskContext /*context*/)
// Chained TaskScheduler::Schedule calls are both relative to now, so the
// aggro step has to be nested. Jump first, then turn hostile.
player->GetScheduler().Schedule(Milliseconds(3000), [clone, player](TaskContext context)
{
clone->CastSpell(player, SPELL_JUMP_BEHIND, true);
clone->SetFaction(14);
clone->SetReactState(REACT_AGGRESSIVE);
clone->AI()->Talk(1, player);
// Never run from your foe! Victory or death!
// Never run from your opponent.Stand your ground and fight until the end!
})
.Schedule(Milliseconds(5000), [clone, player](TaskContext /*context*/)
{
if (clone->GetEntry() == 166583) // @TODO: add this text to cole too
clone->AI()->Talk(3, player); // Always face your enemy!
});
PlayExileConversation(player, CONVERSATION_STAND_JUMP);
context.Schedule(Milliseconds(2000), [clone, player](TaskContext /*context*/)
{
clone->SetFaction(14);
clone->SetReactState(REACT_AGGRESSIVE);
PlayExileConversation(player, CONVERSATION_STAND_AGGRO);
});
});
}
break;
case QUEST_BRACE_FOR_IMPACT:
@@ -299,7 +324,7 @@ struct npc_exiles_reach_ship_166583_160664 : public ScriptedAI
me->AI()->Talk(4, player);
BeFriendly();
// I concede! Your strength will see our mission through.
me->DespawnOrUnsummon();
me->DespawnOrUnsummon();
});
}
else
@@ -354,6 +354,11 @@ struct npc_exiles_reach_ogre_ruins_167126_167128_156799 : public ScriptedAI
{
npc_exiles_reach_ogre_ruins_167126_167128_156799(Creature* creature) : ScriptedAI(creature) {}
void Reset() override
{
_playedHenry = false;
}
void MoveInLineOfSight(Unit* who) override
{
Player* player = who->ToPlayer();
@@ -363,8 +368,18 @@ struct npc_exiles_reach_ogre_ruins_167126_167128_156799 : public ScriptedAI
if (player->GetQuestObjectiveProgress(QUEST_H_THE_REDEATHER, 2) == 8 && player->GetQuestObjectiveProgress(QUEST_H_THE_REDEATHER, 1) == 0 && !player->GetSummonedCreatureByEntry(NPC_OGRE_RUINS_TORGOK_FIGHTER_H))
{
player->SummonCreature(NPC_OGRE_RUINS_TORGOK_FIGHTER_H, FindSummonPosition(player, NPC_OGRE_RUINS_TORGOK_FIGHTER_H), TEMPSUMMON_MANUAL_DESPAWN, 0s, 0U, 0U, player->GetGUID());
PlayExileConversation(player, CONVERSATION_GRIMAXE_REDEATHER_SPAWN);
}
if (!_playedHenry && QuestAccepted(player, QUEST_RIDE_OF_THE_SCIENTIFICALLY_ENHANCED_BOAR) && player->IsInAlliance() && player->IsInDist(me, 15.0f))
{
_playedHenry = true;
PlayExileConversation(player, CONVERSATION_BOAR_HENRY);
}
}
private:
bool _playedHenry = false;
};
/* ## ACHIEVEMENT SCRIPTS > ## */
@@ -166,14 +166,9 @@ struct npc_exiles_reach_quilboar_briarpatch_166996_154327 : public ScriptedAI
ObjectGuid mithdranGuid = tempMithdranOrAustin->GetGUID();
ObjectGuid brekaGuid = tempBrekaOrGarrick->GetGUID();
tempBrekaOrGarrick->AI()->Talk(0, player);
PlayExileConversation(player, player->IsInHorde() ? CONVERSATION_BRIARPATCH_H : CONVERSATION_BRIARPATCH_A);
player->GetScheduler()
.Schedule(5s + 500ms, [mithdranGuid, player](TaskContext /*context*/)
{
if (Creature* mithdran = ObjectAccessor::GetCreature(*player, mithdranGuid))
mithdran->AI()->Talk(0, player);
})
.Schedule(10s, [mithdranGuid, brekaGuid, player](TaskContext /*context*/)
{
Creature* mithdran = ObjectAccessor::GetCreature(*player, mithdranGuid);
@@ -181,8 +176,6 @@ struct npc_exiles_reach_quilboar_briarpatch_166996_154327 : public ScriptedAI
if (!mithdran || !breka)
return;
breka->AI()->Talk(1, player);
mithdran->SetStandState(UNIT_STAND_STATE_STAND);
breka->SetStandState(UNIT_STAND_STATE_STAND);
@@ -222,11 +215,10 @@ struct npc_exiles_reach_geolord_grekog_151091 : public ScriptedAI
return;
me->SetEmoteState(EMOTE_ONESHOT_NONE);
Talk(0);
PlayExileConversation(player, CONVERSATION_GEOLORD_AGGRO);
}
void JustDied(Unit* attacker) override
void JustDied(Unit* /*attacker*/) override
{
// No need for personal summon
@@ -238,12 +230,7 @@ struct npc_exiles_reach_geolord_grekog_151091 : public ScriptedAI
if (creature->GetEntry() == NPC_QUILBOAR_BRIARPATCH_FIZZLEPOP || creature->GetEntry() == NPC_QUILBOAR_BRIARPATCH_SPRINGSTOCK)
{
creature->RemoveAura(SPELL_NECROTIC_RITUAL);
creature->AI()->Talk(1, attacker->ToPlayer());
creature->GetMotionMaster()->MovePoint(0, 16.6666f, -2511.82f, 73.39143f, true);
creature->AI()->Talk(1, attacker->ToPlayer());
// And that's why I always bet on the Horde! Let's scram!
// @TODO: Alliance text missing
creature->GetScheduler().Schedule(Milliseconds(3000), [creature, this](TaskContext /*context*/)
{
@@ -26,6 +26,10 @@ void AddSC_exiles_reach_hruns_barrow();
void AddSC_exiles_reach_harpy_roost();
void AddSC_exiles_reach_darkmaul_bridge();
void AddSC_exiles_reach_darkmaul_citadel();
void AddSC_instance_darkmaul_citadel();
void AddSC_boss_tunk();
void AddSC_boss_gorgroth_ravnyr();
void AddSC_exiles_reach_conversations();
void AddExilesReachScripts()
{
@@ -40,4 +44,8 @@ void AddExilesReachScripts()
AddSC_exiles_reach_harpy_roost();
AddSC_exiles_reach_darkmaul_bridge();
AddSC_exiles_reach_darkmaul_citadel();
AddSC_instance_darkmaul_citadel();
AddSC_boss_tunk();
AddSC_boss_gorgroth_ravnyr();
AddSC_exiles_reach_conversations();
}
@@ -0,0 +1,232 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Creature.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "TemporarySummon.h"
#include "the_stonevault.h"
enum SpeakerSpells
{
// Shared
SPELL_SILENCED_SPEAKER = 439577,
// Speaker Dorlita
SPELL_IGNEOUS_HAMMER = 428711,
SPELL_LAVA_CANNON = 449167,
SPELL_BLAZING_CRESCENDO = 428508,
SPELL_MAGMA_WAVE = 463057,
// Speaker Brokk
SPELL_MOLTEN_METAL = 430097,
SPELL_EXHAUST_VENTS = 428819,
SPELL_FLAMING_SCRAP = 429999,
SPELL_SCRAP_SONG = 428202
};
enum SpeakerEvents
{
// Speaker Dorlita
EVENT_IGNEOUS_HAMMER = 1,
EVENT_LAVA_CANNON,
// Speaker Brokk
EVENT_MOLTEN_METAL,
EVENT_EXHAUST_VENTS,
EVENT_FLAMING_SCRAP,
EVENT_SCRAP_SONG,
EVENT_SUMMON_SCRAP_CUBE,
EVENT_TRIGGER_CRESCENDO
};
enum SpeakerTexts
{
SAY_AGGRO = 0,
SAY_DEATH = 1
};
enum SpeakerMisc
{
FLAMING_SCRAP_TICKS = 12
};
// 213216 - Speaker Dorlita
struct boss_speaker_dorlita : public BossAI
{
boss_speaker_dorlita(Creature* creature) : BossAI(creature, DATA_MASTER_MACHINISTS) { }
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
if (Creature* brokk = instance->GetCreature(DATA_SPEAKER_BROKK))
DoZoneInCombat(brokk);
events.ScheduleEvent(EVENT_IGNEOUS_HAMMER, 2s);
events.ScheduleEvent(EVENT_LAVA_CANNON, 8s);
}
void DoAction(int32 action) override
{
if (action != ACTION_START_BLAZING_CRESCENDO)
return;
if (Creature* cube = me->FindNearestCreature(NPC_SCRAP_CUBE, 100.0f, true))
{
DoCast(cube, SPELL_BLAZING_CRESCENDO);
if (IsHeroic() || IsMythic() || IsMythicPlus())
me->CastSpell(cube->GetPosition(), SPELL_MAGMA_WAVE, true);
}
else
DoCastSelf(SPELL_BLAZING_CRESCENDO);
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
HandlePartnerDeath(killer);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_IGNEOUS_HAMMER:
DoCastVictim(SPELL_IGNEOUS_HAMMER);
events.Repeat(12s);
break;
case EVENT_LAVA_CANNON:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f, true))
DoCast(target, SPELL_LAVA_CANNON);
events.Repeat(17s);
break;
default:
break;
}
}
private:
void HandlePartnerDeath(Unit* killer)
{
Creature* partner = instance->GetCreature(DATA_SPEAKER_BROKK);
if (partner && partner->IsAlive())
{
partner->CastSpell(partner, SPELL_SILENCED_SPEAKER, true);
return;
}
BossAI::JustDied(killer);
}
};
// 213217 - Speaker Brokk
struct boss_speaker_brokk : public BossAI
{
boss_speaker_brokk(Creature* creature) : BossAI(creature, DATA_MASTER_MACHINISTS), _flamingScrapTicks(0) { }
void Reset() override
{
BossAI::Reset();
_flamingScrapTicks = 0;
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
if (Creature* dorlita = instance->GetCreature(DATA_SPEAKER_DORLITA))
DoZoneInCombat(dorlita);
events.ScheduleEvent(EVENT_MOLTEN_METAL, 1s);
events.ScheduleEvent(EVENT_EXHAUST_VENTS, 3s);
events.ScheduleEvent(EVENT_SCRAP_SONG, 11s);
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
HandlePartnerDeath(killer);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_MOLTEN_METAL:
DoCastSelf(SPELL_MOLTEN_METAL);
events.Repeat(12s);
break;
case EVENT_EXHAUST_VENTS:
DoCastSelf(SPELL_EXHAUST_VENTS, true);
_flamingScrapTicks = FLAMING_SCRAP_TICKS;
events.ScheduleEvent(EVENT_FLAMING_SCRAP, 500ms);
events.Repeat(30s);
break;
case EVENT_FLAMING_SCRAP:
me->CastSpell(me->GetRandomNearPosition(14.0f), SPELL_FLAMING_SCRAP, true);
if (--_flamingScrapTicks > 0)
events.Repeat(500ms);
break;
case EVENT_SCRAP_SONG:
DoCastSelf(SPELL_SCRAP_SONG);
events.ScheduleEvent(EVENT_SUMMON_SCRAP_CUBE, 2500ms);
events.Repeat(35s);
break;
case EVENT_SUMMON_SCRAP_CUBE:
me->SummonCreature(NPC_SCRAP_CUBE, me->GetPosition(), TEMPSUMMON_TIMED_DESPAWN, 25s);
events.ScheduleEvent(EVENT_TRIGGER_CRESCENDO, 6s);
break;
case EVENT_TRIGGER_CRESCENDO:
if (Creature* dorlita = instance->GetCreature(DATA_SPEAKER_DORLITA))
if (dorlita->IsAlive())
dorlita->AI()->DoAction(ACTION_START_BLAZING_CRESCENDO);
break;
default:
break;
}
}
private:
void HandlePartnerDeath(Unit* killer)
{
Creature* partner = instance->GetCreature(DATA_SPEAKER_DORLITA);
if (partner && partner->IsAlive())
{
partner->CastSpell(partner, SPELL_SILENCED_SPEAKER, true);
return;
}
BossAI::JustDied(killer);
}
uint8 _flamingScrapTicks;
};
void AddSC_boss_speaker_dorlita()
{
RegisterTheStonevaultCreatureAI(boss_speaker_dorlita);
RegisterTheStonevaultCreatureAI(boss_speaker_brokk);
}
@@ -0,0 +1,173 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Creature.h"
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "TemporarySummon.h"
#include "the_stonevault.h"
enum EirichSpells
{
SPELL_VOID_RIFT = 427315,
SPELL_VOID_CORRUPTION = 427461,
SPELL_VOID_CORRUPTION_AURA = 427329,
SPELL_UNBRIDLED_VOID = 427869,
SPELL_ENTROPIC_RECKONING = 427852
};
enum EirichEvents
{
EVENT_UNBRIDLED_VOID = 1,
EVENT_VOID_CORRUPTION,
EVENT_ENTROPIC_RECKONING,
EVENT_OPEN_VOID_RIFT
};
enum EirichTexts
{
SAY_AGGRO = 0,
SAY_DEATH = 1
};
enum EirichMisc
{
VOID_CORRUPTION_RIFT_RANGE = 2
};
// 213119 - Void Speaker Eirich
struct boss_void_speaker_eirich : public BossAI
{
boss_void_speaker_eirich(Creature* creature) : BossAI(creature, DATA_VOID_SPEAKER_EIRICH) { }
void Reset() override
{
BossAI::Reset();
summons.DespawnAll();
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
OpenVoidRift();
OpenVoidRift();
events.ScheduleEvent(EVENT_UNBRIDLED_VOID, 2s);
events.ScheduleEvent(EVENT_VOID_CORRUPTION, 8s);
events.ScheduleEvent(EVENT_ENTROPIC_RECKONING, 13s);
events.ScheduleEvent(EVENT_OPEN_VOID_RIFT, 30s);
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
summons.DespawnAll();
BossAI::JustDied(killer);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_UNBRIDLED_VOID:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 55.0f, true))
DoCast(target, SPELL_UNBRIDLED_VOID);
events.Repeat(22s);
break;
case EVENT_VOID_CORRUPTION:
{
std::list<Unit*> targets;
SelectTargetList(targets, IsMythicPlus() ? 4 : 2, SelectTargetMethod::Random, 0, 50.0f, true);
for (Unit* target : targets)
DoCast(target, SPELL_VOID_CORRUPTION);
events.Repeat(29s);
break;
}
case EVENT_ENTROPIC_RECKONING:
{
std::list<Unit*> targets;
SelectTargetList(targets, 3, SelectTargetMethod::Random, 0, 55.0f, true);
for (Unit* target : targets)
DoCast(target, SPELL_ENTROPIC_RECKONING);
events.Repeat(28s);
break;
}
case EVENT_OPEN_VOID_RIFT:
OpenVoidRift();
events.Repeat(30s);
break;
default:
break;
}
}
private:
void OpenVoidRift()
{
Position pos = me->GetRandomNearPosition(20.0f);
me->SummonCreature(NPC_VOID_RIFT, pos, TEMPSUMMON_TIMED_DESPAWN, 90s);
}
};
// 213125 - Void Rift
struct npc_void_rift : public ScriptedAI
{
npc_void_rift(Creature* creature) : ScriptedAI(creature) { }
void JustAppeared() override
{
DoCastSelf(SPELL_VOID_RIFT, true);
me->SetReactState(REACT_PASSIVE);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
}
};
// 427329 - Void Corruption
class spell_eirich_void_corruption : public AuraScript
{
void PeriodicTick(AuraEffect const* /*aurEff*/) const
{
Unit* target = GetTarget();
if (Creature* rift = target->FindNearestCreature(NPC_VOID_RIFT, VOID_CORRUPTION_RIFT_RANGE, true))
{
target->RemoveAurasDueToSpell(SPELL_VOID_CORRUPTION_AURA);
return;
}
if (AuraEffect* effect = GetAura()->GetEffect(EFFECT_0))
effect->SetAmount(int32(effect->GetAmount() * 1.25f));
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_eirich_void_corruption::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
}
};
void AddSC_boss_void_speaker_eirich()
{
RegisterTheStonevaultCreatureAI(boss_void_speaker_eirich);
RegisterTheStonevaultCreatureAI(npc_void_rift);
RegisterSpellScript(spell_eirich_void_corruption);
}
@@ -122,6 +122,22 @@ public:
}
}
void OnPlayerEnter(Player* player) override
{
if (!player->GetGroup() || !player->GetGroup()->GetLeaderGUID())
return;
player->GetGroup()->AddFollowerModeBots(player);
}
void OnPlayerExit(Player* player)
{
if (!player->GetGroup())
return;
player->GetGroup()->DespawnFollowerModeBots(player);
}
private:
uint8 _ednaIntroState;
uint8 _ednaIntroNPCsKillCount;
@@ -46,7 +46,12 @@ enum TheStonevaultCreatureIds
BOSS_SKARMORAX = 210156,
BOSS_SPEAKER_DORLITA = 213216,
BOSS_SPEAKER_BROKK = 213217,
BOSS_VOID_SPEAKER_EIRICH = 213119
BOSS_VOID_SPEAKER_EIRICH = 213119,
// Encounter Mechanics
NPC_SCRAP_CUBE = 213694,
NPC_VENT_STALKER = 214049,
NPC_VOID_RIFT = 213125
};
enum TheStonevaultGameObjectIds
@@ -58,7 +63,8 @@ enum TheStonevaultGameObjectIds
enum TheStonevaultSharedActions
{
ACTION_START_EDNA_INTRO = 0
ACTION_START_EDNA_INTRO = 0,
ACTION_START_BLAZING_CRESCENDO = 1
};
template <class AI, class T>
@@ -36,6 +36,8 @@ void AddSC_campaign_visions_of_a_shadowed_sun();
void AddSC_instance_the_stonevault();
void AddSC_boss_edna();
void AddSC_boss_skarmorak();
void AddSC_boss_speaker_dorlita();
void AddSC_boss_void_speaker_eirich();
// Nerub'ar Palace
void AddSC_instance_nerubar_palace();
@@ -97,6 +99,8 @@ void AddKhazAlgarScripts()
AddSC_instance_the_stonevault();
AddSC_boss_edna();
AddSC_boss_skarmorak();
AddSC_boss_speaker_dorlita();
AddSC_boss_void_speaker_eirich();
// Nerub'ar Palace
AddSC_instance_nerubar_palace();
@@ -36,7 +36,7 @@ static constexpr DungeonEncounterData encounters[] =
class instance_maisara_caverns : public InstanceMapScript
{
public:
instance_maisara_caverns() : InstanceMapScript("instance_maisara_caverns", 16395) { }
instance_maisara_caverns() : InstanceMapScript("instance_maisara_caverns", 2874) { }
struct instance_maisara_caverns_InstanceMapScript : public InstanceScript
{
@@ -0,0 +1,70 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MIDNIGHT_ZONE_AI_H_
#define MIDNIGHT_ZONE_AI_H_
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
namespace Scripts::QuelThalas::MidnightZones
{
inline void CreditMidnightObjective(Player* player, Creature* creature)
{
player->TalkedToCreature(creature->GetEntry(), creature->GetGUID());
player->KilledMonsterCredit(creature->GetEntry(), creature->GetGUID());
}
inline bool GossipCreditMidnightObjective(Creature* me, Player* player)
{
if (me->IsQuestGiver())
player->PrepareQuestMenu(me->GetGUID());
CreditMidnightObjective(player, me);
SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID());
return true;
}
// Talk-to / stay-awhile / speak-with objectives from Wowhead.
struct npc_midnight_objective_talk : public ScriptedAI
{
npc_midnight_objective_talk(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return GossipCreditMidnightObjective(me, player);
}
};
// Arrive / meet / reach / take-portal credits from Wowhead.
struct npc_midnight_objective_proximity : public ScriptedAI
{
npc_midnight_objective_proximity(Creature* creature) : ScriptedAI(creature) { }
void MoveInLineOfSight(Unit* who) override
{
Player* player = who->ToPlayer();
if (!player || !player->IsInDist(me, 15.0f))
return;
CreditMidnightObjective(player, me);
}
};
}
#endif
@@ -0,0 +1,27 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "midnight_zone_ai.h"
void AddSC_midnight_zone_objectives()
{
using namespace Scripts::QuelThalas::MidnightZones;
RegisterCreatureAI(npc_midnight_objective_talk);
RegisterCreatureAI(npc_midnight_objective_proximity);
}
@@ -20,6 +20,15 @@
// Silvermoon
void AddSC_silvermoon_city_midnight();
// Midnight zone campaigns
void AddSC_midnight_zone_objectives();
void AddSC_zone_isle_of_queldanas_midnight();
void AddSC_zone_eversong_woods_midnight();
void AddSC_zone_zulaman_midnight();
void AddSC_zone_harandar();
void AddSC_zone_voidstorm();
void AddSC_zone_arator_journey();
// Harandar
void AddSC_harandar_chapter_1_of_caves_and_cradles();
@@ -38,6 +47,15 @@ void AddQuelThalasScripts()
// Silvermoon
AddSC_silvermoon_city_midnight();
// Midnight zone campaigns
AddSC_midnight_zone_objectives();
AddSC_zone_isle_of_queldanas_midnight();
AddSC_zone_eversong_woods_midnight();
AddSC_zone_zulaman_midnight();
AddSC_zone_harandar();
AddSC_zone_voidstorm();
AddSC_zone_arator_journey();
// Harandar
AddSC_harandar_chapter_1_of_caves_and_cradles();
@@ -0,0 +1,65 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "midnight_zone_ai.h"
namespace Scripts::QuelThalas::AratorsJourney
{
// 237565 / 240747 - Alonsus Faol (Wowhead: Arator / One Final Relic)
struct npc_alonsus_faol_arator : public ScriptedAI
{
npc_alonsus_faol_arator(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 244644 / 240267 / 239827 / 254884 - Arator (Wowhead Path of Light / Gathering / Foothold)
struct npc_arator_journey : public ScriptedAI
{
npc_arator_journey(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 240334 - Shandris Feathermoon (Wowhead: Children of the Stars)
struct npc_shandris_children_of_the_stars : public ScriptedAI
{
npc_shandris_children_of_the_stars(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
}
void AddSC_zone_arator_journey()
{
using namespace Scripts::QuelThalas::AratorsJourney;
RegisterCreatureAI(npc_alonsus_faol_arator);
RegisterCreatureAI(npc_arator_journey);
RegisterCreatureAI(npc_shandris_children_of_the_stars);
}
@@ -0,0 +1,89 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "midnight_zone_ai.h"
namespace Scripts::QuelThalas::EversongWoodsMidnight
{
// 236716 - Arator (Wowhead: The Wayward Magister)
struct npc_arator_wayward_magister : public ScriptedAI
{
npc_arator_wayward_magister(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 236087 - Magister Umbric (Wowhead: The Wayward Magister)
struct npc_umbric_tranquillien : public ScriptedAI
{
npc_umbric_tranquillien(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 245004 - Lord Antenorian (Wowhead: The First to Know / Chance Meeting)
struct npc_lord_antenorian_tranquillien : public ScriptedAI
{
npc_lord_antenorian_tranquillien(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 235787 / 245704 - Lor'themar (Wowhead: Diplomacy / Fractured)
struct npc_lorthemar_sanctum_of_light : public ScriptedAI
{
npc_lorthemar_sanctum_of_light(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 241050 / 239685 - Contact Silvermoon (Wowhead: Silvermoon Must Know / Anything but Reprieve)
struct npc_contact_silvermoon_midnight : public ScriptedAI
{
npc_contact_silvermoon_midnight(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
}
void AddSC_zone_eversong_woods_midnight()
{
using namespace Scripts::QuelThalas::EversongWoodsMidnight;
RegisterCreatureAI(npc_arator_wayward_magister);
RegisterCreatureAI(npc_umbric_tranquillien);
RegisterCreatureAI(npc_lord_antenorian_tranquillien);
RegisterCreatureAI(npc_lorthemar_sanctum_of_light);
RegisterCreatureAI(npc_contact_silvermoon_midnight);
}
@@ -0,0 +1,105 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Conversation.h"
#include "Player.h"
#include "QuestDef.h"
#include "ScriptMgr.h"
#include "midnight_zone_ai.h"
namespace Scripts::QuelThalas::HarandarZone
{
namespace Quests
{
static constexpr uint32 TheRootCause = 86899;
static constexpr uint32 ToHarathir = 86900;
static constexpr uint32 HarandarBreadcrumb = 89402;
}
namespace Conversations
{
static constexpr uint32 TheRootCauseOpenPortal = 28444;
static constexpr uint32 FollowOrweyna = 29720;
static constexpr uint32 TalkToKupaal = 29598;
}
// 236704 - Orweyna (Wowhead: Harandar / The Root Cause)
struct npc_orweyna_fairbreeze_midnight : public ScriptedAI
{
npc_orweyna_fairbreeze_midnight(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
void OnQuestAccept(Player* player, Quest const* quest) override
{
if (quest->GetQuestId() == Quests::TheRootCause || quest->GetQuestId() == Quests::HarandarBreadcrumb)
Conversation::CreateConversation(Conversations::TheRootCauseOpenPortal, player, *player, player->GetGUID(), nullptr, false);
}
};
// 237209 - Follow Orweyna (Wowhead: To Har'athir)
struct npc_follow_orweyna_harandar : public ScriptedAI
{
npc_follow_orweyna_harandar(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
if (player->GetQuestStatus(Quests::ToHarathir) == QUEST_STATUS_INCOMPLETE)
Conversation::CreateConversation(Conversations::FollowOrweyna, player, *player, player->GetGUID(), nullptr, false);
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 237866 - Talk to Ku'paal (Wowhead: To Har'athir)
struct npc_kupaal_harandar : public ScriptedAI
{
npc_kupaal_harandar(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
if (player->GetQuestStatus(Quests::ToHarathir) == QUEST_STATUS_INCOMPLETE)
Conversation::CreateConversation(Conversations::TalkToKupaal, player, *player, player->GetGUID(), nullptr, false);
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 237345 - Talk to Halduron (Wowhead: The Council Assembles)
struct npc_halduron_harandar_council : public ScriptedAI
{
npc_halduron_harandar_council(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
}
void AddSC_zone_harandar()
{
using namespace Scripts::QuelThalas::HarandarZone;
RegisterCreatureAI(npc_orweyna_fairbreeze_midnight);
RegisterCreatureAI(npc_follow_orweyna_harandar);
RegisterCreatureAI(npc_kupaal_harandar);
RegisterCreatureAI(npc_halduron_harandar_council);
}
@@ -0,0 +1,62 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "QuestDef.h"
#include "ScriptMgr.h"
#include "midnight_zone_ai.h"
namespace Scripts::QuelThalas::IsleOfQuelDanasMidnight
{
namespace Quests
{
static constexpr uint32 LightLastStand = 86852;
}
// 241677 - Image of Lady Liadrin (Wowhead: Midnight / A Voice from the Light)
struct npc_image_of_lady_liadrin_midnight : public ScriptedAI
{
npc_image_of_lady_liadrin_midnight(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 241575 - Speak with Liadrin to begin the ritual (Wowhead: Light's Last Stand)
struct npc_liadrin_lights_last_stand : public ScriptedAI
{
npc_liadrin_lights_last_stand(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
if (player->GetQuestStatus(Quests::LightLastStand) == QUEST_STATUS_INCOMPLETE)
Scripts::QuelThalas::MidnightZones::CreditMidnightObjective(player, me);
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
}
void AddSC_zone_isle_of_queldanas_midnight()
{
using namespace Scripts::QuelThalas::IsleOfQuelDanasMidnight;
RegisterCreatureAI(npc_image_of_lady_liadrin_midnight);
RegisterCreatureAI(npc_liadrin_lights_last_stand);
}
@@ -0,0 +1,77 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "midnight_zone_ai.h"
namespace Scripts::QuelThalas::Voidstorm
{
// 235386 / 235405 - Magister Umbric (Wowhead: Voidstorm / Homecoming)
struct npc_umbric_voidstorm : public ScriptedAI
{
npc_umbric_voidstorm(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 235606 / 260236 - Lothraxion / Decimus (Wowhead: Reliable Enemies)
struct npc_lothraxion_voidstorm : public ScriptedAI
{
npc_lothraxion_voidstorm(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 247414 - Lady Liadrin (Wowhead: Feeding the Flame)
struct npc_liadrin_feeding_the_flame : public ScriptedAI
{
npc_liadrin_feeding_the_flame(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 242722 - Speak to Umbric (Wowhead: Clarity of Purpose)
struct npc_umbric_clarity_of_purpose : public ScriptedAI
{
npc_umbric_clarity_of_purpose(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
}
void AddSC_zone_voidstorm()
{
using namespace Scripts::QuelThalas::Voidstorm;
RegisterCreatureAI(npc_umbric_voidstorm);
RegisterCreatureAI(npc_lothraxion_voidstorm);
RegisterCreatureAI(npc_liadrin_feeding_the_flame);
RegisterCreatureAI(npc_umbric_clarity_of_purpose);
}
@@ -0,0 +1,65 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "midnight_zone_ai.h"
namespace Scripts::QuelThalas::ZulAmanMidnight
{
// 236436 - Lady Liadrin (Wowhead: The Gates of Zul'Aman)
struct npc_liadrin_gates_of_zulaman : public ScriptedAI
{
npc_liadrin_gates_of_zulaman(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 236126 / 240186 / 240934 - Zul'jarra (Wowhead Path / Hash'ey Away)
struct npc_zuljarra_midnight : public ScriptedAI
{
npc_zuljarra_midnight(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
// 249211 - Zul'jan (Wowhead: The Line Must Be Drawn Here)
struct npc_zuljan_midnight : public ScriptedAI
{
npc_zuljan_midnight(Creature* creature) : ScriptedAI(creature) { }
bool OnGossipHello(Player* player) override
{
return Scripts::QuelThalas::MidnightZones::GossipCreditMidnightObjective(me, player);
}
};
}
void AddSC_zone_zulaman_midnight()
{
using namespace Scripts::QuelThalas::ZulAmanMidnight;
RegisterCreatureAI(npc_liadrin_gates_of_zulaman);
RegisterCreatureAI(npc_zuljarra_midnight);
RegisterCreatureAI(npc_zuljan_midnight);
}
@@ -7,7 +7,7 @@
enum Data
{
DATA_HAKKAR_THE_SOULFLAYER = 1,
DATA_HAKKAR_THE_SOULFLAYER = 0,
DATA_THE_MANASTORMS,
DATA_DEALER_XYEXA,
DATA_MUEHZALA
@@ -9,6 +9,7 @@
#include "Player.h"
#include "InstanceScript.h"
#include "AreaTriggerAI.h"
#include "de_other_side.h"
class instance_de_other_side : public InstanceMapScript
{
@@ -19,6 +20,8 @@ public:
{
instance_de_other_side_InstanceMapScript(InstanceMap* map) : InstanceScript(map)
{
SetHeaders("DeOtherSide");
SetBossNumber(4);
}
};
@@ -5002,6 +5002,7 @@ enum BloodlustExhaustionSpell : uint32
// 256740 - Drums of the Maelstrom
// 309658 - Drums of Deathly Ferocity
// 381301 - Feral Hide Drums
// 466904 - Harrier's Cry
class spell_gen_bloodlust : public SpellScript
{
public:
@@ -6026,6 +6027,7 @@ void AddSC_generic_spell_scripts()
RegisterSpellScriptWithArgs(spell_gen_bloodlust, "spell_sha_heroism", SPELL_SHAMAN_EXHAUSTION);
RegisterSpellScriptWithArgs(spell_gen_bloodlust, "spell_mage_time_warp", SPELL_MAGE_TEMPORAL_DISPLACEMENT);
RegisterSpellScriptWithArgs(spell_gen_bloodlust, "spell_hun_primal_rage", SPELL_HUNTER_FATIGUED);
RegisterSpellScriptWithArgs(spell_gen_bloodlust, "spell_hun_harriers_cry", SPELL_HUNTER_FATIGUED);
RegisterSpellScriptWithArgs(spell_gen_bloodlust, "spell_evo_fury_of_the_aspects", SPELL_EVOKER_EXHAUSTION);
RegisterSpellScriptWithArgs(spell_gen_bloodlust, "spell_item_bloodlust_drums", SPELL_SHAMAN_EXHAUSTION);
RegisterSpellScript(spell_gen_spirit_heal_aoe);
+124 -4
View File
@@ -58,6 +58,7 @@ enum HunterSpells
SPELL_HUNTER_EXHILARATION_PET = 128594,
SPELL_HUNTER_EXHILARATION_R2 = 231546,
SPELL_HUNTER_EXPLOSIVE_SHOT_DAMAGE = 212680,
SPELL_HUNTER_FLARE_DISPEL = 132951,
SPELL_HUNTER_GREVIOUS_INJURY = 1217789,
SPELL_HUNTER_HIGH_EXPLOSIVE_TRAP = 236775,
SPELL_HUNTER_HIGH_EXPLOSIVE_TRAP_DAMAGE = 236777,
@@ -65,6 +66,7 @@ enum HunterSpells
SPELL_HUNTER_IMPLOSIVE_TRAP_DAMAGE = 462033,
SPELL_HUNTER_INTIMIDATION = 19577,
SPELL_HUNTER_INTIMIDATION_MARKSMANSHIP = 474421,
SPELL_HUNTER_KINDLING_FLARE = 459506,
SPELL_HUNTER_LATENT_POISON_STACK = 378015,
SPELL_HUNTER_LATENT_POISON_DAMAGE = 378016,
SPELL_HUNTER_LATENT_POISON_INJECTORS_STACK = 336903,
@@ -82,6 +84,7 @@ enum HunterSpells
SPELL_HUNTER_POSTHASTE_INCREASE_SPEED = 118922,
SPELL_HUNTER_POSTHASTE_TALENT = 109215,
SPELL_HUNTER_PRECISE_SHOTS = 260242,
SPELL_HUNTER_QUICK_DRAW_SPEED = 1279347,
SPELL_HUNTER_RAPID_FIRE = 257044,
SPELL_HUNTER_RAPID_FIRE_DAMAGE = 257045,
SPELL_HUNTER_RAPID_FIRE_ENERGIZE = 263585,
@@ -97,6 +100,7 @@ enum HunterSpells
SPELL_HUNTER_STREAMLINE_BUFF = 342076,
SPELL_HUNTER_T9_4P_GREATNESS = 68130,
SPELL_HUNTER_T29_2P_MARKSMANSHIP_DAMAGE = 394371,
SPELL_HUNTER_TAKE_AIM = 1273132,
SPELL_HUNTER_TAR_TRAP = 187699,
SPELL_HUNTER_TAR_TRAP_AREATRIGGER = 187700,
SPELL_HUNTER_TAR_TRAP_SLOW = 135299,
@@ -488,6 +492,25 @@ class spell_hun_explosive_shot : public AuraScript
}
};
// 132950 - Flare
// Ids - 510 and 35958
struct at_hun_legion_flare : public AreaTriggerAI
{
using AreaTriggerAI::AreaTriggerAI;
void OnUnitEnter(Unit* unit) override
{
if (Unit* caster = at->GetCaster())
if (caster->IsValidAttackTarget(unit))
unit->CastSpell(unit, SPELL_HUNTER_FLARE_DISPEL, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR);
}
void OnUnitExit(Unit* unit, AreaTriggerExitReason /*reason*/) override
{
unit->RemoveAurasDueToSpell(SPELL_HUNTER_FLARE_DISPEL);
}
};
// 236775 - High Explosive Trap
// 9810 - AreatriggerId
struct areatrigger_hun_high_explosive_trap : AreaTriggerAI
@@ -565,6 +588,33 @@ struct areatrigger_hun_implosive_trap : AreaTriggerAI
}
};
// 459506 - Kindling Flare (attached to 132950 - Flare)
class spell_hun_kindling_flare : public SpellScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_HUNTER_KINDLING_FLARE });
}
void HandleFlare(SpellEffIndex effIndex)
{
if (GetCaster()->HasAura(SPELL_HUNTER_KINDLING_FLARE))
PreventHitEffect(effIndex);
}
void HandleKindlingFlare(SpellEffIndex effIndex)
{
if (!GetCaster()->HasAura(SPELL_HUNTER_KINDLING_FLARE))
PreventHitEffect(effIndex);
}
void Register() override
{
OnEffectLaunch += SpellEffectFn(spell_hun_kindling_flare::HandleFlare, EFFECT_0, SPELL_EFFECT_CREATE_AREATRIGGER);
OnEffectLaunch += SpellEffectFn(spell_hun_kindling_flare::HandleKindlingFlare, EFFECT_1, SPELL_EFFECT_CREATE_AREATRIGGER);
}
};
// 378016 - Latent Poison
class spell_hun_latent_poison_damage : public SpellScript
{
@@ -1282,6 +1332,48 @@ class spell_hun_precise_shots : public AuraScript
}
};
// 459794 - Quick Draw
class spell_hun_quick_draw : public AuraScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_HUNTER_QUICK_DRAW_SPEED });
}
void HandleProc(ProcEventInfo const& eventInfo) const
{
eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_HUNTER_QUICK_DRAW_SPEED, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR);
}
void Register() override
{
OnProc += AuraProcFn(spell_hun_quick_draw::HandleProc);
}
};
// 1279347 - Quick Draw
class spell_hun_quick_draw_speed : public AuraScript
{
static constexpr uint8 SpeedAmount = 5;
bool Validate(SpellInfo const* spellInfo) override
{
return ValidateSpellEffect({ { spellInfo->Id, EFFECT_0 } })
&& spellInfo->GetEffect(EFFECT_0).IsAura(SPELL_AURA_MOD_INCREASE_SPEED);
}
void HandleSpeed(AuraEffect const* /*aurEff*/) const
{
if (AuraEffect* speed = GetEffect(EFFECT_0))
speed->ChangeAmount(speed->GetAmount() - SpeedAmount);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_hun_quick_draw_speed::HandleSpeed, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY);
}
};
// 257044 - Rapid Fire
class spell_hun_rapid_fire : public AuraScript
{
@@ -1290,10 +1382,10 @@ class spell_hun_rapid_fire : public AuraScript
return ValidateSpellInfo({ SPELL_HUNTER_RAPID_FIRE_DAMAGE });
}
void HandlePeriodic(AuraEffect const* /*aurEff*/)
void HandlePeriodic(AuraEffect const* /*aurEff*/) const
{
if (Unit* caster = GetCaster())
caster->CastSpell(GetTarget(), SPELL_HUNTER_RAPID_FIRE_DAMAGE, true);
caster->CastSpell(GetTarget(), SPELL_HUNTER_RAPID_FIRE_DAMAGE, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR);
}
void Register() override
@@ -1310,9 +1402,12 @@ class spell_hun_rapid_fire_damage : public SpellScript
return ValidateSpellInfo({ SPELL_HUNTER_RAPID_FIRE_ENERGIZE });
}
void HandleHit(SpellEffIndex /*effIndex*/)
void HandleHit(SpellEffIndex /*effIndex*/) const
{
GetCaster()->CastSpell(nullptr, SPELL_HUNTER_RAPID_FIRE_ENERGIZE, true);
GetCaster()->CastSpell(nullptr, SPELL_HUNTER_RAPID_FIRE_ENERGIZE, CastSpellExtraArgsInit{
.TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR,
.TriggeringSpell = GetSpell()
});
}
void Register() override
@@ -1558,6 +1653,26 @@ class spell_hun_surging_shots : public AuraScript
}
};
// 1273132 Take Aim (attached to 257045 - Rapid Fire)
class spell_hun_take_aim : public SpellScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_HUNTER_TAKE_AIM, SPELL_HUNTER_AIMED_SHOT });
}
void HandleOnHit() const
{
if (AuraEffect const* takeAim = GetCaster()->GetAuraEffect(SPELL_HUNTER_TAKE_AIM, EFFECT_1))
GetCaster()->GetSpellHistory()->ModifyChargeRecoveryTime(sSpellMgr->AssertSpellInfo(SPELL_HUNTER_AIMED_SHOT, GetCastDifficulty())->ChargeCategoryId, Milliseconds(-takeAim->GetAmountAsInt()));
}
void Register() override
{
OnHit += SpellHitFn(spell_hun_take_aim::HandleOnHit);
}
};
// 1515 - Tame Beast
class spell_hun_tame_beast : public SpellScript
{
@@ -2412,9 +2527,11 @@ void AddSC_hunter_spell_scripts()
RegisterSpellScript(spell_hun_emergency_salve);
RegisterSpellScript(spell_hun_exhilaration);
RegisterSpellScript(spell_hun_explosive_shot);
RegisterAreaTriggerAI(at_hun_legion_flare);
RegisterAreaTriggerAI(areatrigger_hun_high_explosive_trap);
RegisterSpellScript(spell_hun_hunting_party);
RegisterAreaTriggerAI(areatrigger_hun_implosive_trap);
RegisterSpellScript(spell_hun_kindling_flare);
RegisterSpellScript(spell_hun_latent_poison_damage);
RegisterSpellScript(spell_hun_latent_poison_trigger);
RegisterSpellScript(spell_hun_latent_poison_injectors_damage);
@@ -2430,6 +2547,8 @@ void AddSC_hunter_spell_scripts()
RegisterSpellScript(spell_hun_pet_heart_of_the_phoenix);
RegisterSpellScript(spell_hun_posthaste);
RegisterSpellScript(spell_hun_precise_shots);
RegisterSpellScript(spell_hun_quick_draw);
RegisterSpellScript(spell_hun_quick_draw_speed);
RegisterSpellScript(spell_hun_rapid_fire);
RegisterSpellScript(spell_hun_rapid_fire_damage);
RegisterSpellScript(spell_hun_rejuvenating_wind);
@@ -2441,6 +2560,7 @@ void AddSC_hunter_spell_scripts()
RegisterSpellScript(spell_hun_steady_shot);
RegisterSpellScript(spell_hun_streamline);
RegisterSpellScript(spell_hun_surging_shots);
RegisterSpellScript(spell_hun_take_aim);
RegisterSpellScript(spell_hun_tame_beast);
RegisterAreaTriggerAI(areatrigger_hun_tar_trap);
RegisterAreaTriggerAI(areatrigger_hun_tar_trap_activate);
@@ -0,0 +1,154 @@
/*
* Copyright (C) WowCommunity Project
*
* This SourceCode is NOT free a software. Please hold everything Private
* and read our Terms
*/
#include "ScriptMgr.h"
#include "city_of_echoes.h"
#include "TemporarySummon.h"
enum Spells
{
SPELL_SCARAB_LORD_ENERGY = 434302,
SPELL_INFESTATION = 433740,
SPELL_IMPALE = 435012,
SPELL_BURROW_CHARGE = 439506,
SPELL_EYE_OF_THE_SWARM = 433766,
SPELL_EYE_OF_THE_SWARM_BC = 435151,
SPELL_CEASELESS_SWARM = 433781,
SPELL_SUMMON_WEBMAGE = 1283246,
SPELL_WEB_BOLT = 434786,
SPELL_REVOLTING_VOLLEY = 448248
};
enum Events
{
EVENT_IMPALE = 1,
EVENT_INFESTATION,
EVENT_BURROW_CHARGE,
EVENT_EYE_OF_THE_SWARM,
EVENT_SUMMON_WEBMAGE
};
enum WebmageEvents
{
EVENT_WEB_BOLT = 1,
EVENT_REVOLTING_VOLLEY
};
enum Misc
{
MAX_ENERGY = 100,
ENERGY_GAIN_PER_SEC = 3,
SWARM_ENERGY = 100
};
struct boss_anubzekt : public BossAI
{
boss_anubzekt(Creature* creature) : BossAI(creature, DATA_ANUBZEKT) { }
void ScheduleTasks() override
{
_energy = 0;
DoCastSelf(SPELL_SCARAB_LORD_ENERGY, true);
events.ScheduleEvent(EVENT_INFESTATION, 2s);
events.ScheduleEvent(EVENT_IMPALE, 5s);
events.ScheduleEvent(EVENT_BURROW_CHARGE, 15s);
events.ScheduleEvent(EVENT_SUMMON_WEBMAGE, 25s);
events.ScheduleEvent(EVENT_EYE_OF_THE_SWARM, 1s);
}
void OnSpellCast(SpellInfo const* spell) override
{
if (spell->Id == SPELL_EYE_OF_THE_SWARM)
DoCastSelf(SPELL_EYE_OF_THE_SWARM_BC, true);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_IMPALE:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 0, 40.0f))
DoCast(target, SPELL_IMPALE);
events.Repeat(15s);
break;
case EVENT_INFESTATION:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true))
DoCast(target, SPELL_INFESTATION);
events.Repeat(11s);
break;
case EVENT_BURROW_CHARGE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true))
DoCast(target, SPELL_BURROW_CHARGE);
events.Repeat(35s);
break;
case EVENT_SUMMON_WEBMAGE:
if (Creature* webmage = me->SummonCreature(NPC_BLOODSTAINED_WEBMAGE,
me->GetRandomNearPosition(10.0f), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5s))
{
DoCastSelf(SPELL_SUMMON_WEBMAGE, true);
webmage->AI()->DoZoneInCombat();
}
events.Repeat(IsMythicPlus() ? 30s : 45s);
break;
case EVENT_EYE_OF_THE_SWARM:
{
_energy += ENERGY_GAIN_PER_SEC;
if (_energy >= SWARM_ENERGY && !me->HasUnitState(UNIT_STATE_CASTING))
{
_energy = 0;
DoCastSelf(SPELL_EYE_OF_THE_SWARM);
DoCastSelf(SPELL_CEASELESS_SWARM, true);
events.DelayEvents(8s);
}
events.Repeat(1s);
break;
}
default:
break;
}
}
private:
uint8 _energy;
};
struct npc_bloodstained_webmage : public ScriptedAI
{
npc_bloodstained_webmage(Creature* creature) : ScriptedAI(creature) { }
void ScheduleTasks() override
{
events.ScheduleEvent(EVENT_WEB_BOLT, 2s);
events.ScheduleEvent(EVENT_REVOLTING_VOLLEY, 12s);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_WEB_BOLT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true))
DoCast(target, SPELL_WEB_BOLT);
events.Repeat(4s);
break;
case EVENT_REVOLTING_VOLLEY:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true))
DoCast(target, SPELL_REVOLTING_VOLLEY);
events.Repeat(15s);
break;
default:
break;
}
}
};
void AddSC_boss_anubzekt()
{
RegisterCityOfEchoesCreatureAI(boss_anubzekt);
RegisterCreatureAI(npc_bloodstained_webmage);
}
@@ -0,0 +1,204 @@
/*
* Copyright (C) WowCommunity Project
*
* This SourceCode is NOT free a software. Please hold everything Private
* and read our Terms
*/
#include "ScriptMgr.h"
#include "city_of_echoes.h"
#include "TemporarySummon.h"
enum Spells
{
SPELL_VORACIOUS_BITE = 438471,
SPELL_ALERTING_SHRILL = 438476,
SPELL_ALERTING_SHRILL_DMG = 438495,
SPELL_GOSSAMER_ONSLAUGHT = 438473,
SPELL_VILE_WEBBING = 438960,
SPELL_SPAWN_JUMP = 439065,
SPELL_SCREENSHAKE = 439035,
SPELL_SPAWN_EGGS = 439027,
SPELL_HUNGER = 438490,
SPELL_HUNGER_CHANNEL = 439070,
SPELL_INSATIABLE = 446788,
SPELL_TREMOR_SENSE = 438486,
SPELL_HATCH = 436408
};
enum Events
{
EVENT_VORACIOUS_BITE = 1,
EVENT_ALERTING_SHRILL,
EVENT_GOSSAMER_ONSLAUGHT,
EVENT_CHECK_CRAWLER_DISTANCE,
EVENT_HATCH_EGG
};
struct boss_avanoxx : public BossAI
{
boss_avanoxx(Creature* creature) : BossAI(creature, DATA_AVANOXX) { }
void ScheduleTasks() override
{
_eggs.clear();
_crawlers.clear();
DoCastSelf(SPELL_SCREENSHAKE, true);
DoCastSelf(SPELL_SPAWN_JUMP);
DoCastSelf(SPELL_TREMOR_SENSE, true);
for (uint8 i = 0; i < 4; ++i)
SummonEgg();
events.ScheduleEvent(EVENT_VORACIOUS_BITE, 30s);
events.ScheduleEvent(EVENT_ALERTING_SHRILL, 38s);
events.ScheduleEvent(EVENT_GOSSAMER_ONSLAUGHT, 57s);
events.ScheduleEvent(EVENT_CHECK_CRAWLER_DISTANCE, 2s);
}
void OnSpellCast(SpellInfo const* spell) override
{
if (spell->Id == SPELL_ALERTING_SHRILL)
DoCastAOE(SPELL_ALERTING_SHRILL_DMG);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_VORACIOUS_BITE:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 0, 40.0f))
DoCast(target, SPELL_VORACIOUS_BITE);
events.Repeat(15s);
break;
case EVENT_ALERTING_SHRILL:
DoCastSelf(SPELL_ALERTING_SHRILL);
HatchEggs();
events.Repeat(30s);
break;
case EVENT_GOSSAMER_ONSLAUGHT:
DoCastSelf(SPELL_GOSSAMER_ONSLAUGHT);
for (uint8 i = 0; i < 5; ++i)
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f))
me->CastSpell(target, SPELL_VILE_WEBBING, true);
events.Repeat(45s);
break;
case EVENT_CHECK_CRAWLER_DISTANCE:
CheckCrawlersAndApplyInsatiable();
events.Repeat(1s);
break;
case EVENT_HATCH_EGG:
if (Creature* egg = FindEgg())
{
if (egg->IsAlive())
{
egg->CastSpell(egg, SPELL_HATCH);
SpawnCrawler(egg->GetPosition());
egg->DespawnOrUnsummon(100ms);
}
events.Repeat(500ms);
}
else
events.Repeat(1s);
break;
default:
break;
}
}
private:
void SummonEgg()
{
Position pos = me->GetRandomNearPosition(8.0f);
if (Creature* egg = me->SummonCreature(NPC_CRAWLER_EGG, pos, TEMPSUMMON_MANUAL_DESPAWN))
_eggs.push_back(egg->GetGUID());
}
void HatchEggs()
{
events.ScheduleEvent(EVENT_HATCH_EGG, 1s);
}
void SpawnCrawler(Position pos)
{
if (Creature* crawler = me->SummonCreature(NPC_STARVED_CRAWLER, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3s))
{
crawler->CastSpell(crawler, SPELL_HUNGER, true);
crawler->CastSpell(crawler, SPELL_HUNGER_CHANNEL, true);
_crawlers.push_back(crawler->GetGUID());
}
}
Creature* FindEgg()
{
for (ObjectGuid guid : _eggs)
if (Creature* egg = ObjectAccessor::GetCreature(*me, guid))
if (egg->IsAlive())
return egg;
return nullptr;
}
void CheckCrawlersAndApplyInsatiable()
{
if (_crawlers.empty())
return;
for (auto it = _crawlers.begin(); it != _crawlers.end();)
{
Creature* crawler = ObjectAccessor::GetCreature(*me, *it);
if (!crawler || !crawler->IsAlive())
{
it = _crawlers.erase(it);
continue;
}
if (me->GetDistance(crawler) <= 5.0f)
{
me->CastSpell(me, SPELL_INSATIABLE, true);
crawler->DespawnOrUnsummon();
it = _crawlers.erase(it);
}
else
++it;
}
}
std::vector<ObjectGuid> _eggs;
std::vector<ObjectGuid> _crawlers;
};
struct npc_crawler_egg : public ScriptedAI
{
npc_crawler_egg(Creature* creature) : ScriptedAI(creature)
{
me->SetReactState(REACT_PASSIVE);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
}
};
struct npc_starved_crawler : public ScriptedAI
{
npc_starved_crawler(Creature* creature) : ScriptedAI(creature) { }
void JustAppeared() override
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true))
AttackStart(target);
}
void UpdateAI(uint32 /*diff*/) override
{
if (!UpdateVictim())
return;
me->DoMeleeAttackIfReady();
}
};
void AddSC_boss_avanoxx()
{
RegisterCityOfEchoesCreatureAI(boss_avanoxx);
RegisterCreatureAI(npc_crawler_egg);
RegisterCreatureAI(npc_starved_crawler);
}
@@ -0,0 +1,151 @@
/*
* Copyright (C) WowCommunity Project
*
* This SourceCode is NOT free a software. Please hold everything Private
* and read our Terms
*/
#include "ScriptMgr.h"
#include "city_of_echoes.h"
#include "TemporarySummon.h"
enum Spells
{
SPELL_COSMIC_POWER = 431125,
SPELL_CALL_DRONES = 431960,
SPELL_ERUPTING_WEBS = 432130,
SPELL_VENOM_VOLLEY = 432227,
SPELL_COSMIC_SINGULARITY = 432117,
SPELL_SINGULARITY_PRECAST = 447504,
SPELL_COSMIC_SINGULARITY_KB = 439680,
SPELL_FADED = 432119,
SPELL_BLACK_BLOOD = 431985,
SPELL_BLACK_BLOOD_DMG = 432011,
SPELL_BREAK_PLAYER_TARGET = 140562
};
enum Events
{
EVENT_CALL_DRONES = 1,
EVENT_ERUPTING_WEBS,
EVENT_VENOM_VOLLEY,
EVENT_COSMIC_SINGULARITY
};
enum Misc
{
MAX_ENERGY = 100,
ENERGY_GAIN_PER_SEC = 4,
SINGULARITY_ENERGY = 100
};
enum BloodworkerEvents
{
EVENT_BLACK_BLOOD = 1
};
struct boss_kikatal : public BossAI
{
boss_kikatal(Creature* creature) : BossAI(creature, DATA_KIKATAL_THE_HARVESTER) { }
void ScheduleTasks() override
{
_energy = 0;
DoCastSelf(SPELL_COSMIC_POWER, true);
events.ScheduleEvent(EVENT_CALL_DRONES, 5s);
events.ScheduleEvent(EVENT_ERUPTING_WEBS, 10s);
events.ScheduleEvent(EVENT_VENOM_VOLLEY, 16s);
events.ScheduleEvent(EVENT_COSMIC_SINGULARITY, 1s);
}
void OnSpellCast(SpellInfo const* spell) override
{
if (spell->Id == SPELL_COSMIC_SINGULARITY)
{
DoCastSelf(SPELL_COSMIC_SINGULARITY_KB, true);
for (MapReference const& ref : me->GetMap()->GetPlayers())
if (Player* player = ref.GetSource())
if (player->IsAlive() && me->GetDistance(player) <= 60.0f)
me->CastSpell(player, SPELL_FADED, true);
}
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_CALL_DRONES:
{
Position pos = me->GetRandomNearPosition(20.0f);
if (Creature* bloodworker = me->SummonCreature(NPC_BLOODWORKER, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, IsMythicPlus() ? 30s : 70s))
{
DoCastSelf(SPELL_CALL_DRONES, true);
bloodworker->AI()->DoZoneInCombat();
}
events.Repeat(IsMythicPlus() ? 20s : 35s);
break;
}
case EVENT_ERUPTING_WEBS:
DoCastSelf(SPELL_ERUPTING_WEBS);
events.Repeat(27s);
break;
case EVENT_VENOM_VOLLEY:
DoCastRandom(SPELL_VENOM_VOLLEY);
events.Repeat(27s);
break;
case EVENT_COSMIC_SINGULARITY:
_energy += ENERGY_GAIN_PER_SEC;
if (_energy >= SINGULARITY_ENERGY && !me->HasUnitState(UNIT_STATE_CASTING))
{
_energy = 0;
DoCastSelf(SPELL_SINGULARITY_PRECAST, true);
DoCastSelf(SPELL_COSMIC_SINGULARITY);
events.DelayEvents(8s);
}
events.Repeat(1s);
break;
default:
break;
}
}
private:
uint8 _energy;
};
struct npc_bloodworker : public ScriptedAI
{
npc_bloodworker(Creature* creature) : ScriptedAI(creature) { }
void ScheduleTasks() override
{
events.ScheduleEvent(EVENT_BLACK_BLOOD, 2s);
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_BLACK_BLOOD:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true))
{
DoCast(target, SPELL_BLACK_BLOOD);
DoCast(target, SPELL_BLACK_BLOOD_DMG, true);
DoCast(target, SPELL_BREAK_PLAYER_TARGET, true);
}
events.Repeat(2s);
break;
default:
break;
}
}
private:
};
void AddSC_boss_kikatal()
{
RegisterCityOfEchoesCreatureAI(boss_kikatal);
RegisterCreatureAI(npc_bloodworker);
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) WowCommunity Project
*
* This SourceCode is NOT free a software. Please hold everything Private
* and read our Terms
*/
#ifndef DEF_CITY_OF_ECHOES_H_
#define DEF_CITY_OF_ECHOES_H_
#include "CreatureAIImpl.h"
#define COEScriptName "instance_city_of_echoes"
#define DataHeader "cityofechoes"
uint32 const EncounterCount = 3;
enum CityOfEchoesDataTypes
{
DATA_AVANOXX = 0,
DATA_ANUBZEKT,
DATA_KIKATAL_THE_HARVESTER,
};
enum CityOfEchoesCreatureIds
{
BOSS_AVANOXX = 213179,
BOSS_ANUBZEKT = 215405,
BOSS_KIKATAL_THE_HARVESTER = 215407,
NPC_CRAWLER_EGG = 218183,
NPC_STARVED_CRAWLER = 218961,
NPC_BLOODSTAINED_WEBMAGE = 223253,
NPC_BLOODWORKER = 215826,
NPC_BLACK_BLOOD = 215968,
};
template <class AI, class T>
inline AI* GetCityOfEchoesAI(T* obj)
{
return GetInstanceAI<AI>(obj, COEScriptName);
}
#define RegisterCityOfEchoesCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetCityOfEchoesAI)
#endif
@@ -0,0 +1,83 @@
/*
* Copyright (C) WowCommunity Project
*
* This SourceCode is NOT free a software. Please hold everything Private
* and read our Terms
*/
#include "InstanceScript.h"
#include "ScriptMgr.h"
#include "city_of_echoes.h"
static constexpr ObjectData creatureData[] =
{
{ BOSS_AVANOXX, DATA_AVANOXX },
{ BOSS_ANUBZEKT, DATA_ANUBZEKT },
{ BOSS_KIKATAL_THE_HARVESTER, DATA_KIKATAL_THE_HARVESTER },
};
static constexpr DungeonEncounterData const encounters[] =
{
{ DATA_AVANOXX, {{ 2907 }} },
{ DATA_ANUBZEKT, {{ 2908 }} },
{ DATA_KIKATAL_THE_HARVESTER, {{ 2905 }} },
};
class instance_city_of_echoes : public InstanceMapScript
{
public:
instance_city_of_echoes() : InstanceMapScript(COEScriptName, 2660) { }
struct instance_city_of_echoes_InstanceMapScript: public InstanceScript
{
instance_city_of_echoes_InstanceMapScript(InstanceMap* map) : InstanceScript(map)
{
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadObjectData(creatureData, {});
LoadDungeonEncounterData(encounters);
}
void OnPlayerEnter(Player* player) override
{
if (!player->GetGroup() || !player->GetGroup()->GetLeaderGUID())
return;
ObjectGuid leaderguid = player->GetGroup()->GetLeaderGUID();
if (!leaderguid)
return;
if (Player* leader = ObjectAccessor::GetPlayer(*player, leaderguid))
{
if (leader->GetTeam() == HORDE)
faction_group = HORDE;
else if (leader->GetTeam() == ALLIANCE)
faction_group = ALLIANCE;
else
faction_group = HORDE;
}
player->GetGroup()->AddFollowerModeBots(player);
}
void OnPlayerExit(Player* player)
{
player->GetGroup()->DespawnFollowerModeBots(player);
}
private:
Team faction_group = HORDE;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
return new instance_city_of_echoes_InstanceMapScript(map);
}
};
void AddSC_instance_city_of_echoes()
{
new instance_city_of_echoes();
}
@@ -24,6 +24,12 @@ void AddSC_boss_captain_dailcry();
void AddSC_boss_prioress_murrpray();
void AddSC_priority_of_the_sacred_flame();
//Ara-Kara, City of Echoes
void AddSC_instance_city_of_echoes();
void AddSC_boss_avanoxx();
void AddSC_boss_anubzekt();
void AddSC_boss_kikatal();
// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
void AddWarWithinScripts()
@@ -43,4 +49,10 @@ void AddWarWithinScripts()
AddSC_boss_prioress_murrpray();
AddSC_priority_of_the_sacred_flame();
//Ara-Kara, City of Echoes
AddSC_instance_city_of_echoes();
AddSC_boss_avanoxx();
AddSC_boss_anubzekt();
AddSC_boss_kikatal();
}
File diff suppressed because it is too large Load Diff