From 19264bea0bba49bdff4c8ec7e9918092fe656319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=B9=BF?= <18535853+PkllonG@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:54:44 +0800 Subject: [PATCH 01/10] Fix(Core/Misc): Acore::StringFormat to fmt format (#19867) Update MMapMgr.cpp --- src/common/Collision/Management/MMapMgr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/Collision/Management/MMapMgr.cpp b/src/common/Collision/Management/MMapMgr.cpp index 480a2db2d..e08ecba79 100644 --- a/src/common/Collision/Management/MMapMgr.cpp +++ b/src/common/Collision/Management/MMapMgr.cpp @@ -23,8 +23,8 @@ namespace MMAP { - static char const* const MAP_FILE_NAME_FORMAT = "%s/mmaps/%03i.mmap"; - static char const* const TILE_FILE_NAME_FORMAT = "%s/mmaps/%03i%02i%02i.mmtile"; + static char const* const MAP_FILE_NAME_FORMAT = "{}/mmaps/{:03}.mmap"; + static char const* const TILE_FILE_NAME_FORMAT = "{}/mmaps/{:03}{:02}{:02}.mmtile"; // ######################## MMapMgr ######################## MMapMgr::~MMapMgr() From fa490c21e9262152c59c0ac81f9e8a500f047582 Mon Sep 17 00:00:00 2001 From: Gultask <100873791+Gultask@users.noreply.github.com> Date: Thu, 5 Sep 2024 10:16:26 -0300 Subject: [PATCH 02/10] fix(Scripts/BlackTemple): Rewrite Illidan Stormrage Fight (#19574) * Create rev_1722993051676997100.sql * akama done * Update boss_illidan.cpp * maiev * sssssssssllllll * blerg im sick irl * door handel * Update boss_illidan.cpp * minions * Update boss_illidan.cpp * unneeded * Update boss_illidan.cpp * Update boss_illidan.cpp * meow * Update boss_illidan.cpp * Update boss_illidan.cpp * Update boss_illidan.cpp * demon_phase missing: tear of azzinoth trap + frenzy flying phase wipe + animation * akama's ending not working still * Update boss_illidan.cpp * Update boss_illidan.cpp flying animation akama isn't returning to complete the ending * akama ending fixed * cleanup * Update boss_illidan.cpp * Update boss_illidan.cpp --- .../rev_1722993051676997100.sql | 92 + .../Outland/BlackTemple/boss_illidan.cpp | 1997 ++++++++++------- 2 files changed, 1235 insertions(+), 854 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1722993051676997100.sql diff --git a/data/sql/updates/pending_db_world/rev_1722993051676997100.sql b/data/sql/updates/pending_db_world/rev_1722993051676997100.sql new file mode 100644 index 000000000..f7aece85e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1722993051676997100.sql @@ -0,0 +1,92 @@ +DELETE FROM `creature_text` WHERE (`CreatureID` = 23089); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(23089, 0, 0, 'This door is all that stands between us and the Betrayer. Stand aside, friends.', 12, 0, 100, 1, 0, 0, 21563, 0, 'SAY_AKAMA_DOOR'), +(23089, 1, 0, 'I cannot do this alone...', 12, 0, 100, 274, 0, 0, 21548, 0, 'SAY_AKAMA_ALONE'), +(23089, 2, 0, 'I thank you for your aid, brothers. Our people will be redeemed!', 12, 0, 100, 66, 0, 0, 21554, 0, 'SAY_AKAMA_SALUTE'), +(23089, 3, 0, 'Be wary, friends. The Betrayer meditates in the court just beyond.', 12, 0, 100, 0, 0, 11388, 21555, 0, 'SAY_AKAMA_BETRAYER'), +(23089, 4, 0, 'We\'ve come to end your reign, Illidan. My people, and all of Outland, shall be free!', 14, 0, 100, 25, 0, 11389, 20893, 0, 'SAY_AKAMA_FREE'), +(23089, 5, 0, 'The time has come! The moment is at hand!', 14, 0, 100, 22, 0, 11380, 20894, 0, 'SAY_AKAMA_TIME_HAS_COME'), +(23089, 6, 0, 'I will deal with these mongrels! Strike now, friends! Strike at the Betrayer!', 14, 0, 100, 22, 0, 11390, 21250, 0, 'SAY_AKAMA_MINIONS'), +(23089, 7, 0, 'The Light will bless these dismal halls once again.... I swear it.', 14, 0, 100, 1, 0, 11387, 21514, 0, 'SAY_AKAMA_LIGHT'), +(23089, 8, 0, 'Those who\'ve defiled this temple have all been defeated. All but one!', 12, 0, 100, 1, 0, 0, 21518, 0, 'SAY_AKAMA_COUNCIL_1'), +(23089, 9, 0, 'Let us finish what we\'ve started. I will lead you to Illidan\'s abode once you\'ve recovered your strength.', 12, 0, 100, 1, 0, 0, 21520, 0, 'SAY_AKAMA_COUNCIL_2'); + +DELETE FROM `creature_text` WHERE (`CreatureID` = 22917); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(22917, 0, 0, 'Come, my minions. Deal with this traitor as he deserves!', 14, 0, 100, 0, 0, 11465, 21251, 0, 'Illidan SAY_ILLIDAN_MINION'), +(22917, 1, 0, 'Who shall be next to taste my blades?', 14, 0, 100, 0, 0, 11473, 21499, 0, 'Illidan SAY_ILLIDAN_KILL'), +(22917, 1, 1, 'This is too easy!', 14, 0, 100, 0, 0, 11472, 21498, 0, 'Illidan SAY_ILLIDAN_KILL'), +(22917, 2, 0, 'I will not be touched by rabble such as you!', 14, 0, 100, 0, 0, 11479, 21252, 0, 'Illidan SAY_ILLIDAN_TAKEOFF'), +(22917, 3, 0, 'Behold the Flames of Azzinoth!', 14, 0, 100, 0, 0, 11480, 21275, 0, 'Illidan SAY_ILLIDAN_SUMMONFLAMES'), +(22917, 4, 0, 'Stare into the eyes of the Betrayer!', 14, 0, 100, 0, 0, 11481, 21497, 0, 'Illidan SAY_ILLIDAN_EYE_BLAST'), +(22917, 5, 0, 'Behold the power... of the demon within!', 14, 0, 100, 0, 0, 11475, 21066, 0, 'Illidan SAY_ILLIDAN_MORPH'), +(22917, 6, 0, 'You\'ve wasted too much time, mortals. Now you shall fall.', 14, 0, 100, 0, 0, 11474, 22036, 0, 'Illidan SAY_ILLIDAN_ENRAGE'), +(22917, 7, 0, 'I can feel your hatred.', 14, 0, 100, 0, 0, 11467, 0, 0, 'Illidan SAY_ILLIDAN_TAUNT'), +(22917, 7, 1, 'Give in to your fear!', 14, 0, 100, 0, 0, 11468, 0, 0, 'Illidan SAY_ILLIDAN_TAUNT'), +(22917, 7, 2, 'You know nothing of power!', 14, 0, 100, 0, 0, 11469, 21500, 0, 'Illidan SAY_ILLIDAN_TAUNT'), +(22917, 7, 3, 'Such... arrogance!', 14, 0, 100, 0, 0, 11471, 0, 0, 'Illidan SAY_ILLIDAN_TAUNT'), +(22917, 8, 0, 'Akama. Your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago.', 14, 0, 100, 0, 0, 11463, 20867, 0, 'Illidan SAY_ILLIDAN_DUPLICITY'), +(22917, 9, 0, 'Boldly said. But I remain... unconvinced.', 14, 0, 100, 6, 0, 11464, 20868, 0, 'Illidan SAY_ILLIDAN_UNCONVINCED'), +(22917, 10, 0, 'You are not prepared!', 14, 0, 100, 406, 0, 11466, 20884, 0, 'Illidan SAY_ILLIDAN_PREPARED'), +(22917, 11, 0, 'Is this it, mortals? Is this all the fury you can muster?', 14, 0, 100, 0, 0, 11476, 21068, 0, 'Illidan SAY_ILLIDAN_SHADOW_PRISON'), +(22917, 12, 0, 'Maiev... How is it even possible?', 14, 0, 100, 1, 0, 11477, 21069, 0, 'Illidan SAY_ILLIDAN_CONFRONT_MAIEV'), +(22917, 13, 0, 'Feel the hatred of ten thousand years!', 14, 0, 100, 0, 0, 11470, 21501, 0, 'Illidan SAY_ILLIDAN_FRENZY'), +(22917, 14, 0, 'You have won... Maiev. But the huntress... is nothing without the hunt. You... are nothing... without me.', 14, 0, 100, 0, 0, 11478, 21506, 0, 'Illidan SAY_ILLIDAN_DEFEATED'); + +DELETE FROM `creature_text` WHERE (`CreatureID` = 23197); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(23197, 0, 0, 'That is for Naisha!', 14, 0, 100, 0, 0, 11493, 21489, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_TAUNT'), +(23197, 0, 1, 'Bleed as I have bled!', 14, 0, 100, 0, 0, 11494, 0, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_TAUNT'), +(23197, 0, 2, 'There shall be no prison for you this time!', 14, 0, 100, 0, 0, 11495, 22208, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_TAUNT'), +(23197, 0, 3, 'Meet your end, demon!', 14, 0, 100, 0, 0, 11500, 0, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_TAUNT'), +(23197, 1, 0, 'Their fury pales before mine, Illidan. We have some unsettled business between us.', 14, 0, 100, 6, 0, 11491, 21070, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_APPEAR'), +(23197, 2, 0, 'My long hunt is finally over. Today, Justice will be done!', 14, 0, 100, 5, 0, 11492, 21071, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_JUSTICE'), +(23197, 3, 0, 'There shall be no prison for you this time!', 14, 0, 100, 0, 0, 11495, 22208, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_TRAP'), +(23197, 4, 0, '%s falls to the floor.', 16, 0, 100, 0, 0, 0, 21317, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_DOWN'), +(23197, 5, 0, 'It is finished. You are beaten.', 14, 0, 100, 0, 0, 11496, 21507, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_FINISHED'), +(23197, 6, 0, 'He\'s right. I feel nothing... I am... nothing.', 14, 0, 100, 0, 0, 11497, 21508, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_OUTRO'), +(23197, 7, 0, 'Farewell, champions.', 14, 0, 100, 0, 0, 11498, 21509, 0, 'Maiev Shadowsong SAY_MAIEV_SHADOWSONG_FAREWELL'); + +DELETE FROM `waypoint_data` WHERE `id` IN (230892, 230893, 230894); +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +-- Path Illidari Council 2 +(230892,1,673.1424,354.9833,271.6953,NULL,0,1,0,100,0), +(230892,2,696.67883,380.00977,271.8905,NULL,0,1,0,100,0), +(230892,3,721.36926,374.37347,280.9863,NULL,0,1,0,100,0), +(230892,4,736.7919,352.50674,296.42725,NULL,0,1,0,100,0), +(230892,5,745.5639,336.74622,306.2915,NULL,0,1,0,100,0), +(230892,6,749.1409,319.2256,311.6832,NULL,0,1,0,100,0), +(230892,7,751.4883,308.6376,312.07648,NULL,0,1,0,100,0), +(230892,8,755.7801,304.4006,312.1663,NULL,0,1,0,100,0), +(230892,9,755.7801,304.4006,312.1663,6.2657318115234375,0,0,0,100,0), +-- Path Illidari Council 3 +(230893,1,798.9379,294.3112,319.75885,NULL,0,1,0,100,0), +(230893,2,797.5406,276.17447,330.36548,NULL,0,1,0,100,0), +(230893,3,793.6441,254.72418,341.4547,NULL,0,1,0,100,0), +(230893,4,764.82074,238.01302,353.61133,NULL,0,1,0,100,0), +(230893,5,748.4362,235.80513,352.99878,NULL,0,1,0,100,0), +(230893,6,748.4362,235.80513,352.99878,2.129301786422729492,0,0,0,100,0), +-- Face Minions +(230894,1,745.225,304.946,352.98593,3.140537023544311523,0,1,0,100,0), +(230894,2,743.76953,363.82217,352.98837,NULL,2600,1,0,100,0), +(230894,3,752.2771,369.94006,353.15842,NULL,0,1,0,100,0), +(230894,4,799.1155,304.4322,319.75153,NULL,0,1,0,100,0), +(230894,5,799.1155,304.4322,319.75153,3.071779489517211914,0,1,0,100,0); + +UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_maiev_illidan' WHERE (`entry` = 23197); +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23197); +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2319700); +UPDATE `creature_template_addon` SET `bytes1` = 0 WHERE (`entry` = 22917); + +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 23498; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 23498) AND (`source_type` = 0); +UPDATE `creature_template_addon` SET `bytes2` = 1, `auras` = '34429 41913' WHERE (`entry` = 23498); +UPDATE `creature_template` SET `ScriptName` = 'npc_parasitic_shadowfiend' WHERE (`entry` = 23498); + +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 22996; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 22996); +UPDATE `creature_template` SET `ScriptName` = 'npc_blade_of_azzinoth' WHERE (`entry` = 22996); + +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 22997; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 22997); +UPDATE `creature_template` SET `ScriptName` = 'npc_flame_of_azzinoth' WHERE (`entry` = 22997); diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index 7c165d68e..d1e26e712 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -27,55 +27,42 @@ enum Says { - SAY_ILLIDAN_MINION = 0, - SAY_ILLIDAN_KILL = 1, - SAY_ILLIDAN_TAKEOFF = 2, - SAY_ILLIDAN_SUMMONFLAMES = 3, - SAY_ILLIDAN_EYE_BLAST = 4, - SAY_ILLIDAN_MORPH = 5, - SAY_ILLIDAN_ENRAGE = 6, - SAY_ILLIDAN_TAUNT = 7, - SAY_ILLIDAN_AKAMA1 = 8, - SAY_ILLIDAN_AKAMA2 = 9, - SAY_ILLIDAN_AKAMA3 = 10, - SAY_ILLIDAN_MAIEV1 = 11, - SAY_ILLIDAN_MAIEV2 = 12, - SAY_ILLIDAN_MAIEV3 = 13, - SAY_ILLIDAN_FRENZY = 14, + SAY_ILLIDAN_MINION = 0, + SAY_ILLIDAN_KILL = 1, + SAY_ILLIDAN_TAKEOFF = 2, + SAY_ILLIDAN_SUMMONFLAMES = 3, + SAY_ILLIDAN_EYE_BLAST = 4, + SAY_ILLIDAN_MORPH = 5, + SAY_ILLIDAN_ENRAGE = 6, + SAY_ILLIDAN_TAUNT = 7, + SAY_ILLIDAN_DUPLICITY = 8, + SAY_ILLIDAN_UNCONVINCED = 9, + SAY_ILLIDAN_PREPARED = 10, + SAY_ILLIDAN_SHADOW_PRISON = 11, + SAY_ILLIDAN_CONFRONT_MAIEV = 12, + SAY_ILLIDAN_FRENZY = 13, + SAY_ILLIDAN_DEFEATED = 14, - SAY_UDALO = 0, - SAY_OLUM = 0, - SAY_AKAMA_DOORS = 0, - SAY_AKAMA_FAIL = 1, - SAY_AKAMA_BEWARE = 2, - SAY_AKAMA_LEAVE = 3, - SAY_AKAMA_ILLIDAN1 = 4, - SAY_AKAMA_ILLIDAN2 = 5, - SAY_AKAMA_ILLIDAN3 = 6, - SAY_AKAMA_COUNCIL_1 = 9, - SAY_AKAMA_COUNCIL_2 = 10, - - SAY_MAIEV_SHADOWSONG_TAUNT = 0, - SAY_MAIEV_SHADOWSONG_ILLIDAN1 = 1, - SAY_MAIEV_SHADOWSONG_ILLIDAN2 = 2, - SAY_MAIEV_SHADOWSONG_ILLIDAN3 = 3, - SAY_MAIEV_SHADOWSONG_ILLIDAN4 = 4, - SAY_MAIEV_SHADOWSONG_ILLIDAN5 = 5, - - EMOTE_AZZINOTH_GAZE = 0 + EMOTE_AZZINOTH_GAZE = 0 }; enum Spells { - // Phase 1 + SPELL_ILLIDAN_KNEEL_INTRO = 39656, // Aura removal does not play the full animation, using StandState instead SPELL_DUAL_WIELD = 42459, SPELL_BERSERK = 45078, + SPELL_EMOTE_TALK_QUESTION = 41616, + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_HIT_CHANCE = 43689, + + // Phase 1 SPELL_FLAME_CRASH = 40832, SPELL_DRAW_SOUL = 40904, SPELL_DRAW_SOUL_HEAL = 40903, SPELL_PARASITIC_SHADOWFIEND = 41917, SPELL_PARASITIC_SHADOWFIEND_TRIGGER = 41914, SPELL_SUMMON_PARASITIC_SHADOWFIENDS = 41915, + SPELL_SHEAR = 41032, // Phase 2 SPELL_THROW_GLAIVE = 39635, @@ -85,15 +72,11 @@ enum Spells SPELL_FIREBALL = 40598, SPELL_DARK_BARRAGE = 40585, SPELL_EYE_BLAST = 39908, - SPELL_UNCAGED_WRATH = 39869, - SPELL_FLAME_BLAST = 40631, - SPELL_CHARGE = 42003, // Phase 3 SPELL_AGONIZING_FLAMES = 40932, SPELL_SUMMON_MAIEV = 40403, SPELL_SHADOW_PRISON = 40647, - SPELL_TELEPORT_VISUAL_ONLY = 41232, // Phase 4 SPELL_DEMON_TRANSFORM_1 = 40511, @@ -110,90 +93,62 @@ enum Spells // Phase 5 SPELL_FRENZY = 40683, SPELL_TELEPORT_MAIEV = 41221, - SPELL_SHADOW_STRIKE = 40685, - SPELL_THROW_DAGGER = 41152, - SPELL_DEATH = 41220, + SPELL_DEATH = 41218, // Cage + SPELL_CAGE_TRAP = 40693, + SPELL_CAGE_TRAP_PERIODIC = 40760, + SPELL_CAGE_TRAP_DUMMY = 40761, SPELL_CAGED_DEBUFF = 40695, SPELL_CAGED_SUMMON1 = 40696, - SPELL_CAGED_SUMMON8 = 40703, - SPELL_CAGE_TRAP = 40760 + SPELL_CAGED_SUMMON8 = 40703 }; enum Misc { - ACTION_ILLIDARI_COUNCIL_DONE = 0, - ACTION_FIGHT_MINIONS = 1, - ACTION_RETURN_BLADE = 2, - ACTION_ILLIDAN_CAGED = 3, - ACTION_ILLIDAN_DEAD = 4, - ACTION_MAIEV_SET_DIST30 = 5, - ACTION_MAIEV_SET_DIST0 = 6, - ACTION_MAIEV_OUTRO = 7, + EQUIPMENT_UNARMED = 0, + EQUIPMENT_GLAIVES = 1, - MAX_EYE_BEAM_POS = 4, + // Illidan + ACTION_START_EVENT = 1, + ACTION_ILLIDAN_LIFTOFF = 2, + ACTION_ILLIDAN_CAGED = 3, + ACTION_SHADOW_PRISON = 4, + ACTION_ILLIDAN_DIE = 5, + ACTION_ILLIDAN_DEMON_TRANSFORM = 6, + ACTION_ILLIDAN_DEMON_TRANSFORM_BACK = 7, - POINT_ILLIDAN_HOVER = 10, - POINT_ILLIDAN_MIDDLE = 11, + // Akama + ACTION_ILLIDARI_COUNCIL_DONE = 0, + ACTION_AKAMA_MINIONS = 1, + ACTION_AKAMA_ENDING = 2, + ACTION_AKAMA_MAIEV_DESPAWN = 3, - NPC_ILLIDAN_DB_TARGET = 23070, - NPC_MAIEV_SHADOWSONG = 23197, + // Summons + ACTION_MAIEV_ENDING = 1, + ACTION_RETURN_BLADE = 2, // Sent to 22996 (Blade of Azzinoth) - GO_CAGE_TRAP = 185916, + MAX_EYE_BEAM_POS = 4, - PATH_AKAMA_ILLIDARI_COUNCIL_1 = 230891 -}; + POINT_ILLIDAN_TAKEOFF = 1, + POINT_ILLIDAN_HOVER = 2, + POINT_ILLIDAN_LAND = 3, -enum Events -{ - EVENT_SPELL_FLAME_CRASH = 1, - EVENT_SPELL_BERSERK = 2, - EVENT_SPELL_DRAW_SOUL = 3, - EVENT_SPELL_PARASITIC_SHADOWFIEND = 4, - EVENT_SPELL_AGONIZING_FLAMES = 5, - EVENT_SPELL_FRENZY = 6, + GROUP_BERSERK = 1, + GROUP_PHASE_FLYING = 2, + GROUP_DEMON_FORM = 3, - EVENT_SUMMON_MINIONS = 20, - EVENT_SUMMON_MINIONS2 = 21, + NPC_WORLD_TRIGGER = 22515, + NPC_ILLIDAN_DB_TARGET = 23070, + NPC_MAIEV_SHADOWSONG = 23197, - EVENT_PHASE_2_START = 40, - EVENT_SPELL_FIREBALL = 41, - EVENT_SPELL_DARK_BARRAGE = 42, - EVENT_START_PHASE_2_END = 43, - EVENT_START_PHASE_2_WEAPON = 44, - EVENT_START_PHASE_3_LAND = 45, - EVENT_PHASE_2_SUMMON1 = 46, - EVENT_PHASE_2_SUMMON2 = 47, - EVENT_PHASE_2_EYE_BEAM = 48, - EVENT_PHASE_2_EYE_BEAM_START = 49, - EVENT_PHASE_2_CHANGE_POS = 50, - EVENT_PHASE_2_INTERRUPT = 51, + GO_CAGE_TRAP = 185916, - EVENT_PHASE_4_START = 60, - EVENT_PHASE_5_START = 61, - EVENT_PHASE_5_SCENE1 = 62, - EVENT_PHASE_5_SCENE2 = 63, - EVENT_PHASE_5_SCENE3 = 64, - EVENT_PHASE_5_SCENE4 = 65, - EVENT_PHASE_5_SCENE5 = 66, - - EVENT_REMOVE_DEMON_FORM = 80, - EVENT_SPELL_FLAME_BURST = 81, - EVENT_SPELL_SHADOW_BLAST = 82, - EVENT_SPELL_SHADOW_DEMONS = 83, - EVENT_MOVE_MAIEV = 84, - EVENT_FINISH_TRANSFORM = 85, // Dummy, counter only - - EVENT_OUTRO_DEMON = 90, - EVENT_OUTRO_1 = 91, - EVENT_OUTRO_2 = 92, - EVENT_OUTRO_3 = 93, - - EVENT_KILL_TALK = 100, - EVENT_SAY_TAUNT = 101, - - GROUP_PHASE_2_ABILITY = 1 + PHASE_INITIAL = 1, + PHASE_FLYING = 2, + PHASE_LANDING = 3, + PHASE_DEMON = 4, + PHASE_MAIEV = 5 }; const Position eyeBeamPos[MAX_EYE_BEAM_POS * 2] = @@ -217,814 +172,1145 @@ const Position airHoverPos[MAX_EYE_BEAM_POS] = {656.86f, 344.07f, 356.0f, 0.0f} }; -class boss_illidan_stormrage : public CreatureScript +Position illidanTakeoffPoint = { 727.6356f, 305.62753, 359.1486f }; +Position illidanLand = { 676.648f, 304.76074f, 354.18906f, 6.230825424194335937f }; +Position roomCenter = { 676.021f, 305.455f, 353.582f, 3.82227f }; + +struct boss_illidan_stormrage : public BossAI { -public: - boss_illidan_stormrage() : CreatureScript("boss_illidan_stormrage") { } + boss_illidan_stormrage(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE), _canTalk(true), _dying(false), _inCutscene(false), beamPosId(0) { } - struct boss_illidan_stormrageAI : public BossAI + void Reset() override { - boss_illidan_stormrageAI(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE) - { - } + BossAI::Reset(); + me->m_Events.CancelEventGroup(GROUP_BERSERK); + me->m_Events.CancelEventGroup(GROUP_PHASE_FLYING); + me->m_Events.CancelEventGroup(GROUP_DEMON_FORM); + _canTalk = true; + _dying = false; + _inCutscene = false; + beamPosId = 0; + me->ReplaceAllUnitFlags(UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + me->SetDisableGravity(false); + me->SetHover(false); + DoCastSelf(SPELL_DUAL_WIELD, true); + me->LoadEquipment(EQUIPMENT_GLAIVES, true); + me->SetStandState(UNIT_STAND_STATE_KNEEL); + me->SetSheath(SHEATH_STATE_UNARMED); - EventMap events2; - uint8 beamPosId; - - void Reset() override - { - BossAI::Reset(); - events2.Reset(); - me->SetDisableGravity(false); - me->CastSpell(me, SPELL_DUAL_WIELD, true); - me->LoadEquipment(0, true); - me->SetImmuneToAll(true); - beamPosId = urand(0, 3); - } - - void EnterEvadeMode(EvadeReason why) override - { + ScheduleHealthCheckEvent(90, [&] { + // Call for minions + Talk(SAY_ILLIDAN_MINION); if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN)) - akama->DespawnOrUnsummon(); + akama->AI()->DoAction(ACTION_AKAMA_MINIONS); + }); + ScheduleHealthCheckEvent(65, [&] { + // Phase 2 + scheduler.CancelAll(); + DoAction(ACTION_ILLIDAN_LIFTOFF); + }); + ScheduleHealthCheckEvent(30, [&] { + // Maiev Spawn Scene + scheduler.CancelAll(); + if (me->HasAura(SPELL_DEMON_FORM)) + DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM_BACK); + DoAction(ACTION_SHADOW_PRISON); + }); + } - BossAI::EnterEvadeMode(why); - } - - bool CanAIAttack(Unit const* target) const override + void DoAction(int32 param) override + { + scheduler.CancelAll(); + switch (param) { - return target->GetEntry() != NPC_AKAMA_ILLIDAN && target->GetEntry() != NPC_MAIEV_SHADOWSONG; - } - - void DoAction(int32 param) override - { - if (param == ACTION_ILLIDAN_CAGED) + case ACTION_START_EVENT: { + me->SetStandState(UNIT_STAND_STATE_STAND); + + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_DUPLICITY); + }, 2210ms); + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); + }, 5670ms); // 3460ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); + }, 8100ms); // 2430ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); + }, 11750ms); // 3650ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_UNCONVINCED); + }, 26950ms); // 15200ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); + }, 29980ms); // 3030ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_PREPARED); + }, 39710ms); // 9730ms + me->m_Events.AddEventAtOffset([&] { + me->SetSheath(SHEATH_STATE_MELEE); + }, 40920ms); // 1210ms + me->m_Events.AddEventAtOffset([&] { + me->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + me->SetInCombatWithZone(); + }, 43370ms); // 2450ms + } + break; + case ACTION_ILLIDAN_LIFTOFF: + { + me->SetReactState(REACT_PASSIVE); + DoStopAttack(); + me->GetMotionMaster()->Clear(); + me->StopMovingOnCurrentPos(); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + me->SetDisableGravity(true); + me->SetHover(true); + me->SetOrientation(2.837961435317993164f); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_TAKEOFF); + }, 3640ms); + me->m_Events.AddEventAtOffset([&] { + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_TAKEOFF, illidanTakeoffPoint); + }, 7290ms); // 3650ms + } + break; + case ACTION_SHADOW_PRISON: + { + scheduler.CancelAll(); + _inCutscene = true; + DoCastSelf(SPELL_SHADOW_PRISON, true); + DoStopAttack(); + me->SetReactState(REACT_PASSIVE); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_EMOTE_TALK_QUESTION); + }, 1200ms); + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_SHADOW_PRISON); + }, 1430ms); // 230ms + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_SUMMON_MAIEV); + }, 10940ms); // 9510ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_CONFRONT_MAIEV); + }, 19490ms); // 8550ms + me->m_Events.AddEventAtOffset([&] { + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); + }, 23080ms); // 3590ms + me->m_Events.AddEventAtOffset([&] { + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + ScheduleAbilities(PHASE_MAIEV); + _inCutscene = false; + }, 32830ms); // 9750ms + } + break; + case ACTION_ILLIDAN_DEMON_TRANSFORM: + { + scheduler.CancelAll(); + me->SetReactState(REACT_PASSIVE); + DoStopAttack(); + me->SetControlled(true, UNIT_STATE_ROOT); + me->SetCombatMovement(false); + DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); + + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_MORPH); + }, 2630ms); + me->m_Events.AddEventAtOffset([&] { + // me->SetControlled(false, UNIT_STATE_ROOT); + me->SetReactState(REACT_AGGRESSIVE); + ScheduleAbilities(PHASE_DEMON); + }, 12230ms); + } + break; + case ACTION_ILLIDAN_DEMON_TRANSFORM_BACK: + { + scheduler.CancelAll(); + me->SetReactState(REACT_AGGRESSIVE); + me->SetCombatMovement(true); + me->SetControlled(false, UNIT_STATE_ROOT); + me->RemoveAurasDueToSpell(SPELL_DEMON_TRANSFORM_1); + me->RemoveAurasDueToSpell(SPELL_DEMON_TRANSFORM_2); + me->RemoveAurasDueToSpell(SPELL_DEMON_TRANSFORM_3); + me->RemoveAurasDueToSpell(SPELL_DEMON_FORM); + me->LoadEquipment(EQUIPMENT_GLAIVES, true); + } + break; + case ACTION_ILLIDAN_CAGED: + { + scheduler.CancelAll(); me->RemoveAurasDueToSpell(SPELL_FRENZY); - events.Reset(); - events.ScheduleEvent(EVENT_PHASE_4_START, 16000); + DoCastSelf(SPELL_CAGE_TRAP_PERIODIC, true); + + me->m_Events.AddEventAtOffset([&] { + DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM); + }, 15s, GROUP_DEMON_FORM); } - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (summon->GetEntry() == NPC_ILLIDAN_DB_TARGET) - me->CastSpell(summon, SPELL_EYE_BLAST, false); - else if (summon->GetEntry() == NPC_MAIEV_SHADOWSONG) + break; + case ACTION_ILLIDAN_DIE: { - me->SetTarget(summon->GetGUID()); - me->SetFacingToObject(summon); - summon->SetFacingToObject(me); - summon->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - summon->SetReactState(REACT_PASSIVE); - summon->CastSpell(summon, SPELL_TELEPORT_VISUAL_ONLY, true); - } - } + me->m_Events.CancelEventGroup(GROUP_DEMON_FORM); + scheduler.CancelAll(); - void SummonedCreatureDies(Creature* summon, Unit*) override - { - summons.Despawn(summon); - } + if (me->HasAura(SPELL_DEMON_FORM)) + DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM_BACK); - void SummonedCreatureDespawn(Creature* summon) override - { - summons.Despawn(summon); - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - return; - - if (id == POINT_ILLIDAN_HOVER) - { - if (events2.GetNextEventTime(EVENT_START_PHASE_2_END) == 0) - { - events.ScheduleEvent(EVENT_PHASE_2_SUMMON2, 2000); - events.ScheduleEvent(EVENT_PHASE_2_CHANGE_POS, 6000); - events.ScheduleEvent(EVENT_PHASE_2_EYE_BEAM, 15000, GROUP_PHASE_2_ABILITY); - events2.ScheduleEvent(EVENT_START_PHASE_2_END, 10000); - } - me->SetFacingTo(me->GetAngle(676.02f, 305.45f)); - } - else if (id == POINT_ILLIDAN_MIDDLE) - { - EntryCheckPredicate pred(NPC_BLADE_OF_AZZINOTH); - summons.DoAction(ACTION_RETURN_BLADE, pred); - events.ScheduleEvent(EVENT_START_PHASE_2_WEAPON, 1500); - events.ScheduleEvent(EVENT_START_PHASE_3_LAND, 4000); - } - } - - void ScheduleNormalEvents(uint8 phase) - { - events.ScheduleEvent(EVENT_SPELL_FLAME_CRASH, 15000); - events.ScheduleEvent(EVENT_SPELL_DRAW_SOUL, 30000); - events.ScheduleEvent(EVENT_SPELL_PARASITIC_SHADOWFIEND, 20000); - events.ScheduleEvent(EVENT_SAY_TAUNT, 40000); - if (phase >= 3) - { - events.ScheduleEvent(EVENT_PHASE_4_START, 60000); - events.ScheduleEvent(EVENT_SPELL_AGONIZING_FLAMES, 10000); - } - if (phase >= 5) - events.ScheduleEvent(EVENT_SPELL_FRENZY, 40000); - } - - void JustEngagedWith(Unit* who) override - { - summons.DespawnAll(); - BossAI::JustEngagedWith(who); - ScheduleNormalEvents(1); - events.ScheduleEvent(EVENT_SPELL_BERSERK, 25 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SUMMON_MINIONS, 1000); - events.ScheduleEvent(EVENT_PHASE_2_START, 1000); - } - - void AttackStart(Unit* victim) override - { - if (victim && me->Attack(victim, true)) - me->GetMotionMaster()->MoveChase(victim, events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0 ? 35.0f : 0.0f); - } - - void MoveInLineOfSight(Unit*) override { } - - void JustDied(Unit* /*killer*/) override - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - summons.DespawnEntry(NPC_PARASITIC_SHADOWFIEND); - _JustDied(); - } - - void KilledUnit(Unit* /*victim*/) override - { - if (events.GetNextEventTime(EVENT_KILL_TALK) == 0) - { - Talk(SAY_ILLIDAN_KILL); - events.ScheduleEvent(EVENT_KILL_TALK, 6000); - } - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth()) - { - damage = 0; - - // xinef: do not allow to start outro when transforming - if (events.GetNextEventTime(EVENT_FINISH_TRANSFORM)) - return; - - if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) - { - if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) - { - maiev->StopMovingOnCurrentPos(); - maiev->GetMotionMaster()->Clear(); - maiev->SetReactState(REACT_PASSIVE); - maiev->SetFullHealth(); - maiev->RemoveAllAuras(); - maiev->CombatStop(); - } - - if (events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0) - { - me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true); - events2.ScheduleEvent(EVENT_OUTRO_DEMON, 12000); - } - else - { - me->CastSpell(me, SPELL_TELEPORT_MAIEV, true); - me->CastSpell(me, SPELL_DEATH, true); - events2.ScheduleEvent(EVENT_OUTRO_1, 1000); - } - - if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) - { - maiev->SetTarget(me->GetGUID()); - maiev->SetFacingToObject(me); - } - - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - } - } - } - - void UpdateAI(uint32 diff) override - { - events2.Update(diff); - switch (events2.ExecuteEvent()) - { - case EVENT_SUMMON_MINIONS2: + _dying = true; + if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) + maiev->AI()->DoAction(ACTION_MAIEV_ENDING); + if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN)) + akama->AI()->DoAction(ACTION_AKAMA_ENDING); + me->m_Events.AddEventAtOffset([&] { if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN)) - akama->AI()->DoAction(ACTION_FIGHT_MINIONS); - break; - case EVENT_PHASE_2_EYE_BEAM_START: + akama->AI()->DoAction(ACTION_AKAMA_ENDING); + }, 1s); + + me->SetControlled(true, UNIT_STATE_ROOT); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true); + DoCastSelf(SPELL_DEATH, true); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_ILLIDAN_DEFEATED); + }, 7490ms); + me->m_Events.AddEventAtOffset([&] { + Unit::Kill(nullptr, me); + }, 25530ms); // 18040ms + } + break; + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE) + { + switch (id) + { + case POINT_ILLIDAN_TAKEOFF: + { + me->SetFacingTo(me->GetAngle(&roomCenter)); + Talk(SAY_ILLIDAN_SUMMONFLAMES); + + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_THROW_GLAIVE2); + }, 1210ms); + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_THROW_GLAIVE); + me->LoadEquipment(EQUIPMENT_UNARMED, true); + }, 2430ms); // 1220ms + me->m_Events.AddEventAtOffset([&] { + ScheduleAbilities(PHASE_FLYING); + }, 3090ms); // 660ms + } + break; + case POINT_ILLIDAN_HOVER: + { + me->SetControlled(true, UNIT_STATE_ROOT); + scheduler.CancelAll(); + ScheduleAbilities(PHASE_FLYING); + } + break; + case POINT_ILLIDAN_LAND: + { + EntryCheckPredicate pred(NPC_BLADE_OF_AZZINOTH); + summons.DoAction(ACTION_RETURN_BLADE, pred); + + me->m_Events.AddEventAtOffset([&] { + me->LoadEquipment(EQUIPMENT_GLAIVES); + }, 1215ms); + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->SetDisableGravity(false); + me->SetHover(false); + }, 3665ms); // 2450ms + me->m_Events.AddEventAtOffset([&] { + DoResetThreatList(); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + me->SetControlled(false, UNIT_STATE_ROOT); + ScheduleAbilities(PHASE_LANDING); + }, 6095ms); // 2430ms + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_HIT_CHANCE, true); + }, 7305ms); // 1210ms + } + break; + } + } + } + + void ScheduleAbilities(uint8 phase) + { + switch (phase) + { + case PHASE_INITIAL: + { + ScheduleTimedEvent(25s, 30s, [&] { + DoCastVictim(SPELL_FLAME_CRASH); + }, 26s, 35s); + + ScheduleTimedEvent(10s, [&] { + DoCastVictim(SPELL_SHEAR); + }, 12s, 15s); + + ScheduleTimedEvent(32s, [&] { + DoCastVictim(SPELL_DRAW_SOUL); + }, 32s); + + ScheduleTimedEvent(25s, 30s, [&] { + DoCastRandomTarget(SPELL_PARASITIC_SHADOWFIEND, 0U, 100.f); + }, 25s, 30s); + + // Custom from SunwellCore? + ScheduleTimedEvent(30s, 60s, [&] { + Talk(SAY_ILLIDAN_TAUNT); + }, 30s, 60s); + } + break; + case PHASE_FLYING: + { + me->SetFacingTo(me->GetAngle(&roomCenter)); + + scheduler.Schedule(0s, [this](TaskContext context) + { + // Do not repeat if interrupted (Eye Beam is cast) + if (DoCastRandomTarget(SPELL_FIREBALL, 0U, 50000.f, true, false, true) == SPELL_CAST_OK) + context.Repeat(2400ms); + }).Schedule(25s, 45s, [this](TaskContext /*context*/) + { + // Eye Blast + me->InterruptNonMeleeSpells(false); Talk(SAY_ILLIDAN_EYE_BLAST); + me->SummonCreature(NPC_ILLIDAN_DB_TARGET, eyeBeamPos[beamPosId], TEMPSUMMON_TIMED_DESPAWN, 30000); if (Creature* trigger = summons.GetCreatureWithEntry(NPC_ILLIDAN_DB_TARGET)) trigger->GetMotionMaster()->MovePoint(0, eyeBeamPos[beamPosId + MAX_EYE_BEAM_POS], false, true); - break; - case EVENT_PHASE_2_INTERRUPT: - me->InterruptNonMeleeSpells(false); - break; - case EVENT_START_PHASE_2_END: + + // Reposition + me->m_Events.AddEventAtOffset([&] { + scheduler.CancelAll(); + me->InterruptNonMeleeSpells(false); + me->SetControlled(false, UNIT_STATE_ROOT); + beamPosId = (beamPosId + 1) % MAX_EYE_BEAM_POS; + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_HOVER, airHoverPos[beamPosId], false, true); + }, 20s, GROUP_PHASE_FLYING); + }); + // Check for Phase Transition + scheduler.Schedule(5s, [this](TaskContext context) { + if (!SelectTargetFromPlayerList(150.0f)) + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + summons.RemoveNotExisting(); if (!summons.HasEntry(NPC_FLAME_OF_AZZINOTH)) { - events.Reset(); - events2.Reset(); me->InterruptNonMeleeSpells(false); - me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_MIDDLE, 676.02f, 305.45f, 353.6f); - break; - } - events2.ScheduleEvent(EVENT_START_PHASE_2_END, 1000); - break; - case EVENT_OUTRO_DEMON: - me->CastSpell(me, SPELL_TELEPORT_MAIEV, true); - me->CastSpell(me, SPELL_DEATH, true); - events2.ScheduleEvent(EVENT_OUTRO_1, 1000); - break; - case EVENT_OUTRO_1: - if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) - { - maiev->SetTarget(me->GetGUID()); - maiev->SetFacingToObject(me); - maiev->AI()->DoAction(ACTION_MAIEV_OUTRO); - maiev->AI()->Talk(SAY_MAIEV_SHADOWSONG_ILLIDAN3); - } - - if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN)) - { - akama->AI()->DoAction(ACTION_ILLIDAN_DEAD); - akama->SetTarget(me->GetGUID()); - akama->GetMotionMaster()->MovePoint(0, 695.63f, 306.63f, 354.26f); - } - events2.ScheduleEvent(EVENT_OUTRO_2, 6000); - break; - case EVENT_OUTRO_2: - Talk(SAY_ILLIDAN_MAIEV3); - events2.ScheduleEvent(EVENT_OUTRO_3, 17000); - break; - case EVENT_OUTRO_3: - Unit::Kill(nullptr, me); - break; - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SUMMON_MINIONS: - if (me->HealthBelowPct(90)) - { - Talk(SAY_ILLIDAN_MINION); - events2.ScheduleEvent(EVENT_SUMMON_MINIONS2, 10000); - break; - } - events.ScheduleEvent(EVENT_SUMMON_MINIONS, 1000); - break; - // /////////////////////////// - // PHASE 1, 3, 5 - // /////////////////////////// - case EVENT_SPELL_BERSERK: - Talk(SAY_ILLIDAN_ENRAGE); - me->CastSpell(me, SPELL_BERSERK, true); - break; - case EVENT_SPELL_FLAME_CRASH: - me->CastSpell(me->GetVictim(), SPELL_FLAME_CRASH, false); - events.ScheduleEvent(EVENT_SPELL_FLAME_CRASH, 25000); - break; - case EVENT_SPELL_DRAW_SOUL: - me->CastSpell(me->GetVictim(), SPELL_DRAW_SOUL, false); - events.ScheduleEvent(EVENT_SPELL_DRAW_SOUL, 40000); - break; - case EVENT_SPELL_PARASITIC_SHADOWFIEND: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - me->CastSpell(target, SPELL_PARASITIC_SHADOWFIEND, false); - events.ScheduleEvent(EVENT_SPELL_PARASITIC_SHADOWFIEND, 30000); - break; - case EVENT_SAY_TAUNT: - Talk(SAY_ILLIDAN_TAUNT); - events.ScheduleEvent(EVENT_SAY_TAUNT, urand(30000, 60000)); - break; - case EVENT_SPELL_FRENZY: - Talk(SAY_ILLIDAN_FRENZY); - me->CastSpell(me, SPELL_FRENZY, false); - break; - case EVENT_SPELL_AGONIZING_FLAMES: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - me->CastSpell(target, SPELL_AGONIZING_FLAMES, false); - break; - case EVENT_PHASE_5_START: - if (me->HealthBelowPct(30)) - { - me->CastSpell(me, SPELL_SHADOW_PRISON, true); - me->SendMeleeAttackStop(me->GetVictim()); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - Talk(SAY_ILLIDAN_MAIEV1); - events.Reset(); - events.ScheduleEvent(EVENT_PHASE_5_SCENE1, 9000); - events.ScheduleEvent(EVENT_PHASE_5_SCENE2, 18000); - events.ScheduleEvent(EVENT_PHASE_5_SCENE3, 24000); - events.ScheduleEvent(EVENT_PHASE_5_SCENE4, 27000); - events.ScheduleEvent(EVENT_PHASE_5_SCENE5, 30000); - break; - } - events.ScheduleEvent(EVENT_PHASE_5_START, 1000); - break; - case EVENT_PHASE_5_SCENE1: - me->CastSpell(me, SPELL_SUMMON_MAIEV, true); - break; - case EVENT_PHASE_5_SCENE2: - Talk(SAY_ILLIDAN_MAIEV2); - break; - case EVENT_PHASE_5_SCENE3: - if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) - maiev->AI()->Talk(SAY_MAIEV_SHADOWSONG_ILLIDAN2); - break; - case EVENT_PHASE_5_SCENE4: - if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) - maiev->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); - break; - case EVENT_PHASE_5_SCENE5: - me->SetTarget(me->GetVictim()->GetGUID()); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) - { - maiev->SetReactState(REACT_AGGRESSIVE); - maiev->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - maiev->AI()->AttackStart(me); - } - ScheduleNormalEvents(5); - break; - // /////////////////////////// - // PHASE 2 - // /////////////////////////// - case EVENT_PHASE_2_START: - if (me->HealthBelowPct(65)) - { - if (events2.GetNextEventTime(EVENT_SUMMON_MINIONS2) != 0) - events2.RescheduleEvent(EVENT_SUMMON_MINIONS2, 0); - - Talk(SAY_ILLIDAN_TAKEOFF); - me->SendMeleeAttackStop(me->GetVictim()); - me->SetTarget(); - me->GetMotionMaster()->Clear(); - me->StopMovingOnCurrentPos(); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); - me->SetDisableGravity(true); - - events.Reset(); - events.ScheduleEvent(EVENT_PHASE_2_SUMMON1, 6000); - break; - } - events.ScheduleEvent(EVENT_PHASE_2_START, 1000); - break; - case EVENT_PHASE_2_SUMMON1: - me->LoadEquipment(1, true); - me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_HOVER, 727.875f, 305.365f, 356.0f, false, true); - break; - case EVENT_PHASE_2_SUMMON2: - Talk(SAY_ILLIDAN_SUMMONFLAMES); - me->LoadEquipment(0, true); - me->CastSpell(me, SPELL_THROW_GLAIVE, false); - me->CastSpell(me, SPELL_THROW_GLAIVE2, false); - break; - case EVENT_PHASE_2_CHANGE_POS: - beamPosId = (beamPosId + 1) % MAX_EYE_BEAM_POS; - events.ScheduleEvent(EVENT_SPELL_FIREBALL, 8000, GROUP_PHASE_2_ABILITY); - events.ScheduleEvent(EVENT_SPELL_DARK_BARRAGE, 18000, GROUP_PHASE_2_ABILITY); - events.ScheduleEvent(EVENT_PHASE_2_EYE_BEAM, urand(25000, 50000), GROUP_PHASE_2_ABILITY); - me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_HOVER, airHoverPos[beamPosId], false, true); - break; - case EVENT_PHASE_2_EYE_BEAM: - me->SummonCreature(NPC_ILLIDAN_DB_TARGET, eyeBeamPos[beamPosId], TEMPSUMMON_TIMED_DESPAWN, 15000); - events.CancelEventGroup(GROUP_PHASE_2_ABILITY); - events.ScheduleEvent(EVENT_PHASE_2_CHANGE_POS, 20000); - - events2.ScheduleEvent(EVENT_PHASE_2_EYE_BEAM_START, 2000); - events2.ScheduleEvent(EVENT_PHASE_2_INTERRUPT, 20000); - break; - case EVENT_SPELL_FIREBALL: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - me->CastSpell(target, SPELL_FIREBALL, false); - events.ScheduleEvent(EVENT_SPELL_FIREBALL, 2200, GROUP_PHASE_2_ABILITY); - break; - case EVENT_SPELL_DARK_BARRAGE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - me->CastSpell(target, SPELL_DARK_BARRAGE, false); - events.ScheduleEvent(EVENT_SPELL_DARK_BARRAGE, 30000, GROUP_PHASE_2_ABILITY); - break; - case EVENT_START_PHASE_2_WEAPON: - me->LoadEquipment(1, true); - me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); - me->SetDisableGravity(false); - break; - case EVENT_START_PHASE_3_LAND: - me->GetThreatMgr().ResetAllThreat(); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetTarget(me->GetVictim()->GetGUID()); - AttackStart(me->GetVictim()); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - ScheduleNormalEvents(3); - events.ScheduleEvent(EVENT_PHASE_5_START, 1000); - break; - // /////////////////////////// - // PHASE 4 - // /////////////////////////// - case EVENT_PHASE_4_START: - me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true); - me->GetThreatMgr().ResetAllThreat(); - me->GetMotionMaster()->MoveChase(me->GetVictim(), 35.0f); - events.Reset(); - events.ScheduleEvent(EVENT_SPELL_SHADOW_BLAST, 11000); - events.ScheduleEvent(EVENT_MOVE_MAIEV, 5000); - events.ScheduleEvent(EVENT_FINISH_TRANSFORM, 10500); - events.ScheduleEvent(EVENT_SPELL_FLAME_BURST, 21000); - events.ScheduleEvent(EVENT_SPELL_SHADOW_DEMONS, 36000); - events.ScheduleEvent(EVENT_REMOVE_DEMON_FORM, 60000); - break; - case EVENT_SPELL_SHADOW_BLAST: - me->CastSpell(me->GetVictim(), SPELL_SHADOW_BLAST, false); - events.ScheduleEvent(EVENT_SPELL_SHADOW_BLAST, 2200); - break; - case EVENT_SPELL_FLAME_BURST: - me->CastSpell(me, SPELL_FLAME_BURST, false); - events.ScheduleEvent(EVENT_SPELL_FLAME_BURST, 22000); - break; - case EVENT_SPELL_SHADOW_DEMONS: - me->CastSpell(me, SPELL_SUMMON_SHADOW_DEMON, false); - break; - case EVENT_REMOVE_DEMON_FORM: - me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true); - me->GetThreatMgr().ResetAllThreat(); - events.Reset(); - if (summons.HasEntry(NPC_MAIEV_SHADOWSONG)) - { - ScheduleNormalEvents(5); - events.DelayEvents(11000); - events.ScheduleEvent(EVENT_MOVE_MAIEV, 10000); - events.ScheduleEvent(EVENT_FINISH_TRANSFORM, 10500); + me->SetControlled(false, UNIT_STATE_ROOT); + me->m_Events.CancelEventGroup(GROUP_PHASE_FLYING); + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_LAND, illidanLand); + scheduler.CancelAll(); } else - { - ScheduleNormalEvents(3); - events.ScheduleEvent(EVENT_PHASE_5_START, 1000); - events.DelayEvents(11000); - events.ScheduleEvent(EVENT_FINISH_TRANSFORM, 10500); - } - break; - case EVENT_MOVE_MAIEV: - if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) - { - if (events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0) - { - maiev->AI()->DoAction(ACTION_MAIEV_SET_DIST30); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, -25.0f, true)) - maiev->GetMotionMaster()->MoveCharge(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 7.0f, 0); - else - maiev->GetMotionMaster()->MoveCharge(678.04f, 378.34f, 353.0f, 7.0f, 0); - } - else - { - maiev->AI()->DoAction(ACTION_MAIEV_SET_DIST0); - maiev->GetMotionMaster()->MoveChase(me, 0.0f); - } - } - break; - case EVENT_FINISH_TRANSFORM: - me->GetMotionMaster()->MoveChase(me->GetVictim(), events.GetNextEventTime(EVENT_REMOVE_DEMON_FORM) != 0 ? 35.0f : 0.0f); - break; + context.Repeat(3s); + }); } + break; + case PHASE_LANDING: + { + scheduler.CancelAll(); - if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - DoMeleeAttackIfReady(); + ScheduleTimedEvent(25s, 30s, [&] { + DoCastVictim(SPELL_FLAME_CRASH); + }, 26s, 35s); + + ScheduleTimedEvent(10s, [&] { + DoCastVictim(SPELL_SHEAR); + }, 12s, 15s); + + ScheduleTimedEvent(32s, [&] { + DoCastVictim(SPELL_DRAW_SOUL); + }, 32s); + + ScheduleTimedEvent(25s, 30s, [&] { + DoCastRandomTarget(SPELL_PARASITIC_SHADOWFIEND, 0U, 100.f); + }, 25s, 30s); + + ScheduleTimedEvent(25s, [&] { + DoCastSelf(SPELL_AGONIZING_FLAMES); + }, 24s); + + ScheduleTimedEvent(60s, [&] { + DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM); + }, 60s); + } + break; + case PHASE_DEMON: + { + scheduler.CancelAll(); + DoCastSelf(SPELL_SUMMON_SHADOW_DEMON, true); + + ScheduleTimedEvent(1s, 2500ms, [&] { + DoCastVictim(SPELL_SHADOW_BLAST); + }, 2500ms); + + ScheduleTimedEvent(7s, [&] { + DoCastSelf(SPELL_FLAME_BURST); + }, 19500ms); + + ScheduleTimedEvent(60s, [&] { + DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM_BACK); + if (summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) + ScheduleAbilities(PHASE_MAIEV); + else + ScheduleAbilities(PHASE_LANDING); + DoResetThreatList(); + }, 60s); + } + break; + case PHASE_MAIEV: + { + ScheduleAbilities(PHASE_LANDING); + + ScheduleTimedEvent(40s, [&] { + Talk(SAY_ILLIDAN_FRENZY); + DoCastSelf(SPELL_FRENZY, true); + }, 100s); + + ScheduleTimedEvent(30s, [&] { + if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG)) + DoCast(maiev, SPELL_CAGE_TRAP, true); + }, 45s); + } + break; } - - bool CheckEvadeIfOutOfCombatArea() const override - { - return me->GetHomePosition().GetExactDist(me) > 90.0f || !SelectTargetFromPlayerList(80.0f); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI(creature); } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + ScheduleAbilities(PHASE_INITIAL); + if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN)) + akama->AI()->AttackStart(me); + + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_BERSERK, true); + }, 25min, GROUP_BERSERK); + } + + void EnterEvadeMode(EvadeReason why) override + { + if (_inCutscene) + return; + + if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN)) + akama->DespawnOnEvade(); + + BossAI::EnterEvadeMode(why); + me->DespawnOnEvade(); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + if (summon->GetEntry() == NPC_ILLIDAN_DB_TARGET) + DoCast(summon, SPELL_EYE_BLAST); + else if (summon->GetEntry() == NPC_MAIEV_SHADOWSONG) + { + me->SetTarget(summon->GetGUID()); + me->SetFacingToObject(summon); + } + } + + void KilledUnit(Unit* /*victim*/) override + { + if (_canTalk) + { + Talk(SAY_ILLIDAN_KILL); + _canTalk = false; + + me->m_Events.AddEventAtOffset([&] { + _canTalk = true; + }, 6s); // 3590ms + } + } + + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override + { + if (damage >= me->GetHealth()) + { + damage = me->GetHealth() - 1; + + if (!_dying) + DoAction(ACTION_ILLIDAN_DIE); + } + + if (!_dying) + BossAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask); + } + + void JustDied(Unit* killer) override + { + summons.clear(); + BossAI::JustDied(killer); + } + + bool CanAIAttack(Unit const* target) const override + { + return target->GetEntry() != NPC_AKAMA_ILLIDAN && target->GetEntry() != NPC_MAIEV_SHADOWSONG; + } + +private: + bool _canTalk; + bool _dying; + bool _inCutscene; + uint8 beamPosId; }; enum Akama { - POINT_DOORS = 20, - POINT_ILLIDAN = 32, - POINT_FIGHT_MINIONS = 40, + POINT_FACE_ILLIDAN = 1, + POINT_ILLIDAN_DEFEATED_1 = 2, + POINT_ILLIDAN_DEFEATED_2 = 3, - SPELL_AKAMA_DOOR_OPEN = 41268, - SPELL_AKAMA_DOOR_FAIL = 41271, - SPELL_DEATHSWORN_DOOR_OPEN = 41269, - SPELL_HEALING_POTION = 40535, - SPELL_CHAIN_LIGHTNING = 40536, + SPELL_AKAMA_DOOR_OPEN = 41268, + SPELL_AKAMA_DOOR_FAIL = 41271, + SPELL_DEATHSWORN_DOOR_OPEN = 41269, + SPELL_ARCANE_EXPLOSION_VIS = 35426, + SPELL_HEALING_POTION = 40535, + SPELL_CHAIN_LIGHTNING = 40536, + SPELL_REDUCED_THREAT = 41000, + SPELL_AKAMA_TELEPORT = 41077, + SPELL_AKAMA_DESPAWN = 41242, - NPC_SPIRIT_OF_OLUM = 23411, - NPC_SPIRIT_OF_UDALO = 23410, - NPC_ILLIDARI_ELITE = 23226, + NPC_ILLIDAN_DOOR_TRIGGER = 23412, + NPC_SPIRIT_OF_OLUM = 23411, + NPC_SPIRIT_OF_UDALO = 23410, + NPC_ILLIDARI_ELITE = 23226, - EVENT_AKAMA_SCENE_1 = 1, - EVENT_AKAMA_SCENE_2 = 2, - EVENT_AKAMA_SCENE_3 = 3, - EVENT_AKAMA_SCENE_4 = 4, - EVENT_AKAMA_SCENE_5 = 5, - EVENT_AKAMA_SCENE_6 = 6, - EVENT_AKAMA_SCENE_7 = 7, - EVENT_AKAMA_SCENE_8 = 8, - EVENT_AKAMA_SCENE_9 = 9, - EVENT_AKAMA_SCENE_10 = 10, - EVENT_AKAMA_SCENE_11 = 11, - EVENT_AKAMA_SCENE_20 = 20, - EVENT_AKAMA_SCENE_21 = 21, - EVENT_AKAMA_SCENE_22 = 22, - EVENT_AKAMA_SCENE_23 = 23, - EVENT_AKAMA_SCENE_24 = 24, - EVENT_AKAMA_SCENE_25 = 25, - EVENT_AKAMA_SCENE_26 = 26, - EVENT_AKAMA_SCENE_27 = 27, - EVENT_AKAMA_SCENE_28 = 28, - EVENT_AKAMA_SCENE_29 = 29, + PATH_AKAMA_ILLIDARI_COUNCIL_1 = 230891, + PATH_AKAMA_ILLIDARI_COUNCIL_2 = 230892, + PATH_AKAMA_ILLIDARI_COUNCIL_3 = 230893, + PATH_AKAMA_MINIONS = 230894, - EVENT_AKAMA_SUMMON_ILLIDARI = 100, - EVENT_AKAMA_SPELL_CHAIN = 101, - EVENT_AKAMA_HEALTH = 102 + SAY_UDALO = 0, + SAY_OLUM = 0, + + SAY_AKAMA_DOOR = 0, + SAY_AKAMA_ALONE = 1, + SAY_AKAMA_SALUTE = 2, + SAY_AKAMA_BETRAYER = 3, + SAY_AKAMA_FREE = 4, + SAY_AKAMA_TIME_HAS_COME = 5, + SAY_AKAMA_MINIONS = 6, + SAY_AKAMA_LIGHT = 7, + SAY_AKAMA_COUNCIL_1 = 8, + SAY_AKAMA_COUNCIL_2 = 9 }; -Position AkamaTeleport = { 609.772f, 308.456f, 271.826f, 6.1972566f }; - -class npc_akama_illidan : public CreatureScript +Position AkamaIllidariCouncilTeleport = { 609.772f, 308.456f, 271.826f, 6.1972566f }; +Position SpiritUdaloPos = { 751.4565f, 311.01065f, 312.18997f, 0.f }; +Position SpiritOlumPos = { 751.6437f, 297.2233f, 312.20825f, 6.038839340209960937f }; +Position FaceIllidan = { 745.225f, 304.946f, 352.98593f }; +Position IllidanDefeated = { 753.04553f, 369.30273f, 353.1165f }; +Position IllidariMinionPos[10] = { -public: - npc_akama_illidan() : CreatureScript("npc_akama_illidan") { } +{ 750.0472f , 282.32742f, 309.4353f , 3.071779489517211914f }, +{ 747.0576f , 326.42682f, 309.06885f, 0.0f }, +{ 754.0332f , 325.81363f, 310.31952f, 2.914699792861938476f }, +{ 745.25525f, 322.15738f, 310.45963f, 6.038839340209960937f }, +{ 748.8422f , 288.06195f, 310.9782f , 1.884955525398254394f }, +{ 745.3237f , 283.986f , 309.2765f , 0.628318548202514648f }, +{ 743.9686f , 289.64468f, 311.18066f, 6.056292533874511718f }, +{ 751.08777f, 327.6505f , 309.45758f, 6.17846536636352539f }, +{ 750.03217f, 323.60635f, 310.27567f, 5.497786998748779296f }, +{ 753.8425f , 286.56195f, 310.9353f , 1.029744267463684082f } +}; - struct npc_akama_illidanAI : public npc_escortAI +struct npc_akama_illidan : public ScriptedAI +{ + npc_akama_illidan(Creature* creature) : ScriptedAI(creature), summons(me) { - npc_akama_illidanAI(Creature* creature) : npc_escortAI(creature), summons(me) - { - instance = creature->GetInstanceScript(); - if (instance->GetBossState(DATA_AKAMA_ILLIDAN) == DONE) - { - me->GetMap()->LoadGrid(751.664f, 238.933f); - me->SetHomePosition(751.664f, 238.933f, 353.106f, 2.18f); - me->NearTeleportTo(751.664f, 238.933f, 353.106f, 2.18f); - } - } + instance = creature->GetInstanceScript(); + } - void DoAction(int32 param) override + void Reset() override + { + scheduler.CancelAll(); + me->m_Events.KillAllEvents(true); + me->SetReactState(REACT_AGGRESSIVE); + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->setActive(false); + summons.DespawnAll(); + DoCastSelf(SPELL_REDUCED_THREAT, true); + me->SetControlled(false, UNIT_STATE_ROOT); + } + + void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + { + CloseGossipMenuFor(player); + me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); + me->setActive(true); + + if (instance->GetBossState(DATA_AKAMA_ILLIDAN) != DONE) { - if (param == ACTION_FIGHT_MINIONS) + me->GetMotionMaster()->MovePath(PATH_AKAMA_ILLIDARI_COUNCIL_2, false); + } + else + { + me->SetSheath(SHEATH_STATE_UNARMED); + me->GetMotionMaster()->MovePoint(POINT_FACE_ILLIDAN, FaceIllidan); + } + } + + void DoAction(int32 param) override + { + switch (param) + { + case ACTION_ILLIDARI_COUNCIL_DONE: { - me->CombatStop(true); - events.ScheduleEvent(EVENT_AKAMA_SUMMON_ILLIDARI, 8000); - events.ScheduleEvent(EVENT_AKAMA_SPELL_CHAIN, 7000); - events.ScheduleEvent(EVENT_AKAMA_HEALTH, 1000); - me->GetMotionMaster()->MoveCharge(741.97f, 358.74f, 353.0f, 10.0f, POINT_FIGHT_MINIONS); - Talk(SAY_AKAMA_LEAVE); - } - else if (param == ACTION_ILLIDAN_DEAD) - { - events.Reset(); - summons.DespawnAll(); - me->CombatStop(true); - } - else if (param == ACTION_ILLIDARI_COUNCIL_DONE) - { - me->NearTeleportTo(AkamaTeleport); + me->NearTeleportTo(AkamaIllidariCouncilTeleport); me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); me->GetMotionMaster()->MovePath(PATH_AKAMA_ILLIDARI_COUNCIL_1, false); } - } - - void Reset() override - { - me->SetReactState(REACT_AGGRESSIVE); - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->setActive(false); - events.Reset(); - summons.DespawnAll(); - } - - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override - { - CloseGossipMenuFor(player); - me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); - me->setActive(true); - - if (instance->GetBossState(DATA_AKAMA_ILLIDAN) != DONE) + break; + case ACTION_AKAMA_MINIONS: { - me->SetReactState(REACT_PASSIVE); - Start(false, true); - SetDespawnAtEnd(false); + EnterEvadeMode(EVADE_REASON_OTHER); } - else + break; + case ACTION_AKAMA_ENDING: { - me->GetMotionMaster()->MovePoint(POINT_ILLIDAN, 744.45f, 304.84f, 353.0f); - events.Reset(); - events.ScheduleEvent(EVENT_AKAMA_SCENE_20, 5000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_21, 8000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_22, 10000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_23, 23000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_24, 34000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_25, 41000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_26, 46000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_27, 49000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_28, 49200); - events.ScheduleEvent(EVENT_AKAMA_SCENE_29, 52000); - } - } - - void PathEndReached(uint32 pathId) override - { - if (pathId == PATH_AKAMA_ILLIDARI_COUNCIL_1) - { - ScheduleUniqueTimedEvent(200ms, [&] + if (me->IsEngaged()) { - Talk(SAY_AKAMA_COUNCIL_1); - }, 1); - ScheduleUniqueTimedEvent(7800ms, [&] + summons.DespawnAll(); + me->SetReactState(REACT_PASSIVE); + me->SetControlled(false, UNIT_STATE_ROOT); + } + else { - Talk(SAY_AKAMA_COUNCIL_2); - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - }, 2); + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_DEFEATED_1, IllidanDefeated); + } } - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - summon->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); - if (summon->GetEntry() == NPC_ILLIDARI_ELITE) + break; + case ACTION_AKAMA_MAIEV_DESPAWN: { - me->AddThreat(summon, 1000000.0f); - summon->AddThreat(me, 1000000.0f); - summon->AI()->AttackStart(me); - AttackStart(summon); + Talk(SAY_AKAMA_LIGHT); + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + }, 3490ms); + me->m_Events.AddEventAtOffset([&] { + me->SetRespawnDelay(WEEK); + DoCastSelf(SPELL_AKAMA_DESPAWN); + }, 8340ms); // 4850ms + me->m_Events.AddEventAtOffset([&] { + me->DespawnOnEvade(); + }, 8740ms); } + break; } + } - void WaypointReached(uint32 pointId) override + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE) { - if (pointId == POINT_DOORS) + switch (id) { - SetEscortPaused(true); - events.ScheduleEvent(EVENT_AKAMA_SCENE_1, 0); - events.ScheduleEvent(EVENT_AKAMA_SCENE_2, 4000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_3, 7000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_4, 17000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_5, 23000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_6, 25000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_7, 31000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_8, 40000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_9, 54000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_10, 57000); - events.ScheduleEvent(EVENT_AKAMA_SCENE_11, 62000); - } - else if (pointId == POINT_ILLIDAN) - { - me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_GOSSIP); - me->setActive(false); - me->SetReactState(REACT_AGGRESSIVE); - } - } - - void MoveInLineOfSight(Unit* /*who*/) override { } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth()) - damage = 0; - } - - void UpdateEscortAI(uint32 diff) override - { - scheduler.Update(diff); - events.Update(diff); - switch (events.ExecuteEvent()) - { - case EVENT_AKAMA_SCENE_1: - me->SetFacingTo(0.0f); - break; - case EVENT_AKAMA_SCENE_2: - Talk(SAY_AKAMA_DOORS); - break; - case EVENT_AKAMA_SCENE_3: - me->CastSpell(me, SPELL_AKAMA_DOOR_FAIL, false); - break; - case EVENT_AKAMA_SCENE_4: - Talk(SAY_AKAMA_FAIL); - break; - case EVENT_AKAMA_SCENE_5: - me->SummonCreature(NPC_SPIRIT_OF_UDALO, me->GetPositionX() - 5.0f, me->GetPositionY() + 8.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); - me->SummonCreature(NPC_SPIRIT_OF_OLUM, me->GetPositionX() - 5.0f, me->GetPositionY() - 8.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); - break; - case EVENT_AKAMA_SCENE_6: - if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f)) - udalo->AI()->Talk(SAY_UDALO); - break; - case EVENT_AKAMA_SCENE_7: - if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f)) - olum->AI()->Talk(SAY_OLUM); - break; - case EVENT_AKAMA_SCENE_8: - me->CastSpell(me, SPELL_AKAMA_DOOR_OPEN, false); - if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f)) - olum->CastSpell(olum, SPELL_AKAMA_DOOR_OPEN, false); - if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f)) - udalo->CastSpell(udalo, SPELL_AKAMA_DOOR_OPEN, false); - break; - case EVENT_AKAMA_SCENE_9: - instance->SetBossState(DATA_AKAMA_ILLIDAN, NOT_STARTED); - instance->SetBossState(DATA_AKAMA_ILLIDAN, DONE); - break; - case EVENT_AKAMA_SCENE_10: - Talk(SAY_AKAMA_BEWARE); - break; - case EVENT_AKAMA_SCENE_11: - SetEscortPaused(false); - break; - case EVENT_AKAMA_SCENE_20: - if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->SetStandState(UNIT_STAND_STATE_STAND); - break; - case EVENT_AKAMA_SCENE_21: - me->SetFacingTo(M_PI); - break; - case EVENT_AKAMA_SCENE_22: - if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->Talk(SAY_ILLIDAN_AKAMA1); - break; - case EVENT_AKAMA_SCENE_23: - Talk(SAY_AKAMA_ILLIDAN1); - break; - case EVENT_AKAMA_SCENE_24: - if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->Talk(SAY_ILLIDAN_AKAMA2); - break; - case EVENT_AKAMA_SCENE_25: - Talk(SAY_AKAMA_ILLIDAN2); - break; - case EVENT_AKAMA_SCENE_26: - if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->Talk(SAY_ILLIDAN_AKAMA3); - break; - case EVENT_AKAMA_SCENE_27: - if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->LoadEquipment(1, true); - break; - case EVENT_AKAMA_SCENE_28: - if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NO_SHEATHE); - break; - case EVENT_AKAMA_SCENE_29: + case POINT_FACE_ILLIDAN: + { if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) { - illidan->SetImmuneToAll(false); - illidan->SetInCombatWithZone(); - AttackStart(illidan); + me->SetFacingToObject(illidan); + illidan->AI()->DoAction(ACTION_START_EVENT); + me->SetHomePosition(me->GetPosition()); } - break; - case EVENT_AKAMA_SUMMON_ILLIDARI: - me->SummonCreature(NPC_ILLIDARI_ELITE, 742.55f, 359.12f, 353.0f, 4.35f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); - events.ScheduleEvent(EVENT_AKAMA_SUMMON_ILLIDARI, urand(2000, 6000)); - break; - case EVENT_AKAMA_SPELL_CHAIN: - if (me->GetVictim()) - me->CastSpell(me->GetVictim(), SPELL_CHAIN_LIGHTNING, false); - events.ScheduleEvent(EVENT_AKAMA_SPELL_CHAIN, 20000); - break; - case EVENT_AKAMA_HEALTH: - if (me->HealthBelowPct(20)) - me->CastSpell(me, SPELL_HEALING_POTION, false); - events.ScheduleEvent(EVENT_AKAMA_HEALTH, 1000); - break; + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_FREE); + }, 15400ms); + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + }, 19440ms); // 4040ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + }, 23080ms); // 3640ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_TIME_HAS_COME); + }, 33840ms); // 10760ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + me->SetSheath(SHEATH_STATE_MELEE); + }, 35210ms); // 1370ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); + }, 37640ms); // 2430ms + } + break; + case POINT_ILLIDAN_DEFEATED_1: + { + me->SetWalk(true); + if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + { + float x, y, z; + me->GetNearPoint(illidan, x, y, z, 15.f, 0, me->GetAngle(illidan)); + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_DEFEATED_2, x, y, z); // Maiev starts Akama's Ending scene + } + } + break; + } + } + else if (type == WAYPOINT_MOTION_TYPE) + { + if (me->GetCurrentWaypointID() == PATH_AKAMA_MINIONS) + if (id == 2) + DoCastSelf(SPELL_AKAMA_TELEPORT); + } + } + + void PathEndReached(uint32 pathId) override + { + switch (pathId) + { + // Talk to Open Door + case PATH_AKAMA_ILLIDARI_COUNCIL_1: + { + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_COUNCIL_1); + }, 200ms); + + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_COUNCIL_2); + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + }, 8000ms); // 7800ms + } + break; + // Reached Door + case PATH_AKAMA_ILLIDARI_COUNCIL_2: + { + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_DOOR); + }, 4600ms); + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_AKAMA_DOOR_FAIL); + }, 8120ms); // 3520ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_ALONE); + }, 17860ms); // 9740ms + me->m_Events.AddEventAtOffset([&] { + me->SummonCreature(NPC_SPIRIT_OF_UDALO, SpiritUdaloPos, TEMPSUMMON_TIMED_DESPAWN, 60000); + me->SummonCreature(NPC_SPIRIT_OF_OLUM, SpiritOlumPos, TEMPSUMMON_TIMED_DESPAWN, 60000); + }, 23930ms); // 6070ms + me->m_Events.AddEventAtOffset([&] { + if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f)) + udalo->AI()->Talk(SAY_UDALO); + }, 25190ms); // 1260ms + me->m_Events.AddEventAtOffset([&] { + if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f)) + olum->AI()->Talk(SAY_OLUM); + }, 31370ms); // 6180ms + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_AKAMA_DOOR_OPEN); + if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f)) + udalo->AI()->DoCastSelf(SPELL_DEATHSWORN_DOOR_OPEN); + if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f)) + olum->AI()->DoCastSelf(SPELL_DEATHSWORN_DOOR_OPEN); + }, 39710ms); // 8340ms + me->m_Events.AddEventAtOffset([&] { + if (Creature* door = me->FindNearestCreature(NPC_ILLIDAN_DOOR_TRIGGER, 15.0f)) + door->AI()->DoCastSelf(SPELL_ARCANE_EXPLOSION_VIS); + }, 50660ms); // 10950ms + me->m_Events.AddEventAtOffset([&] { + me->InterruptNonMeleeSpells(false); + if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f)) + udalo->InterruptNonMeleeSpells(false); + if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f)) + olum->InterruptNonMeleeSpells(false); + instance->SetBossState(DATA_AKAMA_ILLIDAN, NOT_STARTED); + instance->SetBossState(DATA_AKAMA_ILLIDAN, DONE); + }, 50680ms); // 20ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_SALUTE); + }, 56955ms); // 6275ms + me->m_Events.AddEventAtOffset([&] { + me->GetMotionMaster()->MovePath(PATH_AKAMA_ILLIDARI_COUNCIL_3, false); + }, 64030ms); // 7075ms + } + break; + // Talk to Initiate Fight + case PATH_AKAMA_ILLIDARI_COUNCIL_3: + { + Talk(SAY_AKAMA_BETRAYER); + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + } + break; + case PATH_AKAMA_MINIONS: + { + me->SetControlled(true, UNIT_STATE_ROOT); + me->SetReactState(REACT_AGGRESSIVE); + + for (int i = 0; i < 10; ++i) + me->SummonCreature(NPC_ILLIDARI_ELITE, IllidariMinionPos[i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1200); + } + break; + } + } + + void JustReachedHome() override + { + // Minions Event + if (instance->GetBossState(DATA_ILLIDAN_STORMRAGE) == IN_PROGRESS && !instance->GetCreature(DATA_ILLIDAN_STORMRAGE)->HasAura(SPELL_DEATH)) + { + me->SetReactState(REACT_PASSIVE); + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); + + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_AKAMA_MINIONS); + }, 6700ms); + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + }, 9530ms); // 2830ms + me->m_Events.AddEventAtOffset([&] { + me->GetMotionMaster()->MovePath(PATH_AKAMA_MINIONS, false); + }, 14400ms); // 4870ms + } + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + summon->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); + if (summon->GetEntry() == NPC_ILLIDARI_ELITE) + { + me->AddThreat(summon, 1000000.0f); + summon->AddThreat(me, 1000000.0f); + summon->AI()->AttackStart(me); + AttackStart(summon); + } + } + + void SummonedCreatureDies(Creature* summon, Unit*) override + { + summons.Despawn(summon); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetEntry() == NPC_ILLIDARI_ELITE) + me->SummonCreature(NPC_ILLIDARI_ELITE, IllidariMinionPos[urand(0, 9)], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1200); + } + + void JustEngagedWith(Unit* /*who*/) override + { + ScheduleTimedEvent(12s, 18s, [&] { + DoCastVictim(SPELL_CHAIN_LIGHTNING); + }, 16s, 24s); + + ScheduleTimedEvent(5s, 10s, [&] { + if (me->HealthBelowPct(20)) + DoCastSelf(SPELL_HEALING_POTION); + }, 5s, 10s); + } + + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + +private: + InstanceScript* instance; + SummonList summons; +}; + +enum Maiev +{ + SPELL_MAIEV_DOWN = 40409, + SPELL_THROW_DAGGER = 41152, + SPELL_SHADOW_STRIKE = 40685, + SPELL_CAGE_TRAP_SUMMON = 40694, + SPELL_TELEPORT_VISUAL = 41236, + + SAY_MAIEV_SHADOWSONG_TAUNT = 0, + SAY_MAIEV_SHADOWSONG_APPEAR = 1, + SAY_MAIEV_SHADOWSONG_JUSTICE = 2, + SAY_MAIEV_SHADOWSONG_TRAP = 3, + SAY_MAIEV_SHADOWSONG_DOWN = 4, + SAY_MAIEV_SHADOWSONG_FINISHED = 5, + SAY_MAIEV_SHADOWSONG_OUTRO = 6, + SAY_MAIEV_SHADOWSONG_FAREWELL = 7 +}; + +struct npc_maiev_illidan : public ScriptedAI +{ + npc_maiev_illidan(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() override + { + scheduler.CancelAll(); + me->m_Events.KillAllEvents(true); + } + + void DoAction(int32 param) override + { + if (param == ACTION_MAIEV_ENDING) + { + scheduler.CancelAll(); + me->SetReactState(REACT_PASSIVE); + DoStopAttack(); + me->CombatStop(); + me->StopMovingOnCurrentPos(); + me->RemoveAurasDueToSpell(SPELL_MAIEV_DOWN); + me->SetWalk(true); + if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + { + if (!me->IsWithinDist(illidan, 15.f)) + { + float x, y, z; + me->GetNearPoint(illidan, x, y, z, 15.f, 0, me->GetAngle(illidan)); + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_DEFEATED_2, x, y, z); + } } - DoMeleeAttackIfReady(); + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_MAIEV_SHADOWSONG_FINISHED); + }, 1420ms); + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_MAIEV_SHADOWSONG_OUTRO); + }, 28550ms); // 27130ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_MAIEV_SHADOWSONG_FAREWELL); + }, 39510ms); // 10960ms + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_TELEPORT_VISUAL); + }, 41700ms); // 2190ms + me->m_Events.AddEventAtOffset([&] { + if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN)) + akama->AI()->DoAction(ACTION_AKAMA_MAIEV_DESPAWN); + me->DespawnOnEvade(); + }, 43320ms); // 1620ms } - - private: - EventMap events; - SummonList summons; - InstanceScript* instance; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI(creature); } + + void IsSummonedBy(WorldObject* summoner) override + { + me->SetReactState(REACT_PASSIVE); + me->SetFacingToObject(summoner); + + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_MAIEV_SHADOWSONG_APPEAR); + }, 25ms); + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + }, 2415ms); // 2390ms + me->m_Events.AddEventAtOffset([&] { + Talk(SAY_MAIEV_SHADOWSONG_JUSTICE); + }, 14815ms); // 12400ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_YES); + }, 17015ms); // 2200ms + me->m_Events.AddEventAtOffset([&] { + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + }, 19445ms); // 2430ms + me->m_Events.AddEventAtOffset([&] { + me->SetReactState(REACT_AGGRESSIVE); + }, 21920ms); // 2475ms + me->m_Events.AddEventAtOffset([&] { + if (Creature* illidan = me->FindNearestCreature(NPC_ILLIDAN_STORMRAGE, 15.0f)) + AttackStart(illidan); + }, 23095ms); // 1175ms + } + + void JustEngagedWith(Unit* /*who*/) override + { + ScheduleTimedEvent(7s, [&] { + DoCastVictim(SPELL_THROW_DAGGER); + }, 30s); + + ScheduleTimedEvent(22s, [&] { + DoCastVictim(SPELL_SHADOW_STRIKE); + if (roll_chance_i(50)) + Talk(SAY_MAIEV_SHADOWSONG_TAUNT); + }, 30s); + + ScheduleTimedEvent(1200ms, [&] { + if (me->HealthBelowPct(20)) + { + Talk(SAY_MAIEV_SHADOWSONG_DOWN); + DoCastSelf(SPELL_MAIEV_DOWN); + } + }, 1200ms); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_CAGE_TRAP) + { + DoCastSelf(SPELL_CAGE_TRAP_SUMMON, true); + Talk(SAY_MAIEV_SHADOWSONG_TRAP); + } + } + + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } + + void UpdateAI(uint32 diff) override + { + if (!me->HasUnitState(UNIT_STATE_STUNNED)) + scheduler.Update(diff); + + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + +private: + InstanceScript* instance; +}; + +struct npc_parasitic_shadowfiend : public ScriptedAI +{ + npc_parasitic_shadowfiend(Creature* creature) : ScriptedAI(creature) { } + + void IsSummonedBy(WorldObject* /*summoner*/) override + { + // Simulate blizz-like AI delay to avoid extreme overpopulation of adds + me->SetReactState(REACT_DEFENSIVE); + me->m_Events.AddEventAtOffset([&] { + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + }, 2400ms); + } +}; + +enum WarbladeTear +{ + SOUND_WARBLADE_SPAWN = 11689, + + SPELL_SUMMON_TEAR = 39855, + SPELL_TEAR_CHANNEL = 39857, + + MODEL_INVISIBLE = 11686 +}; + +struct npc_blade_of_azzinoth : public ScriptedAI +{ + npc_blade_of_azzinoth(Creature* creature) : ScriptedAI(creature) { } + + void IsSummonedBy(WorldObject* /*summoner*/) override + { + me->SetReactState(REACT_PASSIVE); + me->PlayRadiusSound(SOUND_WARBLADE_SPAWN, 150.f); + me->SetInCombatWithZone(); + + me->m_Events.AddEventAtOffset([&] { + DoCastSelf(SPELL_SUMMON_TEAR); + }, 2700ms); + } + + void JustSummoned(Creature* summon) override + { + DoCast(summon, SPELL_TEAR_CHANNEL, true); + } + + void DoAction(int32 param) override + { + if (param == ACTION_RETURN_BLADE) + { + if (Creature* illidan = me->FindNearestCreature(NPC_ILLIDAN_STORMRAGE, 100.0f)) + DoCast(illidan, SPELL_GLAIVE_RETURNS, true); + + me->m_Events.AddEventAtOffset([&] { + me->SetDisplayId(MODEL_INVISIBLE); + }, 10ms); + me->m_Events.AddEventAtOffset([&] { + me->DespawnOrUnsummon(); + }, 2020ms); + } + } +}; + +enum FlameAzzinoth +{ + NPC_BLAZE = 23259, + + SPELL_BLAZE_EFFECT = 40610, + SPELL_FLAME_BLAST = 40631, + SPELL_CHARGE = 42003, + SPELL_UNCAGED_WRATH = 39869, + SPELL_BLAZE = 40637 +}; + +struct npc_flame_of_azzinoth : public ScriptedAI +{ + npc_flame_of_azzinoth(Creature* creature) : ScriptedAI(creature), _bladeSummoner(nullptr) { } + + void IsSummonedBy(WorldObject* /*summoner*/) override + { + // Flame is set to be Illidan's summon, so we check for nearest blade + _bladeSummoner = me->FindNearestCreature(NPC_BLADE_OF_AZZINOTH, 15.0f); + + me->SetCorpseDelay(2); + me->SetReactState(REACT_DEFENSIVE); + me->m_Events.AddEventAtOffset([&] { + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + }, 2020ms); + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_BLAZE) + { + summon->SetReactState(REACT_PASSIVE); + summon->AI()->DoCastSelf(SPELL_BLAZE_EFFECT, true); + summon->SetCombatMovement(false); + } + } + + void JustEngagedWith(Unit* /*who*/) override + { + ScheduleTimedEvent(10s, [&] { + if (_bladeSummoner) + if (Unit* target = _bladeSummoner->AI()->SelectTarget(SelectTargetMethod::Random, 0, 30.f, true)) + DoCast(target, SPELL_CHARGE); + }, 5s, 20s); + + ScheduleTimedEvent(10s, 20s, [&] { + DoCastVictim(SPELL_FLAME_BLAST); + + me->m_Events.AddEventAtOffset([&] { + if (Unit* target = me->GetVictim()) + target->CastSpell(target, SPELL_BLAZE, true); + }, 1s); + }, 15s, 20s); + } + +private: + Creature* _bladeSummoner; }; class spell_illidan_draw_soul : public SpellScript @@ -1144,7 +1430,7 @@ class spell_illidan_tear_of_azzinoth_summon_channel_aura : public AuraScript PreventDefaultAction(); if (Unit* caster = GetCaster()) { - if (GetTarget()->GetDistance2d(caster) > 25.0f) + if (GetTarget()->GetDistance2d(caster) > 30.0f) { SetDuration(0); GetTarget()->CastSpell(GetTarget(), SPELL_UNCAGED_WRATH, true); @@ -1323,7 +1609,6 @@ class spell_illidan_cage_trap : public SpellScript if (GetCaster()->GetExactDist2d(target) < 4.0f) { target->AI()->DoAction(ACTION_ILLIDAN_CAGED); - target->CastSpell(target, SPELL_CAGE_TRAP, true); GetCaster()->ToCreature()->DespawnOrUnsummon(1); if (GameObject* gobject = GetCaster()->FindNearestGameObject(GO_CAGE_TRAP, 10.0f)) gobject->SetLootState(GO_JUST_DEACTIVATED); @@ -1363,8 +1648,12 @@ class spell_illidan_cage_trap_stun_aura : public AuraScript void AddSC_boss_illidan() { - new boss_illidan_stormrage(); - new npc_akama_illidan(); + RegisterBlackTempleCreatureAI(boss_illidan_stormrage); + RegisterBlackTempleCreatureAI(npc_maiev_illidan); + RegisterBlackTempleCreatureAI(npc_akama_illidan); + RegisterBlackTempleCreatureAI(npc_parasitic_shadowfiend); + RegisterBlackTempleCreatureAI(npc_blade_of_azzinoth); + RegisterBlackTempleCreatureAI(npc_flame_of_azzinoth); RegisterSpellScript(spell_illidan_draw_soul); RegisterSpellScript(spell_illidan_parasitic_shadowfiend_aura); RegisterSpellAndAuraScriptPair(spell_illidan_parasitic_shadowfiend_trigger, spell_illidan_parasitic_shadowfiend_trigger_aura); From d1f77ae60caf93e9bf3180cad8c9a683169b1f70 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 5 Sep 2024 13:17:25 +0000 Subject: [PATCH 03/10] chore(DB): import pending files Referenced commit(s): fa490c21e9262152c59c0ac81f9e8a500f047582 --- .../rev_1722993051676997100.sql => db_world/2024_09_05_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1722993051676997100.sql => db_world/2024_09_05_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1722993051676997100.sql b/data/sql/updates/db_world/2024_09_05_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1722993051676997100.sql rename to data/sql/updates/db_world/2024_09_05_00.sql index f7aece85e..bb3af2e66 100644 --- a/data/sql/updates/pending_db_world/rev_1722993051676997100.sql +++ b/data/sql/updates/db_world/2024_09_05_00.sql @@ -1,3 +1,4 @@ +-- DB update 2024_09_03_01 -> 2024_09_05_00 DELETE FROM `creature_text` WHERE (`CreatureID` = 23089); INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES (23089, 0, 0, 'This door is all that stands between us and the Betrayer. Stand aside, friends.', 12, 0, 100, 1, 0, 0, 21563, 0, 'SAY_AKAMA_DOOR'), From 9dae87595dc2f0f14d06ce7984250535630f54df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=B9=BF?= <18535853+PkllonG@users.noreply.github.com> Date: Thu, 5 Sep 2024 22:56:17 +0800 Subject: [PATCH 04/10] fix(Core/Creature): Creature Scale. (#19722) Update Creature.cpp --- src/server/game/Entities/Creature/Creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d9e0dc574..7fe1b532b 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3480,7 +3480,7 @@ void Creature::UpdateMovementFlags() float Creature::GetNativeObjectScale() const { - return GetCreatureTemplate()->scale; + return ObjectMgr::ChooseDisplayId(GetCreatureTemplate())->DisplayScale; } void Creature::SetObjectScale(float scale) From 6e5cd04591f3e5ef9034a49bcb45562744dfbbaa Mon Sep 17 00:00:00 2001 From: Ben Carter <110695027+ben-of-codecraft@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:37:00 -0400 Subject: [PATCH 05/10] fix(docker): Modules SQL do not get updated or populated for docker set ups (#19870) * Added modules to dbimport so sql cab accessed by loader * updated dbimport tools config option to enable module sql to be updated and populated * Updated casting around GetOption and changed default to all --- apps/docker/Dockerfile | 3 +++ src/tools/CMakeLists.txt | 4 ++++ src/tools/dbimport/Main.cpp | 11 +++++++++-- src/tools/dbimport/dbimport.conf.dist | 11 +++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apps/docker/Dockerfile b/apps/docker/Dockerfile index e62b6b354..e003ae875 100644 --- a/apps/docker/Dockerfile +++ b/apps/docker/Dockerfile @@ -199,6 +199,9 @@ ENV ACORE_COMPONENT=dbimport COPY --chown=$DOCKER_USER:$DOCKER_USER \ data data +COPY --chown=$DOCKER_USER:$DOCKER_USER \ + modules modules + COPY --chown=$DOCKER_USER:$DOCKER_USER\ --from=build \ /azerothcore/env/dist/bin/dbimport /azerothcore/env/dist/bin/dbimport diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 1edce4cbf..9efea7aae 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -122,6 +122,8 @@ foreach(TOOL_NAME ${TOOLS_BUILD_LIST}) PUBLIC database PRIVATE + modules + scripts acore-core-interface) # Install config @@ -153,6 +155,8 @@ foreach(TOOL_NAME ${TOOLS_BUILD_LIST}) target_include_directories(${TOOL_PROJECT_NAME} PUBLIC ${TOOL_PUBLIC_INCLUDES} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/modules PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/${TOOL_NAME}) diff --git a/src/tools/dbimport/Main.cpp b/src/tools/dbimport/Main.cpp index 0ab64be72..87371f62a 100644 --- a/src/tools/dbimport/Main.cpp +++ b/src/tools/dbimport/Main.cpp @@ -103,8 +103,15 @@ bool StartDB() { MySQL::Library_Init(); - // Load databases - DatabaseLoader loader("dbimport"); + // Load modules conditionally based on what modules are allowed to auto-update or none + std::string modules = sConfigMgr->GetOption("Updates.AllowedModules", "all"); + LOG_INFO("dbimport", "Loading modules: {}", modules.empty() ? "none" : modules); + + DatabaseLoader loader = + modules.empty() ? DatabaseLoader("dbimport") : + (modules == "all") ? DatabaseLoader("dbimport", DatabaseLoader::DATABASE_NONE, AC_MODULES_LIST) : + DatabaseLoader("dbimport", DatabaseLoader::DATABASE_NONE, modules); + loader .AddDatabase(LoginDatabase, "Login") .AddDatabase(CharacterDatabase, "Character") diff --git a/src/tools/dbimport/dbimport.conf.dist b/src/tools/dbimport/dbimport.conf.dist index 656bdd35b..37ea25e5e 100644 --- a/src/tools/dbimport/dbimport.conf.dist +++ b/src/tools/dbimport/dbimport.conf.dist @@ -159,6 +159,17 @@ CharacterDatabase.SynchThreads = 1 Updates.EnableDatabases = 7 +# +# Updates.AllowedModules +# Description: A list of modules that are allowed to be updated automatically by the DBImport tool. +# If the list is empty, no modules are allowed to automatically update. (current behavior) +# Default: "" - (No modules are allowed) +# +# Example: "mod_name,mod_name2,mod_name3" (selected modules) +# "all" - (All modules are allowed) +# +Updates.AllowedModules = "all" + # # Updates.AutoSetup # Description: Auto populate empty databases. From 632d7f5f9ec1b44c3288168952a68533a6791686 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:02:53 -0400 Subject: [PATCH 06/10] fix(Core/Unit): Add leash extension with damage dealt. (#19880) Init. --- src/server/game/Entities/Unit/Unit.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 8d843c062..df843dabe 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1041,6 +1041,10 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage if (!victim->IsPlayer()) { + /// @fix: Hack to avoid premature leashing + if (damagetype != DOT && damage > 0 && !victim->GetOwnerGUID().IsPlayer() && (!spellProto || !spellProto->HasAura(SPELL_AURA_DAMAGE_SHIELD))) + victim->ToCreature()->UpdateLeashExtensionTime(); + if (attacker) { if (spellProto && victim->CanHaveThreatList() && !victim->HasUnitState(UNIT_STATE_EVADE) && !victim->IsInCombatWith(attacker)) From 89b08dd5dc112be3defbfebd943369dd8ffb60b7 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sat, 7 Sep 2024 00:43:12 +0200 Subject: [PATCH 07/10] fix(Scripts/Commands): .gps formatting transport offset (#19888) fix transport offset --- src/server/scripts/Commands/cs_misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 5d6898979..a9e1894d3 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -627,7 +627,7 @@ public: if (object->GetTransport()) { - handler->PSendSysMessage("Transport offset: %.2f, %.2f, %.2f, %.2f", object->m_movementInfo.transport.pos.GetPositionX(), object->m_movementInfo.transport.pos.GetPositionY(), object->m_movementInfo.transport.pos.GetPositionZ(), object->m_movementInfo.transport.pos.GetOrientation()); + handler->PSendSysMessage("Transport offset: {0:.2f}, {0:.2f}, {0:.2f}, {0:.2f}", object->m_movementInfo.transport.pos.GetPositionX(), object->m_movementInfo.transport.pos.GetPositionY(), object->m_movementInfo.transport.pos.GetPositionZ(), object->m_movementInfo.transport.pos.GetOrientation()); } return true; From 78d40e4078e131677327420c7151876ebdafff89 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 6 Sep 2024 20:41:36 -0300 Subject: [PATCH 08/10] =?UTF-8?q?fix(Scripts/BlackTemple):=20Remove=20Shea?= =?UTF-8?q?r=20from=20Illidan=20following=20wrath=20p=E2=80=A6=20(#19881)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(Scripts/BlackTemple): Remove Shear from Illidan following wrath patch changes --- src/server/scripts/Outland/BlackTemple/boss_illidan.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index d1e26e712..a07fc810d 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -62,7 +62,6 @@ enum Spells SPELL_PARASITIC_SHADOWFIEND = 41917, SPELL_PARASITIC_SHADOWFIEND_TRIGGER = 41914, SPELL_SUMMON_PARASITIC_SHADOWFIENDS = 41915, - SPELL_SHEAR = 41032, // Phase 2 SPELL_THROW_GLAIVE = 39635, @@ -456,10 +455,6 @@ struct boss_illidan_stormrage : public BossAI DoCastVictim(SPELL_FLAME_CRASH); }, 26s, 35s); - ScheduleTimedEvent(10s, [&] { - DoCastVictim(SPELL_SHEAR); - }, 12s, 15s); - ScheduleTimedEvent(32s, [&] { DoCastVictim(SPELL_DRAW_SOUL); }, 32s); @@ -528,10 +523,6 @@ struct boss_illidan_stormrage : public BossAI DoCastVictim(SPELL_FLAME_CRASH); }, 26s, 35s); - ScheduleTimedEvent(10s, [&] { - DoCastVictim(SPELL_SHEAR); - }, 12s, 15s); - ScheduleTimedEvent(32s, [&] { DoCastVictim(SPELL_DRAW_SOUL); }, 32s); From dd2167b00b138e97ff71cf04b54e2de147bb7089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=B9=BF?= <18535853+PkllonG@users.noreply.github.com> Date: Sat, 7 Sep 2024 12:03:19 +0800 Subject: [PATCH 09/10] fix(Core/AI): Creature SetInCombatState Leashing. (#19889) * Update Unit.cpp * Update pit_of_saron.cpp * Update Unit.cpp --- src/server/game/Entities/Unit/Unit.cpp | 4 ++-- .../scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index df843dabe..1f0dd7832 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -13541,8 +13541,6 @@ void Unit::SetInCombatWith(Unit* enemy, uint32 duration) return; } } - if (Creature* pCreature = ToCreature()) - pCreature->UpdateLeashExtensionTime(); SetInCombatState(false, enemy, duration); } @@ -13701,6 +13699,8 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy, uint32 duration) if (enemy) { + creature->UpdateLeashExtensionTime(); + if (IsAIEnabled) creature->AI()->JustEngagedWith(enemy); diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp index 515476bfd..578a4e3af 100644 --- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp @@ -1130,7 +1130,7 @@ public: me->LoadCreaturesAddon(true); me->SetLootRecipient(nullptr); me->ResetPlayerDamageReq(); - me->UpdateLeashExtensionTime(); + me->ClearLastLeashExtensionTimePtr(); } }; From e7448f29862e67b25bffbbd8a011c4f0d6b53cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=B9=BF?= <18535853+PkllonG@users.noreply.github.com> Date: Sat, 7 Sep 2024 12:03:41 +0800 Subject: [PATCH 10/10] fix(Scripts/Commands): .gps formatting transport offset (#19890) * Update cs_misc.cpp * Update PlayerUpdates.cpp --- src/server/game/Entities/Player/PlayerUpdates.cpp | 2 +- src/server/scripts/Commands/cs_misc.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index e74493499..594100616 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -265,7 +265,7 @@ void Player::Update(uint32 p_time) if (!IsPositionValid()) // pussywizard: will crash below at eg. GetZoneAndAreaId { - LOG_INFO("misc", "Player::Update - invalid position ({0:.1f}, {0:.1f}, {0:.1f})! Map: {}, MapId: {}, {}", + LOG_INFO("misc", "Player::Update - invalid position ({:0.1f}, {:0.1f}, {:0.1f})! Map: {}, MapId: {}, {}", GetPositionX(), GetPositionY(), GetPositionZ(), (FindMap() ? FindMap()->GetId() : 0), GetMapId(), GetGUID().ToString()); GetSession()->KickPlayer("Invalid position"); return; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index a9e1894d3..db29dcb36 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -627,7 +627,7 @@ public: if (object->GetTransport()) { - handler->PSendSysMessage("Transport offset: {0:.2f}, {0:.2f}, {0:.2f}, {0:.2f}", object->m_movementInfo.transport.pos.GetPositionX(), object->m_movementInfo.transport.pos.GetPositionY(), object->m_movementInfo.transport.pos.GetPositionZ(), object->m_movementInfo.transport.pos.GetOrientation()); + handler->PSendSysMessage("Transport offset: {:0.2f}, {:0.2f}, {:0.2f}, {:0.2f}", object->m_movementInfo.transport.pos.GetPositionX(), object->m_movementInfo.transport.pos.GetPositionY(), object->m_movementInfo.transport.pos.GetPositionZ(), object->m_movementInfo.transport.pos.GetOrientation()); } return true;