diff options
author | Manuel <manue.l@live.com.ar> | 2011-03-26 21:36:06 -0300 |
---|---|---|
committer | Manuel <manue.l@live.com.ar> | 2011-03-26 21:36:06 -0300 |
commit | 21a2c56af32f006664d3e982e34b181848b49635 (patch) | |
tree | 801a21ca615c9e8e8890f914cad8460ad0c588f3 | |
parent | 75bf08c08144892ff6c7351d2602af2046a47db7 (diff) |
Scripts/Eye of Eternity: Implemented script for Malygos encounter, please report any issue you find.
Thanks for the help to Machiavelli and Shauren.
Signed-off-by: Manuel <manue.l@live.com.ar>
15 files changed, 1489 insertions, 121 deletions
diff --git a/sql/updates/world/2011_03_26_0_creature_text.sql b/sql/updates/world/2011_03_26_0_creature_text.sql new file mode 100644 index 00000000000..2268a00e9e6 --- /dev/null +++ b/sql/updates/world/2011_03_26_0_creature_text.sql @@ -0,0 +1,30 @@ + +DELETE FROM `creature_text` WHERE `entry` IN (32295,28859); +INSERT INTO `creature_text` (entry,groupid,id,text,type,language,sound,comment) VALUE +(28859,0,0,'My patience has reached its limit, I will be rid of you!',1,0,14517,'Malygos - Aggro (Phase 1)'), +(28859,1,0,'Your stupidity has finally caught up to you',1,0,14519,'Malygos - Killed Player (1) (Phase 1)'), +(28859,1,1,'More artifacts to confiscate...',1,0,14520,'Malygos - Killed Player (2) (Phase 1)'), +(28859,1,2,'<Laughs> How very... naïve...',1,0,14521,'Malygos - Killed Player (3) (Phase 1)'), +(28859,2,0,'I had hoped to end your lives quickly, but you have proven more... resilient then I had anticipated. Nonetheless, your efforts are in vain, it is you reckless, careless mortals who are to blame for this war! I do what I must... And if it means your... extinction... THEN SO BE IT',1,0,14522,'Malygos - End Phase One'), +(28859,3,0,'Few have experienced the pain I will now inflict upon you!',1,0,14523,'Malygos - Aggro (Phase 2)'), +(28859,4,0,'I will teach you IGNORANT children just how little you know of magic...',1,0,14524,'Malygos - Anti-Magic Shell'), +(28859,5,0,'Watch helplessly as your hopes are swept away...',1,0,14525,'Malygos - Magic Blast'), +(28859,6,0,'Your energy will be put to good use!',1,0,14526,'Malygos - Killed Player 1 (Phase 2)'), +(28859,6,1,'I am the spell-weaver! My power is infinite!',1,0,14527,'Malygos - Killed Player 2 (Phase 2)'), +(28859,6,2,'Your spirit will linger here forever!',1,0,14528,'Malygos - Killed Player 3 (Phase 2)'), +(28859,7,0,'ENOUGH! If you intend to reclaim Azeroth''s magic, then you shall have it...',1,0,14529,'Malygos - End Phase 2'), +(28859,8,0,'Now your benefactors make their appearance... But they are too late. The powers contained here are sufficient to destroy the world ten times over! What do you think they will do to you?',1,0,14530,'Intro Phase 3'), +(28859,9,0,'SUBMIT!',1,0,14531,'Malygos - Aggro (Phase 3)'), +(28859,10,0,'The powers at work here exceed anything you could possibly imagine!',1,0,14532,'Malygos - Surge of Power'), +(28859,11,0,'I AM UNSTOPPABLE!',1,0,14533,'Malygos - Buffed by a spark'), +(28859,12,0,'Alexstrasza! Another of your brood falls!',1,0,14534,'Malygos - Killed Player 1 (Phase 3)'), +(28859,12,1,'Little more then gnats!',1,0,14535,'Malygos - Killed Player 2 (Phase 3)'), +(28859,12,2,'Your red allies will share your fate...',1,0,14536,'Malygos - Killed Player 3 (Phase 3)'), +(28859,13,0,'Still standing? Not for long...',1,0,14537,'Malygos - Spell Casting 1(Phase 3)'), +(28859,13,1,'Your cause is lost',1,0,14538,'Malygos - Spell Casting 2 (Phase 3)'), +(28859,13,2,'Your fragile mind will be shattered!',1,0,14539,'Malygos - Spell Casting 3 (Phase 3)'), +(28859,14,0,'Unthinkable! The mortals will destroy... everything... my sister... what have you...',1,0,0,'Malygos - Death'), +(32295,0,0,'I did what I had to, brother. You gave me no alternative.',1,0,0,'Alexstrasza - Yell One'), +(32295,1,0,'And so ends the Nexus War.',1,0,0,'Alexstrasza - Yell Two'), +(32295,2,0,'This resolution pains me deeply, but the destruction, the monumental loss of life had to end. Regardless of Malygos'' recent transgressions, I will mourn his loss. He was once a guardian, a protector. This day, one of the world''s mightiest has fallen.',1,0,0,'Alexstrasza - Yell Three'), +(32295,3,0,'The red dragonflight will take on the burden of mending the devastation wrought on Azeroth. Return home to your people and rest. Tomorrow will bring you new challenges, and you must be ready to face them. Life... goes on.',1,0,0,'Alexstrasza - Yell Four'); diff --git a/sql/updates/world/2011_03_26_0_world_conditions.sql b/sql/updates/world/2011_03_26_0_world_conditions.sql new file mode 100644 index 00000000000..f276a0f24b2 --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_conditions.sql @@ -0,0 +1,8 @@ +-- conditions for spells used by Malygos +DELETE FROM `conditions` WHERE `SourceGroup`=0 AND `SourceEntry` IN (56263,55853,56505,56429,56152) AND `ConditionTypeOrReference`=18; +INSERT INTO `conditions` (SourceTypeOrReferenceId,ConditionTypeOrReference,SourceGroup,SourceEntry,ConditionValue1,ConditionValue2) VALUES +(13,18,0,55853,1,30090), +(13,18,0,56263,1,30090), +(13,18,0,56505,1,30334), +(13,18,0,56429,1,30334), +(13,18,0,56152,1,28859); diff --git a/sql/updates/world/2011_03_26_0_world_creature_template.sql b/sql/updates/world/2011_03_26_0_world_creature_template.sql new file mode 100644 index 00000000000..dd2a90833b1 --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_creature_template.sql @@ -0,0 +1,24 @@ +-- vortex trigger +UPDATE `creature_template` SET `modelid1`=11686,`modelid2`=169,`InhabitType`=5,`unit_flags`=0x02000000,`VehicleId`=214,`flags_extra`=130 WHERE `entry`=30090; +UPDATE `creature` SET `modelid`=0 WHERE `id`=30090; + +-- Portal (Malygos) +UPDATE `creature_template` SET `InhabitType`=5 WHERE `entry`=30118; +UPDATE `creature_template` SET `speed_walk`=2.4,`speed_run`=0.857142857 WHERE `entry`=30084; + +-- Hover Disk +UPDATE `creature_template` SET `VehicleId`=224,`faction_A`=35,`faction_H`=35,`InhabitType`=5 WHERE `entry` IN (30234,30248); + +-- Surge of Power +UPDATE `creature_template` SET flags_extra=130 WHERE entry=30334; + +-- Wyrmrest Skytalon (Player's Mount) +UPDATE `creature_template` SET `InhabitType`=5 WHERE `entry`=30161; + +-- Alexstrasza the Life-Binder +UPDATE `creature_template` SET `InhabitType`=5 WHERE `entry`=32295; + +-- static Field +UPDATE `creature_template` SET `flags_extra`=130,`InhabitType`=5 WHERE `entry`=30592; + +UPDATE `creature_template` SET `VehicleId`=220,`spell1`=56091,`spell2`=56092,`spell3`=57090,`spell4`=57143,`spell5`=57108,`spell6`=57092,`spell7`=60534 WHERE `entry`=30161;
\ No newline at end of file diff --git a/sql/updates/world/2011_03_26_0_world_creature_template_addon.sql b/sql/updates/world/2011_03_26_0_world_creature_template_addon.sql new file mode 100644 index 00000000000..79a24670a8d --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_creature_template_addon.sql @@ -0,0 +1,2 @@ +DELETE FROM `creature_template_addon` WHERE `entry`=30592; +INSERT INTO `creature_template_addon` (entry,auras) VALUES (30592,'57428 0');
\ No newline at end of file diff --git a/sql/updates/world/2011_03_26_0_world_gameobject.sql b/sql/updates/world/2011_03_26_0_world_gameobject.sql new file mode 100644 index 00000000000..1d587b199a8 --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_gameobject.sql @@ -0,0 +1,9 @@ +-- Spawning Focusing Iris +DELETE FROM `gameobject` WHERE `id`=193958; +INSERT INTO `gameobject` (guid,id,map,spawnMask,phaseMask,position_x,position_y,position_z,orientation,rotation0,rotation1,rotation2,rotation3,spawntimesecs,animprogress,state) VALUES +(151791,193958,616,1,1,754.2546,1301.71973,266.170319,-1.60570168,0,0,0,0,120,0,1); +-- Spawning chests +DELETE FROM `gameobject` WHERE `id` IN (193967,193905); +INSERT INTO `gameobject` (guid,id,map,spawnMask,phaseMask,position_x,position_y,position_z,orientation,spawntimesecs,animprogress,state) VALUES +(151792,193905,616,1,1,764.56,1284.63,269,1.82,-604800,100,1), +(151793,193967,616,2,1,764.56,1284.63,269,1.82,-604800,100,1);
\ No newline at end of file diff --git a/sql/updates/world/2011_03_26_0_world_npc_spellclick_spells.sql b/sql/updates/world/2011_03_26_0_world_npc_spellclick_spells.sql new file mode 100644 index 00000000000..61ace8c0877 --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_npc_spellclick_spells.sql @@ -0,0 +1,5 @@ +-- Hover Disk +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (30248,30234); +INSERT INTO `npc_spellclick_spells` (npc_entry,spell_id,quest_start,quest_end,cast_flags) VALUES +(30234,61421,0,0,0), +(30248,61421,0,0,0); diff --git a/sql/updates/world/2011_03_26_0_world_scriptname.sql b/sql/updates/world/2011_03_26_0_world_scriptname.sql new file mode 100644 index 00000000000..c849a870155 --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_scriptname.sql @@ -0,0 +1,15 @@ +DELETE FROM `instance_template` WHERE `map`=616; +INSERT INTO `instance_template` (map,parent,script) VALUES (616,571,'instance_eye_of_eternity'); + +UPDATE `creature_template` SET `ScriptName`='boss_malygos' WHERE `entry`=28859; +UPDATE `creature_template` SET `ScriptName`='npc_power_spark' WHERE `entry`=30084; +UPDATE `creature_template` SET `ScriptName`='npc_portal_eoe' WHERE `entry`=30118; +UPDATE `creature_template` SET `ScriptName`='npc_hover_disk' WHERE `entry` IN (30234,30248); +UPDATE `creature_template` SET `ScriptName`='npc_arcane_overload' WHERE `entry`=30282; +UPDATE `creature_template` SET `ScriptName`='npc_wyrmrest_skytalon' WHERE `entry`=30161; +UPDATE `creature_template` SET `ScriptName`='npc_alexstrasza_eoe' WHERE `entry`=32295; + +DELETE FROM `spell_script_names` WHERE `spell_id`=56105; +INSERT INTO `spell_script_names` VALUES (56105,'spell_malygos_vortex_dummy'); +DELETE FROM `spell_script_names` WHERE `spell_id`=55873; +INSERT INTO `spell_script_names` VALUES (55873,'spell_malygos_vortex_visual');
\ No newline at end of file diff --git a/sql/updates/world/2011_03_26_0_world_smart_scripts.sql.txt b/sql/updates/world/2011_03_26_0_world_smart_scripts.sql.txt new file mode 100644 index 00000000000..3f5431cd39a --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_smart_scripts.sql.txt @@ -0,0 +1,11 @@ +-- Scripts for Nexus Lord and Scion of Eternity +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry` IN (30249,30245); +DELETE FROM `smart_scripts` WHERE (`entryorguid`=30249 AND `source_type`=0); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(30249, 0, 0, 0, 0, 0, 100, 6, 3000, 5000, 4000, 6000, 11, 56397, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 'Scion of Eternity - Cast Arcane Barrage (Random)'), +(30249, 0, 1, 0, 7, 0, 100, 6, 1, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Scion of Eternity - Despawn in EvadeMode'); +DELETE FROM `smart_scripts` WHERE (`entryorguid`=30245 AND `source_type`=0); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(30245, 0, 0, 0, 0, 0, 100, 6, 7000, 10000, 10000, 15000, 11, 57060, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Nexus Lord - Cast Haste (Self)'), +(30245, 0, 1, 0, 0, 0, 100, 6, 5000, 8000, 9000, 12000, 11, 57058, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Nexus Lord - Cast Arcane Shock (Random)'), +(30245, 0, 2, 0, 7, 0, 100, 6, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Nexus Lord - Despawn in EvadeMode'); diff --git a/sql/updates/world/2011_03_26_0_world_vehicle_template_accessory.sql b/sql/updates/world/2011_03_26_0_world_vehicle_template_accessory.sql new file mode 100644 index 00000000000..a5b0963181a --- /dev/null +++ b/sql/updates/world/2011_03_26_0_world_vehicle_template_accessory.sql @@ -0,0 +1,5 @@ +-- Hover Disk +DELETE FROM `vehicle_template_accessory` WHERE `entry` IN (30234,30248); +INSERT INTO `vehicle_template_accessory` (entry,accessory_entry,seat_id,minion,description) VALUES +(30234,30245,0,0,'Hover Disk - Nexus Lord'), +(30248,30249,0,0,'Hover Disk - Scion of Eternity');
\ No newline at end of file diff --git a/sql/updates/world/2011_03_26_3_world_spell_linked_spell.sql b/sql/updates/world/2011_03_26_3_world_spell_linked_spell.sql new file mode 100644 index 00000000000..caa8e51b9b1 --- /dev/null +++ b/sql/updates/world/2011_03_26_3_world_spell_linked_spell.sql @@ -0,0 +1,3 @@ +-- Spell Arcane Barrage +DELETE FROM `spell_linked_spell` WHERE `spell_trigger`=56397; +INSERT INTO `spell_linked_spell` (spell_trigger,spell_effect,type,comment) VALUES (56397,63934,1,'Arcane Barrage - Arcane Barrage'); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fad6eb8a706..eff6f5b577d 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15242,6 +15242,9 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss) sScriptMgr->OnPlayerKilledByCreature(killer, killed); } } + + if (Vehicle* veh = pVictim->GetVehicle()) + pVictim->ExitVehicle(); } void Unit::SetControlled(bool apply, UnitState state) diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 1076efe82d8..9069bf401fb 100755 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -400,6 +400,8 @@ void AddSC_boss_varos(); void AddSC_boss_eregos(); void AddSC_instance_oculus(); void AddSC_oculus(); +void AddSC_boss_malygos(); // The Nexus: Eye of Eternity +void AddSC_instance_eye_of_eternity(); void AddSC_boss_sartharion(); //Obsidian Sanctum void AddSC_instance_obsidian_sanctum(); void AddSC_boss_bjarngrim(); //Ulduar Halls of Lightning @@ -1091,6 +1093,8 @@ void AddNorthrendScripts() AddSC_boss_eregos(); AddSC_instance_oculus(); AddSC_oculus(); + AddSC_boss_malygos(); // The Nexus: Eye of Eternity + AddSC_instance_eye_of_eternity(); AddSC_boss_sartharion(); //Obsidian Sanctum AddSC_instance_obsidian_sanctum(); AddSC_boss_bjarngrim(); //Ulduar Halls of Lightning diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index fa5d138a428..6c73120c0aa 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -17,175 +17,1120 @@ /* Script Data Start SDName: Boss malygos -SDAuthor: LordVanMartin -SD%Complete: -SDComment: -SDCategory: Script Data End */ +// TO-DOs: +// Implement a better pathing for Malygos. +// Find sniffed spawn position for chest +// Implement a better way to disappear the gameobjects +// Implement achievements +// Implement scaling for player's drakes (should be done with aura 66668, it is casted - but it is not working as it should) +// Remove hack that re-adds targets to the aggro list after they enter to a vehicle when it works as expected +// Improve whatever can be improved :) + #include "ScriptPCH.h" +#include "eye_of_eternity.h" +#include "ScriptedEscortAI.h" -//Spells -#define SPELL_ARCANE_BREATH_N 56272 -#define SPELL_ARCANE_BREATH_H 60072 -#define SPELL_ARCANE_PULSE 57432 -#define SPELL_ARCANE_STORM_1 57459 -#define SPELL_ARCANE_STORM_2 61693 -#define SPELL_ARCANE_STORM_3 61694 -#define SPELL_STATIC_FIELD 57430 -#define SPELL_SURGE_OF_POWER_1 56505 -#define SPELL_SURGE_OF_POWER_2 57407 -#define SPELL_SURGE_OF_POWER_3 60936 -#define SPELL_VORTEX 56105 - -//Dragon "mounts" spells in Phase3 -//they use Rugelike energy -#define SPELL_DMOUNT_FLAME_SPIKE 56091 //maybe not accurate -#define SPELL_DMOUNT_ENGULF_IN_FLAMES 61621 -#define SPELL_DMOUNT_REVIVIFY 57090 -#define SPELL_DMOUNT_LIFE_BURST 57143 -#define SPELL_DMOUNT_FLAME_SHIELD 57108 -//#define SPELL_DMOUNT_UNKNOWN XYZ //Increases your drake's flight speed by 500%. - -//not in db -//Yell -//-->Other -#define SAY_ANTI_MAGIC_SHELL -1616000 -#define SAY_BREATH_ATTACK -1616001 -#define SAY_HIGH_DAMAGE_MODE -1616002 -#define SAY_MAGIC_BLAST -1616003 -//--> Generic Spells -#define SAY_GENERIC_SPELL_1 -1616004 -#define SAY_GENERIC_SPELL_2 -1616005 -#define SAY_GENERIC_SPELL_3 -1616006 -#define SAY_DEATH -1616007 -//--> Prefight -#define SAY_PREFIGHT_1 -1616008 -#define SAY_PREFIGHT_2 -1616009 -#define SAY_PREFIGHT_3 -1616010 -#define SAY_PREFIGHT_4 -1616011 -#define SAY_PREFIGHT_5 -1616012 -//--> Phase1 -#define SAY_PHASE1_AGGRO -1616013 -#define SAY_PHASE1_END -1616014 -#define SAY_PHASE1_SLAY_1 -1616015 -#define SAY_PHASE1_SLAY_2 -1616016 -#define SAY_PHASE1_SLAY_3 -1616017 - -//--> Phase2 at 50% HP, - -/*Malygos himself is not targetable during this phase, it will end when the adds he spawns are all killed. However, he does continue to play a part in the encounter. -During this phase he drops anti-magic zones onto the ground the raid MUST stand inside of, it reduces magical damage taken by 50%. They shrink over time, so it's important that your raid moves to each new one he drops. -Throughout the phase, he will deep breath doing ~4k damage per second, unless you are standing inside of the anti-magic zone. -The way the fight works during this phase is there are NPCs riding around on disks in the room. There are two types of mobs, Lords and Scions. -The Lords will move down onto the group, and need to be tanked (They will one-shot a non-tank). After they die, they drop a disk that a raid member can mount onto, which allows them to fly, to attack the Scions that do not come down to the ground. -It is recommended to let melee take the first disks, then ranged. As those mobs die, they also drop disks, which allows the rest of your dps to get onto them. -The Scions will continually cast Arcane Blast on random targets on the floor, which is mitigated by the anti-magic zones. While mounted on a disk, you will not take damage. -After all of the NPCs riding on the disks die, the players on the disks need to dismount as Phase 3 is about to begin.*/ - -//not in db -#define SAY_PHASE2_AGGRO -1616018 -#define SAY_PHASE2_END -1616019 -#define SAY_PHASE2_SLAY_1 -1616020 -#define SAY_PHASE2_SLAY_2 -1616021 -#define SAY_PHASE2_SLAY_3 -1616022 -//--> Phase3 Malygos destroys the floor, encounter continues on dragon "mounts" -#define SAY_PHASE3_INTRO -1616023 -#define SAY_PHASE3_AGGRO -1616024 -#define SAY_PHASE3_SLAY_1 -1616025 -#define SAY_PHASE3_SLAY_2 -1616026 -#define SAY_PHASE3_SLAY_3 -1616027 -#define SAY_PHASE3_BIG_ATTACK -1616028 - -enum +// not implemented +enum Achievements { ACHIEV_TIMED_START_EVENT = 20387, }; +enum Events +{ + // =========== PHASE ONE =============== + EVENT_ARCANE_BREATH = 1, + EVENT_ARCANE_STORM = 2, + EVENT_VORTEX = 3, + EVENT_POWER_SPARKS = 4, + + // =========== PHASE TWO =============== + EVENT_SURGE_POWER = 5, // wowhead is wrong, Surge of Power is casted instead of Arcane Pulse (source sniffs!) + EVENT_SUMMON_ARCANE = 6, + + // =========== PHASE TWO =============== + EVENT_SURGE_POWER_PHASE_3 = 7, + EVENT_STATIC_FIELD = 8, + + // =============== YELLS =============== + EVENT_YELL_0 = 9, + EVENT_YELL_1 = 10, + EVENT_YELL_2 = 11, + EVENT_YELL_3 = 12, + EVENT_YELL_4 = 13, +}; + +enum Phases +{ + PHASE_ONE = 1, + PHASE_TWO = 2, + PHASE_THREE = 3 +}; + +enum Spells +{ + SPELL_ARCANE_BREATH = 56272, + SPELL_ARCANE_STORM = 57459, + SPELL_BERSEKER = 60670, + + SPELL_VORTEX_1 = 56237, // seems that frezze object animation + SPELL_VORTEX_2 = 55873, // visual effect + SPELL_VORTEX_3 = 56105, // this spell must handle all the script - casted by the boss and to himself + //SPELL_VORTEX_4 = 55853, // damage | used to enter to the vehicle - defined in eye_of_eternity.h + //SPELL_VORTEX_5 = 56263, // damage | used to enter to the vehicle - defined in eye_of_eternity.h + SPELL_VORTEX_6 = 73040, // teleport - (casted to all raid) | caster 30090 | target player + + SPELL_PORTAL_VISUAL_CLOSED = 55949, + SPELL_SUMMON_POWER_PARK = 56142, + SPELL_POWER_SPARK_DEATH = 55852, + SPELL_POWER_SPARK_MALYGOS = 56152, + + SPELL_SURGE_POWER = 56505, // used in phase 2 + SPELL_SUMMON_ARCANE_BOMB = 56429, + SPELL_ARCANE_OVERLOAD = 56432, + SPELL_SUMMOM_RED_DRAGON = 56070, + SPELL_SURGE_POWER_PHASE_3 = 57407, + SPELL_STATIC_FIELD = 57430 +}; + +enum Movements +{ + MOVE_VORTEX = 1, + MOVE_PHASE_TWO, + MOVE_DEEP_BREATH_ROTATION, + MOVE_INIT_PHASE_ONE, + MOVE_CENTER_PLATFORM +}; + +enum Seats +{ + SEAT_0 = 0, +}; + +enum Factions +{ + FACTION_FRIENDLY = 35, + FACTION_HOSTILE = 14 +}; + +enum Actions +{ + ACTION_HOVER_DISK_START_WP_1, + ACTION_HOVER_DISK_START_WP_2 +}; + +enum MalygosEvents +{ + DATA_SUMMON_DEATHS, // phase 2 + DATA_PHASE +}; + +#define TEN_MINUTES 600000 + +enum MalygosSays +{ + SAY_AGGRO_P_ONE, + SAY_KILLED_PLAYER_P_ONE, + SAY_END_P_ONE, + SAY_AGGRO_P_TWO, + SAY_ANTI_MAGIC_SHELL, // not sure when execute it + SAY_MAGIC_BLAST, // not sure when execute it + SAY_KILLED_PLAYER_P_TWO, + SAY_END_P_TWO, + SAY_INTRO_P_THREE, + SAY_AGGRO_P_THREE, + SAY_SURGE_POWER, // not sure when execute it + SAY_BUFF_SPARK, + SAY_KILLED_PLAYER_P_THREE, + SAY_SPELL_CASTING_P_THREE, + SAY_DEATH +}; + +#define MAX_HOVER_DISK_WAYPOINTS 18 + +// Sniffed data +const Position HoverDiskWaypoints[MAX_HOVER_DISK_WAYPOINTS] = +{ + {782.9821f,1296.652f,282.1114f}, + {779.5459f,1287.228f,282.1393f}, + {773.0028f,1279.52f,282.4164f}, + {764.3626f,1274.476f,282.4731f}, + {754.3961f,1272.639f,282.4171f}, + {744.4422f,1274.412f,282.222f}, + {735.575f,1279.742f,281.9674f}, + {729.2788f,1287.187f,281.9943f}, + {726.1191f,1296.688f,282.2997f}, + {725.9396f,1306.531f,282.2448f}, + {729.3045f,1316.122f,281.9108f}, + {735.8322f,1323.633f,282.1887f}, + {744.4616f,1328.999f,281.9948f}, + {754.4739f,1330.666f,282.049f}, + {764.074f,1329.053f,281.9949f}, + {772.8409f,1323.951f,282.077f}, + {779.5085f,1316.412f,281.9145f}, + {782.8365f,1306.778f,282.3035f}, +}; + +#define GROUND_Z 268 + +// Source: Sniffs +#define MALYGOS_MAX_WAYPOINTS 16 +const Position MalygosPhaseTwoWaypoints[MALYGOS_MAX_WAYPOINTS] = +{ + {812.7299f,1391.672f,283.2763f}, + {848.2912f,1358.61f,283.2763f}, + {853.9227f,1307.911f,283.2763f}, + {847.1437f,1265.538f,283.2763f}, + {839.9229f,1245.245f,283.2763f}, + {827.3463f,1221.818f,283.2763f}, + {803.2727f,1203.851f,283.2763f}, + {772.9372f,1197.981f,283.2763f}, + {732.1138f,1200.647f,283.2763f}, + {693.8761f,1217.995f,283.2763f}, + {664.5038f,1256.539f,283.2763f}, + {650.1497f,1303.485f,283.2763f}, + {662.9109f,1350.291f,283.2763f}, + {677.6391f,1377.607f,283.2763f}, + {704.8198f,1401.162f,283.2763f}, + {755.2642f,1417.1f,283.2763f}, +}; + +#define MAX_SUMMONS_PHASE_TWO 4 + +#define MAX_MALYGOS_POS 2 +const Position MalygosPositions[MAX_MALYGOS_POS] = +{ + {754.544f,1301.71f,320.0f}, + {754.39f, 1301.27f, 292.91f} +}; + class boss_malygos : public CreatureScript { public: - boss_malygos() : CreatureScript("boss_malygos") { } + boss_malygos() : CreatureScript("boss_malygos") {} - CreatureAI* GetAI(Creature* pCreature) const + CreatureAI* GetAI(Creature* creature) const { - return new boss_malygosAI (pCreature); + return new boss_malygosAI(creature); } - struct boss_malygosAI : public ScriptedAI + struct boss_malygosAI : public BossAI { - boss_malygosAI(Creature *c) : ScriptedAI(c) + boss_malygosAI(Creature* creature) : BossAI(creature, DATA_MALYGOS_EVENT) { - instance = me->GetInstanceScript(); + // If we enter in combat when MovePoint generator is active, it overrwrites our homeposition + homePosition = creature->GetHomePosition(); } - InstanceScript *instance; + void Reset() + { - uint32 phase; - uint32 enrage; + _Reset(); - void Reset() + bersekerTimer = 0; + currentPos = 0; + + SetPhase(PHASE_ONE,true); + + delayedMovementTimer = 8000; + delayedMovement = false; + + summonDeaths = 0; + + me->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_NOT_SELECTABLE); + + cannotMove = true; + } + + uint32 GetData(uint32 data) { - phase = 1; - enrage = 615000; //Source Deadly Boss Mod + if (data == DATA_SUMMON_DEATHS) + return summonDeaths; + else if (data == DATA_PHASE) + return phase; + + return 0; + } + + void SetData(uint32 data, uint32 value) + { + if (data == DATA_SUMMON_DEATHS && phase == PHASE_TWO) + { + summonDeaths = value; + + if (summonDeaths >= MAX_SUMMONS_PHASE_TWO) + StartPhaseThree(); + } + } + + void EnterEvadeMode() + { + me->SetHomePosition(homePosition); + + me->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING); + + BossAI::EnterEvadeMode(); if (instance) - instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + instance->SetBossState(DATA_MALYGOS_EVENT, FAIL); + } + + void SetPhase(uint8 _phase, bool setEvents = false) + { + events.Reset(); + + events.SetPhase(_phase); + phase = _phase; + + if (setEvents) + SetPhaseEvents(_phase); + } + + void StartPhaseThree() + { + if (!instance) + return; + + SetPhase(PHASE_THREE,true); + + // this despawns Hover Disks + summons.DespawnAll(); + // players that used Hover Disk are no in the aggro list + me->SetInCombatWithZone(); + std::list<HostileReference*> &m_threatlist = me->getThreatManager().getThreatList(); + for (std::list<HostileReference*>::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + { + if (Unit* target = (*itr)->getTarget()) + { + if (target->GetTypeId() != TYPEID_PLAYER) + continue; + + // The rest is handled in the AI of the vehicle. + target->CastSpell(target,SPELL_SUMMOM_RED_DRAGON,true); + } + } + + if (GameObject* go = GameObject::GetGameObject(*me,instance->GetData64(DATA_PLATFORM))) + go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); // In sniffs it has this flag, but i don't know how is applied. + + // pos sniffed + me->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->MovePoint(MOVE_CENTER_PLATFORM,MalygosPositions[0].GetPositionX(),MalygosPositions[0].GetPositionY(),MalygosPositions[0].GetPositionZ()); + } + + void SetPhaseEvents(uint8 _phase) + { + switch (_phase) + { + case PHASE_ONE: + events.ScheduleEvent(EVENT_ARCANE_BREATH,urand(15,20)*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_ARCANE_STORM,urand(5,10)*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_VORTEX,urand(30,40)*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_POWER_SPARKS,urand(30,35)*IN_MILLISECONDS,0,_phase); + break; + case PHASE_TWO: + events.ScheduleEvent(EVENT_YELL_0,0,0,_phase); + events.ScheduleEvent(EVENT_YELL_1,24*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_SURGE_POWER,urand(60,70)*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_SUMMON_ARCANE,urand(2,5)*IN_MILLISECONDS,0,_phase); + break; + case PHASE_THREE: + events.ScheduleEvent(EVENT_YELL_2,0,0,_phase); + events.ScheduleEvent(EVENT_YELL_3,8*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_YELL_4,16*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_SURGE_POWER_PHASE_3,(7,16)*IN_MILLISECONDS,0,_phase); + events.ScheduleEvent(EVENT_STATIC_FIELD,(20,30)*IN_MILLISECONDS,0,_phase); + break; + default: + break; + } } void EnterCombat(Unit* /*who*/) { - if (phase == 1) + _EnterCombat(); + + me->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING); + me->RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_NOT_SELECTABLE); + + Talk(SAY_AGGRO_P_ONE); + + DoCast(SPELL_BERSEKER); + } + + void KilledUnit(Unit* who) + { + if (who->GetTypeId() != TYPEID_PLAYER) + return; + + switch (phase) + { + case PHASE_ONE: + Talk(SAY_KILLED_PLAYER_P_ONE); + break; + case PHASE_TWO: + Talk(SAY_KILLED_PLAYER_P_TWO); + break; + case PHASE_THREE: + Talk(SAY_KILLED_PLAYER_P_THREE); + break; + } + } + + void SpellHit(Unit* caster, const SpellEntry* spell) + { + if (spell->Id == SPELL_POWER_SPARK_MALYGOS) + { + if (Creature* creature = caster->ToCreature()) + creature->DespawnOrUnsummon(); + + Talk(SAY_BUFF_SPARK); + } + } + + void MoveInLineOfSight(Unit* who) + { + if (!me->isInCombat()) + return; + + if (who->GetEntry() == NPC_POWER_SPARK) { - DoScriptText(SAY_PHASE1_AGGRO, me); - if (instance) - instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + // not sure about the distance | I think it is better check this here than in the UpdateAI function... + if (who->GetDistance(me) <= 2.5f) + who->CastSpell(me, SPELL_POWER_SPARK_MALYGOS, true); + } + } + + void PrepareForVortex() + { + me->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING); + + me->GetMotionMaster()->MovementExpired(); + me->GetMotionMaster()->MovePoint(MOVE_VORTEX,MalygosPositions[1].GetPositionX(), MalygosPositions[1].GetPositionY(), MalygosPositions[1].GetPositionZ()); + // continues in MovementInform function. + } + + void ExecuteVortex() + { + DoCast(me,SPELL_VORTEX_1,true); + DoCast(me,SPELL_VORTEX_2,true); - if (phase == 2) - DoScriptText(SAY_PHASE1_AGGRO, me); - if (phase == 3) - DoScriptText(SAY_PHASE1_AGGRO, me); + // the vortex execution continues in the dummy effect of this spell (see its script) + DoCast(me,SPELL_VORTEX_3,true); } - void UpdateAI(const uint32 /*diff*/) + void MovementInform(uint32 type, uint32 id) + { + if (type != POINT_MOTION_TYPE) + return; + + switch (id) + { + case MOVE_VORTEX: + me->GetMotionMaster()->MoveIdle(); + ExecuteVortex(); + break; + case MOVE_DEEP_BREATH_ROTATION: + currentPos = currentPos == MALYGOS_MAX_WAYPOINTS - 1 ? 0 : currentPos+1; + me->GetMotionMaster()->MovementExpired(); + me->GetMotionMaster()->MovePoint(MOVE_DEEP_BREATH_ROTATION,MalygosPhaseTwoWaypoints[currentPos]); + break; + case MOVE_INIT_PHASE_ONE: + me->SetInCombatWithZone(); + break; + case MOVE_CENTER_PLATFORM: + cannotMove = false; + // malygos will move into center of platform and then he does not chase dragons, he just turns to his current target. + me->GetMotionMaster()->MoveIdle(); + break; + } + } + + void StartPhaseTwo() + { + SetPhase(PHASE_TWO,true); + + me->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING); + + me->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->MovePoint(MOVE_DEEP_BREATH_ROTATION,MalygosPhaseTwoWaypoints[0]); + + Creature* summon = me->SummonCreature(NPC_HOVER_DISK_CASTER,HoverDiskWaypoints[MAX_HOVER_DISK_WAYPOINTS-1]); + if (summon && summon->IsAIEnabled) + summon->AI()->DoAction(ACTION_HOVER_DISK_START_WP_2); + summon = me->SummonCreature(NPC_HOVER_DISK_CASTER,HoverDiskWaypoints[0]); + if (summon && summon->IsAIEnabled) + summon->AI()->DoAction(ACTION_HOVER_DISK_START_WP_1); + + for (uint8 i = 0; i < 2; i++) + { + // not sure about its position. + summon = me->SummonCreature(NPC_HOVER_DISK_MELEE,HoverDiskWaypoints[0]); + if (summon) + summon->SetInCombatWithZone(); + } + } + + void UpdateAI(const uint32 diff) { - //Return since we have no target if (!UpdateVictim()) return; - if (phase == 1 && HealthBelowPct(50)) { - phase = 2; - //spawn adds - //set malygos unatackable untill all adds spawned dead - //start phase3 + events.Update(diff); + + if (phase == PHASE_THREE) + { + if (!cannotMove) + { + // it can change if the player falls from the vehicle. + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != IDLE_MOTION_TYPE) + { + me->GetMotionMaster()->MovementExpired(); + me->GetMotionMaster()->MoveIdle(); + } + } else + { + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE) + { + me->GetMotionMaster()->MovementExpired(); + me->GetMotionMaster()->MovePoint(MOVE_CENTER_PLATFORM,MalygosPositions[0].GetPositionX(),MalygosPositions[0].GetPositionY(),MalygosPositions[0].GetPositionZ()); + } + } + } + + // we need a better way for pathing + if (delayedMovement) + { + if (delayedMovementTimer <= diff) + { + me->GetMotionMaster()->MovePoint(MOVE_DEEP_BREATH_ROTATION,MalygosPhaseTwoWaypoints[currentPos]); + delayedMovementTimer = 8000; + delayedMovement = false; + } delayedMovementTimer -= diff; + } + + // at 50 % health malygos switch to phase 2 + if (me->GetHealthPct() <= 50.0f && phase == PHASE_ONE) + StartPhaseTwo(); + + // the boss is handling vortex + if (me->HasAura(SPELL_VORTEX_2)) + return; + + // We can't cast if we are casting already. + if (me->HasUnitState(UNIT_STAT_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_YELL_2: + Talk(SAY_END_P_TWO); + break; + case EVENT_YELL_3: + Talk(SAY_INTRO_P_THREE); + break; + case EVENT_YELL_4: + Talk(SAY_AGGRO_P_THREE); + break; + case EVENT_YELL_0: + Talk(SAY_END_P_ONE); + break; + case EVENT_YELL_1: + Talk(SAY_AGGRO_P_TWO); + break; + case EVENT_ARCANE_BREATH: + DoCast(me->getVictim(),SPELL_ARCANE_BREATH); + events.ScheduleEvent(EVENT_ARCANE_BREATH,urand(35,60)*IN_MILLISECONDS,0,PHASE_ONE); + break; + case EVENT_ARCANE_STORM: + DoCast(me->getVictim(),SPELL_ARCANE_STORM); + events.ScheduleEvent(EVENT_ARCANE_STORM,urand(5,10)*IN_MILLISECONDS,0,PHASE_ONE); + break; + case EVENT_VORTEX: + PrepareForVortex(); + events.ScheduleEvent(EVENT_VORTEX,urand(60,80)*IN_MILLISECONDS,0,PHASE_ONE); + break; + case EVENT_POWER_SPARKS: + instance->SetData(DATA_POWER_SPARKS_HANDLING,0); + events.ScheduleEvent(EVENT_POWER_SPARKS,urand(30,35)*IN_MILLISECONDS,0,PHASE_ONE); + break; + case EVENT_SURGE_POWER: + me->GetMotionMaster()->MoveIdle(); + delayedMovement = true; + DoCast(SPELL_SURGE_POWER); + events.ScheduleEvent(EVENT_SURGE_POWER,urand(60,70)*IN_MILLISECONDS,0,PHASE_TWO); + break; + case EVENT_SUMMON_ARCANE: + DoCast(SPELL_SUMMON_ARCANE_BOMB); + events.ScheduleEvent(EVENT_SUMMON_ARCANE,urand(12,15)*IN_MILLISECONDS,0,PHASE_TWO); + break; + case EVENT_SURGE_POWER_PHASE_3: + DoCast(GetTargetPhaseThree(),SPELL_SURGE_POWER_PHASE_3); + events.ScheduleEvent(EVENT_SURGE_POWER_PHASE_3,(7,16)*IN_MILLISECONDS,0,PHASE_THREE); + break; + case EVENT_STATIC_FIELD: + DoCast(GetTargetPhaseThree(),SPELL_STATIC_FIELD); + events.ScheduleEvent(EVENT_STATIC_FIELD,(20,30)*IN_MILLISECONDS,0,PHASE_THREE); + break; + default: + break; + } } DoMeleeAttackIfReady(); } + Unit* GetTargetPhaseThree() + { + Unit* target = SelectTarget(SELECT_TARGET_RANDOM,0); + + // we are a drake + if (target->GetVehicleKit()) + return target; + + // we are a player using a drake (or at least you should) + if (target->GetTypeId() == TYPEID_PLAYER) + { + if (Unit* base = target->GetVehicleBase()) + return base; + } + + // is a player falling from a vehicle? + return NULL; + } + void JustDied(Unit* /*killer*/) { - DoScriptText(SAY_DEATH, me); + Talk(SAY_DEATH); + _JustDied(); + } + + private: + uint8 phase; + uint32 bersekerTimer; + uint8 currentPos; // used for phase 2 rotation... + bool delayedMovement; // used in phase2. + uint32 delayedMovementTimer; // used in phase 2 + uint8 summonDeaths; + Position homePosition; // it can get bugged because core thinks we are pathing + bool mustTalk; + bool cannotMove; + }; + +}; + +class spell_malygos_vortex_dummy : public SpellScriptLoader +{ +public: + spell_malygos_vortex_dummy() : SpellScriptLoader("spell_malygos_vortex_dummy") {} + + class spell_malygos_vortex_dummy_SpellScript : public SpellScript + { + PrepareSpellScript(spell_malygos_vortex_dummy_SpellScript) + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + + if (!caster) + return; + + // each player will enter to the trigger vehicle (entry 30090) already spawned (each one can hold up to 5 players, it has 5 seats) + // the players enter to the vehicles casting SPELL_VORTEX_4 OR SPELL_VORTEX_5 + if (InstanceScript* instance = caster->GetInstanceScript()) + instance->SetData(DATA_VORTEX_HANDLING,0); + + // the rest of the vortex execution continues when SPELL_VORTEX_2 is removed. + } + + void Register() + { + OnEffect += SpellEffectFn(spell_malygos_vortex_dummy_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_malygos_vortex_dummy_SpellScript(); + } +}; + +class spell_malygos_vortex_visual : public SpellScriptLoader +{ + public: + spell_malygos_vortex_visual() : SpellScriptLoader("spell_malygos_vortex_visual") { } + + class spell_malygos_vortex_visual_AuraScript : public AuraScript + { + PrepareAuraScript(spell_malygos_vortex_visual_AuraScript); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + std::list<HostileReference*> &m_threatlist = GetCaster()->getThreatManager().getThreatList(); + for (std::list<HostileReference*>::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + { + if (Unit* target = (*itr)->getTarget()) + { + Player* targetPlayer = target->ToPlayer(); + + if (!targetPlayer || targetPlayer->isGameMaster()) + continue; + + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + { + // teleport spell - i am not sure but might be it must be casted by each vehicle when its passenger leaves it + if (Creature* trigger = GetCaster()->GetMap()->GetCreature(instance->GetData64(DATA_TRIGGER))) + trigger->CastSpell(targetPlayer, SPELL_VORTEX_6, true); + } + } + } + + if (Creature* malygos = GetCaster()->ToCreature()) + { + // This is a hack, we have to re add players to the threat list because when they enter to the vehicles they are removed. + // Anyway even with this issue, the boss does not enter in evade mode - this prevents iterate an empty list in the next vortex execution. + malygos->SetInCombatWithZone(); + + malygos->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING); + + malygos->GetMotionMaster()->MoveChase(GetCaster()->getVictim()); + malygos->RemoveAura(SPELL_VORTEX_1); + } + + } + + void Register() + { + OnEffectRemove += AuraEffectRemoveFn(spell_malygos_vortex_visual_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_malygos_vortex_visual_AuraScript(); + } +}; + +class npc_portal_eoe: public CreatureScript +{ +public: + npc_portal_eoe() : CreatureScript("npc_portal_eoe") {} + + CreatureAI* GetAI(Creature* pCreature) const + { + return new npc_portal_eoeAI(pCreature); + } + + struct npc_portal_eoeAI : public ScriptedAI + { + npc_portal_eoeAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() + { + summonTimer = urand(5,7)*IN_MILLISECONDS; + } + + void UpdateAI(const uint32 diff) + { + if (!me->HasAura(SPELL_PORTAL_VISUAL_CLOSED) && + !me->HasAura(SPELL_PORTAL_OPENED)) + DoCast(me,SPELL_PORTAL_VISUAL_CLOSED,true); + + if (instance) + { + if (Creature* malygos = Unit::GetCreature(*me,instance->GetData64(DATA_MALYGOS))) + { + if (malygos->AI()->GetData(DATA_PHASE) != PHASE_ONE) + { + me->RemoveAura(SPELL_PORTAL_OPENED); + DoCast(me,SPELL_PORTAL_VISUAL_CLOSED,true); + } + } + } + + if (!me->HasAura(SPELL_PORTAL_OPENED)) + return; + + if (summonTimer <= diff) + { + DoCast(SPELL_SUMMON_POWER_PARK); + summonTimer = urand(5,7)*IN_MILLISECONDS; + } else summonTimer -= diff; + } + + void JustSummoned(Creature* summon) + { + summon->SetInCombatWithZone(); + } + + private: + uint32 summonTimer; + InstanceScript* instance; + }; +}; + + +class npc_power_spark: public CreatureScript +{ +public: + npc_power_spark() : CreatureScript("npc_power_spark") {} + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_power_sparkAI(creature); + } + + struct npc_power_sparkAI : public ScriptedAI + { + npc_power_sparkAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + + MoveToMalygos(); + } + + void EnterEvadeMode() + { + me->DespawnOrUnsummon(); + } + + void MoveToMalygos() + { + me->GetMotionMaster()->MoveIdle(); + + if (instance) + { + if (Creature* malygos = Unit::GetCreature(*me,instance->GetData64(DATA_MALYGOS))) + me->GetMotionMaster()->MoveFollow(malygos,0.0f,0.0f); + } + } + + void UpdateAI(const uint32 diff) + { + if (!instance) + return; + + if (Creature* malygos = Unit::GetCreature(*me,instance->GetData64(DATA_MALYGOS))) + { + if (malygos->AI()->GetData(DATA_PHASE) != PHASE_ONE) + { + me->DespawnOrUnsummon(); + return; + } + + if (malygos->HasAura(SPELL_VORTEX_1)) + { + me->GetMotionMaster()->MoveIdle(); + return; + } + + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE) + me->GetMotionMaster()->MoveFollow(malygos,0.0f,0.0f); + } + } + + void DamageTaken(Unit* /*done_by*/, uint32& damage) + { + if (damage > me->GetMaxHealth()) + { + damage = 0; + DoCast(me,SPELL_POWER_SPARK_DEATH,true); + me->DespawnOrUnsummon(1000); + } + } + + private: + InstanceScript* instance; + }; +}; + +class npc_hover_disk : public CreatureScript +{ +public: + npc_hover_disk() : CreatureScript("npc_hover_disk") { } + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_hover_diskAI(creature); + } + + struct npc_hover_diskAI : public npc_escortAI + { + npc_hover_diskAI(Creature* creature) : npc_escortAI(creature) + { + if (me->GetEntry() == NPC_HOVER_DISK_CASTER) + me->SetReactState(REACT_PASSIVE); + else + me->SetInCombatWithZone(); + + instance = creature->GetInstanceScript(); + } + + void Reset() + { + if (Vehicle* veh = me->GetVehicleKit()) + veh->Reset(); + } + + void PassengerBoarded(Unit* unit, int8 seat, bool apply) + { + if (apply) + { + if (unit->GetTypeId() == TYPEID_UNIT) + { + me->setFaction(FACTION_HOSTILE); + unit->ToCreature()->SetInCombatWithZone(); + } + } + else + { + // Error found: This is not called if the passenger is a player + + if (unit->GetTypeId() == TYPEID_UNIT) + { + // This will only be called if the passenger dies + if (instance) + { + if (Creature* malygos = Unit::GetCreature(*me,instance->GetData64(DATA_MALYGOS))) + malygos->AI()->SetData(DATA_SUMMON_DEATHS,malygos->AI()->GetData(DATA_SUMMON_DEATHS)+1); + } + + me->RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_NOT_SELECTABLE); + } + + me->GetMotionMaster()->MoveIdle(); + + if (me->GetEntry() == NPC_HOVER_DISK_MELEE) + { + // Hack: Fall ground function can fail (remember the platform is a gameobject), we will teleport the disk to the ground + if (me->GetPositionZ() > GROUND_Z) + me->NearTeleportTo(me->GetPositionX(),me->GetPositionY(),GROUND_Z,0); + me->SetHomePosition(me->GetPositionX(),me->GetPositionY(),me->GetPositionZ(),me->GetOrientation()); + me->setFaction(FACTION_FRIENDLY); + me->AI()->EnterEvadeMode(); + } + } + } + + void EnterEvadeMode() + { + // we dont evade } - void KilledUnit(Unit * victim) + void DoAction(const int32 action) { - if (victim == me) + if (me->GetEntry() != NPC_HOVER_DISK_CASTER) return; - if (phase == 1) - DoScriptText(RAND(SAY_PHASE1_SLAY_1,SAY_PHASE1_SLAY_2,SAY_PHASE1_SLAY_3), me); - if (phase == 2) - DoScriptText(RAND(SAY_PHASE2_SLAY_1,SAY_PHASE2_SLAY_2,SAY_PHASE2_SLAY_3), me); - if (phase == 3) - DoScriptText(RAND(SAY_PHASE3_SLAY_1,SAY_PHASE3_SLAY_2,SAY_PHASE3_SLAY_3), me); + switch (action) + { + case ACTION_HOVER_DISK_START_WP_1: + for (uint8 i = 0; i < MAX_HOVER_DISK_WAYPOINTS; i++) + AddWaypoint(i,HoverDiskWaypoints[i].GetPositionX(),HoverDiskWaypoints[i].GetPositionY(),HoverDiskWaypoints[i].GetPositionZ()); + break; + case ACTION_HOVER_DISK_START_WP_2: + { + uint8 count = 0; + for (uint8 i = MAX_HOVER_DISK_WAYPOINTS-1; i > 0; i--) + { + AddWaypoint(count,HoverDiskWaypoints[i].GetPositionX(),HoverDiskWaypoints[i].GetPositionY(),HoverDiskWaypoints[i].GetPositionZ()); + count++; + } + break; + } + default: + return; + } + + Start(true,false,0,0,false,true); + } + + void UpdateEscortAI(const uint32 /*diff*/) + { + // we dont do melee damage! } + + void WaypointReached(uint32 i) + { + + } + + private: + InstanceScript* instance; }; +}; + +// The reason of this AI is to make the creature able to enter in combat otherwise the spell casting of SPELL_ARCANE_OVERLOAD fails. +class npc_arcane_overload : public CreatureScript +{ +public: + npc_arcane_overload() : CreatureScript("npc_arcane_overload") {} + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_arcane_overloadAI (creature); + } + + struct npc_arcane_overloadAI : public ScriptedAI + { + npc_arcane_overloadAI(Creature* creature) : ScriptedAI(creature) {} + + void AttackStart(Unit* who) + { + DoStartNoMovement(who); + } + + void Reset() + { + DoCast(me,SPELL_ARCANE_OVERLOAD,false); + } + + void UpdateAI(const uint32 diff) + { + // we dont do melee damage! + } + + }; +}; + +// SmartAI does not work correctly for this (vehicles) +class npc_wyrmrest_skytalon : public CreatureScript +{ +public: + npc_wyrmrest_skytalon() : CreatureScript("npc_wyrmrest_skytalon") {} + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_wyrmrest_skytalonAI (creature); + } + + struct npc_wyrmrest_skytalonAI : public NullCreatureAI + { + npc_wyrmrest_skytalonAI(Creature* creature) : NullCreatureAI(creature) + { + instance = creature->GetInstanceScript(); + + timer = 1000; + entered = false; + } + + // we can't call this in reset function, it fails. + void MakePlayerEnter() + { + if (!instance) + return; + + if (Creature* malygos = Unit::GetCreature(*me,instance->GetData64(DATA_MALYGOS))) + { + if (Unit* summoner = me->ToTempSummon()->GetSummoner()) + { + summoner->CastSpell(me,SPELL_RIDE_RED_DRAGON,true); + if (Creature* malygos = Unit::GetCreature(*me,instance->GetData64(DATA_MALYGOS))) + { + float victim_threat = malygos->getThreatManager().getThreat(summoner); + malygos->getThreatManager().resetAllAggro(); + malygos->AI()->AttackStart(me); + malygos->AddThreat(me, victim_threat); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!entered) + { + if (timer <= diff) + { + MakePlayerEnter(); + entered = true; + } else timer -= diff; + } + } + + private: + InstanceScript* instance; + uint32 timer; + bool entered; + }; +}; + +enum AlexstraszaYells +{ + SAY_ONE, + SAY_TWO, + SAY_THREE, + SAY_FOUR +}; + +class npc_alexstrasza_eoe : public CreatureScript +{ +public: + npc_alexstrasza_eoe() : CreatureScript("npc_alexstrasza_eoe") { } + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_alexstrasza_eoeAI (creature); + } + + struct npc_alexstrasza_eoeAI : public ScriptedAI + { + npc_alexstrasza_eoeAI(Creature* creature) : ScriptedAI(creature) {} + + void Reset() + { + events.Reset(); + + events.ScheduleEvent(EVENT_YELL_1,0); + } + + void UpdateAI(const uint32 diff) + { + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_YELL_1: + Talk(SAY_ONE); + events.ScheduleEvent(EVENT_YELL_2,4*IN_MILLISECONDS); + break; + case EVENT_YELL_2: + Talk(SAY_TWO); + events.ScheduleEvent(EVENT_YELL_3,4*IN_MILLISECONDS); + break; + case EVENT_YELL_3: + Talk(SAY_THREE); + events.ScheduleEvent(EVENT_YELL_4,7*IN_MILLISECONDS); + break; + case EVENT_YELL_4: + Talk(SAY_FOUR); + break; + } + } + } + private: + EventMap events; + }; }; void AddSC_boss_malygos() { new boss_malygos(); + new npc_portal_eoe(); + new npc_power_spark(); + new npc_hover_disk(); + new npc_arcane_overload(); + new npc_wyrmrest_skytalon(); + new spell_malygos_vortex_dummy(); + new spell_malygos_vortex_visual(); + new npc_alexstrasza_eoe(); } diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h index 04415f0af07..5bd66b9ffa4 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h @@ -18,4 +18,55 @@ #ifndef DEF_EYE_OF_ETERNITY_H #define DEF_EYE_OF_ETERNITY_H +enum InstanceData +{ + DATA_MALYGOS_EVENT, + MAX_ENCOUNTER, + + DATA_VORTEX_HANDLING, + DATA_POWER_SPARKS_HANDLING +}; + +enum InstanceData64 +{ + DATA_TRIGGER, + DATA_MALYGOS, + DATA_PLATFORM +}; + +enum InstanceNpcs +{ + NPC_MALYGOS = 28859, + NPC_VORTEX_TRIGGER = 30090, + NPC_PORTAL_TRIGGER = 30118, + NPC_POWER_SPARK = 30084, + NPC_HOVER_DISK_MELEE = 30234, + NPC_HOVER_DISK_CASTER = 30248, + NPC_ARCANE_OVERLOAD = 30282, + NPC_WYRMREST_SKYTALON = 30161, + NPC_ALEXSTRASZA = 32295 +}; + +enum InstanceGameObjects +{ + GO_NEXUS_RAID_PLATFORM = 193070, + GO_EXIT_PORTAL = 193908, + GO_FOCUSING_IRIS = 193958, + GO_ALEXSTRASZA_S_GIFT = 193905, + GO_ALEXSTRASZA_S_GIFT_2 = 193967 +}; + +enum InstanceEvents +{ + EVENT_FOCUSING_IRIS = 20711 +}; + +enum InstanceSpells +{ + SPELL_VORTEX_4 = 55853, // damage | used to enter to the vehicle + SPELL_VORTEX_5 = 56263, // damage | used to enter to the vehicle + SPELL_PORTAL_OPENED = 61236, + SPELL_RIDE_RED_DRAGON = 56071, +}; + #endif diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp index 49d7997ddc5..a28b5258e4b 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp @@ -21,22 +21,275 @@ class instance_eye_of_eternity : public InstanceMapScript { public: - instance_eye_of_eternity() : InstanceMapScript("instance_eye_of_eternity", 616) { } + instance_eye_of_eternity() : InstanceMapScript("instance_eye_of_eternity", 616) {} - InstanceScript* GetInstanceScript(InstanceMap* pMap) const + InstanceScript* GetInstanceScript(InstanceMap* map) const { - return new instance_eye_of_eternity_InstanceMapScript(pMap); + return new instance_eye_of_eternity_InstanceMapScript(map); } struct instance_eye_of_eternity_InstanceMapScript : public InstanceScript { - instance_eye_of_eternity_InstanceMapScript(Map* pMap) : InstanceScript(pMap) {Initialize();}; - }; + instance_eye_of_eternity_InstanceMapScript(Map* map) : InstanceScript(map) + { + SetBossNumber(MAX_ENCOUNTER); + + vortexTriggers.clear(); + portalTriggers.clear(); + + malygosGUID = 0; + lastPortalGUID = 0; + platformGUID = 0; + exitPortalGUID = 0; + }; + + bool SetBossState(uint32 type, EncounterState state) + { + if (!InstanceScript::SetBossState(type, state)) + return false; + + if (type == DATA_MALYGOS_EVENT) + { + if (state == FAIL) + { + for (std::list<uint64>::const_iterator itr_trigger = portalTriggers.begin(); itr_trigger != portalTriggers.end(); ++itr_trigger) + { + if (Creature* trigger = instance->GetCreature(*itr_trigger)) + { + // just in case + trigger->RemoveAllAuras(); + trigger->AI()->Reset(); + } + } + + SpawnGameObject(GO_FOCUSING_IRIS,focusingIrisPosition); + SpawnGameObject(GO_EXIT_PORTAL,exitPortalPosition); + + if (GameObject* platform = instance->GetGameObject(platformGUID)) + platform->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); + } else if (state == DONE) + { + if (Creature* malygos = instance->GetCreature(malygosGUID)) + malygos->SummonCreature(NPC_ALEXSTRASZA,829.0679f,1244.77f,279.7453f,2.32f); + + SpawnGameObject(GO_EXIT_PORTAL,exitPortalPosition); + + // we make the platform appear again because at the moment we don't support looting using a vehicle + if (GameObject* platform = instance->GetGameObject(platformGUID)) + platform->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); + + if (GameObject* chest = instance->GetGameObject(chestGUID)) + chest->SetRespawnTime(chest->GetRespawnDelay()); + } + } + return true; + } + + // There is no other way afaik... + void SpawnGameObject(uint32 entry, Position& pos) + { + GameObject* go = new GameObject; + if (!go->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_GAMEOBJECT),entry, instance, + PHASEMASK_NORMAL, pos.GetPositionX(),pos.GetPositionY(),pos.GetPositionZ(),pos.GetOrientation(), + 0,0,0,0,120,GO_STATE_READY)) + { + delete go; + return; + } + + instance->Add(go); + } + + void OnGameObjectCreate(GameObject* go) + { + switch (go->GetEntry()) + { + case GO_NEXUS_RAID_PLATFORM: + platformGUID = go->GetGUID(); + break; + case GO_FOCUSING_IRIS: + go->GetPosition(&focusingIrisPosition); + break; + case GO_EXIT_PORTAL: + exitPortalGUID = go->GetGUID(); + go->GetPosition(&exitPortalPosition); + break; + case GO_ALEXSTRASZA_S_GIFT: + case GO_ALEXSTRASZA_S_GIFT_2: + chestGUID = go->GetGUID(); + break; + } + } + + void OnCreatureCreate(Creature* creature) + { + switch (creature->GetEntry()) + { + case NPC_VORTEX_TRIGGER: + vortexTriggers.push_back(creature->GetGUID()); + break; + case NPC_MALYGOS: + malygosGUID = creature->GetGUID(); + break; + case NPC_PORTAL_TRIGGER: + portalTriggers.push_back(creature->GetGUID()); + break; + } + } + + void ProcessEvent(GameObject* go, uint32 eventId) + { + if (eventId == EVENT_FOCUSING_IRIS) + { + go->Delete(); // this is not the best way. + if (Creature* malygos = instance->GetCreature(malygosGUID)) + malygos->GetMotionMaster()->MovePoint(4,770.10f, 1275.33f, 267.23f); // MOVE_INIT_PHASE_ONE + + if (GameObject* exitPortal = instance->GetGameObject(exitPortalGUID)) + exitPortal->Delete(); + } + } + + void VortexHandling() + { + if (Creature* malygos = instance->GetCreature(malygosGUID)) + { + std::list<HostileReference*> m_threatlist = malygos->getThreatManager().getThreatList(); + for (std::list<uint64>::const_iterator itr_vortex = vortexTriggers.begin(); itr_vortex != vortexTriggers.end(); ++itr_vortex) + { + if (m_threatlist.empty()) + return; + + uint8 counter = 0; + if (Creature* trigger = instance->GetCreature(*itr_vortex)) + { + // each trigger have to cast the spell to 5 players. + for (std::list<HostileReference*>::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + { + if (counter >= 5) + break; + + if (Unit* target = (*itr)->getTarget()) + { + Player* player = target->ToPlayer(); + if (!player || player->isGameMaster() || player->HasAura(SPELL_VORTEX_4)) + continue; + + player->CastSpell(trigger,SPELL_VORTEX_4,true); + counter++; + } + } + } + } + } + } + + void PowerSparksHandling() + { + bool next = (lastPortalGUID == portalTriggers.back() || !lastPortalGUID ? true : false); + + for (std::list<uint64>::const_iterator itr_trigger = portalTriggers.begin(); itr_trigger != portalTriggers.end(); ++itr_trigger) + { + if (next) + { + if (Creature* trigger = instance->GetCreature(*itr_trigger)) + { + lastPortalGUID = trigger->GetGUID(); + trigger->CastSpell(trigger,SPELL_PORTAL_OPENED,true); + return; + } + } + + if (*itr_trigger == lastPortalGUID) + next = true; + } + } + + void SetData(uint32 data,uint32 /*value*/) + { + switch (data) + { + case DATA_VORTEX_HANDLING: + VortexHandling(); + break; + case DATA_POWER_SPARKS_HANDLING: + PowerSparksHandling(); + break; + } + } + + uint64 GetData64(uint32 data) + { + switch (data) + { + case DATA_TRIGGER: + return vortexTriggers.front(); + case DATA_MALYGOS: + return malygosGUID; + case DATA_PLATFORM: + return platformGUID; + } + + return 0; + } + + std::string GetSaveData() + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << "E E " << GetBossSaveData(); + + OUT_SAVE_INST_DATA_COMPLETE; + return saveStream.str(); + } + + void Load(const char* str) + { + if (!str) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(str); + + char dataHead1, dataHead2; + + std::istringstream loadStream(str); + loadStream >> dataHead1 >> dataHead2; + + if (dataHead1 == 'E' && dataHead2 == 'E') + { + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + { + uint32 tmpState; + loadStream >> tmpState; + if (tmpState == IN_PROGRESS || tmpState > SPECIAL) + tmpState = NOT_STARTED; + SetBossState(i, EncounterState(tmpState)); + } + + } else OUT_LOAD_INST_DATA_FAIL; + + OUT_LOAD_INST_DATA_COMPLETE; + } + + private: + std::list<uint64> vortexTriggers; + std::list<uint64> portalTriggers; + uint64 malygosGUID; + uint64 lastPortalGUID; + uint64 platformGUID; + uint64 exitPortalGUID; + uint64 chestGUID; + Position focusingIrisPosition; + Position exitPortalPosition; + }; }; void AddSC_instance_eye_of_eternity() { - // doesn't exist in the database? - //new instance_eye_of_eternity(); + new instance_eye_of_eternity(); } |