diff options
24 files changed, 2112 insertions, 767 deletions
diff --git a/sql/updates/world/2016_01_19_05_world.sql b/sql/updates/world/2016_01_19_05_world.sql new file mode 100644 index 00000000000..5afd9e2b8a5 --- /dev/null +++ b/sql/updates/world/2016_01_19_05_world.sql @@ -0,0 +1 @@ +DELETE FROM `creature_text` WHERE `entry` IN (17900,17901) AND `BroadcastTextId`=8329; diff --git a/sql/updates/world/2016_01_19_06_world.sql b/sql/updates/world/2016_01_19_06_world.sql new file mode 100644 index 00000000000..cece0c623bf --- /dev/null +++ b/sql/updates/world/2016_01_19_06_world.sql @@ -0,0 +1,97 @@ +-- gothik the harvester rework +-- creature (13 entries) +SET @CGUID = 127618; -- PR NOTE: This needs to match the value set in boss_gothik.cpp for the CGUID_TRIGGER const + +-- boss messages +DELETE FROM `creature_text` WHERE `entry`=16060; +INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`probability`,`sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(16060,0,0,"Foolishly you have sought your own demise.",14,100,8807,13030,3,"Gothik SAY_INTRO_1"), +(16060,1,0,"Brazenly you have disregarded powers beyond your understanding.",14,100,0,13031,3,"Gothik SAY_INTRO_2"), +(16060,2,0,"You have fought hard to invade the realm of the harvester.",14,100,0,13032,3,"Gothik SAY_INTRO_3"), +(16060,3,0,"Now there is only one way out- to walk the lonely path of the damned.",14,100,0,13033,3,"Gothik SAY_INTRO_4"), +(16060,4,0,"I have waited long enough. Now you face the harvester of souls.",14,100,8808,13028,3,"Gothik SAY_PHASE_TWO"), +(16060,5,0,"I... am... undone.",14,100,8805,13026,3,"Gothik SAY_DEATH"), +(16060,6,0,"Death is the only escape!",14,20,8806,13027,3,"Gothik SAY_KILL"), +(16060,7,0,"%s teleports into the fray!",41,100,0,32306,3,"Gothik EMOTE_PHASE_TWO"), +(16060,8,0,"The central gate opens!",41,100,0,32307,3,"Gothik EMOTE_GATE_OPENED"); + +-- minion AI +UPDATE `creature_template` SET `ScriptName`="npc_gothik_minion_livingtrainee" WHERE `entry`=16124; +UPDATE `creature_template` SET `ScriptName`="npc_gothik_minion_livingknight" WHERE `entry`=16125; +UPDATE `creature_template` SET `ScriptName`="npc_gothik_minion_livingrider" WHERE `entry`=16126; +UPDATE `creature_template` SET `ScriptName`="npc_gothik_minion_spectraltrainee" WHERE `entry`=16127; +UPDATE `creature_template` SET `ScriptName`="npc_gothik_minion_spectralknight" WHERE `entry`=16148; +UPDATE `creature_template` SET `ScriptName`="npc_gothik_minion_spectralrider" WHERE `entry`=16150; +UPDATE `creature_template` SET `ScriptName`="npc_gothik_minion_spectralhorse" WHERE `entry`=16149; + +-- minion difficulty spell entries +DELETE FROM `spelldifficulty_dbc` WHERE `id` IN (55604,27825,27831,27989,56408,27993,55606,27994,55648); +INSERT INTO `spelldifficulty_dbc` (`id`,`spellid0`,`spellid1`) VALUES +(55604,55604,55645), -- death plague +(27831,27831,55638), -- shadow bolt volley +(27989,27989,56407), -- arcane explosion +(55606,55606,55608), -- unholy aura +(27994,27994,55646), -- drain life +(55648,55648,27995); -- unholy frenzy + +-- rider damage aura + visual aura +DELETE FROM `creature_template_addon` WHERE `entry` IN (16126,29986,16148,29990,16150,29988); +INSERT INTO `creature_template_addon` (`entry`,`mount`,`auras`) VALUES +(16126,25278,"55606"), +(29986,25278,"55608"), +(16148, 0,"10848"), +(29990, 0,"10848"), +(16150, 0,"55606 10848"), +(29988, 0,"55608 10848"); + +-- trigger AI +UPDATE `creature_template` SET `ScriptName`="npc_gothik_trigger",`InhabitType`=7 WHERE `entry`=16137; +-- re-do spawn locations for triggers +DELETE FROM `creature` WHERE `id`=16137; +INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`movementtype`) VALUES +(@CGUID+00,16137,533,3,1,2643.731,-3399.681,284.1829,0,0,0,0), -- living side soul trigger (south) +(@CGUID+01,16137,533,3,1,2739.995,-3399.779,284.2946,0,0,0,0), -- living side soul trigger (north) +(@CGUID+02,16137,533,3,1,2643.731,-3321.727,284.2327,0,0,0,0), -- spectral side soul trigger (south) +(@CGUID+03,16137,533,3,1,2739.995,-3321.727,284.2316,0,0,0,0), -- spectral side soul trigger (north) +(@CGUID+04,16137,533,3,1,2692.161,-3430.746,268.6462,0,0,0,0), -- living side spawn trigger (center back) +(@CGUID+05,16137,533,3,1,2714.562,-3430.61 ,268.6462,0,0,0,0), -- living side spawn trigger (north) +(@CGUID+06,16137,533,3,1,2692.213,-3428.783,268.6462,0,0,0,0), -- living side spawn trigger (center front) +(@CGUID+07,16137,533,3,1,2669.581,-3428.859,268.6462,0,0,0,0), -- living side spawn trigger (south) +(@CGUID+08,16137,533,3,1,2733.457,-3349.388,267.7677,0,0,0,0), -- spectral side spawn trigger (northeast) +(@CGUID+09,16137,533,3,1,2725.818,-3309.567,267.7686,0,0,0,0), -- spectral side spawn trigger (northwest) +(@CGUID+10,16137,533,3,1,2700.269,-3322.354,267.7678,0,0,0,0), -- spectral side spawn trigger (center) +(@CGUID+11,16137,533,3,1,2664.872,-3340.749,267.7674,0,0,0,0), -- spectral side spawn trigger (southeast) +(@CGUID+12,16137,533,3,1,2683.886,-3304.213,267.768 ,0,0,0,0); -- spectral side spawn trigger (southwest) + +-- make visuals target proper triggers +-- to anchor 1: 27892 (Trainee), 27928 (DK), 27935 (Rider) +-- to anchor 2: 27893 (Trainee), 27929 (DK), 27936 (Rider) +-- anchor -> skull: 27915 (Trainee), 27931 (DK), 27937 (Rider) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (27892,27928,27935,27893,27929,27936,27915,27931,27937); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionTarget`,`ElseGroup`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`Comment`) VALUES +(13,1,27892,31,0,0,3,16137,@CGUID+0,"To Anchor 1 - Target Anchor Living South"), +(13,1,27892,31,0,1,3,16137,@CGUID+1,"To Anchor 1 - Target Anchor Living North"), +(13,1,27928,31,0,0,3,16137,@CGUID+0,"To Anchor 1 - Target Anchor Living South"), +(13,1,27928,31,0,1,3,16137,@CGUID+1,"To Anchor 1 - Target Anchor Living North"), +(13,1,27935,31,0,0,3,16137,@CGUID+0,"To Anchor 1 - Target Anchor Living South"), +(13,1,27935,31,0,1,3,16137,@CGUID+1,"To Anchor 1 - Target Anchor Living North"), + +(13,1,27893,31,0,0,3,16137,@CGUID+2,"To Anchor 2 - Target Anchor Spectral South"), +(13,1,27893,31,0,1,3,16137,@CGUID+3,"To Anchor 2 - Target Anchor Spectral North"), +(13,1,27929,31,0,0,3,16137,@CGUID+2,"To Anchor 2 - Target Anchor Spectral South"), +(13,1,27929,31,0,1,3,16137,@CGUID+3,"To Anchor 2 - Target Anchor Spectral North"), +(13,1,27936,31,0,0,3,16137,@CGUID+2,"To Anchor 2 - Target Anchor Spectral South"), +(13,1,27936,31,0,1,3,16137,@CGUID+3,"To Anchor 2 - Target Anchor Spectral North"); + +-- disable LoS check for visuals to ensure they always work +DELETE FROM `disables` WHERE `sourceType`=0 AND `entry` IN (27892,27928,27935,27893,27929,27936,27915,27931,27937); +INSERT INTO `disables` (`sourceType`,`entry`,`flags`,`comment`) VALUES +(0,27892,64,"Gothik - To Anchor 1 - Ignore LoS"), +(0,27928,64,"Gothik - To Anchor 1 - Ignore LoS"), +(0,27935,64,"Gothik - To Anchor 1 - Ignore LoS"), +(0,27893,64,"Gothik - To Anchor 2 - Ignore LoS"), +(0,27929,64,"Gothik - To Anchor 2 - Ignore LoS"), +(0,27936,64,"Gothik - To Anchor 2 - Ignore LoS"), +(0,27915,64,"Gothik - Anchor To Skulls - Ignore LoS"), +(0,27931,64,"Gothik - Anchor To Skulls - Ignore LoS"), +(0,27937,64,"Gothik - Anchor To Skulls - Ignore LoS"); diff --git a/sql/updates/world/2016_01_19_07_world.sql b/sql/updates/world/2016_01_19_07_world.sql new file mode 100644 index 00000000000..e1a72398a5f --- /dev/null +++ b/sql/updates/world/2016_01_19_07_world.sql @@ -0,0 +1,23 @@ +-- four horsemen rewrite +UPDATE `creature_template` SET `ScriptName`='boss_four_horsemen_baron' WHERE `entry`=30549; +UPDATE `creature_template` SET `ScriptName`='boss_four_horsemen_thane' WHERE `entry`=16064; +UPDATE `creature_template` SET `ScriptName`='boss_four_horsemen_lady' WHERE `entry`=16065; +UPDATE `creature_template` SET `ScriptName`='boss_four_horsemen_sir' WHERE `entry`=16063; + +-- add ragecast texts +DELETE FROM `creature_text` WHERE `entry` IN (16064,16065,16063,30549) AND `groupid`=7; +INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`probability`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(16063,7,0,"%s casts Condemnation on everyone!",41,100,33088,3,"zeliek EMOTE_RAGECAST"), +(16065,7,0,"%s casts Unyielding Pain on everyone!",41,100,33087,3,"blaumeux EMOTE_RAGECAST"); +-- slay text probability to DB +UPDATE `creature_text` SET `probability`=30 WHERE `entry` IN (16064,16065,16063,30549) AND `groupid`=3; + +-- difficulty based spell ids +DELETE FROM `spelldifficulty_dbc` WHERE `id` IN (28882,28884,57374,28863,57376,28883); +INSERT INTO `spelldifficulty_dbc` (`id`,`spellid0`,`spellid1`) VALUES +(28882,28882,57369), -- Rivendare: Unholy Shadow +(28884,28884,57467), -- Korth'azz: Meteor +(57374,57374,57464), -- Blaumeux: Shadow Bolt +(28863,28863,57463), -- Blaumeux: Void Zone +(57376,57376,57465), -- Zeliek: Holy Bolt +(28883,28883,57466); -- Zeliek: Holy Wrath diff --git a/sql/updates/world/2016_01_19_08_world.sql b/sql/updates/world/2016_01_19_08_world.sql new file mode 100644 index 00000000000..d078c059201 --- /dev/null +++ b/sql/updates/world/2016_01_19_08_world.sql @@ -0,0 +1,2 @@ +-- +UPDATE `creature_template` SET `npcflag`=3, `gossip_menu_id`=140 WHERE `entry`=1139; diff --git a/sql/updates/world/2016_01_20_00_world.sql b/sql/updates/world/2016_01_20_00_world.sql new file mode 100644 index 00000000000..26ce069edd8 --- /dev/null +++ b/sql/updates/world/2016_01_20_00_world.sql @@ -0,0 +1,70 @@ +DELETE FROM gossip_menu_option WHERE `menu_id` IN (11013, 11014, 11015, 11016, 11017, 11018); +INSERT INTO gossip_menu_option (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(11013, 1, 0, 'Teleport to the Oratory of the Damned.', 37722, 1, 1, 0, 0, 0, 0, '', 0), -- Light's Hammer +(11013, 3, 0, 'Teleport to the Rampart of Skulls.', 37723, 1, 1, 0, 0, 0, 0, '', 0), -- Light's Hammer +(11013, 4, 0, 'Teleport to the Deathbringer''s Rise.', 37724, 1, 1, 0, 0, 0, 0, '', 0), -- Light's Hammer +(11013, 5, 0, 'Teleport to the Upper Spire.', 37725, 1, 1, 0, 0, 0, 0, '', 0), -- Light's Hammer +(11013, 6, 0, 'Teleport to Sindragosa''s Lair.', 37728, 1, 1, 0, 0, 0, 0, '', 0), -- Light's Hammer + +(11014, 0, 0, 'Teleport to Light''s Hammer.', 37671, 1, 1, 0, 0, 0, 0, '', 0), -- Oratory of the Damned +(11014, 3, 0, 'Teleport to the Rampart of Skulls.', 37723, 1, 1, 0, 0, 0, 0, '', 0), -- Oratory of the Damned +(11014, 4, 0, 'Teleport to the Deathbringer''s Rise.', 37724, 1, 1, 0, 0, 0, 0, '', 0), -- Oratory of the Damned +(11014, 5, 0, 'Teleport to the Upper Spire.', 37725, 1, 1, 0, 0, 0, 0, '', 0), -- Oratory of the Damned +(11014, 6, 0, 'Teleport to Sindragosa''s Lair.', 37728, 1, 1, 0, 0, 0, 0, '', 0), -- Oratory of the Damned + +(11015, 0, 0, 'Teleport to Light''s Hammer.', 37671, 1, 1, 0, 0, 0, 0, '', 0), -- Rampart of Skulls +(11015, 1, 0, 'Teleport to the Oratory of the Damned.', 37722, 1, 1, 0, 0, 0, 0, '', 0), -- Rampart of Skulls +(11015, 4, 0, 'Teleport to the Deathbringer''s Rise.', 37724, 1, 1, 0, 0, 0, 0, '', 0), -- Rampart of Skulls +(11015, 5, 0, 'Teleport to the Upper Spire.', 37725, 1, 1, 0, 0, 0, 0, '', 0), -- Rampart of Skulls +(11015, 6, 0, 'Teleport to Sindragosa''s Lair.', 37728, 1, 1, 0, 0, 0, 0, '', 0), -- Rampart of Skulls + +(11016, 0, 0, 'Teleport to Light''s Hammer.', 37671, 1, 1, 0, 0, 0, 0, '', 0), -- Deathbringer's Rise +(11016, 1, 0, 'Teleport to the Oratory of the Damned.', 37722, 1, 1, 0, 0, 0, 0, '', 0), -- Deathbringer's Rise +(11016, 3, 0, 'Teleport to the Rampart of Skulls.', 37723, 1, 1, 0, 0, 0, 0, '', 0), -- Deathbringer's Rise +(11016, 5, 0, 'Teleport to the Upper Spire.', 37725, 1, 1, 0, 0, 0, 0, '', 0), -- Deathbringer's Rise +(11016, 6, 0, 'Teleport to Sindragosa''s Lair.', 37728, 1, 1, 0, 0, 0, 0, '', 0), -- Deathbringer's Rise + +(11017, 0, 0, 'Teleport to Light''s Hammer.', 37671, 1, 1, 0, 0, 0, 0, '', 0), -- Upper Spire +(11017, 1, 0, 'Teleport to the Oratory of the Damned.', 37722, 1, 1, 0, 0, 0, 0, '', 0), -- Upper Spire +(11017, 3, 0, 'Teleport to the Rampart of Skulls.', 37723, 1, 1, 0, 0, 0, 0, '', 0), -- Upper Spire +(11017, 4, 0, 'Teleport to the Deathbringer''s Rise.', 37724, 1, 1, 0, 0, 0, 0, '', 0), -- Upper Spire +(11017, 6, 0, 'Teleport to Sindragosa''s Lair.', 37728, 1, 1, 0, 0, 0, 0, '', 0), -- Upper Spire + +(11018, 0, 0, 'Teleport to Light''s Hammer.', 37671, 1, 1, 0, 0, 0, 0, '', 0), -- Sindragosa's Lair +(11018, 1, 0, 'Teleport to the Oratory of the Damned.', 37722, 1, 1, 0, 0, 0, 0, '', 0), -- Sindragosa's Lair +(11018, 3, 0, 'Teleport to the Rampart of Skulls.', 37723, 1, 1, 0, 0, 0, 0, '', 0), -- Sindragosa's Lair +(11018, 4, 0, 'Teleport to the Deathbringer''s Rise.', 37724, 1, 1, 0, 0, 0, 0, '', 0), -- Sindragosa's Lair +(11018, 5, 0, 'Teleport to the Upper Spire.', 37725, 1, 1, 0, 0, 0, 0, '', 0); -- Sindragosa's Lair + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup` IN (11013, 11014, 11015, 11016, 11017, 11018); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(15,11013,1,0,0,13,0,0,3,2,0,0,0,'','Show option ''Teleport to the Oratory of the Damned.'' only if BossState DATA_LORD_MARROWGAR is DONE'), +(15,11013,3,0,0,13,0,1,3,2,0,0,0,'','Show option ''Teleport to the Rampart of Skulls.'' only if BossState DATA_LADY_DEATHWHISPER is DONE'), +(15,11013,4,0,0,13,0,2,3,2,0,0,0,'','Show option ''Teleport to the Deathbringer''s Rise.'' only if BossState DATA_ICECROWN_GUNSHIP_BATTLE is DONE'), +(15,11013,5,0,0,13,0,41,3,0,0,0,0,'','Show option ''Teleport to the Upper Spire.'' only if DATA_UPPERSPIRE_TELE_ACT is DONE'), +(15,11013,6,0,0,13,0,10,3,2,0,0,0,'','Show option ''Teleport to Sindragosa''s Lair.'' only if BossState DATA_VALITHRIA_DREAMWALKER is DONE'), + +(15,11014,3,0,0,13,0,1,3,2,0,0,0,'','Show option ''Teleport to the Rampart of Skulls.'' only if BossState DATA_LADY_DEATHWHISPER is DONE'), +(15,11014,4,0,0,13,0,2,3,2,0,0,0,'','Show option ''Teleport to the Deathbringer''s Rise.'' only if BossState DATA_ICECROWN_GUNSHIP_BATTLE is DONE'), +(15,11014,5,0,0,13,0,41,3,0,0,0,0,'','Show option ''Teleport to the Upper Spire.'' only if DATA_UPPERSPIRE_TELE_ACT is DONE'), +(15,11014,6,0,0,13,0,10,3,2,0,0,0,'','Show option ''Teleport to Sindragosa''s Lair.'' only if BossState DATA_VALITHRIA_DREAMWALKER is DONE'), + +(15,11015,1,0,0,13,0,0,3,2,0,0,0,'','Show option ''Teleport to the Oratory of the Damned.'' only if BossState DATA_LORD_MARROWGAR is DONE'), +(15,11015,4,0,0,13,0,2,3,2,0,0,0,'','Show option ''Teleport to the Deathbringer''s Rise.'' only if BossState DATA_ICECROWN_GUNSHIP_BATTLE is DONE'), +(15,11015,5,0,0,13,0,41,3,0,0,0,0,'','Show option ''Teleport to the Upper Spire.'' only if DATA_UPPERSPIRE_TELE_ACT is DONE'), +(15,11015,6,0,0,13,0,10,3,2,0,0,0,'','Show option ''Teleport to Sindragosa''s Lair.'' only if BossState DATA_VALITHRIA_DREAMWALKER is DONE'), + +(15,11016,1,0,0,13,0,0,3,2,0,0,0,'','Show option ''Teleport to the Oratory of the Damned.'' only if BossState DATA_LORD_MARROWGAR is DONE'), +(15,11016,3,0,0,13,0,1,3,2,0,0,0,'','Show option ''Teleport to the Rampart of Skulls.'' only if BossState DATA_LADY_DEATHWHISPER is DONE'), +(15,11016,5,0,0,13,0,41,3,0,0,0,0,'','Show option ''Teleport to the Upper Spire.'' only if DATA_UPPERSPIRE_TELE_ACT is DONE'), +(15,11016,6,0,0,13,0,10,3,2,0,0,0,'','Show option ''Teleport to Sindragosa''s Lair.'' only if BossState DATA_VALITHRIA_DREAMWALKER is DONE'), + +(15,11017,1,0,0,13,0,0,3,2,0,0,0,'','Show option ''Teleport to the Oratory of the Damned.'' only if BossState DATA_LORD_MARROWGAR is DONE'), +(15,11017,3,0,0,13,0,1,3,2,0,0,0,'','Show option ''Teleport to the Rampart of Skulls.'' only if BossState DATA_LADY_DEATHWHISPER is DONE'), +(15,11017,4,0,0,13,0,2,3,2,0,0,0,'','Show option ''Teleport to the Deathbringer''s Rise.'' only if BossState DATA_ICECROWN_GUNSHIP_BATTLE is DONE'), +(15,11017,6,0,0,13,0,10,3,2,0,0,0,'','Show option ''Teleport to Sindragosa''s Lair.'' only if BossState DATA_VALITHRIA_DREAMWALKER is DONE'), + +(15,11018,1,0,0,13,0,0,3,2,0,0,0,'','Show option ''Teleport to the Oratory of the Damned.'' only if BossState DATA_LORD_MARROWGAR is DONE'), +(15,11018,3,0,0,13,0,1,3,2,0,0,0,'','Show option ''Teleport to the Rampart of Skulls.'' only if BossState DATA_LADY_DEATHWHISPER is DONE'), +(15,11018,4,0,0,13,0,2,3,2,0,0,0,'','Show option ''Teleport to the Deathbringer''s Rise.'' only if BossState DATA_ICECROWN_GUNSHIP_BATTLE is DONE'), +(15,11018,5,0,0,13,0,41,3,0,0,0,0,'','Show option ''Teleport to the Upper Spire.'' only if DATA_UPPERSPIRE_TELE_ACT is DONE'); diff --git a/sql/updates/world/2016_01_20_01_world.sql b/sql/updates/world/2016_01_20_01_world.sql new file mode 100644 index 00000000000..b1c6e0de756 --- /dev/null +++ b/sql/updates/world/2016_01_20_01_world.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `gossip_menu` WHERE `entry` IN (9578) AND `text_id`=12927; +INSERT INTO `gossip_menu` (`entry`, `text_id`) VALUES +(9578, 12927); diff --git a/sql/updates/world/2016_01_20_02_world.sql b/sql/updates/world/2016_01_20_02_world.sql new file mode 100644 index 00000000000..d026c3cb478 --- /dev/null +++ b/sql/updates/world/2016_01_20_02_world.sql @@ -0,0 +1,475 @@ +DELETE FROM `creature_formations` WHERE `leaderGUID`=12869; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(12869, 12869, 0, 0, 2), +(12869, 12853, 10, 90, 2), +(12869, 12854, 10, 270, 2); + +DELETE FROM `creature_formations` WHERE `leaderGUID`=12866; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(12866, 12866, 0, 0, 2), +(12866, 12851, 10, 90, 2), +(12866, 12852, 10, 270, 2); + +DELETE FROM `creature_formations` WHERE `leaderGUID`=13415; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(13415, 13415, 0, 0, 2), +(13415, 13414, 3, 90, 2); + +-- Pathing for Aqueous Spawn Entry: 22883 'TDB FORMAT' +SET @NPC := 13415; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=467.9448,`position_y`=847.3433,`position_z`=15.00103 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,467.9448,847.3433,15.00103,0,0,1,0,100,0), -- 20:18:04 +(@PATH,2,469.2407,849.0333,14.97786,0,0,1,0,100,0), -- 20:18:07 +(@PATH,3,473.5281,853.7736,15.31754,0,0,1,0,100,0), -- 20:18:07 +(@PATH,4,474.0281,857.5236,15.31754,0,0,1,0,100,0), -- 20:18:07 +(@PATH,5,473.7495,860.6138,15.416,0,0,1,0,100,0), -- 20:18:10 +(@PATH,6,472.7495,861.8638,15.416,0,0,1,0,100,0), -- 20:18:10 +(@PATH,7,471.7495,864.1138,15.166,0,0,1,0,100,0), -- 20:18:10 +(@PATH,8,467.3445,873.2086,15.48424,0,0,1,0,100,0), -- 20:18:12 +(@PATH,9,466.7931,879.0881,15.64043,0,0,1,0,100,0), -- 20:18:15 +(@PATH,10,465.5614,888.2678,15.55727,0,0,1,0,100,0), -- 20:18:17 +(@PATH,11,465.6077,896.3718,15.43029,0,0,1,0,100,0), -- 20:18:20 +(@PATH,12,467.8163,900.2317,15.73285,0,0,1,0,100,0), -- 20:18:21 +(@PATH,13,468.6377,905.9084,15.83779,0,0,1,0,100,0), -- 20:18:22 +(@PATH,14,466.0126,916.0297,15.82569,0,0,1,0,100,0), -- 20:18:24 +(@PATH,15,470.3526,931.8629,14.75142,0,0,1,0,100,0), -- 20:18:27 +(@PATH,16,469.243,921.0674,15.69238,0,0,1,0,100,0), -- 20:18:33 +(@PATH,17,466.0515,931.413,15.39575,0,0,1,0,100,0), -- 20:18:37 +(@PATH,18,466.0515,916.163,15.89575,0,0,1,0,100,0), -- 20:18:37 +(@PATH,19,466.9939,913.4012,15.84451,0,0,1,0,100,0), -- 20:18:44 +(@PATH,20,468.8083,905.5721,15.84772,0,0,1,0,100,0), -- 20:18:46 +(@PATH,21,465.8907,896.822,15.64862,0,0,1,0,100,0), -- 20:18:48 +(@PATH,22,465.9131,888.7125,15.57837,0,0,1,0,100,0), -- 20:18:50 +(@PATH,23,466.5535,879.3544,15.68201,0,0,1,0,100,0), -- 20:18:52 +(@PATH,24,468.5858,869.7411,15.54825,0,0,1,0,100,0), -- 20:18:55 +(@PATH,25,472.7863,862.353,15.26673,0,0,1,0,100,0), -- 20:18:57 +(@PATH,26,473.7863,860.603,15.51673,0,0,1,0,100,0), -- 20:18:57 +(@PATH,27,473.6846,860.4615,15.38147,0,0,1,0,100,0), -- 20:18:58 +(@PATH,28,474.4346,859.4615,15.38147,0,0,1,0,100,0), -- 20:18:58 +(@PATH,29,474.1846,858.2115,15.38147,0,0,1,0,100,0), -- 20:18:58 +(@PATH,30,473.6846,854.2115,15.13147,0,0,1,0,100,0), -- 20:18:58 +(@PATH,31,473.1513,852.6404,15.02536,0,0,1,0,100,0); -- 20:19:01 +-- 0x1C16F446801658C0006A5B000A1D4854 .go 467.9448 847.3433 15.00103 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=13418; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(13418, 13418, 0, 0, 2), +(13418, 13416, 3, 90, 2); + +-- Pathing for Aqueous Spawn Entry: 22883 'TDB FORMAT' +SET @NPC := 13418; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=277.868,`position_y`=842.4459,`position_z`=-23.75307 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,277.868,842.4459,-23.75307,0,0,1,0,100,0), -- 20:20:58 +(@PATH,2,277.4133,846.7463,-23.75651,0,0,1,0,100,0), -- 20:21:00 +(@PATH,3,278.9133,848.4963,-23.75651,0,0,1,0,100,0), -- 20:21:00 +(@PATH,4,280.4133,850.2463,-23.75651,0,0,1,0,100,0), -- 20:21:00 +(@PATH,5,282.6633,852.9963,-23.50651,0,0,1,0,100,0), -- 20:21:00 +(@PATH,6,283.827,860.8285,-23.04886,0,0,1,0,100,0), -- 20:21:01 +(@PATH,7,284.327,862.8285,-23.04886,0,0,1,0,100,0), -- 20:21:01 +(@PATH,8,282.827,867.8285,-23.79886,0,0,1,0,100,0), -- 20:21:01 +(@PATH,9,281.3427,869.1567,-23.76081,0,0,1,0,100,0), -- 20:21:02 +(@PATH,10,277.5927,871.6567,-23.76081,0,0,1,0,100,0), -- 20:21:02 +(@PATH,11,277.0927,875.9067,-23.76081,0,0,1,0,100,0), -- 20:21:02 +(@PATH,12,276.0927,884.1567,-23.76081,0,0,1,0,100,0), -- 20:21:02 +(@PATH,13,275.7303,886.521,-23.90997,0,0,1,0,100,0), -- 20:21:04 +(@PATH,14,277.0383,899.6908,-30.63255,0,0,1,0,100,0), -- 20:21:05 +(@PATH,15,280.329,915.5177,-38.46265,0,0,1,0,100,0), -- 20:21:06 +(@PATH,16,281.5613,931.75,-46.90573,0,0,1,0,100,0), -- 20:21:07 +(@PATH,17,277.2371,957.6002,-59.79856,0,0,1,0,100,0), -- 20:21:09 +(@PATH,18,276.8787,969.0604,-59.80925,0,0,1,0,100,0), -- 20:21:11 +(@PATH,19,280.0755,977.5538,-60.08774,0,0,1,0,100,0), -- 20:21:12 +(@PATH,20,283.3255,979.3038,-60.08774,0,0,1,0,100,0), -- 20:21:12 +(@PATH,21,284.8255,980.0538,-60.08774,0,0,1,0,100,0), -- 20:21:12 +(@PATH,22,289.5755,980.3038,-60.33774,0,0,1,0,100,0), -- 20:21:12 +(@PATH,23,292.3255,980.3038,-60.33774,0,0,1,0,100,0), -- 20:21:12 +(@PATH,24,295.7121,979.2697,-60.03576,0,0,1,0,100,0), -- 20:21:13 +(@PATH,25,299.2121,976.2697,-60.03576,0,0,1,0,100,0), -- 20:21:13 +(@PATH,26,295.9117,979.6497,-60.04585,0,0,1,0,100,0), -- 20:21:16 +(@PATH,27,292.6617,980.3997,-60.04585,0,0,1,0,100,0), -- 20:21:16 +(@PATH,28,289.9117,980.3997,-60.04585,0,0,1,0,100,0), -- 20:21:16 +(@PATH,29,285.0618,980.2185,-60.09282,0,0,1,0,100,0), -- 20:21:17 +(@PATH,30,283.3118,979.2185,-60.09282,0,0,1,0,100,0), -- 20:21:17 +(@PATH,31,280.0618,977.4685,-60.09282,0,0,1,0,100,0), -- 20:21:17 +(@PATH,32,278.8118,976.9685,-60.09282,0,0,1,0,100,0), -- 20:21:17 +(@PATH,33,276.8872,969.0083,-59.83573,0,0,1,0,100,0), -- 20:21:18 +(@PATH,34,277.1372,958.0083,-59.83573,0,0,1,0,100,0), -- 20:21:18 +(@PATH,35,281.6861,931.6771,-46.47077,0,0,1,0,100,0), -- 20:21:22 +(@PATH,36,280.2155,915.3389,-38.22446,0,0,1,0,100,0), -- 20:21:23 +(@PATH,37,276.9022,899.6358,-30.47937,0,0,1,0,100,0), -- 20:21:24 +(@PATH,38,275.9023,884.4814,-23.95735,0,0,1,0,100,0), -- 20:21:26 +(@PATH,39,276.1523,882.4814,-23.95735,0,0,1,0,100,0), -- 20:21:26 +(@PATH,40,276.9797,876.0508,-23.88462,0,0,1,0,100,0), -- 20:21:27 +(@PATH,41,277.4797,871.5508,-23.88462,0,0,1,0,100,0), -- 20:21:27 +(@PATH,42,281.2297,869.0508,-23.88462,0,0,1,0,100,0), -- 20:21:27 +(@PATH,43,282.8904,867.748,-23.63595,0,0,1,0,100,0), -- 20:21:28 +(@PATH,44,284.3904,863.248,-23.13595,0,0,1,0,100,0), -- 20:21:28 +(@PATH,45,283.8904,860.998,-23.13595,0,0,1,0,100,0), -- 20:21:28 +(@PATH,46,282.8904,853.748,-23.13595,0,0,1,0,100,0), -- 20:21:28 +(@PATH,47,280.6286,850.2665,-24.00388,0,0,1,0,100,0), -- 20:21:29 +(@PATH,48,278.8786,848.5165,-23.75388,0,0,1,0,100,0), -- 20:21:29 +(@PATH,49,277.3786,846.7665,-23.75388,0,0,1,0,100,0); -- 20:21:29 +-- 0x1C16F446801658C0006A5B000B1D4854 .go 277.868 842.4459 -23.75307 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=13774; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(13774, 13774, 0, 0, 2), +(13774, 13429, 3, 270, 2); + +-- Pathing for Aqueous Spawn Entry: 22883 'TDB FORMAT' +SET @NPC := 13774; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=226.3641,`position_y`=842.9598,`position_z`=-23.60787 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,226.3641,842.9598,-23.60787,0,0,1,0,100,0), -- 20:18:44 +(@PATH,2,224.6499,848.3411,-23.63753,0,0,1,0,100,0), -- 20:18:46 +(@PATH,3,222.3999,850.3411,-23.63753,0,0,1,0,100,0), -- 20:18:46 +(@PATH,4,219.3999,852.8411,-23.13753,0,0,1,0,100,0), -- 20:18:46 +(@PATH,5,220.1499,858.5911,-23.13753,0,0,1,0,100,0), -- 20:18:46 +(@PATH,6,220.6499,863.3411,-23.13753,0,0,1,0,100,0), -- 20:18:46 +(@PATH,7,220.7801,865.7321,-23.06377,0,0,1,0,100,0), -- 20:18:48 +(@PATH,8,223.5301,867.7321,-23.31377,0,0,1,0,100,0), -- 20:18:48 +(@PATH,9,226.2801,869.7321,-23.81377,0,0,1,0,100,0), -- 20:18:48 +(@PATH,10,229.0301,872.2321,-24.31377,0,0,1,0,100,0), -- 20:18:48 +(@PATH,11,230.5301,873.4821,-24.81377,0,0,1,0,100,0), -- 20:18:48 +(@PATH,12,231.5301,874.2321,-25.56377,0,0,1,0,100,0), -- 20:18:48 +(@PATH,13,234.6127,879.7501,-25.39395,0,0,1,0,100,0), -- 20:18:49 +(@PATH,14,235.3627,886.7501,-25.39395,0,0,1,0,100,0), -- 20:18:49 +(@PATH,15,235.054,896.0424,-30.38491,0,0,1,0,100,0), -- 20:18:50 +(@PATH,16,235.666,933.9497,-49.77283,0,0,1,0,100,0), -- 20:18:54 +(@PATH,17,235.166,951.4497,-58.52283,0,0,1,0,100,0), -- 20:18:54 +(@PATH,18,239.472,955.8605,-61.06985,0,0,1,0,100,0), -- 20:18:56 +(@PATH,19,241.722,957.8605,-62.31985,0,0,1,0,100,0), -- 20:18:56 +(@PATH,20,245.0305,960.8606,-62.1322,0,0,1,0,100,0), -- 20:18:57 +(@PATH,21,241.0371,985.8208,-62.71188,0,0,1,0,100,0), -- 20:18:58 +(@PATH,22,237.0147,1001.326,-62.45633,0,0,1,0,100,0), -- 20:19:00 +(@PATH,23,236.5147,1005.076,-62.45633,0,0,1,0,100,0), -- 20:19:00 +(@PATH,24,230.5381,1024.512,-61.63638,0,0,1,0,100,0), -- 20:19:02 +(@PATH,25,228.5381,1026.512,-60.88638,0,0,1,0,100,0), -- 20:19:02 +(@PATH,26,225.485,1029.62,-60.43474,0,0,1,0,100,0), -- 20:19:03 +(@PATH,27,223.485,1032.12,-60.43474,0,0,1,0,100,0), -- 20:19:03 +(@PATH,28,220.485,1035.37,-59.68474,0,0,1,0,100,0), -- 20:19:03 +(@PATH,29,220.5529,1045.221,-59.55603,0,0,1,0,100,0), -- 20:19:05 +(@PATH,30,224.0529,1048.471,-59.80603,0,0,1,0,100,0), -- 20:19:05 +(@PATH,31,225.0529,1049.221,-60.30603,0,0,1,0,100,0), -- 20:19:05 +(@PATH,32,226.3029,1050.721,-60.55603,0,0,1,0,100,0), -- 20:19:05 +(@PATH,33,228.6076,1053.052,-60.2848,0,0,1,0,100,0), -- 20:19:06 +(@PATH,34,229.8576,1054.552,-60.5348,0,0,1,0,100,0), -- 20:19:06 +(@PATH,35,230.8576,1055.552,-61.2848,0,0,1,0,100,0), -- 20:19:06 +(@PATH,36,233.3576,1058.052,-62.2848,0,0,1,0,100,0), -- 20:19:06 +(@PATH,37,230.8576,1061.052,-61.2848,0,0,1,0,100,0), -- 20:19:06 +(@PATH,38,229.8576,1062.302,-60.2848,0,0,1,0,100,0), -- 20:19:06 +(@PATH,39,227.7166,1065.36,-60.65356,0,0,1,0,100,0), -- 20:19:07 +(@PATH,40,227.7166,1066.86,-60.65356,0,0,1,0,100,0), -- 20:19:07 +(@PATH,41,229.6661,1062.701,-60.48591,0,0,1,0,100,0), -- 20:19:10 +(@PATH,42,230.9161,1061.201,-61.48591,0,0,1,0,100,0), -- 20:19:10 +(@PATH,43,233.2057,1058.173,-62.12091,0,0,1,0,100,0), -- 20:19:12 +(@PATH,44,230.7057,1055.173,-61.37091,0,0,1,0,100,0), -- 20:19:12 +(@PATH,45,229.9557,1054.423,-60.62091,0,0,1,0,100,0), -- 20:19:12 +(@PATH,46,228.7057,1052.923,-60.37091,0,0,1,0,100,0), -- 20:19:12 +(@PATH,47,226.2057,1050.923,-60.37091,0,0,1,0,100,0), -- 20:19:12 +(@PATH,48,224.9557,1049.423,-60.37091,0,0,1,0,100,0), -- 20:19:12 +(@PATH,49,224.1507,1048.499,-59.69673,0,0,1,0,100,0), -- 20:19:13 +(@PATH,50,220.6507,1045.249,-59.69673,0,0,1,0,100,0), -- 20:19:13 +(@PATH,51,220.4007,1035.749,-59.69673,0,0,1,0,100,0), -- 20:19:13 +(@PATH,52,223.1507,1032.499,-59.94673,0,0,1,0,100,0), -- 20:19:13 +(@PATH,53,225.6915,1029.523,-60.42624,0,0,1,0,100,0), -- 20:19:14 +(@PATH,54,228.1915,1026.773,-60.92624,0,0,1,0,100,0), -- 20:19:14 +(@PATH,55,230.4415,1024.773,-61.42624,0,0,1,0,100,0), -- 20:19:14 +(@PATH,56,231.6957,1023.582,-61.96358,0,0,1,0,100,0), -- 20:19:16 +(@PATH,57,236.1957,1005.332,-62.21358,0,0,1,0,100,0), -- 20:19:16 +(@PATH,58,236.6957,1001.582,-62.21358,0,0,1,0,100,0), -- 20:19:16 +(@PATH,59,239.3251,989.6144,-62.72815,0,0,1,0,100,0), -- 20:19:18 +(@PATH,60,239.3251,989.1144,-62.72815,0,0,1,0,100,0), -- 20:19:18 +(@PATH,61,241.0751,985.1144,-62.72815,0,0,1,0,100,0), -- 20:19:18 +(@PATH,62,244.6287,965.7533,-62.47226,0,0,1,0,100,0), -- 20:19:19 +(@PATH,63,242.1768,958.4319,-62.1866,0,0,1,0,100,0), -- 20:19:21 +(@PATH,64,239.1768,956.1819,-61.1866,0,0,1,0,100,0), -- 20:19:21 +(@PATH,65,235.3233,952.653,-59.28426,0,0,1,0,100,0), -- 20:19:22 +(@PATH,66,236.0733,933.903,-49.78426,0,0,1,0,100,0), -- 20:19:22 +(@PATH,67,235.6809,889.4034,-27.06503,0,0,1,0,100,0), -- 20:19:25 +(@PATH,68,235.3604,887.6078,-26.10506,0,0,1,0,100,0), -- 20:19:28 +(@PATH,69,234.6104,879.8578,-25.60506,0,0,1,0,100,0), -- 20:19:28 +(@PATH,70,231.5102,874.4667,-25.5617,0,0,1,0,100,0), -- 20:19:29 +(@PATH,71,229.0102,872.4667,-24.3117,0,0,1,0,100,0), -- 20:19:29 +(@PATH,72,226.2602,869.9667,-23.8117,0,0,1,0,100,0), -- 20:19:29 +(@PATH,73,223.6137,867.7607,-23.35828,0,0,1,0,100,0), -- 20:19:30 +(@PATH,74,220.8637,865.5107,-23.10828,0,0,1,0,100,0), -- 20:19:30 +(@PATH,75,220.6137,863.5107,-23.10828,0,0,1,0,100,0), -- 20:19:30 +(@PATH,76,219.2268,853.074,-23.14473,0,0,1,0,100,0), -- 20:19:31 +(@PATH,77,221.9768,850.824,-23.39473,0,0,1,0,100,0), -- 20:19:31 +(@PATH,78,224.7268,848.324,-23.64473,0,0,1,0,100,0), -- 20:19:31 +(@PATH,79,225.7792,847.1718,-23.69841,0,0,1,0,100,0); -- 20:19:33 +-- 0x1C16F446801658C0006A5B000D1D4854 .go 226.3641 842.9598 -23.60787 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=13411; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(13411, 13411, 0, 0, 2), +(13411, 13410, 3, 90, 2); + +-- Pathing for Aqueous Spawn Entry: 22883 'TDB FORMAT' +SET @NPC := 13411; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=341.5303,`position_y`=833.1406,`position_z`=3.273792 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,341.5303,833.1406,3.273792,0,0,1,0,100,0), -- 20:28:58 +(@PATH,2,348.7275,829.2892,1.651913,0,0,1,0,100,0), -- 20:29:00 +(@PATH,3,354.4775,827.0392,3.901913,0,0,1,0,100,0), -- 20:29:00 +(@PATH,4,359.5842,829.374,5.242831,0,0,1,0,100,0), -- 20:29:01 +(@PATH,5,361.0842,829.874,5.742831,0,0,1,0,100,0), -- 20:29:01 +(@PATH,6,364.5842,835.374,7.242831,0,0,1,0,100,0), -- 20:29:01 +(@PATH,7,367.8342,835.374,8.242831,0,0,1,0,100,0), -- 20:29:01 +(@PATH,8,373.5842,835.124,10.74283,0,0,1,0,100,0), -- 20:29:01 +(@PATH,9,379.5842,835.124,12.49283,0,0,1,0,100,0), -- 20:29:01 +(@PATH,10,385.9604,833.4543,14.87675,0,0,1,0,100,0), -- 20:29:04 +(@PATH,11,389.4604,828.2043,16.12675,0,0,1,0,100,0), -- 20:29:04 +(@PATH,12,385.9604,833.4543,14.87675,0,0,1,0,100,0), -- 20:29:04 +(@PATH,13,379.3574,835.0835,12.43179,0,0,1,0,100,0), -- 20:29:05 +(@PATH,14,373.6074,835.0835,10.93179,0,0,1,0,100,0), -- 20:29:05 +(@PATH,15,367.6338,835.3248,8.312333,0,0,1,0,100,0), -- 20:29:06 +(@PATH,16,364.6338,835.3248,7.062333,0,0,1,0,100,0), -- 20:29:06 +(@PATH,17,360.7854,829.9193,5.526537,0,0,1,0,100,0), -- 20:29:07 +(@PATH,18,359.5354,829.4193,5.276537,0,0,1,0,100,0), -- 20:29:07 +(@PATH,19,354.5487,827.0881,3.989071,0,0,1,0,100,0), -- 20:29:08 +(@PATH,20,348.5487,829.3381,1.739071,0,0,1,0,100,0); -- 20:29:08 +-- 0x1C16F446801658C0006A5B000E1D4854 .go 341.5303 833.1406 3.273792 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=13412; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(13412, 13412, 0, 0, 2), +(13412, 13413, 3, 270, 2); + +-- Pathing for Aqueous Spawn Entry: 22883 'TDB FORMAT' +SET @NPC := 13412; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=225.5994,`position_y`=787.1674,`position_z`=-24.18544 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,225.5994,787.1674,-24.18544,0,0,1,0,100,0), -- 20:21:40 +(@PATH,2,226.2546,784.0085,-23.94931,0,0,1,0,100,0), -- 20:21:42 +(@PATH,3,219.7546,774.2585,-23.44931,0,0,1,0,100,0), -- 20:21:42 +(@PATH,4,219.399,766.433,-23.47102,0,0,1,0,100,0), -- 20:21:44 +(@PATH,5,225.7847,743.8611,-23.80148,0,0,1,0,100,0), -- 20:21:45 +(@PATH,6,225.8589,730.2358,-15.95089,0,0,1,0,100,0), -- 20:21:46 +(@PATH,7,226.6089,726.4858,-13.95089,0,0,1,0,100,0), -- 20:21:46 +(@PATH,8,236.528,706.583,-4.811245,0,0,1,0,100,0), -- 20:21:50 +(@PATH,9,265.2675,707.3212,-4.301136,0,0,1,0,100,0), -- 20:21:51 +(@PATH,10,272.3554,719.12,-10.19584,0,0,1,0,100,0), -- 20:21:52 +(@PATH,11,275.1298,743.1734,-24.00938,0,0,1,0,100,0), -- 20:21:54 +(@PATH,12,279.3289,758.15,-23.90574,0,0,1,0,100,0), -- 20:21:56 +(@PATH,13,281.9902,760.6749,-23.69596,0,0,1,0,100,0), -- 20:21:57 +(@PATH,14,282.8818,767.9795,-23.45871,0,0,1,0,100,0), -- 20:21:58 +(@PATH,15,277.356,793.042,-24.15166,0,0,1,0,100,0), -- 20:22:00 +(@PATH,16,285.9649,799.7314,-23.8582,0,0,1,0,100,0), -- 20:22:01 +(@PATH,17,292.1205,799.7032,-22.67317,0,0,1,0,100,0), -- 20:22:02 +(@PATH,18,303.1205,795.2032,-17.17317,0,0,1,0,100,0), -- 20:22:02 +(@PATH,19,295.6266,798.3845,-20.68238,0,0,1,0,100,0), -- 20:22:04 +(@PATH,20,286.1266,799.6345,-23.93238,0,0,1,0,100,0), -- 20:22:04 +(@PATH,21,276.8643,782.9451,-23.92551,0,0,1,0,100,0), -- 20:22:07 +(@PATH,22,282.1143,770.6951,-23.42551,0,0,1,0,100,0), -- 20:22:07 +(@PATH,23,279.3628,758.2788,-24.0036,0,0,1,0,100,0), -- 20:22:09 +(@PATH,24,275.2263,743.7703,-24.04942,0,0,1,0,100,0), -- 20:22:10 +(@PATH,25,272.4308,719.3824,-10.38001,0,0,1,0,100,0), -- 20:22:12 +(@PATH,26,266.0668,708.079,-4.459149,0,0,1,0,100,0), -- 20:22:14 +(@PATH,27,258.0668,704.329,-4.959149,0,0,1,0,100,0), -- 20:22:14 +(@PATH,28,236.2906,706.6432,-3.633104,0,0,1,0,100,0), -- 20:22:16 +(@PATH,29,226.6268,726.214,-13.62222,0,0,1,0,100,0), -- 20:22:18 +(@PATH,30,225.8902,730.3877,-15.93307,0,0,1,0,100,0), -- 20:22:19 +(@PATH,31,226.0625,743.5709,-23.90323,0,0,1,0,100,0), -- 20:22:20 +(@PATH,32,220.6384,762.8275,-23.16604,0,0,1,0,100,0), -- 20:22:21 +(@PATH,33,218.6304,769.206,-23.46703,0,0,1,0,100,0), -- 20:22:23 +(@PATH,34,226.297,784.1191,-24.1624,0,0,1,0,100,0); -- 20:22:25 +-- 0x1C16F446801658C0006A5B000C1D4854 .go 225.5994 787.1674 -24.18544 + +-- Update spawn distance & movementype so they move around randomly +UPDATE `creature` SET `spawndist`=5, `MovementType`=1 WHERE `guid` IN (13393, 13395, 13399, 13396, 13236, 13268, 13398, 13255, 13316, 13397, 13408, 13401, 13407, 13403, 13402, 13409, 13406, 13404, 13405, 13400); + +-- Pathing for Bonechewer Taskmaster Entry: 23028 'TDB FORMAT' +SET @NPC := 53819; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=824.2386,`position_y`=922.0986,`position_z`=56.91497 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,824.2386,922.0986,56.91497,0,0,0,0,100,0), -- 20:24:16 +(@PATH,2,821.0174,923.827,56.86291,0,0,0,0,100,0), -- 20:24:21 +(@PATH,3,818.0731,927.4094,57.30012,0,0,0,0,100,0), -- 20:24:23 +(@PATH,4,814.8231,931.1594,56.80012,0,0,0,0,100,0), -- 20:24:23 +(@PATH,5,812.9963,933.0051,56.42346,0,0,0,0,100,0), -- 20:24:28 +(@PATH,6,812.416,948.0172,56.81141,0,0,0,0,100,0), -- 20:24:31 +(@PATH,7,813.5654,956.6572,55.9874,0,0,0,0,100,0), -- 20:24:35 +(@PATH,8,814.4669,959.6799,55.61625,0,0,0,0,100,0), -- 20:24:39 +(@PATH,9,815.8135,960.5793,55.61554,0,0,0,0,100,0), -- 20:24:44 +(@PATH,10,813.7318,955.5559,56.19109,0,0,0,0,100,0), -- 20:24:47 +(@PATH,11,812.7902,950.8799,56.58345,0,0,0,0,100,0), -- 20:24:50 +(@PATH,12,812.5013,941.1736,56.22784,0,0,0,0,100,0), -- 20:24:54 +(@PATH,13,816.401,926.7815,57.39321,0,0,0,0,100,0), -- 20:24:57 +(@PATH,14,818.651,925.7815,57.39321,0,0,0,0,100,0); -- 20:24:57 +-- 0x1C16F44680167D00006A5B00001D4972 .go 824.2386 922.0986 56.91497 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=53817; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(53817, 53817, 0, 0, 2), +(53817, 53818, 4, 270, 2); + +-- Pathing for Bonechewer Taskmaster Entry: 23028 'TDB FORMAT' +SET @NPC := 53817; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=779.7964,`position_y`=907.3997,`position_z`=55.31417 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,779.7964,907.3997,55.31417,0,0,0,0,100,0), -- 20:24:46 +(@PATH,2,787.9648,919.118,56.01221,0,0,0,0,100,0), -- 20:24:51 +(@PATH,3,793.1609,922.2444,56.78314,0,0,0,0,100,0), -- 20:24:55 +(@PATH,4,795.3989,923.1378,56.98447,0,0,0,0,100,0), -- 20:24:57 +(@PATH,5,801.3021,923.3855,57.17341,0,0,0,0,100,0), -- 20:25:00 +(@PATH,6,808.4121,919.6089,57.23282,0,0,0,0,100,0), -- 20:25:04 +(@PATH,7,812.0118,913.3118,57.18176,0,0,0,0,100,0), -- 20:25:06 +(@PATH,8,812.204,913.6752,57.22612,0,0,0,0,100,0), -- 20:25:12 +(@PATH,9,808.2915,919.7905,57.18939,0,0,0,0,100,0), -- 20:25:14 +(@PATH,10,801.1031,923.2393,57.11441,0,0,0,0,100,0), -- 20:25:18 +(@PATH,11,790.0347,920.629,56.37698,0,0,0,0,100,0), -- 20:25:21 +(@PATH,12,786.2445,917.2216,55.7047,0,0,0,0,100,0), -- 20:25:23 +(@PATH,13,783.3309,914.0374,55.27817,0,0,0,0,100,0); -- 20:25:27 +-- 0x1C16F44680167D00006A5B00009D4972 .go 779.7964 907.3997 55.31417 + +-- Pathing for Bonechewer Taskmaster Entry: 23028 'TDB FORMAT' +SET @NPC := 53815; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=614.9688,`position_y`=905.1718,`position_z`=59.02773 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,614.9688,905.1718,59.02773,0,0,0,0,100,0), -- 20:27:39 +(@PATH,2,602.5994,900.3304,59.40689,0,0,0,0,100,0), -- 20:27:41 +(@PATH,3,601.3251,900.2347,59.64486,0,0,0,0,100,0), -- 20:27:45 +(@PATH,4,596.3672,905.3165,59.73195,0,0,0,0,100,0), -- 20:27:47 +(@PATH,5,594.7749,909.9604,59.21187,0,0,0,0,100,0), -- 20:27:49 +(@PATH,6,598.7663,913.3591,58.69057,0,0,0,0,100,0), -- 20:27:53 +(@PATH,7,609.4606,912.4673,58.62482,0,0,0,0,100,0), -- 20:27:57 +(@PATH,8,615.0436,905.1978,58.98429,0,0,0,0,100,0); -- 20:28:00 +-- 0x1C16F44680167D00006A5B00019D4973 .go 614.9688 905.1718 59.02773 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=53820; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(53820, 53820, 0, 0, 2), +(53820, 53821, 4, 270, 2); + +-- Pathing for Bonechewer Taskmaster Entry: 23028 'TDB FORMAT' +SET @NPC := 53820; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=633.2257,`position_y`=963.4147,`position_z`=55.95343 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,633.2257,963.4147,55.95343,0,0,0,0,100,0), -- 20:24:38 +(@PATH,2,626.0544,963.9803,56.37569,0,0,0,0,100,0), -- 20:24:42 +(@PATH,3,620.5211,966.0339,56.25259,0,0,0,0,100,0), -- 20:24:43 +(@PATH,4,620.7354,966.1735,56.31302,0,0,0,0,100,0), -- 20:24:49 +(@PATH,5,626.1029,963.9597,56.25856,0,0,0,0,100,0), -- 20:24:52 +(@PATH,6,639.0172,964.6981,55.65588,0,0,0,0,100,0), -- 20:24:55 +(@PATH,7,640.4229,965.3899,55.41632,0,0,0,0,100,0), -- 20:24:58 +(@PATH,8,640.1735,965.0356,55.36687,0,0,0,0,100,0), -- 20:25:03 +(@PATH,9,634.595,963.6308,56.07304,0,0,0,0,100,0); -- 20:25:05 +-- 0x1C16F44680167D00006A5B00001D4973 .go 633.2257 963.4147 55.95343 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=52890; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(52890, 52890, 0, 0, 2), +(52890, 52889, 4, 270, 2); + +-- Pathing for Dragonmaw Wyrmcaller Entry: 22960 'TDB FORMAT' +SET @NPC := 52890; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=648.1636,`position_y`=938.5795,`position_z`=55.75506 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,648.1636,938.5795,55.75506,0,0,0,0,100,0), -- 20:22:53 +(@PATH,2,645.2552,922.6402,57.29848,0,0,0,0,100,0), -- 20:22:58 +(@PATH,3,645.6837,916.9199,58.23938,0,0,0,0,100,0), -- 20:23:03 +(@PATH,4,646.1837,910.1699,58.73938,0,0,0,0,100,0), -- 20:23:03 +(@PATH,5,645.2875,920.4247,58.00103,0,0,0,0,100,0), -- 20:23:11 +(@PATH,6,645.5131,925.1226,56.93448,0,0,0,0,100,0), -- 20:23:15 +(@PATH,7,645.7631,930.8726,56.43448,0,0,0,0,100,0), -- 20:23:15 +(@PATH,8,650.084,943.375,55.52758,0,0,0,0,100,0); -- 20:23:21 +-- 0x1C16F44680166C00006A5B00031D4972 .go 648.1636 938.5795 55.75506 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=52887; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(52887, 52887, 0, 0, 2), +(52887, 52888, 4, 270, 2); + +-- Pathing for Dragonmaw Wyrmcaller Entry: 22960 'TDB FORMAT' +SET @NPC := 52887; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=785.6938,`position_y`=775.3997,`position_z`=66.39993 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,785.6938,775.3997,66.39993,0,0,0,0,100,0), -- 20:24:19 +(@PATH,2,785.8984,794.7227,65.27033,0,0,0,0,100,0), -- 20:24:27 +(@PATH,3,786.6484,798.7227,64.77033,0,0,0,0,100,0), -- 20:24:27 +(@PATH,4,787.1109,818.1139,62.89856,0,0,0,0,100,0), -- 20:24:34 +(@PATH,5,787.1109,821.1139,62.39856,0,0,0,0,100,0), -- 20:24:34 +(@PATH,6,787.1893,815.1113,63.54066,0,0,0,0,100,0), -- 20:24:44 +(@PATH,7,787.1893,811.3613,64.04066,0,0,0,0,100,0), -- 20:24:44 +(@PATH,8,785.7648,793.4683,65.52825,0,0,0,0,100,0), -- 20:24:51 +(@PATH,9,785.2648,789.4683,66.27825,0,0,0,0,100,0), -- 20:24:51 +(@PATH,10,787.0689,764.1604,68.09256,0,0,0,0,100,0); -- 20:25:00 +-- 0x1C16F44680166C00006A5B00021D4972 .go 785.6938 775.3997 66.39993 + +DELETE FROM `creature_formations` WHERE `leaderGUID`=52873; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(52873, 52873, 0, 0, 2), +(52873, 52874, 4, 270, 2); + +-- Pathing for Dragonmaw Wyrmcaller Entry: 22960 'TDB FORMAT' +SET @NPC := 52873; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=703.7258,`position_y`=793.4444,`position_z`=63.46073 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,703.7258,793.4444,63.46073,0,0,0,0,100,0), -- 20:22:58 +(@PATH,2,707.7258,805.4444,62.96073,0,0,0,0,100,0), -- 20:22:58 +(@PATH,3,707.654,812.1962,61.59881,0,0,0,0,100,0), -- 20:23:08 +(@PATH,4,706.654,814.9462,60.84881,0,0,0,0,100,0), -- 20:23:08 +(@PATH,5,705.904,817.9462,60.09881,0,0,0,0,100,0), -- 20:23:08 +(@PATH,6,705.404,820.6962,59.59881,0,0,0,0,100,0), -- 20:23:08 +(@PATH,7,704.5902,823.67,59.79628,0,0,0,0,100,0), -- 20:23:15 +(@PATH,8,704.8102,841.6521,58.77011,0,0,0,0,100,0), -- 20:23:20 +(@PATH,9,704.6913,842.5567,58.75265,0,0,0,0,100,0), -- 20:23:27 +(@PATH,10,704.126,829.2064,60.07757,0,0,0,0,100,0), -- 20:23:31 +(@PATH,11,707.3738,813.0848,61.42086,0,0,0,0,100,0), -- 20:23:36 +(@PATH,12,706.2325,801.4998,63.37791,0,0,0,0,100,0), -- 20:23:42 +(@PATH,13,702.9825,791.2498,63.87791,0,0,0,0,100,0), -- 20:23:42 +(@PATH,14,700.8631,770.6361,65.44987,0,0,0,0,100,0), -- 20:23:52 +(@PATH,15,700.8631,765.6361,65.94987,0,0,0,0,100,0), -- 20:23:52 +(@PATH,16,700.9891,782.0386,64.53194,0,0,0,0,100,0); -- 20:24:04 +-- 0x1C16F44680166C00006A5B00011D4972 .go 703.7258 793.4444 63.46073 diff --git a/sql/updates/world/2016_01_23_00_world.sql b/sql/updates/world/2016_01_23_00_world.sql new file mode 100644 index 00000000000..02eaa7c370d --- /dev/null +++ b/sql/updates/world/2016_01_23_00_world.sql @@ -0,0 +1,43 @@ +DELETE FROM `creature_formations` WHERE `leaderGUID`=63381; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`) VALUES +(63381, 63381, 0, 0, 1), +(63381, 63380, 2, 270, 2); + +-- Pathing for Blade of Argus Entry: 17659 'TDB FORMAT' +SET @NPC := 63381; +SET @PATH := @NPC * 10; +UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=-2393.32,`position_y`=-12009.38,`position_z`=26.83788 WHERE `guid`=@NPC; +DELETE FROM `creature_addon` WHERE `guid`=@NPC; +INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES (@NPC,@PATH,0,0,1,0, ''); +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,-2393.32,-12009.38,26.83788,0,0,0,0,100,0), -- 09:35:02 +(@PATH,2,-2430.895,-12030.52,28.6322,0,0,0,0,100,0), -- 09:35:35 +(@PATH,3,-2449.131,-12041.39,30.04015,0,0,0,0,100,0), -- 09:35:47 +(@PATH,4,-2469.279,-12052.41,30.91039,0,0,0,0,100,0), -- 09:35:58 +(@PATH,5,-2485.917,-12057.62,31.67413,0,0,0,0,100,0), -- 09:36:06 +(@PATH,6,-2499.339,-12060.41,32.19662,0,0,0,0,100,0), -- 09:36:16 +(@PATH,7,-2551.131,-12097.43,31.56079,0,0,0,0,100,0), -- 09:36:26 +(@PATH,8,-2561.849,-12106.37,30.36333,0,0,0,0,100,0), -- 09:36:45 +(@PATH,9,-2594.441,-12132.8,25.2879,0,0,0,0,100,0), -- 09:36:59 +(@PATH,10,-2614.055,-12144.08,22.40907,0,0,0,0,100,0), -- 09:37:10 +(@PATH,11,-2646.337,-12163.92,17.16356,0,0,0,0,100,0), -- 09:37:24 +(@PATH,12,-2662.804,-12174.26,14.88417,0,0,0,0,100,0), -- 09:37:33 +(@PATH,13,-2684.956,-12188.63,12.24028,0,0,0,0,100,0), -- 09:37:43 +(@PATH,14,-2717.39,-12205.54,9.461565,0,0,0,0,100,0), -- 09:37:57 +(@PATH,15,-2734.083,-12209.19,8.946226,0,0,0,0,100,0), -- 09:38:09 +(@PATH,16,-2734.196,-12209.07,8.88302,0,0,0,0,100,0), -- 09:38:21 +(@PATH,17,-2716.481,-12205.85,9.632504,0,0,0,0,100,0), -- 09:38:31 +(@PATH,18,-2690.927,-12193.18,11.25666,0,0,0,0,100,0), -- 09:38:43 +(@PATH,19,-2673.976,-12180.86,13.79457,0,0,0,0,100,0), -- 09:38:55 +(@PATH,20,-2653.271,-12168.26,16.31552,0,0,0,0,100,0), -- 09:39:06 +(@PATH,21,-2634.485,-12156.3,19.4272,0,0,0,0,100,0), -- 09:39:16 +(@PATH,22,-2600.122,-12135.83,24.54786,0,0,0,0,100,0), -- 09:39:29 +(@PATH,23,-2580.325,-12122.89,27.64962,0,0,0,0,100,0), -- 09:39:40 +(@PATH,24,-2535.282,-12085.34,32.13941,0,0,0,0,100,0), -- 09:39:54 +(@PATH,25,-2520.165,-12073.53,32.51933,0,0,0,0,100,0), -- 09:40:14 +(@PATH,26,-2475.724,-12055.97,31.22906,0,0,0,0,100,0), -- 09:40:24 +(@PATH,27,-2463.655,-12049.68,30.5184,0,0,0,0,100,0), -- 09:40:33 +(@PATH,28,-2435.532,-12033.4,29.11832,0,0,0,0,100,0), -- 09:40:42 +(@PATH,29,-2407.538,-12018.4,27.76891,0,0,0,0,100,0), -- 09:40:53 +(@PATH,30,-2393.296,-12009.36,26.8369,0,0,0,0,100,0); -- 09:41:05 diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index f72c1430370..1f3b78a8d56 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -27,6 +27,7 @@ #include <vector> #include <list> #include <map> +#include <ctime> // Searcher for map of structs template<typename T, class S> struct Finder diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index a826f841058..30ba95eb174 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -7099,9 +7099,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere float chance = 100.0f / procSpell->Effects[effIndex].ChainTarget; if (!roll_chance_f(chance)) return false; - - // Remove cooldown (Chain Lightning - has Category Recovery time) - GetSpellHistory()->ResetCooldown(spellId); } CastSpell(victim, spellId, true, castItem, triggeredByAura); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index a4e7f61d83c..6a10d113553 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1103,18 +1103,11 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const if (apply) { - // Remove cooldown of spells triggered on stance change - they may share cooldown with stance spell if (spellId) - { - target->GetSpellHistory()->ResetCooldown(spellId); target->CastSpell(target, spellId, true, NULL, this); - } if (spellId2) - { - target->GetSpellHistory()->ResetCooldown(spellId2); target->CastSpell(target, spellId2, true, NULL, this); - } if (target->GetTypeId() == TYPEID_PLAYER) { diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 84bff4a69d6..63fe148dd93 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -4701,7 +4701,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_NOT_READY; } - if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry)) + if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) { if (m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 40bd4e3c263..f961654f279 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -877,11 +877,6 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) values.AddSpellMod(SPELLVALUE_BASE_POINT2, damage); } - // Remove spell cooldown (not category) if spell triggering spell with cooldown and same category - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime - && m_spellInfo->GetCategory() == spellInfo->GetCategory()) - m_caster->GetSpellHistory()->ResetCooldown(spellInfo->Id); - // original caster guid only for GO cast m_caster->CastSpell(targets, spellInfo, &values, TRIGGERED_FULL_MASK, NULL, NULL, m_originalCasterGUID); } @@ -930,11 +925,6 @@ void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex) values.AddSpellMod(SPELLVALUE_BASE_POINT2, damage); } - // Remove spell cooldown (not category) if spell triggering spell with cooldown and same category - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime - && m_spellInfo->GetCategory() == spellInfo->GetCategory()) - m_caster->GetSpellHistory()->ResetCooldown(spellInfo->Id); - // original caster guid only for GO cast m_caster->CastSpell(targets, spellInfo, &values, TRIGGERED_FULL_MASK, NULL, NULL, m_originalCasterGUID); } diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index ac08f681933..adf5fc47c77 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -182,13 +182,13 @@ void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Sp StartCooldown(spellInfo, itemID, spell); } -bool SpellHistory::IsReady(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const +bool SpellHistory::IsReady(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const { if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) if (IsSchoolLocked(spellInfo->GetSchoolMask())) return false; - if (HasCooldown(spellInfo->Id, itemId)) + if (HasCooldown(spellInfo->Id, itemId, ignoreCategoryCooldown)) return false; return true; @@ -465,11 +465,14 @@ void SpellHistory::ResetAllCooldowns() _spellCooldowns.clear(); } -bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const +bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const { if (_spellCooldowns.count(spellInfo->Id) != 0) return true; + if (ignoreCategoryCooldown) + return false; + uint32 category = 0; GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr); if (!category) @@ -478,9 +481,9 @@ bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/ return _categoryCooldowns.count(category) != 0; } -bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/) const +bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const { - return HasCooldown(sSpellMgr->EnsureSpellInfo(spellId), itemId); + return HasCooldown(sSpellMgr->EnsureSpellInfo(spellId), itemId, ignoreCategoryCooldown); } uint32 SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h index 00c790a670d..f0a53fe130d 100644 --- a/src/server/game/Spells/SpellHistory.h +++ b/src/server/game/Spells/SpellHistory.h @@ -62,7 +62,7 @@ public: void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr); void HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell = nullptr); - bool IsReady(SpellInfo const* spellInfo, uint32 itemId = 0) const; + bool IsReady(SpellInfo const* spellInfo, uint32 itemId = 0, bool ignoreCategoryCooldown = false) const; template<class OwnerType> void WritePacket(WorldPacket& packet) const; @@ -105,8 +105,8 @@ public: } void ResetAllCooldowns(); - bool HasCooldown(SpellInfo const* spellInfo, uint32 itemId = 0) const; - bool HasCooldown(uint32 spellId, uint32 itemId = 0) const; + bool HasCooldown(SpellInfo const* spellInfo, uint32 itemId = 0, bool ignoreCategoryCooldown = false) const; + bool HasCooldown(uint32 spellId, uint32 itemId = 0, bool ignoreCategoryCooldown = false) const; uint32 GetRemainingCooldown(SpellInfo const* spellInfo) const; // School lockouts diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index 2af8e7f3643..5a5b7aefc02 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -115,7 +115,7 @@ enum DataTypes DATA_ARTHAS_PLATFORM = 38, DATA_TERENAS_MENETHIL = 39, DATA_ENEMY_GUNSHIP = 40, - DATA_UPPERSPIRE_TELE_ACT = 41, + DATA_UPPERSPIRE_TELE_ACT = 41, /// also used by conditions DATA_BLOOD_QUEEN_LANA_THEL_COUNCIL = 42 }; @@ -569,14 +569,10 @@ class spell_trigger_spell_from_caster : public SpellScriptLoader TriggerCastFlags _triggerFlags; }; -template<class AI> -CreatureAI* GetIcecrownCitadelAI(Creature* creature) +template<class AI, class T> +inline AI* GetIcecrownCitadelAI(T* obj) { - if (InstanceMap* instance = creature->GetMap()->ToInstanceMap()) - if (instance->GetInstanceScript()) - if (instance->GetScriptId() == sObjectMgr->GetScriptId(ICCScriptName)) - return new AI(creature); - return NULL; + return GetInstanceAI<AI>(obj, ICCScriptName); } #endif // ICECROWN_CITADEL_H_ diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp index 7723a37094c..8b5e03203e1 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp @@ -15,6 +15,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "GameObjectAI.h" #include "InstanceScript.h" #include "Player.h" #include "ScriptedGossip.h" @@ -22,57 +23,55 @@ #include "Spell.h" #include "icecrown_citadel.h" -#define GOSSIP_SENDER_ICC_PORT 631 +static std::vector<uint32> const TeleportSpells = +{ + LIGHT_S_HAMMER_TELEPORT, // 0 + ORATORY_OF_THE_DAMNED_TELEPORT, // 1 + 0, // 2 + RAMPART_OF_SKULLS_TELEPORT, // 3 + DEATHBRINGER_S_RISE_TELEPORT, // 4 + UPPER_SPIRE_TELEPORT, // 5 + SINDRAGOSA_S_LAIR_TELEPORT // 6 +}; class icecrown_citadel_teleport : public GameObjectScript { + static_assert(DATA_UPPERSPIRE_TELE_ACT == 41, "icecrown_citadel.h DATA_UPPERSPIRE_TELE_ACT set to value != 41, gossip condition of the teleporters won't work as intended."); + public: icecrown_citadel_teleport() : GameObjectScript("icecrown_citadel_teleport") { } - bool OnGossipHello(Player* player, GameObject* go) override + struct icecrown_citadel_teleportAI : public GameObjectAI { - if (InstanceScript* instance = go->GetInstanceScript()) + icecrown_citadel_teleportAI(GameObject* go) : GameObjectAI(go) { - if (instance->GetBossState(DATA_LORD_MARROWGAR) == DONE) - { - if (go->GetEntry() != GO_SCOURGE_TRANSPORTER_LIGHTSHAMMER) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to Light's Hammer.", GOSSIP_SENDER_ICC_PORT, LIGHT_S_HAMMER_TELEPORT); - if (go->GetEntry() != GO_SCOURGE_TRANSPORTER_ORATORY) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Oratory of the Damned.", GOSSIP_SENDER_ICC_PORT, ORATORY_OF_THE_DAMNED_TELEPORT); - } - if (instance->GetBossState(DATA_LADY_DEATHWHISPER) == DONE && go->GetEntry() != GO_SCOURGE_TRANSPORTER_RAMPART) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Rampart of Skulls.", GOSSIP_SENDER_ICC_PORT, RAMPART_OF_SKULLS_TELEPORT); - if (instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == DONE && go->GetEntry() != GO_SCOURGE_TRANSPORTER_DEATHBRINGER) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Deathbringer's Rise.", GOSSIP_SENDER_ICC_PORT, DEATHBRINGER_S_RISE_TELEPORT); - if (instance->GetData(DATA_UPPERSPIRE_TELE_ACT) == DONE && go->GetEntry() != GO_SCOURGE_TRANSPORTER_UPPERSPIRE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Upper Spire.", GOSSIP_SENDER_ICC_PORT, UPPER_SPIRE_TELEPORT); - /// @todo Gauntlet event before Sindragosa - if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == DONE && go->GetEntry() != GO_SCOURGE_TRANSPORTER_SINDRAGOSA) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to Sindragosa's Lair", GOSSIP_SENDER_ICC_PORT, SINDRAGOSA_S_LAIR_TELEPORT); } - player->SEND_GOSSIP_MENU(player->GetGossipTextId(go), go->GetGUID()); - return true; - } + bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + if (gossipListId >= TeleportSpells.size()) + return false; - bool OnGossipSelect(Player* player, GameObject* /*go*/, uint32 sender, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - player->CLOSE_GOSSIP_MENU(); - SpellInfo const* spell = sSpellMgr->GetSpellInfo(action); - if (!spell) - return false; + player->PlayerTalkClass->ClearMenus(); + player->CLOSE_GOSSIP_MENU(); + SpellInfo const* spell = sSpellMgr->GetSpellInfo(TeleportSpells[gossipListId]); + if (!spell) + return false; - if (player->IsInCombat()) - { - Spell::SendCastResult(player, spell, 0, SPELL_FAILED_AFFECTING_COMBAT); - return true; - } + if (player->IsInCombat()) + { + Spell::SendCastResult(player, spell, 0, SPELL_FAILED_AFFECTING_COMBAT); + return true; + } - if (sender == GOSSIP_SENDER_ICC_PORT) player->CastSpell(player, spell, true); + return true; + } + }; - return true; + GameObjectAI* GetAI(GameObject* go) const override + { + return GetIcecrownCitadelAI<icecrown_citadel_teleportAI>(go); } }; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp index 3e7de89edd9..a7a89f44d81 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp @@ -15,382 +15,681 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "Player.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "SpellScript.h" #include "SpellAuraEffects.h" #include "naxxramas.h" -enum Horsemen +enum Horseman { - HORSEMEN_THANE, - HORSEMEN_LADY, - HORSEMEN_BARON, - HORSEMEN_SIR, + THANE = DATA_THANE, + LADY = DATA_LADY, + BARON = DATA_BARON, + SIR = DATA_SIR, }; +static const std::vector<Horseman> horsemen = { THANE, LADY, BARON, SIR }; // for iterating enum Spells { - SPELL_MARK_DAMAGE = 28836 + /* all */ + SPELL_MARK_DAMAGE = 28836, + SPELL_BERSERK = 26662, + SPELL_ENCOUNTER_CREDIT = 59450, + + /* baron */ + SPELL_BARON_MARK = 28834, + SPELL_UNHOLY_SHADOW = 28882, + + /* thane */ + SPELL_THANE_MARK = 28832, + SPELL_METEOR = 28884, + + /* lady */ + SPELL_SHADOW_BOLT = 57374, + SPELL_LADY_MARK = 28833, + SPELL_VOID_ZONE = 28863, + SPELL_UNYIELDING_PAIN = 57381, + + /* sir */ + SPELL_HOLY_BOLT = 57376, + SPELL_SIR_MARK = 28835, + SPELL_HOLY_WRATH = 28883, + SPELL_CONDEMNATION = 57377 }; -enum Events +enum Actions { - EVENT_NONE, - EVENT_MARK, - EVENT_CAST, - EVENT_BERSERK, + ACTION_BEGIN_MOVEMENT = 1, + ACTION_BEGIN_FIGHTING +}; + +enum HorsemenData +{ + DATA_HORSEMEN_IS_TIMED_KILL = Data::DATA_HORSEMEN_CHECK_ACHIEVEMENT_CREDIT, // inherit from naxxramas.h - this needs to be the first entry to ensure that there are no conflicts + DATA_MOVEMENT_FINISHED, + DATA_DEATH_TIME }; -const Position WaypointPositions[12] = +enum Events { - // Thane waypoints - {2542.3f, -2984.1f, 241.49f, 5.362f}, - {2547.6f, -2999.4f, 241.34f, 5.049f}, - {2542.9f, -3015.0f, 241.35f, 4.654f}, - // Lady waypoints - {2498.3f, -2961.8f, 241.28f, 3.267f}, - {2487.7f, -2959.2f, 241.28f, 2.890f}, - {2469.4f, -2947.6f, 241.28f, 2.576f}, - // Baron waypoints - {2553.8f, -2968.4f, 241.33f, 5.757f}, - {2564.3f, -2972.5f, 241.33f, 5.890f}, - {2583.9f, -2971.67f, 241.35f, 0.008f}, - // Sir waypoints - {2534.5f, -2921.7f, 241.53f, 1.363f}, - {2523.5f, -2902.8f, 241.28f, 2.095f}, - {2517.8f, -2896.6f, 241.28f, 2.315f}, + /* all */ + EVENT_BERSERK = 1, + EVENT_MARK, + + /* rivendare */ + EVENT_UNHOLYSHADOW, + + /* thane */ + EVENT_METEOR, + + /* lady */ + EVENT_VOIDZONE, + + /* sir */ + EVENT_HOLYWRATH }; -const uint32 NPC_HORSEMEN[] = {16064, 16065, 30549, 16063}; -const uint32 SPELL_MARK[] = {28832, 28833, 28834, 28835}; -#define SPELL_PRIMARY(i) RAID_MODE(SPELL_PRIMARY_N[i], SPELL_PRIMARY_H[i]) -const uint32 SPELL_PRIMARY_N[] = {28884, 28863, 28882, 28883}; -const uint32 SPELL_PRIMARY_H[] = {57467, 57463, 57369, 57466}; -#define SPELL_SECONDARY(i) RAID_MODE(SPELL_SECONDARY_N[i], SPELL_SECONDARY_H[i]) -const uint32 SPELL_SECONDARY_N[]= {0, 57374, 0, 57376}; -const uint32 SPELL_SECONDARY_H[]= {0, 57464, 0, 57465}; -const uint32 SPELL_PUNISH[] = {0, 57381, 0, 57377}; -#define SPELL_BERSERK 26662 - -enum FourHorsemen +enum Yells { SAY_AGGRO = 0, - SAY_TAUNT = 1, SAY_SPECIAL = 2, SAY_SLAY = 3, - SAY_DEATH = 4 -}; + SAY_DEATH = 4, -class boss_four_horsemen : public CreatureScript -{ -public: - boss_four_horsemen() : CreatureScript("boss_four_horsemen") { } + EMOTE_RAGECAST = 7 +}; - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_four_horsemenAI>(creature); - } +static const Position baronPath[3] = { { 2552.427f, -2969.737f, 241.3021f },{ 2566.759f, -2972.535f, 241.3217f },{ 2584.32f, -2971.96f, 241.3489f } }; +static const Position thanePath[3] = { { 2540.095f, -2983.192f, 241.3344f },{ 2546.005f, -2999.826f, 241.3665f },{ 2542.697f, -3014.055f, 241.3371f } }; +static const Position ladyPath[3] = { { 2507.94f, -2961.444f, 242.4557f },{ 2488.763f, -2960.007f, 241.2757f },{ 2468.26f, -2947.499f, 241.2753f } }; +static const Position sirPath[3] = { { 2533.141f, -2922.14f, 241.2764f },{ 2525.254f, -2905.907f, 241.2761f },{ 2517.636f, -2897.253f, 241.2758f } }; - struct boss_four_horsemenAI : public BossAI - { - boss_four_horsemenAI(Creature* creature) : BossAI(creature, BOSS_HORSEMEN) +struct boss_four_horsemen_baseAI : public BossAI +{ + public: + Creature* getHorsemanHandle(Horseman who) const { - Initialize(); - id = Horsemen(0); - for (uint8 i = 0; i < 4; ++i) - if (me->GetEntry() == NPC_HORSEMEN[i]) - id = Horsemen(i); - caster = (id == HORSEMEN_LADY || id == HORSEMEN_SIR); + if (_which == who) + return me; + else + return ObjectAccessor::GetCreature(*me, instance->GetGuidData(who)); } - - void Initialize() + boss_four_horsemen_baseAI(Creature* creature, Horseman which, Position const* initialPath) : + BossAI(creature, BOSS_HORSEMEN), _which(which), _initialPath(initialPath), _myMovementFinished(false), _nextMovement(0), _timeDied(0), _ourMovementFinished(false) { - uiEventStarterGUID.Clear(); - nextWP = 0; - punishTimer = 2000; - nextMovementStarted = false; - movementCompleted = false; - movementStarted = false; - encounterActionAttack = false; - encounterActionReset = false; - doDelayPunish = false; + if (!me->IsAlive() && instance->GetBossState(BOSS_HORSEMEN) != DONE) + me->SetRespawnTime(10); } - Horsemen id; - ObjectGuid uiEventStarterGUID; - uint8 nextWP; - uint32 punishTimer; - bool caster; - bool nextMovementStarted; - bool movementCompleted; - bool movementStarted; - bool encounterActionAttack; - bool encounterActionReset; - bool doDelayPunish; - - void Reset() override + uint32 GetData(uint32 type) const override { - if (!encounterActionReset) - DoEncounteraction(NULL, false, true, false); - - instance->SetData(DATA_HORSEMEN0 + id, NOT_STARTED); - - me->SetReactState(REACT_AGGRESSIVE); - Initialize(); - _Reset(); + switch (type) + { + case DATA_MOVEMENT_FINISHED: + return _myMovementFinished ? 1 : 0; + case DATA_DEATH_TIME: + return _timeDied; + case DATA_HORSEMEN_IS_TIMED_KILL: + { + uint32 minTime = 0, maxTime = 0; + for (Horseman boss : horsemen) + if (Creature* cBoss = getHorsemanHandle(boss)) + { + uint32 deathTime = cBoss->AI()->GetData(DATA_DEATH_TIME); + if (!deathTime) + { + TC_LOG_WARN("scripts", "FourHorsemenAI: Checking for achievement credit but horseman %s is reporting not dead", cBoss->GetName().c_str()); + return 0; + } + if (!minTime || deathTime < minTime) + minTime = deathTime; + if (!maxTime || deathTime > maxTime) + maxTime = deathTime; + } + else + { + TC_LOG_WARN("scripts", "FourHorsemenAI: Checking for achievement credit but horseman with id %u is not present", uint32(boss)); + return 0; + } + return (getMSTimeDiff(minTime, maxTime) <= 15 * IN_MILLISECONDS) ? 1 : 0; + } + default: + return 0; + } } - bool DoEncounteraction(Unit* who, bool attack, bool reset, bool checkAllDead) + void DoAction(int32 action) override { - Creature* Thane = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THANE)); - Creature* Lady = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_LADY)); - Creature* Baron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_BARON)); - Creature* Sir = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIR)); + switch (action) + { + case ACTION_BEGIN_MOVEMENT: + me->GetMotionMaster()->MovePoint(1, _initialPath[0], true); + break; + case ACTION_BEGIN_FIGHTING: + if (_ourMovementFinished) + break; + me->SetCombatPulseDelay(5); + BeginFighting(); + _ourMovementFinished = true; + break; + } + } - if (Thane && Lady && Baron && Sir) + void CheckIsMovementFinished() + { + for (Horseman boss : horsemen) { - if (attack && who) + if (Creature* cBoss = getHorsemanHandle(boss)) { - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Thane->AI())->encounterActionAttack = true; - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Lady->AI())->encounterActionAttack = true; - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Baron->AI())->encounterActionAttack = true; - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Sir->AI())->encounterActionAttack = true; - - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Thane->AI())->AttackStart(who); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Lady->AI())->AttackStart(who); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Baron->AI())->AttackStart(who); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Sir->AI())->AttackStart(who); + if (cBoss->IsAlive() && !cBoss->AI()->GetData(DATA_MOVEMENT_FINISHED)) + return; } - - if (reset) + else { - if (instance->GetBossState(BOSS_HORSEMEN) != NOT_STARTED) - { - if (!Thane->IsAlive()) - Thane->Respawn(); - - if (!Lady->IsAlive()) - Lady->Respawn(); + TC_LOG_WARN("scripts", "FourHorsemenAI: Checking if movement is finished but horseman with id %u is not present", uint32(boss)); + ResetEncounter(); + return; + } + } - if (!Baron->IsAlive()) - Baron->Respawn(); + for (Horseman boss : horsemen) + if (Creature* cBoss = getHorsemanHandle(boss)) + cBoss->AI()->DoAction(ACTION_BEGIN_FIGHTING); + } - if (!Sir->IsAlive()) - Sir->Respawn(); + void BeginEncounter() + { + if (instance->GetBossState(BOSS_HORSEMEN) == IN_PROGRESS) + return; + if (!instance->CheckRequiredBosses(BOSS_HORSEMEN)) + { + ResetEncounter(); + return; + } + instance->SetBossState(BOSS_HORSEMEN, IN_PROGRESS); + Map::PlayerList const &players = me->GetMap()->GetPlayers(); + if (players.isEmpty()) // sanity check + ResetEncounter(); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Thane->AI())->encounterActionReset = true; - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Lady->AI())->encounterActionReset = true; - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Baron->AI())->encounterActionReset = true; - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Sir->AI())->encounterActionReset = true; + for (Horseman boss : horsemen) + { + if (Creature* cBoss = getHorsemanHandle(boss)) + { + if (!cBoss->IsAlive()) + { + ResetEncounter(); + return; + } + cBoss->SetReactState(REACT_PASSIVE); + cBoss->AttackStop(); // clear initial target that was set on enter combat + cBoss->setActive(true); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Thane->AI())->EnterEvadeMode(); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Lady->AI())->EnterEvadeMode(); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Baron->AI())->EnterEvadeMode(); - ENSURE_AI(boss_four_horsemen::boss_four_horsemenAI, Sir->AI())->EnterEvadeMode(); + for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) + { + if (Player* player = it->GetSource()) + { + if (player->IsGameMaster()) + continue; + + if (player->IsAlive()) + { + cBoss->AddThreat(player, 0.0f); + cBoss->SetInCombatWith(player); + player->SetInCombatWith(cBoss); + } + } } - } - if (checkAllDead) - return !Thane->IsAlive() && !Lady->IsAlive() && !Baron->IsAlive() && !Sir->IsAlive(); + /* Why do the Four Horsemen run to opposite corners of the room when engaged? * + * They saw all the mobs leading up to them being AoE'd down and made a judgment call. */ + cBoss->AI()->DoAction(ACTION_BEGIN_MOVEMENT); + } + else + { + TC_LOG_WARN("scripts", "FourHorsemenAI: Encounter starting but horseman with id %u is not present", uint32(boss)); + ResetEncounter(); + return; + } } - return false; } - void BeginFourHorsemenMovement() + void ResetEncounter() { - movementStarted = true; - me->SetReactState(REACT_PASSIVE); - me->SetWalk(false); - me->SetSpeed(MOVE_RUN, me->GetSpeedRate(MOVE_RUN), true); - - switch (id) + if (instance->GetBossState(BOSS_HORSEMEN) == NOT_STARTED || instance->GetBossState(BOSS_HORSEMEN) == DONE) + return; + instance->SetBossState(BOSS_HORSEMEN, NOT_STARTED); + for (Horseman boss : horsemen) { - case HORSEMEN_THANE: - me->GetMotionMaster()->MovePoint(0, WaypointPositions[0]); - break; - case HORSEMEN_LADY: - me->GetMotionMaster()->MovePoint(3, WaypointPositions[3]); - break; - case HORSEMEN_BARON: - me->GetMotionMaster()->MovePoint(6, WaypointPositions[6]); - break; - case HORSEMEN_SIR: - me->GetMotionMaster()->MovePoint(9, WaypointPositions[9]); - break; + if (Creature* cBoss = getHorsemanHandle(boss)) + { + cBoss->DespawnOrUnsummon(0); + cBoss->SetRespawnTime(15); + } + else + { + TC_LOG_WARN("scripts", "FourHorsemenAI: Encounter resetting but horseman with id %u is not present", uint32(boss)); + } } } - void MovementInform(uint32 type, uint32 point) override + void EncounterCleared() { - if (type != POINT_MOTION_TYPE) + if (instance->GetBossState(BOSS_HORSEMEN) == DONE) return; + instance->SetBossState(BOSS_HORSEMEN, DONE); + //instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, SPELL_ENCOUNTER_CREDIT); + DoCastAOE(SPELL_ENCOUNTER_CREDIT, true); + } - if (point == 2 || point == 5 || point == 8 || point == 11) - { - movementCompleted = true; - me->SetReactState(REACT_AGGRESSIVE); + void EnterCombat(Unit* /*who*/) override + { + if (instance->GetBossState(BOSS_HORSEMEN) != NOT_STARTED) // another horseman already did it + return; + Talk(SAY_AGGRO); + BeginEncounter(); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + ResetEncounter(); + } + + void Reset() override + { + if (!me->IsAlive()) + return; + _myMovementFinished = false; + _nextMovement = 0; + _timeDied = 0; + _ourMovementFinished = false; + me->SetReactState(REACT_AGGRESSIVE); + SetCombatMovement(false); + me->SetCombatPulseDelay(0); + me->ResetLootMode(); + events.Reset(); + summons.DespawnAll(); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY, victim); + } - Unit* eventStarter = ObjectAccessor::GetUnit(*me, uiEventStarterGUID); + void JustDied(Unit* /*killer*/) override + { + if (instance->GetBossState(BOSS_HORSEMEN) != IN_PROGRESS) // necessary in case a horseman gets one-shot + { + BeginEncounter(); + return; + } - if (eventStarter && me->IsValidAttackTarget(eventStarter)) - AttackStart(eventStarter); - else if (!UpdateVictim()) + Talk(SAY_DEATH); + _timeDied = getMSTime(); + for (Horseman boss : horsemen) + { + if (Creature* cBoss = getHorsemanHandle(boss)) { - EnterEvadeMode(); - return; + if (cBoss->IsAlive()) + { + // in case a horseman dies while moving (unlikely but possible especially in non-335 branch) + CheckIsMovementFinished(); + return; + } } - - if (caster) + else { - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); + TC_LOG_WARN("scripts", "FourHorsemenAI: %s died but horseman with id %u is not present", me->GetName().c_str(), uint32(boss)); + ResetEncounter(); } - - return; } - nextMovementStarted = false; - nextWP = point + 1; + EncounterCleared(); } - // switch to "who" if nearer than current target. - void SelectNearestTarget(Unit* who) + void MovementInform(uint32 type, uint32 i) override { - if (me->GetVictim() && me->GetDistanceOrder(who, me->GetVictim()) && me->IsValidAttackTarget(who)) + if (type != POINT_MOTION_TYPE) + return; + if (i < 3) + _nextMovement = i; // delay to next updateai to prevent it from instantly expiring + else { - me->getThreatManager().modifyThreatPercent(me->GetVictim(), -100); - me->AddThreat(who, 1000000.0f); + _myMovementFinished = true; + CheckIsMovementFinished(); } } - void MoveInLineOfSight(Unit* who) override - + void UpdateAI(uint32 diff) override { - BossAI::MoveInLineOfSight(who); - if (caster) - SelectNearestTarget(who); + if (_nextMovement) + { + me->GetMotionMaster()->MovePoint(_nextMovement + 1, _initialPath[_nextMovement], true); + _nextMovement = 0; + } + _UpdateAI(diff); } - void AttackStart(Unit* who) override + virtual void BeginFighting() = 0; + virtual void _UpdateAI(uint32 /*diff*/) = 0; + + private: + const Horseman _which; + const Position* _initialPath; + bool _myMovementFinished; + uint8 _nextMovement; + uint32 _timeDied; + protected: + bool _ourMovementFinished; +}; + +class boss_four_horsemen_baron : public CreatureScript +{ + public: + boss_four_horsemen_baron() : CreatureScript("boss_four_horsemen_baron") { } + + struct boss_four_horsemen_baronAI : public boss_four_horsemen_baseAI { - if (!movementCompleted && !movementStarted) + boss_four_horsemen_baronAI(Creature* creature) : boss_four_horsemen_baseAI(creature, BARON, baronPath) { } + void BeginFighting() override { - uiEventStarterGUID = who->GetGUID(); - BeginFourHorsemenMovement(); + SetCombatMovement(true); + me->SetReactState(REACT_AGGRESSIVE); + ThreatManager& threat = me->getThreatManager(); + if (threat.isThreatListEmpty()) + { + if (Unit* nearest = me->SelectNearestPlayer(5000.0f)) + { + me->AddThreat(nearest, 1.0f); + AttackStart(nearest); + } + else + ResetEncounter(); + } + else + AttackStart(threat.getHostilTarget()); - if (!encounterActionAttack) - DoEncounteraction(who, true, false, false); + events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(3,7)); } - else if (movementCompleted && movementStarted) + + void _UpdateAI(uint32 diff) override { - if (caster) - me->Attack(who, false); - else - BossAI::AttackStart(who); + if (!_ourMovementFinished || !UpdateVictim()) + return; + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BERSERK: + DoCastAOE(SPELL_BERSERK, true); + break; + case EVENT_MARK: + DoCastAOE(SPELL_BARON_MARK, true); + events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS); + break; + case EVENT_UNHOLYSHADOW: + DoCastVictim(SPELL_UNHOLY_SHADOW); + events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(10,30)); + break; + } + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + DoMeleeAttackIfReady(); } - } - void KilledUnit(Unit* /*victim*/) override + void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_UNHOLY_SHADOW) + Talk(SAY_SPECIAL); + } + }; + + CreatureAI* GetAI(Creature* creature) const override { - if (!(rand32() % 5)) - Talk(SAY_SLAY); + return GetInstanceAI<boss_four_horsemen_baronAI>(creature); } +}; - void JustDied(Unit* /*killer*/) override - { - events.Reset(); - summons.DespawnAll(); +class boss_four_horsemen_thane : public CreatureScript +{ + public: + boss_four_horsemen_thane() : CreatureScript("boss_four_horsemen_thane") { } - instance->SetData(DATA_HORSEMEN0 + id, DONE); + struct boss_four_horsemen_thaneAI : public boss_four_horsemen_baseAI + { + boss_four_horsemen_thaneAI(Creature* creature) : boss_four_horsemen_baseAI(creature, THANE, thanePath), _shouldSay(true) { } + void BeginFighting() override + { + SetCombatMovement(true); + me->SetReactState(REACT_AGGRESSIVE); + ThreatManager& threat = me->getThreatManager(); + if (threat.isThreatListEmpty()) + { + if (Unit* nearest = me->SelectNearestPlayer(5000.0f)) + { + me->AddThreat(nearest, 1.0f); + AttackStart(nearest); + } + else + ResetEncounter(); + } + else + AttackStart(threat.getHostilTarget()); - if (DoEncounteraction(NULL, false, false, true)) + events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_METEOR, urandms(10,15)); + } + void _UpdateAI(uint32 diff) override { - instance->SetBossState(BOSS_HORSEMEN, DONE); - instance->SaveToDB(); + if (!_ourMovementFinished || !UpdateVictim()) + return; + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BERSERK: + DoCastAOE(SPELL_BERSERK, true); + break; + case EVENT_MARK: + DoCastAOE(SPELL_THANE_MARK, true); + events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS); + break; + case EVENT_METEOR: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true)) + { + DoCast(target, SPELL_METEOR); + _shouldSay = true; + } + events.ScheduleEvent(EVENT_METEOR, urandms(13,17)); + break; + } + } - // Achievements related to the 4-horsemen are given through spell 59450 which does not exist. - // There is thus no way it can be given by casting the spell on the players. - instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, 59450); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + DoMeleeAttackIfReady(); } - Talk(SAY_DEATH); - } + void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override + { + if (_shouldSay && spell->Id == SPELL_METEOR) + { + Talk(SAY_SPECIAL); + _shouldSay = false; + } + } - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - Talk(SAY_AGGRO); + private: + bool _shouldSay; // throttle to make sure we only talk on first target hit by meteor + }; - events.ScheduleEvent(EVENT_MARK, 15000); - events.ScheduleEvent(EVENT_CAST, 20000 + rand32() % 5000); - events.ScheduleEvent(EVENT_BERSERK, 15*100*1000); + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_four_horsemen_thaneAI>(creature); } +}; - void UpdateAI(uint32 diff) override +class boss_four_horsemen_lady : public CreatureScript +{ + public: + boss_four_horsemen_lady() : CreatureScript("boss_four_horsemen_lady") { } + + struct boss_four_horsemen_ladyAI : public boss_four_horsemen_baseAI { - if (nextWP && movementStarted && !movementCompleted && !nextMovementStarted) + boss_four_horsemen_ladyAI(Creature* creature) : boss_four_horsemen_baseAI(creature, LADY, ladyPath) { } + void BeginFighting() override { - nextMovementStarted = true; - me->GetMotionMaster()->MovePoint(nextWP, WaypointPositions[nextWP]); + events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_VOIDZONE, urandms(5,10)); } - if (!UpdateVictim() || !CheckInRoom() || !movementCompleted) - return; + void _UpdateAI(uint32 diff) override + { + if (!me->IsInCombat()) + return; + if (!_ourMovementFinished) + return; + if (me->getThreatManager().isThreatListEmpty()) + { + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + return; + } - events.Update(diff); + events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BERSERK: + DoCastAOE(SPELL_BERSERK, true); + break; + case EVENT_MARK: + DoCastAOE(SPELL_LADY_MARK, true); + events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS); + break; + case EVENT_VOIDZONE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true)) + { + DoCast(target, SPELL_VOID_ZONE, true); + Talk(SAY_SPECIAL); + } + events.ScheduleEvent(EVENT_VOIDZONE, urandms(12, 18)); + break; + } + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 45.0f, true)) + DoCast(target, SPELL_SHADOW_BOLT); + else + { + DoCastAOE(SPELL_UNYIELDING_PAIN); + Talk(EMOTE_RAGECAST); + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_four_horsemen_ladyAI>(creature); + } +}; + +class boss_four_horsemen_sir : public CreatureScript +{ + public: + boss_four_horsemen_sir() : CreatureScript("boss_four_horsemen_sir") { } - while (uint32 eventId = events.ExecuteEvent()) + struct boss_four_horsemen_sirAI : public boss_four_horsemen_baseAI + { + boss_four_horsemen_sirAI(Creature* creature) : boss_four_horsemen_baseAI(creature, SIR, sirPath), _shouldSay(true) { } + void BeginFighting() override { - switch (eventId) + events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_HOLYWRATH, urandms(13,18)); + } + + void _UpdateAI(uint32 diff) override + { + if (!me->IsInCombat()) + return; + if (!_ourMovementFinished) + return; + if (me->getThreatManager().isThreatListEmpty()) { - case EVENT_MARK: - if (!(rand32() % 5)) - Talk(SAY_SPECIAL); - DoCastAOE(SPELL_MARK[id]); - events.ScheduleEvent(EVENT_MARK, 15000); - break; - case EVENT_CAST: - if (!(rand32() % 5)) - Talk(SAY_TAUNT); + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + return; + } - if (caster) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true)) - DoCast(target, SPELL_PRIMARY(id)); - } - else - DoCastVictim(SPELL_PRIMARY(id)); + events.Update(diff); - events.ScheduleEvent(EVENT_CAST, 15000); - break; - case EVENT_BERSERK: - Talk(SAY_SPECIAL); - DoCast(me, EVENT_BERSERK); - break; + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BERSERK: + DoCastAOE(SPELL_BERSERK, true); + break; + case EVENT_MARK: + DoCastAOE(SPELL_SIR_MARK, true); + events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS); + break; + case EVENT_HOLYWRATH: + if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 45.0f, true)) + { + DoCast(target, SPELL_HOLY_WRATH, true); + _shouldSay = true; + } + events.ScheduleEvent(EVENT_HOLYWRATH, urandms(10,18)); + break; + } + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 45.0f, true)) + DoCast(target, SPELL_HOLY_BOLT); + else + { + DoCastAOE(SPELL_CONDEMNATION); + Talk(EMOTE_RAGECAST); } } - if (punishTimer <= diff) + void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override { - if (doDelayPunish) + if (_shouldSay && spell->Id == SPELL_HOLY_WRATH) { - DoCastAOE(SPELL_PUNISH[id], true); - doDelayPunish = false; + Talk(SAY_SPECIAL); + _shouldSay = false; } - punishTimer = 2000; - } else punishTimer -= diff; + } - if (!caster) - DoMeleeAttackIfReady(); - else if ((!DoSpellAttackIfReady(SPELL_SECONDARY(id)) || !me->IsWithinLOSInMap(me->GetVictim())) && movementCompleted && !doDelayPunish) - doDelayPunish = true; - } - }; + private: + bool _shouldSay; // throttle to make sure we only talk on first target hit by holy wrath + }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_four_horsemen_sirAI>(creature); + } }; class spell_four_horsemen_mark : public SpellScriptLoader @@ -450,6 +749,9 @@ class spell_four_horsemen_mark : public SpellScriptLoader void AddSC_boss_four_horsemen() { - new boss_four_horsemen(); + new boss_four_horsemen_baron(); + new boss_four_horsemen_thane(); + new boss_four_horsemen_lady(); + new boss_four_horsemen_sir(); new spell_four_horsemen_mark(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index c9684cf10cf..81f1e071da0 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -20,30 +20,70 @@ #include "SpellScript.h" #include "GridNotifiers.h" #include "CombatAI.h" +#include "AreaBoundary.h" #include "naxxramas.h" +/* Constants */ enum Yells { - SAY_SPEECH = 0, - SAY_KILL = 1, - SAY_DEATH = 2, - SAY_TELEPORT = 3 + SAY_INTRO_1 = 0, + SAY_INTRO_2 = 1, + SAY_INTRO_3 = 2, + SAY_INTRO_4 = 3, + SAY_PHASE_TWO = 4, + SAY_DEATH = 5, + SAY_KILL = 6, + + EMOTE_PHASE_TWO = 7, + EMOTE_GATE_OPENED = 8 }; -//Gothik enum Spells { + /* living trainee spells */ + SPELL_DEATH_PLAGUE = 55604, + + /* living knight spells */ + SPELL_SHADOW_MARK = 27825, + + /* living rider spells */ + SPELL_SHADOW_BOLT_VOLLEY = 27831, + + /* spectral trainee spells */ + SPELL_ARCANE_EXPLOSION = 27989, + + /* spectral knight spells */ + SPELL_WHIRLWIND = 56408, + + /* spectral rider spells */ + SPELL_DRAIN_LIFE = 27994, + SPELL_UNHOLY_FRENZY = 55648, + + /* spectral horse spells */ + SPELL_STOMP = 27993, + + /* gothik phase two spells */ SPELL_HARVEST_SOUL = 28679, SPELL_SHADOW_BOLT = 29317, - SPELL_INFORM_LIVE_TRAINEE = 27892, - SPELL_INFORM_LIVE_KNIGHT = 27928, - SPELL_INFORM_LIVE_RIDER = 27935, - SPELL_INFORM_DEAD_TRAINEE = 27915, - SPELL_INFORM_DEAD_KNIGHT = 27931, - SPELL_INFORM_DEAD_RIDER = 27937, - - SPELL_SHADOW_MARK = 27825 + + /* visual spells */ + SPELL_ANCHOR_1_TRAINEE = 27892, + SPELL_ANCHOR_1_DK = 27928, + SPELL_ANCHOR_1_RIDER = 27935, + + SPELL_ANCHOR_2_TRAINEE = 27893, + SPELL_ANCHOR_2_DK = 27929, + SPELL_ANCHOR_2_RIDER = 27936, + + SPELL_SKULLS_TRAINEE = 27915, + SPELL_SKULLS_DK = 27931, + SPELL_SKULLS_RIDER = 27937, + + /* teleport spells */ + SPELL_TELEPORT_DEAD = 28025, + SPELL_TELEPORT_LIVE = 28026 }; +#define SPELLHELPER_UNHOLY_FRENZY RAID_MODE<uint32>(SPELL_UNHOLY_FRENZY,27995) enum Creatures { @@ -53,108 +93,205 @@ enum Creatures NPC_DEAD_TRAINEE = 16127, NPC_DEAD_KNIGHT = 16148, NPC_DEAD_RIDER = 16150, - NPC_DEAD_HORSE = 16149 + NPC_DEAD_HORSE = 16149, + + NPC_TRIGGER = 16137 }; -struct Waves { uint32 entry, time, mode; }; -// wave setups are not the same in heroic and normal difficulty, -// mode is 0 only normal, 1 both and 2 only heroic -// but this is handled in DoGothikSummon function -const Waves waves[] = +enum Phases { - {NPC_LIVE_TRAINEE, 20000, 1}, - {NPC_LIVE_TRAINEE, 20000, 1}, - {NPC_LIVE_TRAINEE, 10000, 1}, - {NPC_LIVE_KNIGHT, 10000, 1}, - {NPC_LIVE_TRAINEE, 15000, 1}, - {NPC_LIVE_KNIGHT, 5000, 1}, - {NPC_LIVE_TRAINEE, 20000, 1}, - {NPC_LIVE_TRAINEE, 0, 1}, - {NPC_LIVE_KNIGHT, 10000, 1}, - {NPC_LIVE_TRAINEE, 10000, 2}, - {NPC_LIVE_RIDER, 10000, 0}, - {NPC_LIVE_RIDER, 5000, 2}, - {NPC_LIVE_TRAINEE, 5000, 0}, - {NPC_LIVE_TRAINEE, 15000, 2}, - {NPC_LIVE_KNIGHT, 15000, 0}, - {NPC_LIVE_TRAINEE, 0, 0}, - {NPC_LIVE_RIDER, 10000, 1}, - {NPC_LIVE_KNIGHT, 10000, 1}, - {NPC_LIVE_TRAINEE, 10000, 0}, - {NPC_LIVE_RIDER, 10000, 2}, - {NPC_LIVE_TRAINEE, 0, 2}, - {NPC_LIVE_RIDER, 5000, 1}, - {NPC_LIVE_TRAINEE, 0, 2}, - {NPC_LIVE_KNIGHT, 5000, 1}, - {NPC_LIVE_RIDER, 0, 2}, - {NPC_LIVE_TRAINEE, 20000, 1}, - {NPC_LIVE_RIDER, 0, 1}, - {NPC_LIVE_KNIGHT, 0, 1}, - {NPC_LIVE_TRAINEE, 25000, 2}, - {NPC_LIVE_TRAINEE, 15000, 0}, - {NPC_LIVE_TRAINEE, 25000, 0}, - {0, 0, 1}, + PHASE_ONE = 1, + PHASE_TWO = 2 }; -#define POS_Y_GATE -3360.78f -#define POS_Y_WEST -3285.0f -#define POS_Y_EAST -3434.0f -#define POS_X_NORTH 2750.49f -#define POS_X_SOUTH 2633.84f - -#define IN_LIVE_SIDE(who) (who->GetPositionY() < POS_Y_GATE) - enum Events { - EVENT_NONE, + EVENT_INTRO_2 = 1, + EVENT_INTRO_3, + EVENT_INTRO_4, + EVENT_PHASE_TWO, EVENT_SUMMON, + EVENT_DOORS_UNLOCK, + EVENT_TELEPORT, EVENT_HARVEST, EVENT_BOLT, - EVENT_TELEPORT -}; -enum Pos -{ - POS_LIVE = 6, - POS_DEAD = 5 + EVENT_RESUME_ATTACK }; -const Position PosSummonLive[POS_LIVE] = +enum Actions { - {2669.7f, -3428.76f, 268.56f, 1.6f}, - {2692.1f, -3428.76f, 268.56f, 1.6f}, - {2714.4f, -3428.76f, 268.56f, 1.6f}, - {2669.7f, -3431.67f, 268.56f, 1.6f}, - {2692.1f, -3431.67f, 268.56f, 1.6f}, - {2714.4f, -3431.67f, 268.56f, 1.6f}, + ACTION_GATE_OPENED = 1, + ACTION_MINION_EVADE, + ACTION_ACQUIRE_TARGET }; -const Position PosSummonDead[POS_DEAD] = + +/* Room side checking logic */ +static AreaBoundary* const livingSide = new RectangleBoundary(2633.84f, 2750.49f, -3434.0f, -3360.78f); +static AreaBoundary* const deadSide = new RectangleBoundary(2633.84f, 2750.49f, -3360.78f, -3285.0f); +enum Side { - {2725.1f, -3310.0f, 268.85f, 3.4f}, - {2699.3f, -3322.8f, 268.60f, 3.3f}, - {2733.1f, -3348.5f, 268.84f, 3.1f}, - {2682.8f, -3304.2f, 268.85f, 3.9f}, - {2664.8f, -3340.7f, 268.23f, 3.7f}, + SIDE_NONE = 0, + SIDE_LIVING, + SIDE_DEAD }; +inline static Side GetSide(Position const* who) +{ + if (livingSide->IsWithinBoundary(who)) + return SIDE_LIVING; + if (deadSide->IsWithinBoundary(who)) + return SIDE_DEAD; + return SIDE_NONE; +} +inline static bool IsOnSameSide(Position const* who, Position const* other) +{ + return (GetSide(who) == GetSide(other)); +} +static Player* FindEligibleTarget(Creature const* me, bool isGateOpen) +{ + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) + { + Player* player = it->GetSource(); + if (player && (isGateOpen || IsOnSameSide(me, player)) && me->CanSeeOrDetect(player) && me->IsValidAttackTarget(player) && player->isInAccessiblePlaceFor(me)) + { + return player; + } + } -float const PosGroundLiveSide[4] = {2691.2f, -3387.0f, 267.68f, 1.52f}; -float const PosGroundDeadSide[4] = {2693.5f, -3334.6f, 267.68f, 4.67f}; -float const PosPlatform[4] = {2640.5f, -3360.6f, 285.26f, 0.0f}; + return nullptr; +} -// Predicate function to check that the r efzr unit is NOT on the same side as the source. -struct NotOnSameSide : public std::unary_function<Unit*, bool> -{ - NotOnSameSide(Unit* source) : _onLiveSide(IN_LIVE_SIDE(source)) { } - bool operator() (Unit const* target) +/* Wave data */ +typedef std::pair<uint32, uint8> GothikWaveEntry; // (npcEntry, npcCount) +typedef std::set<GothikWaveEntry> GothikWave; +typedef std::pair<GothikWave, uint8> GothikWaveInfo; // (wave, secondsToNext) +typedef std::vector<GothikWaveInfo> GothikWaveData; +const GothikWaveData waves10 = +{ { - return (_onLiveSide != IN_LIVE_SIDE(target)); - } + {{NPC_LIVE_TRAINEE, 2}}, + 20}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 20}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 10}, + { + {{NPC_LIVE_KNIGHT, 1}}, + 10}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 15}, + { + {{NPC_LIVE_KNIGHT, 1}}, + 5}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 20}, + { + {{NPC_LIVE_TRAINEE, 2}, {NPC_LIVE_KNIGHT, 1}}, + 10}, + { + {{NPC_LIVE_RIDER, 1}}, + 10}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 5}, + { + {{NPC_LIVE_KNIGHT, 1}}, + 15}, + { + {{NPC_LIVE_TRAINEE, 2}, {NPC_LIVE_RIDER, 1}}, + 10}, + { + {{NPC_LIVE_KNIGHT, 2}}, + 10}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 10}, + { + {{NPC_LIVE_RIDER, 1}}, + 5}, + { + {{NPC_LIVE_KNIGHT, 1}}, + 5}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 20}, + { + {{NPC_LIVE_RIDER, 1}, {NPC_LIVE_KNIGHT, 1}, {NPC_LIVE_TRAINEE, 2}}, + 15}, + { + {{NPC_LIVE_TRAINEE, 2}}, + 0} +}; - private: - bool _onLiveSide; +const GothikWaveData waves25 = +{ + { + {{NPC_LIVE_TRAINEE, 3}}, + 20}, + { + {{NPC_LIVE_TRAINEE, 3}}, + 20}, + { + {{NPC_LIVE_TRAINEE, 3}}, + 10}, + { + {{NPC_LIVE_KNIGHT, 2}}, + 10}, + { + {{NPC_LIVE_TRAINEE, 3}}, + 15}, + { + {{NPC_LIVE_KNIGHT, 2}}, + 5}, + { + {{NPC_LIVE_TRAINEE, 3}}, + 20}, + { + {{NPC_LIVE_TRAINEE, 3}, {NPC_LIVE_KNIGHT, 2}}, + 10}, + { + {{NPC_LIVE_TRAINEE, 3}}, + 10}, + { + {{NPC_LIVE_RIDER, 1}}, + 5}, + { + {{NPC_LIVE_TRAINEE, 3}}, + 15}, + { + {{NPC_LIVE_RIDER, 1}}, + 10}, + { + {{NPC_LIVE_KNIGHT, 2}}, + 10}, + { + {{NPC_LIVE_RIDER, 1}}, + 10}, + { + {{NPC_LIVE_RIDER, 1}, {NPC_LIVE_TRAINEE, 3}}, + 5}, + { + {{NPC_LIVE_KNIGHT, 1}, {NPC_LIVE_TRAINEE, 3}}, + 5}, + { + {{NPC_LIVE_RIDER, 1}, {NPC_LIVE_TRAINEE, 3}}, + 20}, + { + {{NPC_LIVE_RIDER, 1}, {NPC_LIVE_KNIGHT, 2}, {NPC_LIVE_TRAINEE, 3}}, + 0} }; + +// GUID of first trigger NPC (used as offset for guid checks) +// 0-1 are living side soul triggers, 2-3 are spectral side soul triggers, 4 is living rider spawn trigger, 5-7 are living other spawn trigger, 8-12 are skull pile triggers +const uint32 CGUID_TRIGGER = 127618; +/* Creature AI */ class boss_gothik : public CreatureScript { public: @@ -165,29 +302,18 @@ class boss_gothik : public CreatureScript boss_gothikAI(Creature* creature) : BossAI(creature, BOSS_GOTHIK) { Initialize(); - waveCount = 0; } void Initialize() { - mergedSides = false; - phaseTwo = false; - thirtyPercentReached = false; + _waveCount = 0; + _gateCanOpen = false; + _gateIsOpen = true; + _lastTeleportDead = false; } - uint32 waveCount; - bool mergedSides; - bool phaseTwo; - bool thirtyPercentReached; - - GuidVector LiveTriggerGUID; - GuidVector DeadTriggerGUID; - void Reset() override { - LiveTriggerGUID.clear(); - DeadTriggerGUID.clear(); - me->SetReactState(REACT_PASSIVE); instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE); _Reset(); @@ -196,43 +322,29 @@ class boss_gothik : public CreatureScript void EnterCombat(Unit* /*who*/) override { - for (uint32 i = 0; i < POS_LIVE; ++i) - if (Creature* trigger = DoSummon(WORLD_TRIGGER, PosSummonLive[i])) - LiveTriggerGUID.push_back(trigger->GetGUID()); - for (uint32 i = 0; i < POS_DEAD; ++i) - if (Creature* trigger = DoSummon(WORLD_TRIGGER, PosSummonDead[i])) - DeadTriggerGUID.push_back(trigger->GetGUID()); - - if (LiveTriggerGUID.size() < POS_LIVE || DeadTriggerGUID.size() < POS_DEAD) - { - TC_LOG_ERROR("scripts", "Script Gothik: cannot summon triggers!"); - EnterEvadeMode(); - return; - } - _EnterCombat(); - waveCount = 0; - events.ScheduleEvent(EVENT_SUMMON, 30000); - DoTeleportTo(PosPlatform); - Talk(SAY_SPEECH); + events.SetPhase(PHASE_ONE); + events.ScheduleEvent(EVENT_SUMMON, 25 * IN_MILLISECONDS, 0, PHASE_ONE); + events.ScheduleEvent(EVENT_DOORS_UNLOCK, 205 * IN_MILLISECONDS, 0, PHASE_ONE); + events.ScheduleEvent(EVENT_PHASE_TWO, 270 * IN_MILLISECONDS, 0, PHASE_ONE); + Talk(SAY_INTRO_1); + events.ScheduleEvent(EVENT_INTRO_2, 4 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_INTRO_3, 9 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_INTRO_4, 14 * IN_MILLISECONDS); instance->SetData(DATA_GOTHIK_GATE, GO_STATE_READY); + _gateIsOpen = false; } void JustSummoned(Creature* summon) override { - if (summon->GetEntry() == WORLD_TRIGGER) - summon->setActive(true); - else if (!mergedSides) + summons.Summon(summon); + if (me->IsInCombat()) { - summon->AI()->DoAction(me->HasReactState(REACT_PASSIVE) ? 1 : 0); - summon->AI()->EnterEvadeMode(); + summon->AI()->DoAction(_gateIsOpen ? ACTION_GATE_OPENED : ACTION_ACQUIRE_TARGET); + summon->SetCombatPulseDelay(5); } else - { - summon->AI()->DoAction(0); - summon->AI()->DoZoneInCombat(); - } - summons.Summon(summon); + summon->DespawnOrUnsummon(); } void SummonedCreatureDespawn(Creature* summon) override @@ -240,256 +352,204 @@ class boss_gothik : public CreatureScript summons.Despawn(summon); } - void KilledUnit(Unit* /*victim*/) override + void KilledUnit(Unit* victim) override { - if (!(rand32() % 5)) + if (victim && victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_KILL); } void JustDied(Unit* /*killer*/) override { - LiveTriggerGUID.clear(); - DeadTriggerGUID.clear(); _JustDied(); Talk(SAY_DEATH); instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE); + _gateIsOpen = false; } - void DoGothikSummon(uint32 entry) + void OpenGate() { - if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) - { - switch (entry) - { - case NPC_LIVE_TRAINEE: - { - if (Creature* liveTrigger = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[0])) - DoSummon(NPC_LIVE_TRAINEE, liveTrigger, 1); - if (Creature* liveTrigger1 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[1])) - DoSummon(NPC_LIVE_TRAINEE, liveTrigger1, 1); - if (Creature* liveTrigger2 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[2])) - DoSummon(NPC_LIVE_TRAINEE, liveTrigger2, 1); - break; - } - case NPC_LIVE_KNIGHT: - { - if (Creature* liveTrigger3 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[3])) - DoSummon(NPC_LIVE_KNIGHT, liveTrigger3, 1); - if (Creature* liveTrigger5 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[5])) - DoSummon(NPC_LIVE_KNIGHT, liveTrigger5, 1); - break; - } - case NPC_LIVE_RIDER: - { - if (Creature* liveTrigger4 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[4])) - DoSummon(NPC_LIVE_RIDER, liveTrigger4, 1); - break; - } - } - } - else - { - switch (entry) - { - case NPC_LIVE_TRAINEE: - { - if (Creature* liveTrigger = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[4])) - DoSummon(NPC_LIVE_TRAINEE, liveTrigger, 1); - if (Creature* liveTrigger2 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[4])) - DoSummon(NPC_LIVE_TRAINEE, liveTrigger2, 1); - break; - } - case NPC_LIVE_KNIGHT: - { - if (Creature* liveTrigger5 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[4])) - DoSummon(NPC_LIVE_KNIGHT, liveTrigger5, 1); - break; - } - case NPC_LIVE_RIDER: - { - if (Creature* liveTrigger4 = ObjectAccessor::GetCreature(*me, LiveTriggerGUID[4])) - DoSummon(NPC_LIVE_RIDER, liveTrigger4, 1); - break; - } - } - } - } - - bool CheckGroupSplitted() - { - bool checklife = false; - bool checkdead = false; - Map::PlayerList const &PlayerList = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - { - if (i->GetSource() && i->GetSource()->IsAlive() && - i->GetSource()->GetPositionX() <= POS_X_NORTH && - i->GetSource()->GetPositionX() >= POS_X_SOUTH && - i->GetSource()->GetPositionY() <= POS_Y_GATE && - i->GetSource()->GetPositionY() >= POS_Y_EAST) - { - checklife = true; - } - else if (i->GetSource() && i->GetSource()->IsAlive() && - i->GetSource()->GetPositionX() <= POS_X_NORTH && - i->GetSource()->GetPositionX() >= POS_X_SOUTH && - i->GetSource()->GetPositionY() >= POS_Y_GATE && - i->GetSource()->GetPositionY() <= POS_Y_WEST) - { - checkdead = true; - } - - if (checklife && checkdead) - return true; - } - - return false; - } - - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - uint32 spellId = 0; - switch (spell->Id) - { - case SPELL_INFORM_LIVE_TRAINEE: spellId = SPELL_INFORM_DEAD_TRAINEE; break; - case SPELL_INFORM_LIVE_KNIGHT: spellId = SPELL_INFORM_DEAD_KNIGHT; break; - case SPELL_INFORM_LIVE_RIDER: spellId = SPELL_INFORM_DEAD_RIDER; break; - } - if (spellId && me->IsInCombat()) - { - me->HandleEmoteCommand(EMOTE_ONESHOT_SPELL_CAST); - if (Creature* pRandomDeadTrigger = ObjectAccessor::GetCreature(*me, DeadTriggerGUID[rand32() % POS_DEAD])) - me->CastSpell(pRandomDeadTrigger, spellId, true); - } + if (_gateIsOpen) + return; + instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE); + Talk(EMOTE_GATE_OPENED); + _gateIsOpen = true; + + for (ObjectGuid summonGuid : summons) + if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid)) + summon->AI()->DoAction(ACTION_GATE_OPENED); } void DamageTaken(Unit* /*who*/, uint32& damage) override { - if (!phaseTwo) + if (!events.IsInPhase(PHASE_TWO)) damage = 0; } - void SpellHitTarget(Unit* target, SpellInfo const* spell) override + void DoAction(int32 action) override { - if (!me->IsInCombat()) - return; - - switch (spell->Id) + switch (action) { - case SPELL_INFORM_DEAD_TRAINEE: - DoSummon(NPC_DEAD_TRAINEE, target, 0); - break; - case SPELL_INFORM_DEAD_KNIGHT: - DoSummon(NPC_DEAD_KNIGHT, target, 0); - break; - case SPELL_INFORM_DEAD_RIDER: - DoSummon(NPC_DEAD_RIDER, target, 1.0f); - DoSummon(NPC_DEAD_HORSE, target, 1.0f); + case ACTION_MINION_EVADE: + if (_gateIsOpen || me->getThreatManager().isThreatListEmpty()) + return EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + if (_gateCanOpen) + OpenGate(); break; } } + void EnterEvadeMode(EvadeReason why) override + { + BossAI::EnterEvadeMode(why); + Position const& home = me->GetHomePosition(); + me->NearTeleportTo(home.GetPositionX(), home.GetPositionY(), home.GetPositionZ(), home.GetOrientation()); + } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - events.Update(diff); - - if (!thirtyPercentReached && HealthBelowPct(30) && phaseTwo) + if (me->HasReactState(REACT_AGGRESSIVE) && !_gateIsOpen && !IsOnSameSide(me, me->GetVictim())) { - thirtyPercentReached = true; - instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE); + // NBD: this should only happen in practice if there is nobody left alive on our side (we should open gate) + // thus we only do a cursory check to make sure (edge cases?) + if (Player* newTarget = FindEligibleTarget(me, _gateIsOpen)) + { + me->getThreatManager().resetAllAggro(); + me->AddThreat(newTarget, 1.0f); + AttackStart(newTarget); + } + else + OpenGate(); } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + events.Update(diff); + + if (!_gateIsOpen && HealthBelowPct(30) && events.IsInPhase(PHASE_TWO)) + OpenGate(); while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { case EVENT_SUMMON: - if (waves[waveCount].entry) + { + if (RAID_MODE(waves10,waves25).size() <= _waveCount) // bounds check { - if ((waves[waveCount].mode == 2) && (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)) - DoGothikSummon(waves[waveCount].entry); - else if ((waves[waveCount].mode == 0) && (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL)) - DoGothikSummon(waves[waveCount].entry); - else if (waves[waveCount].mode == 1) - DoGothikSummon(waves[waveCount].entry); - - // if group is not splitted, open gate and merge both sides at ~ 2 minutes (wave 11) - if (waveCount == 11) + TC_LOG_INFO("scripts", "GothikAI: Wave count %d is out of range for difficulty %d.", _waveCount, GetDifficulty()); + break; + } + + std::list<Creature*> triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f); + for (GothikWaveEntry entry : RAID_MODE(waves10, waves25)[_waveCount].first) + for (uint8 i = 0; i < entry.second; ++i) { - if (!CheckGroupSplitted()) + // GUID layout is as follows: + // CGUID+4: center (back of platform) - primary rider spawn + // CGUID+5: north (back of platform) - primary knight spawn + // CGUID+6: center (front of platform) - second spawn + // CGUID+7: south (front of platform) - primary trainee spawn + uint32 targetDBGuid; + switch (entry.first) { - instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE); - DummyEntryCheckPredicate pred; - summons.DoAction(0, pred); //! Magic numbers fail - summons.DoZoneInCombat(); - mergedSides = true; + case NPC_LIVE_RIDER: // only spawns from center (back) > north + targetDBGuid = (CGUID_TRIGGER + 4) + (i % 2); + break; + case NPC_LIVE_KNIGHT: // spawns north > center (front) > south + targetDBGuid = (CGUID_TRIGGER + 5) + (i % 3); + break; + case NPC_LIVE_TRAINEE: // spawns south > center (front) > north + targetDBGuid = (CGUID_TRIGGER + 7) - (i % 3); + break; + default: + targetDBGuid = 0; } + + for (Creature* trigger : triggers) + if (trigger && trigger->GetSpawnId() == targetDBGuid) + { + DoSummon(entry.first, trigger, 1.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + break; + } } - if (waves[waveCount].mode == 1) - events.ScheduleEvent(EVENT_SUMMON, waves[waveCount].time); - else if ((waves[waveCount].mode == 2) && (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)) - events.ScheduleEvent(EVENT_SUMMON, waves[waveCount].time); - else if ((waves[waveCount].mode == 0) && (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL)) - events.ScheduleEvent(EVENT_SUMMON, waves[waveCount].time); - else - events.ScheduleEvent(EVENT_SUMMON, 0); + if (uint8 timeToNext = RAID_MODE(waves10, waves25)[_waveCount].second) + events.ScheduleEvent(EVENT_SUMMON, timeToNext * IN_MILLISECONDS, 0, PHASE_ONE); - ++waveCount; - } - else - { - phaseTwo = true; - Talk(SAY_TELEPORT); - DoTeleportTo(PosGroundLiveSide); - me->SetReactState(REACT_AGGRESSIVE); - DummyEntryCheckPredicate pred; - summons.DoAction(0, pred); //! Magic numbers fail - summons.DoZoneInCombat(); - events.ScheduleEvent(EVENT_BOLT, 1000); - events.ScheduleEvent(EVENT_HARVEST, urand(3000, 15000)); - events.ScheduleEvent(EVENT_TELEPORT, 20000); - } + ++_waveCount; break; - case EVENT_BOLT: - DoCastVictim(SPELL_SHADOW_BOLT); - events.ScheduleEvent(EVENT_BOLT, 1000); + } + case EVENT_DOORS_UNLOCK: + _gateCanOpen = true; + for (ObjectGuid summonGuid : summons) + if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid)) + if (summon->IsAlive() && (!summon->IsInCombat() || summon->IsInEvadeMode())) + { + OpenGate(); + break; + } break; - case EVENT_HARVEST: - DoCastVictim(SPELL_HARVEST_SOUL, true); - events.ScheduleEvent(EVENT_HARVEST, urand(20000, 25000)); + case EVENT_PHASE_TWO: + events.SetPhase(PHASE_TWO); + events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO); + Talk(SAY_PHASE_TWO); + Talk(EMOTE_PHASE_TWO); + me->SetReactState(REACT_PASSIVE); + me->getThreatManager().resetAllAggro(); + DoCastAOE(SPELL_TELEPORT_LIVE); break; case EVENT_TELEPORT: - if (!thirtyPercentReached) + if (!HealthBelowPct(30)) { + me->CastStop(); me->AttackStop(); - if (IN_LIVE_SIDE(me)) - DoTeleportTo(PosGroundDeadSide); - else - DoTeleportTo(PosGroundLiveSide); - - me->getThreatManager().resetAggro(NotOnSameSide(me)); - if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0)) - { - me->getThreatManager().addThreat(target, 100.0f); - AttackStart(target); - } - - events.ScheduleEvent(EVENT_TELEPORT, 20000); + me->StopMoving(); + me->SetReactState(REACT_PASSIVE); + me->getThreatManager().resetAllAggro(); + DoCastAOE(_lastTeleportDead ? SPELL_TELEPORT_LIVE : SPELL_TELEPORT_DEAD); + _lastTeleportDead = !_lastTeleportDead; + + events.CancelEvent(EVENT_BOLT); + events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO); } break; + + case EVENT_HARVEST: + DoCastAOE(SPELL_HARVEST_SOUL, true); // triggered allows this to go "through" shadow bolt + events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO); + break; + case EVENT_RESUME_ATTACK: + me->SetReactState(REACT_AGGRESSIVE); + events.ScheduleEvent(EVENT_BOLT, 0, 0, PHASE_TWO); + // return to the start of this method so victim side etc is re-evaluated + return UpdateAI(0u); // tail recursion for efficiency + case EVENT_BOLT: + DoCastVictim(SPELL_SHADOW_BOLT); + events.ScheduleEvent(EVENT_BOLT, 1 * IN_MILLISECONDS, 0, PHASE_TWO); + break; + case EVENT_INTRO_2: + Talk(SAY_INTRO_2); + break; + case EVENT_INTRO_3: + Talk(SAY_INTRO_3); + break; + case EVENT_INTRO_4: + Talk(SAY_INTRO_4); + break; } } - - if (!phaseTwo) - DoMeleeAttackIfReady(); } + + private: + uint32 _waveCount; + bool _gateCanOpen; + bool _gateIsOpen; + bool _lastTeleportDead; }; CreatureAI* GetAI(Creature* creature) const override @@ -498,86 +558,407 @@ class boss_gothik : public CreatureScript } }; -class npc_gothik_minion : public CreatureScript +struct npc_gothik_minion_baseAI : public ScriptedAI { public: - npc_gothik_minion() : CreatureScript("npc_gothik_minion") { } + npc_gothik_minion_baseAI(Creature* creature, uint32 deathNotify=0) : ScriptedAI(creature), _deathNotify(deathNotify), _gateIsOpen(false) { } + + void JustDied(Unit* /*killer*/) override + { + if (_deathNotify) + DoCastAOE(_deathNotify, true); + } + + inline bool isOnSameSide(Unit const* who) const + { + return IsOnSameSide(me, who); + } - struct npc_gothik_minionAI : public CombatAI + void DamageTaken(Unit* attacker, uint32 &damage) override + { // do not allow minions to take damage before the gate is opened + if (!_gateIsOpen && !isOnSameSide(attacker)) + damage = 0; + } + + void DoAction(int32 action) override { - npc_gothik_minionAI(Creature* creature) : CombatAI(creature) + switch (action) { - liveSide = IN_LIVE_SIDE(me); - gateClose = false; + case ACTION_GATE_OPENED: + _gateIsOpen = true; + // intentional missing break + case ACTION_ACQUIRE_TARGET: + if (Player* target = FindEligibleTarget(me, _gateIsOpen)) + { + me->AddThreat(target, 1.0f); + AttackStart(target); + } + else + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + break; + } + } + + void EnterEvadeMode(EvadeReason why) override + { + ScriptedAI::EnterEvadeMode(why); + + if (InstanceScript* instance = me->GetInstanceScript()) + if (Creature* gothik = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_GOTHIK))) + gothik->AI()->DoAction(ACTION_MINION_EVADE); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (!_gateIsOpen && !isOnSameSide(me->GetVictim())) + { // reset threat, then try to find someone on same side as us to attack + if (Player* newTarget = FindEligibleTarget(me, _gateIsOpen)) + { + me->RemoveAurasByType(SPELL_AURA_MOD_TAUNT); + me->getThreatManager().resetAllAggro(); + me->AddThreat(newTarget, 1.0f); + AttackStart(newTarget); + } + else + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); } - bool liveSide; - bool gateClose; + _UpdateAI(diff); + } + + virtual void _UpdateAI(uint32 diff) { ScriptedAI::UpdateAI(diff); }; - bool isOnSameSide(Unit const* who) const + private: + uint32 _deathNotify; + bool _gateIsOpen; +}; + +class npc_gothik_minion_livingtrainee : public CreatureScript +{ + public: + npc_gothik_minion_livingtrainee() : CreatureScript("npc_gothik_minion_livingtrainee") { } + + struct npc_gothik_minion_livingtraineeAI : public npc_gothik_minion_baseAI + { + npc_gothik_minion_livingtraineeAI(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_TRAINEE), _deathPlagueTimer(urandms(5,20)) { } + + void _UpdateAI(uint32 diff) { - return (liveSide == IN_LIVE_SIDE(who)); + if (diff < _deathPlagueTimer) + _deathPlagueTimer -= diff; + else + { + DoCastAOE(SPELL_DEATH_PLAGUE); + _deathPlagueTimer = urandms(5, 20); + } + DoMeleeAttackIfReady(); } + uint32 _deathPlagueTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_minion_livingtraineeAI>(creature); + } +}; + +class npc_gothik_minion_livingknight : public CreatureScript +{ + public: + npc_gothik_minion_livingknight() : CreatureScript("npc_gothik_minion_livingknight") { } - void DoAction(int32 param) override + struct npc_gothik_minion_livingknightAI : public npc_gothik_minion_baseAI + { + npc_gothik_minion_livingknightAI(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_DK), _whirlwindTimer(urandms(5,10)) { } + + void _UpdateAI(uint32 diff) { - gateClose = param != 0; + if (diff < _whirlwindTimer) + _whirlwindTimer -= diff; + else + { + DoCastAOE(SPELL_SHADOW_MARK); + _whirlwindTimer = urandms(15, 20); + } + DoMeleeAttackIfReady(); } + uint32 _whirlwindTimer; + }; - void DamageTaken(Unit* attacker, uint32 &damage) override + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_minion_livingknightAI>(creature); + } +}; + +class npc_gothik_minion_livingrider : public CreatureScript +{ + public: + npc_gothik_minion_livingrider() : CreatureScript("npc_gothik_minion_livingrider") { } + + struct npc_gothik_minion_livingriderAI : public npc_gothik_minion_baseAI + { + npc_gothik_minion_livingriderAI(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_RIDER), _boltVolleyTimer(urandms(5,10)) { } + + void _UpdateAI(uint32 diff) { - if (gateClose && !isOnSameSide(attacker)) - damage = 0; + if (diff < _boltVolleyTimer) + _boltVolleyTimer -= diff; + else + { + DoCastAOE(SPELL_SHADOW_BOLT_VOLLEY); + _boltVolleyTimer = urandms(10, 15); + } + if (!me->HasUnitState(UNIT_STATE_CASTING)) + DoMeleeAttackIfReady(); } + uint32 _boltVolleyTimer; + }; - void JustDied(Unit* /*killer*/) override + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_minion_livingriderAI>(creature); + } +}; + +class npc_gothik_minion_spectraltrainee : public CreatureScript +{ + public: + npc_gothik_minion_spectraltrainee() : CreatureScript("npc_gothik_minion_spectraltrainee") { } + + struct npc_gothik_minion_spectraltraineeAI : public npc_gothik_minion_baseAI + { + npc_gothik_minion_spectraltraineeAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _explosionTimer(2 * IN_MILLISECONDS) { } + + void _UpdateAI(uint32 diff) + { + if (diff < _explosionTimer) + _explosionTimer -= diff; + else { - if (me->IsSummon()) - if (Unit* owner = me->ToTempSummon()->GetSummoner()) - CombatAI::JustDied(owner); + DoCastAOE(SPELL_ARCANE_EXPLOSION); + _explosionTimer = 2 * IN_MILLISECONDS; } + DoMeleeAttackIfReady(); + } + uint32 _explosionTimer; + }; - void EnterEvadeMode(EvadeReason why) override + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_minion_spectraltraineeAI>(creature); + } +}; + +class npc_gothik_minion_spectralknight : public CreatureScript +{ + public: + npc_gothik_minion_spectralknight() : CreatureScript("npc_gothik_minion_spectralknight") { } + + struct npc_gothik_minion_spectralknightAI : public npc_gothik_minion_baseAI + { + npc_gothik_minion_spectralknightAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _whirlwindTimer(urandms(15,25)) { } + + void _UpdateAI(uint32 diff) + { + if (diff < _whirlwindTimer) + _whirlwindTimer -= diff; + else { - if (!gateClose) - { - CombatAI::EnterEvadeMode(why); - return; - } + DoCastAOE(SPELL_WHIRLWIND); + _whirlwindTimer = urandms(20, 25); + } + DoMeleeAttackIfReady(); + } + uint32 _whirlwindTimer; + }; - if (!_EnterEvadeMode()) - return; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_minion_spectralknightAI>(creature); + } +}; + +class npc_gothik_minion_spectralrider : public CreatureScript +{ + public: + npc_gothik_minion_spectralrider() : CreatureScript("npc_gothik_minion_spectralrider") { } + + struct npc_gothik_minion_spectralriderAI : public npc_gothik_minion_baseAI + { + npc_gothik_minion_spectralriderAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _frenzyTimer(urandms(2,5)), _drainTimer(urandms(8,12)) { } - Map::PlayerList const &PlayerList = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + void _UpdateAI(uint32 diff) + { + if (diff < _frenzyTimer) + _frenzyTimer -= diff; + else if (me->HasUnitState(UNIT_STATE_CASTING)) + _frenzyTimer = 0; + else + { // target priority: knight > other rider > horse > gothik + std::list<Creature*> potentialTargets = DoFindFriendlyMissingBuff(30.0, SPELLHELPER_UNHOLY_FRENZY); + Creature *knightTarget = nullptr, *riderTarget = nullptr, *horseTarget = nullptr, *gothikTarget = nullptr; + for (Creature* pTarget : potentialTargets) { - if (i->GetSource() && i->GetSource()->IsAlive() && isOnSameSide(i->GetSource())) + switch (pTarget->GetEntry()) { - AttackStart(i->GetSource()); - return; + case NPC_DEAD_KNIGHT: + knightTarget = pTarget; + break; + case NPC_DEAD_RIDER: + riderTarget = pTarget; + break; + case NPC_DEAD_HORSE: + horseTarget = pTarget; + break; + case NPC_GOTHIK: + gothikTarget = pTarget; + break; } + if (knightTarget) + break; } + Creature* target = knightTarget ? knightTarget : riderTarget ? riderTarget : horseTarget ? horseTarget : gothikTarget ? gothikTarget : nullptr; + if (target) + DoCast(target, SPELL_UNHOLY_FRENZY); + _frenzyTimer = 20 * IN_MILLISECONDS; + } - me->GetMotionMaster()->MoveIdle(); - Reset(); + if (diff < _drainTimer) + _drainTimer -= diff; + else + { + DoCastVictim(SPELL_DRAIN_LIFE); + _drainTimer = urandms(10,15); } - void UpdateAI(uint32 diff) override + if (!me->HasUnitState(UNIT_STATE_CASTING)) + DoMeleeAttackIfReady(); + } + uint32 _frenzyTimer, _drainTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_minion_spectralriderAI>(creature); + } +}; + +class npc_gothik_minion_spectralhorse : public CreatureScript +{ + public: + npc_gothik_minion_spectralhorse() : CreatureScript("npc_gothik_minion_spectralhorse") { } + + struct npc_gothik_minion_spectralhorseAI : public npc_gothik_minion_baseAI + { + npc_gothik_minion_spectralhorseAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _stompTimer(urandms(10,15)) { } + + void _UpdateAI(uint32 diff) + { + if (diff < _stompTimer) + _stompTimer -= diff; + else { - if (gateClose && (!isOnSameSide(me) || (me->GetVictim() && !isOnSameSide(me->GetVictim())))) - { - EnterEvadeMode(EVADE_REASON_OTHER); - return; - } + DoCastAOE(SPELL_STOMP); + _stompTimer = urandms(14, 18); + } + DoMeleeAttackIfReady(); + } + uint32 _stompTimer; + }; - CombatAI::UpdateAI(diff); + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_minion_spectralhorseAI>(creature); + } +}; + +class npc_gothik_trigger : public CreatureScript +{ +public: + npc_gothik_trigger() : CreatureScript("npc_gothik_trigger") { } + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_gothik_triggerAI>(creature); + } + + struct npc_gothik_triggerAI : public ScriptedAI + { + npc_gothik_triggerAI(Creature* creature) : ScriptedAI(creature) { creature->SetDisableGravity(true); } + + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 /*diff*/) override { } + void EnterCombat(Unit* /*who*/) override { } + void DamageTaken(Unit* /*who*/, uint32& damage) override { damage = 0; } + + Creature* SelectRandomSkullPile() + { + std::list<Creature*> triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f); + uint32 targetDBGuid = CGUID_TRIGGER + urand(8, 12); // CGUID+8 to CGUID+12 are the triggers for the skull piles on dead side + for (Creature* trigger : triggers) + if (trigger && trigger->GetSpawnId() == targetDBGuid) + return trigger; + + return nullptr; + } + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (!spell) + return; + + switch (spell->Id) + { + case SPELL_ANCHOR_1_TRAINEE: + DoCastAOE(SPELL_ANCHOR_2_TRAINEE, true); + break; + case SPELL_ANCHOR_1_DK: + DoCastAOE(SPELL_ANCHOR_2_DK, true); + break; + case SPELL_ANCHOR_1_RIDER: + DoCastAOE(SPELL_ANCHOR_2_RIDER, true); + break; + case SPELL_ANCHOR_2_TRAINEE: + if (Creature* target = SelectRandomSkullPile()) + DoCast(target, SPELL_SKULLS_TRAINEE, true); + break; + case SPELL_ANCHOR_2_DK: + if (Creature* target = SelectRandomSkullPile()) + DoCast(target, SPELL_SKULLS_DK, true); + break; + case SPELL_ANCHOR_2_RIDER: + if (Creature* target = SelectRandomSkullPile()) + DoCast(target, SPELL_SKULLS_RIDER, true); + break; + case SPELL_SKULLS_TRAINEE: + DoSummon(NPC_DEAD_TRAINEE, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + break; + case SPELL_SKULLS_DK: + DoSummon(NPC_DEAD_KNIGHT, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + break; + case SPELL_SKULLS_RIDER: + DoSummon(NPC_DEAD_RIDER, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + DoSummon(NPC_DEAD_HORSE, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + break; } - }; + } - CreatureAI* GetAI(Creature* creature) const override + // dead side summons are "owned" by gothik + void JustSummoned(Creature* summon) override + { + if (Creature* gothik = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetGuidData(DATA_GOTHIK))) + gothik->AI()->JustSummoned(summon); + } + void SummonedCreatureDespawn(Creature* summon) override { - return new npc_gothik_minionAI(creature); + if (Creature* gothik = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetGuidData(DATA_GOTHIK))) + gothik->AI()->SummonedCreatureDespawn(summon); } + }; }; class spell_gothik_shadow_bolt_volley : public SpellScriptLoader @@ -609,6 +990,13 @@ class spell_gothik_shadow_bolt_volley : public SpellScriptLoader void AddSC_boss_gothik() { new boss_gothik(); - new npc_gothik_minion(); + new npc_gothik_minion_livingtrainee(); + new npc_gothik_minion_livingknight(); + new npc_gothik_minion_livingrider(); + new npc_gothik_minion_spectraltrainee(); + new npc_gothik_minion_spectralknight(); + new npc_gothik_minion_spectralrider(); + new npc_gothik_minion_spectralhorse(); + new npc_gothik_trigger(); new spell_gothik_shadow_bolt_volley(); } diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index 916bc3d0438..c8a4eb7fbc8 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -91,15 +91,6 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM } }; -MinionData const minionData[] = -{ - { NPC_SIR, BOSS_HORSEMEN }, - { NPC_THANE, BOSS_HORSEMEN }, - { NPC_LADY, BOSS_HORSEMEN }, - { NPC_BARON, BOSS_HORSEMEN }, - { 0, 0, } -}; - ObjectData const objectData[] = { { GO_NAXX_PORTAL_ARACHNID, DATA_NAXX_PORTAL_ARACHNID }, @@ -152,11 +143,8 @@ class instance_naxxramas : public InstanceMapScript SetBossNumber(EncounterCount); LoadBossBoundaries(boundaries); LoadDoorData(doorData); - LoadMinionData(minionData); LoadObjectData(nullptr, objectData); - minHorsemenDiedTime = 0; - maxHorsemenDiedTime = 0; AbominationCount = 0; hadAnubRekhanGreet = false; hadFaerlinaGreet = false; @@ -179,6 +167,9 @@ class instance_naxxramas : public InstanceMapScript case NPC_RAZUVIOUS: RazuviousGUID = creature->GetGUID(); break; + case NPC_GOTHIK: + GothikGUID = creature->GetGUID(); + break; case NPC_THANE: ThaneGUID = creature->GetGUID(); break; @@ -215,13 +206,6 @@ class instance_naxxramas : public InstanceMapScript default: break; } - - AddMinion(creature, true); - } - - void OnCreatureRemove(Creature* creature) override - { - AddMinion(creature, false); } void ProcessEvent(WorldObject* /*source*/, uint32 eventId) override @@ -351,25 +335,6 @@ class instance_naxxramas : public InstanceMapScript if (GameObject* gate = instance->GetGameObject(GothikGateGUID)) gate->SetGoState(GOState(value)); break; - case DATA_HORSEMEN0: - case DATA_HORSEMEN1: - case DATA_HORSEMEN2: - case DATA_HORSEMEN3: - if (value == NOT_STARTED) - { - minHorsemenDiedTime = 0; - maxHorsemenDiedTime = 0; - } - else if (value == DONE) - { - time_t now = time(NULL); - - if (minHorsemenDiedTime == 0) - minHorsemenDiedTime = now; - - maxHorsemenDiedTime = now; - } - break; case DATA_ABOMINATION_KILLED: AbominationCount = value; break; @@ -416,6 +381,8 @@ class instance_naxxramas : public InstanceMapScript return FaerlinaGUID; case DATA_RAZUVIOUS: return RazuviousGUID; + case DATA_GOTHIK: + return GothikGUID; case DATA_THANE: return ThaneGUID; case DATA_LADY: @@ -648,13 +615,13 @@ class instance_naxxramas : public InstanceMapScript { switch (criteria_id) { - case 7600: // Criteria for achievement 2176: And They Would All Go Down Together 15sec of each other 10-man - if (Difficulty(instance->GetSpawnMode()) == RAID_DIFFICULTY_10MAN_NORMAL && (maxHorsemenDiedTime - minHorsemenDiedTime) < 15) - return true; - return false; - case 7601: // Criteria for achievement 2177: And They Would All Go Down Together 15sec of each other 25-man - if (Difficulty(instance->GetSpawnMode()) == RAID_DIFFICULTY_25MAN_NORMAL && (maxHorsemenDiedTime - minHorsemenDiedTime) < 15) - return true; + // And They Would All Go Down Together (kill 4HM within 15sec of each other) + case 7600: // 25-man + case 7601: // 10-man + if (criteria_id + instance->GetSpawnMode() == 7601) + return false; + if (Creature* baron = instance->GetCreature(BaronGUID)) // it doesn't matter which one we use, really + return (baron->AI()->GetData(DATA_HORSEMEN_CHECK_ACHIEVEMENT_CREDIT) == 1u); return false; // Difficulty checks are done on DB. // Criteria for achievement 2186: The Immortal (25-man) @@ -693,6 +660,7 @@ class instance_naxxramas : public InstanceMapScript // Instructor Razuvious ObjectGuid RazuviousGUID; // Gothik the Harvester + ObjectGuid GothikGUID; ObjectGuid GothikGateGUID; // The Four Horsemen ObjectGuid ThaneGUID; @@ -700,8 +668,6 @@ class instance_naxxramas : public InstanceMapScript ObjectGuid BaronGUID; ObjectGuid SirGUID; ObjectGuid HorsemenChestGUID; - time_t minHorsemenDiedTime; - time_t maxHorsemenDiedTime; /* The Construct Quarter */ // Thaddius diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h index c14ff31bb94..c0caa86e93f 100644 --- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h +++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h @@ -50,10 +50,7 @@ enum Data DATA_HAD_FAERLINA_GREET, DATA_HAD_THADDIUS_GREET, - DATA_HORSEMEN0, - DATA_HORSEMEN1, - DATA_HORSEMEN2, - DATA_HORSEMEN3, + DATA_HORSEMEN_CHECK_ACHIEVEMENT_CREDIT, DATA_ABOMINATION_KILLED, DATA_NAXX_PORTAL_ARACHNID, @@ -67,6 +64,7 @@ enum Data64 DATA_ANUBREKHAN, DATA_FAERLINA, DATA_RAZUVIOUS, + DATA_GOTHIK, DATA_THANE, DATA_LADY, DATA_BARON, @@ -89,6 +87,7 @@ enum CreaturesIds NPC_ANUBREKHAN = 15956, NPC_FAERLINA = 15953, NPC_RAZUVIOUS = 16061, + NPC_GOTHIK = 16060, NPC_THANE = 16064, NPC_LADY = 16065, NPC_BARON = 30549, diff --git a/src/server/scripts/Outland/zone_zangarmarsh.cpp b/src/server/scripts/Outland/zone_zangarmarsh.cpp index 48bbb7bad4a..6f38cce0e5b 100644 --- a/src/server/scripts/Outland/zone_zangarmarsh.cpp +++ b/src/server/scripts/Outland/zone_zangarmarsh.cpp @@ -48,8 +48,6 @@ EndContentData */ enum AshyenAndKeleth { - GOSSIP_REWARD_BLESS = 0, - NPC_ASHYEN = 17900, NPC_KELETH = 17901, @@ -117,7 +115,6 @@ public: if (spell) { creature->CastSpell(player, spell, true); - creature->AI()->Talk(GOSSIP_REWARD_BLESS); } } @@ -145,7 +142,6 @@ public: if (spell) { creature->CastSpell(player, spell, true); - creature->AI()->Talk(GOSSIP_REWARD_BLESS); } } player->CLOSE_GOSSIP_MENU(); diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp index fee47aa1fa2..37584bda2ae 100644 --- a/src/server/scripts/Pet/pet_mage.cpp +++ b/src/server/scripts/Pet/pet_mage.cpp @@ -178,6 +178,9 @@ class npc_pet_mage_mirror_image : public CreatureScript void UpdateAI(uint32 diff) override { Unit* owner = me->GetCharmerOrOwner(); + if (!owner) + return; + Unit* target = owner->getAttackerForHelper(); events.Update(diff); @@ -192,9 +195,6 @@ class npc_pet_mage_mirror_image : public CreatureScript if (me->HasUnitState(UNIT_STATE_CASTING)) return; - if (!owner) - return; - // assign target if image doesnt have any or the target is not actual if (!target || me->GetVictim() != target) { diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 13e7697910b..8bd4b3eb070 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -1195,7 +1195,7 @@ class spell_pal_light_s_beacon : public SpellScriptLoader if (!procSpell) return; - uint32 healSpellId = procSpell->IsRankOf(sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_LIGHT)) ? SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_1 : SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_3; + uint32 healSpellId = procSpell->IsRankOf(sSpellMgr->EnsureSpellInfo(SPELL_PALADIN_HOLY_LIGHT)) ? SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_1 : SPELL_PALADIN_BEACON_OF_LIGHT_HEAL_3; uint32 heal = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount()); Unit* beaconTarget = GetCaster(); |
