diff --git a/data/sql/updates/pending_db_world/rev_1617252770182832100.sql b/data/sql/updates/pending_db_world/rev_1617252770182832100.sql new file mode 100644 index 000000000..115899831 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1617252770182832100.sql @@ -0,0 +1,195 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1617252770182832100'); + +-- Amanitar encounter +DELETE FROM `spell_script_names` WHERE `spell_id`=57283 AND `ScriptName`='spell_amanitar_remove_mushroom_power'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(57283, 'spell_amanitar_remove_mushroom_power'); +DELETE FROM `spell_script_names` WHERE `spell_id`=56648; +DELETE FROM `spell_group_stack_rules` WHERE `group_id`=1113; +INSERT INTO `spell_group_stack_rules` (`group_id`, `stack_rule`, `description`) VALUES +(1113, 8, 'Ahn\'kahet - Potent Fogus and Mini'); +DELETE FROM `spell_group` WHERE `id`=1113; +INSERT INTO `spell_group` (`id`, `spell_id`, `special_flag`) VALUES +(1113, 57055, 0), +(1113, 56648, 0); + +-- Teldaram +DELETE FROM `spell_script_names` WHERE `spell_id` = 55931 AND `ScriptName`='spell_prince_taldaram_conjure_flame_sphere'; +DELETE FROM `spell_script_names` WHERE `spell_id` IN (55895, 59511, 59512) AND `ScriptName` = 'spell_prince_taldaram_flame_sphere_summon'; +INSERT INTO `spell_script_names` (`spell_id` ,`ScriptName`) VALUES +(55931, 'spell_prince_taldaram_conjure_flame_sphere'), +(55895, 'spell_prince_taldaram_flame_sphere_summon'), +(59511, 'spell_prince_taldaram_flame_sphere_summon'), +(59512, 'spell_prince_taldaram_flame_sphere_summon'); +UPDATE `creature_text` SET `TextRange`=3,`comment`='prince taldaram SAY_SPHERE_ACTIVATED' WHERE `CreatureID`=29308 AND `GroupID`=0 AND `ID`=0; +UPDATE `creature_text` SET `TextRange`=3,`comment`='prince taldaram SAY_REMOVE_PRISON' WHERE `CreatureID`=29308 AND `GroupID`=1 AND `ID`=0; +UPDATE `creature_template` SET `flags_extra`=`flags_extra` &~ 512 WHERE `entry` IN (29308, 31469); +UPDATE `creature_template` SET `modelid2` = 19725, `flags_extra` = `flags_extra` |64|128 WHERE `entry` IN (30106, 31458); + +DELETE FROM `creature_summon_groups` WHERE `summonerId`=29308 AND `summonerType`=0; +INSERT INTO `creature_summon_groups` (`summonerId`, `summonerType`, `groupId`, `entry`, `position_x`, `position_y`, `position_z`, `orientation`, `summonType`, `summonTime`) VALUES +(29308, 0, 0, 30181, 519.146, -792.274, 49.4627, 4.15388, 8, 0), +(29308, 0, 0, 30181, 542.994, -762.115, 36.0509, 1.32645, 8, 0), +(29308, 0, 0, 30181, 599.617, -762.315, 35.3111, 1.71042, 8, 0), +(29308, 0, 0, 30181, 506.573, -890.563, 45.1763, 3.35103, 8, 0), +(29308, 0, 0, 30181, 632.232, -774.304, 34.0595, 0.750492, 8, 0), +(29308, 0, 0, 30181, 489.944, -851.356, 52.09, 4.95674, 8, 0), +(29308, 0, 0, 30181, 655.409, -814.264, 35.2257, 0.436332, 8, 0); + +-- Jedoga encounter +DELETE FROM `achievement_criteria_data` WHERE `criteria_id`=7359; +INSERT INTO `achievement_criteria_data` (`criteria_id`, `type`, `value1`, `value2`, `ScriptName`) VALUES +(7359, 11, 0, 0, 'achievement_volunteer_work'), +(7359, 12, 1, 0, ''); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=56312; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 56312, 0, 0, 31, 0, 3, 29310, 0, 0, 0, 0, '', 'Spell \'Beam Visual\' targets Jedoga Shadowseeker'); + +UPDATE `creature_template` SET `ScriptName`='' WHERE `ScriptName` = 'npc_jedoga_initiand'; +UPDATE `creature_template` SET `ScriptName`='npc_twilight_volunteer' WHERE `entry` = 30385; +update `creature_template` SET `modelid1`=169, `modelid2`=11686, `flags_extra`=`flags_extra`|128 WHERE `entry`=30181; +-- "...Immune to all kinds of Crowd Control..." +UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`| + 1| -- CHARM + 2| -- MECHANIC_DISORIENTED + 8| -- MECHANIC_CHARM + 16| -- MECHANIC_FEAR + 32| -- MECHANIC_GRIP + 64| -- MECHANIC_ROOT + 512| -- MECHANIC_SLEEP + 1024| -- MECHANIC_SNARE + 2048| -- MECHANIC_STUN + 4096| -- MECHANIC_FREEZE + 8192| -- MECHANIC_KNOCKOUT + 65536| -- MECHANIC_POLYMORPH + 8388608| -- MECHANIC_HORROR + 67108864| -- MECHANIC_DAZE + 536870912 -- MECHANIC_SAPPED +WHERE `entry` IN (30385, 31474); + +DELETE FROM `spell_script_names` WHERE `spell_id`=56328 AND `ScriptName`='spell_random_lightning_visual_effect'; +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(56328,'spell_random_lightning_visual_effect'); + +DELETE FROM `spell_script_names` WHERE `spell_id`=56150 AND `ScriptName`='spell_jedoga_sacrafice_beam'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(56150, 'spell_jedoga_sacrafice_beam'); + +DELETE FROM `creature_text` WHERE `CreatureID`=30385; +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(30385,0,0,'I have been chosen!',14,0,100,0,0,0,31179,0,'SAY_CHOSEN'), +(30385,1,0,'I give myself to the master!',14,0,100,0,0,0,30863,0,'SAY_SACRIFICED'); + +DELETE FROM `creature_summon_groups` WHERE `summonerId`=29310 AND `summonerType`=0; +INSERT INTO `creature_summon_groups` (`summonerId`, `summonerType`, `groupId`, `entry`, `position_x`, `position_y`, `position_z`, `orientation`, `summonType`, `summonTime`) VALUES +-- Out of Combat +(29310, 0, 0, 30114, 362.458, -714.166, -16.0964, 0.977384, 6, 10000), +(29310, 0, 0, 30114, 368.781, -713.932, -16.0964, 1.46608, 6, 10000), +(29310, 0, 0, 30114, 364.937, -716.11, -16.0964, 1.25664, 6, 10000), +(29310, 0, 0, 30114, 362.02, -719.828, -16.0964, 1.20428, 6, 10000), +(29310, 0, 0, 30114, 368.151, -719.763, -16.0964, 1.53589, 6, 10000), +(29310, 0, 0, 30114, 392.276, -695.895, -16.0964, 3.40339, 6, 10000), +(29310, 0, 0, 30114, 387.224, -698.006, -16.0964, 3.36848, 6, 10000), +(29310, 0, 0, 30114, 389.626, -702.3, -16.0964, 3.07178, 6, 10000), +(29310, 0, 0, 30114, 383.812, -700.41, -16.0964, 3.15905, 6, 10000), +(29310, 0, 0, 30114, 385.693, -694.376, -16.0964, 3.59538, 6, 10000), +(29310, 0, 0, 30114, 379.204, -716.697, -16.0964, 2.1293, 6, 10000), +(29310, 0, 0, 30114, 375.4, -711.434, -16.0964, 2.09439, 6, 10000), +(29310, 0, 0, 30114, 382.583, -711.713, -16.0964, 2.53073, 6, 10000), +(29310, 0, 0, 30114, 379.049, -712.899, -16.0964, 2.28638, 6, 10000), +(29310, 0, 0, 30114, 378.424, -708.388, -16.0964, 2.58309, 6, 10000), +-- Triggers +(29310, 0, 1, 30181, 402.7893, -748.5251, 29.39399, 2.024582, 8, 0 ), +(29310, 0, 1, 30181, 420.1999, -727.0132, 28.88036, 2.042035, 8, 0 ); + +-- Elder Nadox +DELETE FROM `achievement_criteria_data` WHERE `criteria_id`=7317; +INSERT INTO `achievement_criteria_data` (`criteria_id`, `type`, `value1`, `value2`, `ScriptName`) VALUES +(7317, 11, 0, 0, 'achievement_respect_your_elders'), +(7317, 12, 1, 0, ''); + +-- Trash npcs +-- Eye of Teldaram +DELETE FROM `creature_template_addon` WHERE `entry`=31457; +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `isLarge`, `auras`) VALUES +(31457, 0, 0, 0, 0, 0, 0, '56572'); + +-- Visuals +-- Special thanks for Docmin (Dr-J) for base data +DELETE FROM `disables` WHERE `sourceType`=0 AND `entry` IN (56711, 56713); +INSERT INTO `disables` (`sourceType`, `entry`, `flags`, `params_0`, `params_1`, `comment`) VALUES +(0, 56711, 64, '', '', 'Disable LOS for Image Channel'), +(0, 56713, 64, '', '', 'Disable LOS for Image Channel'); +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` IN (56711,56713); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,56711,0,0,31,0,3,30413,132012,0,0,0,"","Group 0: Spell 'Image Channel' targets creature 'Channel Image Target'"), +(13,1,56711,0,1,31,0,3,30413,132013,0,0,0,"","Group 0: Spell 'Image Channel' targets creature 'Channel Image Target'"), +(13,1,56713,0,0,31,0,3,30413,132012,0,0,0,"","Group 0: Spell 'Image Channel' targets creature 'Channel Image Target'"), +(13,1,56713,0,1,31,0,3,30413,132013,0,0,0,"","Group 0: Spell 'Image Channel' targets creature 'Channel Image Target'"); +DELETE FROM `smart_scripts` WHERE `entryorguid` = 30111 AND `source_type` = 0 AND `id` IN (4,5); +DELETE FROM `smart_scripts` WHERE `entryorguid` = 30179 AND `source_type` = 0 AND `id` IN (4,5); +DELETE FROM `smart_scripts` WHERE `entryorguid` = 30319 AND `source_type` = 0 AND `id` IN (5,6); +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`event_param5`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_param4`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(30111,0,4,0,1,0,100,1,1000,1000,0,0,0,11,56711,32,0,0,0,0,19,30413,5,0,0,0,0,0,0,"Twilight Worshipper - Out of Combat - Cast 'Image Channel' (No Repeat)"), +(30111,0,5,0,1,0,100,1,1000,1000,0,0,0,11,56713,32,0,0,0,0,19,30413,5,0,0,0,0,0,0,"Twilight Worshipper - Out of Combat - Cast 'Image Channel' (No Repeat)"), +(30179,0,4,0,1,0,100,1,1000,1000,0,0,0,11,56711,32,0,0,0,0,19,30413,5,0,0,0,0,0,0,"Twilight Apostle - Out of Combat - Cast 'Image Channel' (No Repeat)"), +(30179,0,5,0,1,0,100,1,1000,1000,0,0,0,11,56713,32,0,0,0,0,19,30413,5,0,0,0,0,0,0,"Twilight Apostle - Out of Combat - Cast 'Image Channel' (No Repeat)"), +(30319,0,5,0,1,0,100,1,1000,1000,0,0,0,11,56711,32,0,0,0,0,19,30413,5,0,0,0,0,0,0,"Twilight Darkcaster - Out of Combat - Cast 'Image Channel' (No Repeat)"), +(30319,0,6,0,1,0,100,1,1000,1000,0,0,0,11,56713,32,0,0,0,0,19,30413,5,0,0,0,0,0,0,"Twilight Darkcaster - Out of Combat - Cast 'Image Channel' (No Repeat)"); + +-- Herald Volazj - Insanity spell script +DELETE FROM `spell_script_names` WHERE `spell_id`=57496 AND `ScriptName`='spell_herald_volzaj_insanity'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(57496, 'spell_herald_volzaj_insanity'); + +-- Spell "Shadow Sickle" difficulty data +DELETE FROM `spelldifficulty_dbc` WHERE `ID`=56701; +INSERT INTO `spelldifficulty_dbc` (`ID`, `DifficultySpellID_1`, `DifficultySpellID_2`, `DifficultySpellID_3`, `DifficultySpellID_4`) VALUES +(56701, 56701, 59104, 0, 0); + +-- +DELETE FROM `spell_script_names` WHERE `spell_id` IN (60291,60292,60293,60294,60295,60296,60297) AND `ScriptName` = "spell_volazj_whisper"; +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(60291,"spell_volazj_whisper"), +(60292,"spell_volazj_whisper"), +(60293,"spell_volazj_whisper"), +(60294,"spell_volazj_whisper"), +(60295,"spell_volazj_whisper"), +(60296,"spell_volazj_whisper"), +(60297,"spell_volazj_whisper"); + +-- SLAY_ based on https://wow.gamepedia.com/Herald_Volazj and https://wow.gamepedia.com/Shath%27Yar no sniffs for obvious reasons +DELETE FROM `creature_text` WHERE `CreatureID` = 29311; +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +/* 0 */ (29311,0,0,"Shgla'yos plahf mh'naus.",14,0,100,0,0,14043,32596,0,"Herald Volazj SAY_AGGRO"), +/* 0 */ (29311,1,0,"Gul'kafh an'shel. Yoq'al shn ky ywaq nuul.",14,0,100,0,0,14044,32603,0,"Herald Volazj SAY_INSANITY"), +/* 0 */ (29311,2,0,"Ywaq ma phgwa'cul hnakf.",14,0,100,0,0,14045,32598,0,"Herald Volazj SAY_SLAY_1"), +/* 0 */ (29311,3,0,"Uulwi gag erh'ongg w'ssh.",14,0,100,0,0,14046,32599,0,"Herald Volazj SAY_SLAY_2"), +/* 0 */ (29311,4,0,"Ywaq puul skshgn: on'ma yeh'glu zuq.",14,0,100,0,0,14047,32600,0,"Herald Volazj SAY_SLAY_3"), +/* 0 */ (29311,5,0,"Iilth vwah, uhn'agth fhssh za.",14,0,100,0,0,14048,32601,0,"Herald Volazj SAY_DEATH_1"), +/* 0 */ (29311,6,0,"Ywaq maq oou; ywaq maq ssaggh. Ywaq ma shg'fhn.",14,0,100,0,0,14049,32602,0,"Herald Volazj SAY_DEATH_2"), + +/* 60291 */ (29311,7,0,"They who dine on lost souls know only hunger.",15,0,100,0,0,0,32597,0,"Herald Volazj WHISPER_AGGRO"), +/* 60292 */ (29311,8,0,"Gaze into the void. It is the perpetuity in which they dwell.",15,0,100,0,0,0,32604,0,"Herald Volazj WHISPER_INSANITY"), +/* 60293 */ (29311,9,0,"They are the whisper on the shivering wind.",15,0,100,0,0,0,32605,0,"Herald Volazj WHISPER_SLAY_1"), +/* 60294 */ (29311,10,0,"Their shadow will choke the land for eternity.",15,0,100,0,0,0,32606,0,"Herald Volazj WHISPER_SLAY_2"), +/* 60295 */ (29311,11,0,"They drink your fear: it is the blood of life.",15,0,100,0,0,0,32607,0,"Herald Volazj WHISPER_SLAY_3"), +/* 60296 */ (29311,12,0,"Where one falls, many shall take its place.",15,0,100,0,0,0,32608,0,"Herald Volazj WHISPER_DEATH_1"), +/* 60297 */ (29311,13,0,"They do not die; they do not live. They are outside the cycle.",15,0,100,0,0,0,32609,0,"Herald Volazj WHISPER_DEATH_2"); + +-- Increase Jedoga prieching text range to map wide +UPDATE creature_text SET `TextRange`=3 WHERE `CreatureID`=29310 AND `GroupID`=5; + +-- Taldaram Spheres +-- PS: not sure if "31458" entry actually should be used (its difficulty entry1 of 30106) +UPDATE creature_template SET modelid2 = 0, flags_extra=flags_extra|64|128 WHERE entry IN (30106, 31686, 31687, 31458); + +-- Savage Cave Beast should have herbalism loot (copied skinloot from normal mode) +UPDATE creature_template SET skinloot=80007 WHERE entry=31470; + +-- Amanitar immunities +UPDATE creature_template SET + mechanic_immune_mask=1|2|4|8|16|32|64|256|512|1024|2048|4096|8192|65536|131072|8388608|33554432|67108864|536870912, + skinloot=80007 +WHERE entry IN (30258, 31463); diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/ahnkahet.h b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/ahnkahet.h index 161008be1..87bd4202f 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/ahnkahet.h +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/ahnkahet.h @@ -1,50 +1,56 @@ /* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2008-2016 TrinityCore */ #ifndef DEF_AHNKAHET_H #define DEF_AHNKAHET_H -#define MAX_ENCOUNTER 5 - -#include "CreatureAIImpl.h" - #define AhnahetScriptName "instance_ahnkahet" - -enum Data64 +constexpr uint32 MAX_ENCOUNTER = 5; +enum AhnkahetData { - DATA_ELDER_NADOX, - DATA_PRINCE_TALDARAM, - DATA_JEDOGA_SHADOWSEEKER, - DATA_HERALD_VOLAZJ, - DATA_AMANITAR, + // Main encounters + DATA_ELDER_NADOX = 0, + DATA_PRINCE_TALDARAM = 1, + DATA_JEDOGA_SHADOWSEEKER = 2, + DATA_HERALD_VOLAZJ = 3, + DATA_AMANITAR = 4, + + // Other data + // Teldram encounter related DATA_PRINCE_TALDARAM_PLATFORM, + DATA_TELDRAM_SPHERE1, + DATA_TELDRAM_SPHERE2, }; -enum Data -{ - DATA_ELDER_NADOX_EVENT, - DATA_PRINCE_TALDARAM_EVENT, - DATA_JEDOGA_SHADOWSEEKER_EVENT, - DATA_HERALD_VOLAZJ_EVENT, - DATA_AMANITAR_EVENT, - DATA_SPHERE_EVENT, - - DATA_NADOX_ACHIEVEMENT, - DATA_JEDOGA_ACHIEVEMENT, -}; - -enum Npc +enum AhnKahetCreatures { NPC_ELDER_NADOX = 29309, NPC_PRINCE_TALDARAM = 29308, NPC_JEDOGA_SHADOWSEEKER = 29310, NPC_HERALD_JOLAZJ = 29311, NPC_AMANITAR = 30258, + // Teldaram and Jedoga encounter related + NPC_JEDOGA_CONTROLLER = 30181, +}; - //spells +enum AhnkahetSpells +{ SPELL_SHADOW_SICKLE = 56701, // Shadow Sickle Normal - SPELL_SHADOW_SICKLE_H = 59104 // Shadow Sickle Heroic +}; + +enum AhnkahetObjects +{ + GO_TELDARAM_DOOR = 192236, + GO_TELDARAM_SPHERE1 = 193093, + GO_TELDARAM_SPHERE2 = 193094, + GO_TELDARAM_PLATFORM = 193564, +}; + +enum AhnKahetActions +{ + ACTION_REMOVE_PRISON = -1 }; template @@ -53,4 +59,4 @@ inline AI* GetAhnkahetAI(T* obj) return GetInstanceAI(obj, AhnahetScriptName); } -#endif +#endif // DEF_AHNKAHET_H diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp index 0490f824d..a818c645b 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp @@ -2,9 +2,11 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ -#include "ahnkahet.h" -#include "ScriptedCreature.h" #include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "ahnkahet.h" +#include "Player.h" +#include "SpellScript.h" enum Spells { @@ -12,10 +14,17 @@ enum Spells SPELL_ENTANGLING_ROOTS = 57095, SPELL_MINI = 57055, SPELL_VENOM_BOLT_VOLLEY = 57088, + SPELL_REMOVE_MUSHROOM_POWER = 57283, + + // Mushroom SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648, SPELL_POISONOUS_MUSHROOM_POISON_CLOUD = 57061, SPELL_POISONOUS_MUSHROOM_VISUAL_AURA = 56741, + SPELL_POISONOUS_MUSHROOM_VISUAL_AREA = 61566, // Self SPELL_HEALTHY_MUSHROOM_VISUAL_AURA = 56740, + SPELL_PUTRID_MUSHROOM = 31690, + SPELL_GROW = 57059, + SPELL_SHRINK = 31691, }; enum Creatures @@ -26,138 +35,192 @@ enum Creatures enum Events { - EVENT_AMANITAR_SPAWN = 1, - EVENT_AMANITAR_ROOTS = 2, - EVENT_AMANITAR_BASH = 3, - EVENT_AMANITAR_BOLT = 4, - EVENT_AMANITAR_MINI = 5 + // Boss + EVENT_RESPAWN = 1, + EVENT_ROOTS, + EVENT_BASH, + EVENT_BOLT, + EVENT_REMOVE_MUSHROOM_POWER, + EVENT_MINI, + + // Mushroom + EVENT_GROW, + EVENT_CHECK_PLAYER, + EVENT_KILLSELF, +}; + +constexpr uint8 MAX_MUSHROOMS_COUNT = 32; +Position const MushroomPositions[MAX_MUSHROOMS_COUNT] = +{ + { 373.4807f, -856.5301f, -74.30518f, 0.2094395f }, + { 358.4792f, -879.3193f, -75.9463f, 5.166174f }, + { 356.5531f, -846.3022f, -72.1796f, 3.193953f }, + { 332.369f, -846.081f, -74.30516f, 4.834562f }, + { 360.2234f, -862.055f, -75.22755f, 1.658063f }, + { 351.7189f, -890.9619f, -76.54617f, 1.064651f }, + { 345.8126f, -869.1772f, -77.17728f, 1.361357f }, + { 367.5179f, -884.0129f, -77.32881f, 4.276057f }, + { 370.6044f, -868.4305f, -74.19881f, 0.8901179f }, + { 381.3156f, -873.2377f, -74.82656f, 1.099557f }, + { 371.5869f, -873.8141f, -74.72424f, 1.082104f }, + { 340.4079f, -891.6375f, -74.99128f, 1.134464f }, + { 368.21f, -851.5953f, -73.99741f, 4.694936f }, + { 328.7047f, -853.9812f, -75.51253f, 0.5759587f }, + { 366.4145f, -876.39f, -75.52739f, 5.253441f }, + { 380.1362f, -861.4344f, -73.45917f, 3.787364f }, + { 373.3007f, -888.8057f, -79.03593f, 5.602507f }, + { 348.3599f, -848.0839f, -73.54117f, 1.745329f }, + { 352.5586f, -882.6624f, -75.68202f, 3.822271f }, + { 357.8967f, -871.179f, -75.77553f, 2.443461f }, + { 360.1034f, -842.3351f, -71.08852f, 4.34587f }, + { 348.1334f, -861.5244f, -74.61307f, 2.565634f }, + { 401.4896f, -866.7059f, -73.22395f, 0.8901179f }, + { 360.1683f, -889.1515f, -76.74798f, 3.612832f }, + { 350.1828f, -907.7313f, -74.94678f, 5.044002f }, + { 340.6278f, -856.5973f, -74.23862f, 4.415683f }, + { 366.4849f, -859.7621f, -74.82679f, 1.500983f }, + { 359.1482f, -853.3346f, -74.47543f, 5.654867f }, + { 374.9992f, -879.0921f, -75.56115f, 1.867502f }, + { 339.5252f, -850.4612f, -74.45442f, 4.764749f }, + { 337.0534f, -864.002f, -75.72749f, 4.642576f }, + { 398.2797f, -851.8694f, -68.84419f, 0.5759587f } }; class boss_amanitar : public CreatureScript { public: - boss_amanitar() : CreatureScript("boss_amanitar") { } - - struct boss_amanitarAI : public ScriptedAI + boss_amanitar() : CreatureScript("boss_amanitar") { - boss_amanitarAI(Creature* c) : ScriptedAI(c), summons(me) - { - pInstance = c->GetInstanceScript(); - } + } - InstanceScript* pInstance; - EventMap events; - SummonList summons; + struct boss_amanitarAI : public BossAI + { + boss_amanitarAI(Creature *creature) : BossAI(creature, DATA_AMANITAR), mushroomsSummoned(false) + { + creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); + creature->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE); + } void Reset() override { - events.Reset(); - summons.DespawnAll(); - me->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE); + _Reset(); + _mushroomsDeque.clear(); + mushroomsSummoned = false; + } - if (pInstance) - { - pInstance->SetData(DATA_AMANITAR_EVENT, NOT_STARTED); - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); - } + void EnterCombat(Unit* /*attacker*/) override + { + events.ScheduleEvent(EVENT_ROOTS, urand(5000, 9000)); + events.ScheduleEvent(EVENT_BASH, urand(10000, 14000)); + events.ScheduleEvent(EVENT_BOLT, urand(15000, 20000)); + events.ScheduleEvent(EVENT_MINI, 1000); + events.ScheduleEvent(EVENT_RESPAWN, 40000, 60000); } void JustDied(Unit* /*Killer*/) override { - summons.DespawnAll(); - if (pInstance) + _JustDied(); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + } + + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + _mushroomsDeque.push_back(summon->GetPosition()); + BossAI::SummonedCreatureDies(summon, killer); + } + + void EnterEvadeMode() override + { + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); + BossAI::EnterEvadeMode(); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - pInstance->SetData(DATA_AMANITAR_EVENT, DONE); - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); + case EVENT_RESPAWN: + { + while (!_mushroomsDeque.empty()) + { + SummonMushroom(_mushroomsDeque.front()); + _mushroomsDeque.pop_front(); + } + + events.RepeatEvent(urand(40000, 60000)); + break; + } + case EVENT_ROOTS: + { + if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + { + DoCast(pTarget, SPELL_ENTANGLING_ROOTS, false); + } + + events.RepeatEvent(urand(10000, 15000)); + break; + } + case EVENT_BASH: + { + DoCastVictim(SPELL_BASH, false); + events.RepeatEvent(urand(15000, 20000)); + break; + } + case EVENT_BOLT: + { + if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + { + DoCast(pTarget, SPELL_VENOM_BOLT_VOLLEY, false); + } + + events.RepeatEvent(urand(15000, 20000)); + break; + } + case EVENT_REMOVE_MUSHROOM_POWER: + { + DoCastAOE(SPELL_REMOVE_MUSHROOM_POWER, true); + events.RescheduleEvent(EVENT_MINI, 1000); + break; + } + case EVENT_MINI: + { + if (!mushroomsSummoned) + { + mushroomsSummoned = true; + for (uint8 i = 0; i < MAX_MUSHROOMS_COUNT; ++i) + { + SummonMushroom(MushroomPositions[i]); + } + } + + if (SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_MINI)) + { + DoCastSelf(SPELL_REMOVE_MUSHROOM_POWER, true); + DoCastAOE(SPELL_MINI); + events.RescheduleEvent(EVENT_REMOVE_MUSHROOM_POWER, 29000); + } + else + { + events.RepeatEvent(1000); + } + + break; + } } } - void EnterCombat(Unit* /*who*/) override + private: + std::deque _mushroomsDeque; + bool mushroomsSummoned; + + void SummonMushroom(Position const& pos) { - if (pInstance) - pInstance->SetData(DATA_AMANITAR_EVENT, IN_PROGRESS); - - events.ScheduleEvent(EVENT_AMANITAR_ROOTS, urand(5000, 9000)); - events.ScheduleEvent(EVENT_AMANITAR_BASH, urand(10000, 14000)); - events.ScheduleEvent(EVENT_AMANITAR_BOLT, urand(15000, 20000)); - events.ScheduleEvent(EVENT_AMANITAR_MINI, 30000); - events.ScheduleEvent(EVENT_AMANITAR_SPAWN, 0); - } - - void JustSummoned(Creature* cr) override { summons.Summon(cr); } - - void SpawnAdds() - { - summons.DespawnAll(); - Position center; - center.Relocate(362.6f, -870, -75); - - for (uint8 i = 0; i < 25; ++i) - { - float orientation = 2 * rand_norm() * M_PI; - float x = center.GetPositionX() + i * 2 * cos(orientation); - float y = center.GetPositionY() + i * 2 * sin(orientation); - me->SummonCreature(NPC_POISONOUS_MUSHROOM, x, y, me->GetMap()->GetHeight(x, y, MAX_HEIGHT)); - } - - for (uint8 i = 0; i < 25; ++i) - { - float orientation = 2 * rand_norm() * M_PI; - float x = center.GetPositionX() + i * 2 * cos(orientation); - float y = center.GetPositionY() + i * 2 * sin(orientation); - me->SummonCreature(NPC_HEALTHY_MUSHROOM, x, y, me->GetMap()->GetHeight(x, y, MAX_HEIGHT)); - } - } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_AMANITAR_SPAWN: - { - SpawnAdds(); - events.RepeatEvent(urand(35000, 40000)); - break; - } - case EVENT_AMANITAR_ROOTS: - { - if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - me->CastSpell(pTarget, SPELL_ENTANGLING_ROOTS, false); - - events.RepeatEvent(urand(15000, 20000)); - break; - } - case EVENT_AMANITAR_BASH: - { - me->CastSpell(me->GetVictim(), SPELL_BASH, false); - events.RepeatEvent(urand(15000, 20000)); - break; - } - case EVENT_AMANITAR_BOLT: - { - if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - me->CastSpell(pTarget, SPELL_VENOM_BOLT_VOLLEY, false); - - events.RepeatEvent(urand(15000, 20000)); - break; - } - case EVENT_AMANITAR_MINI: - { - me->CastSpell(me, SPELL_MINI, false); - events.RepeatEvent(30000); - break; - } - } - - DoMeleeAttackIfReady(); + me->SummonCreature(roll_chance_i(40) ? NPC_HEALTHY_MUSHROOM : NPC_POISONOUS_MUSHROOM, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 4000); } }; @@ -174,61 +237,94 @@ public: struct npc_amanitar_mushroomsAI : public ScriptedAI { - npc_amanitar_mushroomsAI(Creature* c) : ScriptedAI(c) + npc_amanitar_mushroomsAI(Creature* pCreature) : ScriptedAI(pCreature) { SetCombatMovement(false); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - } - - uint32 Timer; - void Reset() override - { - me->CastSpell(me, 31690, true); - - Timer = 0; - if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) - { - me->CastSpell(me, SPELL_POISONOUS_MUSHROOM_VISUAL_AURA, true); - me->CastSpell(me, SPELL_POISONOUS_MUSHROOM_POISON_CLOUD, false); - } - else - me->CastSpell(me, SPELL_HEALTHY_MUSHROOM_VISUAL_AURA, true); - } - - void JustDied(Unit* killer) override - { - if (!killer) - return; - - if (me->GetEntry() == NPC_HEALTHY_MUSHROOM) - { - if (killer->HasAura(SPELL_MINI)) - { - killer->RemoveAurasDueToSpell(SPELL_MINI); - } - else - { - DoCast(killer, SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS); - } - } + + //TODO: this prolly needs to be done in database + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pCreature->SetRegeneratingHealth(false); } + // Disabled events void EnterCombat(Unit* /*who*/) override {} void AttackStart(Unit* /*victim*/) override {} + void EnterEvadeMode() override {} + + void Reset() override + { + me->SetReactState(REACT_PASSIVE); + DoCastSelf(SPELL_PUTRID_MUSHROOM); + + if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) + { + DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AURA, true); + } + else + { + DoCastSelf(SPELL_HEALTHY_MUSHROOM_VISUAL_AURA, true); + } + + events.ScheduleEvent(EVENT_GROW, 800); + + if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) + { + events.ScheduleEvent(EVENT_CHECK_PLAYER, 250); + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + if (me->GetEntry() == NPC_HEALTHY_MUSHROOM && damage >= me->GetHealth()) + { + DoCastSelf(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS, true); + } + } void UpdateAI(uint32 diff) override { - if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) + if (events.Empty()) + return; + + events.Update(diff); + while (uint32 const eventId = events.ExecuteEvent()) { - Timer += diff; - if (Timer >= 7000) + switch (eventId) { - me->CastSpell(me, SPELL_POISONOUS_MUSHROOM_POISON_CLOUD, false); - Timer = 0; + case EVENT_GROW: + { + DoCastSelf(SPELL_GROW); + break; + } + case EVENT_CHECK_PLAYER: + { + if (Player* plr = me->SelectNearestPlayer(2.0f)) + { + plr->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS); + DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AREA); + DoCastSelf(SPELL_POISONOUS_MUSHROOM_POISON_CLOUD); + DoCastSelf(SPELL_SHRINK); + events.ScheduleEvent(EVENT_KILLSELF, 4000); + } + else + { + events.RepeatEvent(250); + } + + break; + } + case EVENT_KILLSELF: + { + me->DisappearAndDie(); + break; + } } } } + + private: + EventMap events; }; CreatureAI* GetAI(Creature* creature) const override @@ -237,8 +333,41 @@ public: } }; +// 57283 Remove Mushroom Power +class spell_amanitar_remove_mushroom_power : public SpellScriptLoader +{ +public: + spell_amanitar_remove_mushroom_power() : SpellScriptLoader("spell_amanitar_remove_mushroom_power") { } + + class spell_amanitar_remove_mushroom_power_AuraScript : public AuraScript + { + PrepareAuraScript(spell_amanitar_remove_mushroom_power_AuraScript); + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* target = GetTarget()) + { + target->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS); + } + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_amanitar_remove_mushroom_power_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_amanitar_remove_mushroom_power_AuraScript(); + } +}; + void AddSC_boss_amanitar() { - new boss_amanitar; - new npc_amanitar_mushrooms; + new boss_amanitar(); + new npc_amanitar_mushrooms(); + + // Spells + new spell_amanitar_remove_mushroom_power(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_elder_nadox.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_elder_nadox.cpp index a7db073cd..14dd81d01 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_elder_nadox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_elder_nadox.cpp @@ -2,27 +2,28 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ -#include "ahnkahet.h" -#include "ScriptedCreature.h" #include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "ahnkahet.h" #include "SpellAuras.h" #include "SpellScript.h" +#include "Containers.h" enum Misc { // ACTIONS - ACTION_GUARDIAN_DIED = 1, + DATA_RESPECT_YOUR_ELDERS = 1, }; enum Spells { // NADOX SPELL_BROOD_PLAGUE = 56130, - SPELL_BROOD_PLAGUE_H = 59467, - SPELL_BROOD_RAGE_H = 59465, - SPELL_ENRAGE = 26662, // Enraged if too far away from home - //SPELL_SUMMON_SWARMERS = 56119, //2x 30178 -- 2x every 10secs, spell works fine but i need specific coords - //SPELL_SUMMON_SWARM_GUARD = 56120, //1x 30176 -- at 50%hp, spell works fine but i need specific coords + SPELL_BROOD_RAGE_H = 59465, // Only in heroic + SPELL_ENRAGE = 26662, // Enraged if too far away from home + SPELL_SUMMON_SWARMERS = 56119, // 2x NPC_AHNKAHAR_SWARMER + SPELL_SUMMON_SWARM_GUARD = 56120, // 1x NPC_AHNKAHAR_GUARDIAN_ENTRY -- at 50%hp + SPELL_SWARM = 56281, // ADDS SPELL_SPRINT = 56354, @@ -33,26 +34,26 @@ enum Spells enum Creatures { NPC_AHNKAHAR_SWARMER = 30178, - NPC_AHNKAHAR_GUARDIAN_ENTRY = 30176, + NPC_AHNKAHAR_GUARDIAN = 30176, + NPC_AHNKAHAR_SWARM_EGG = 30172, + NPC_AHNKAHAR_GUARDIAN_EGG = 30173, }; enum Events { - EVENT_CHECK_HEALTH = 1, - EVENT_CHECK_HOME = 2, - EVENT_PLAGUE = 3, - EVENT_BROOD_RAGE = 4, - EVENT_SWARMER = 5, - EVENT_SUMMON_GUARD = 6, + EVENT_CHECK_HOME = 1, + EVENT_PLAGUE, + EVENT_BROOD_RAGE, + EVENT_SWARMER, }; enum Yells { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_EGG_SAC = 3, - EMOTE_HATCHES = 4 + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_EGG_SAC = 3, + EMOTE_HATCHES = 4 }; class boss_elder_nadox : public CreatureScript @@ -60,163 +61,231 @@ class boss_elder_nadox : public CreatureScript public: boss_elder_nadox() : CreatureScript("boss_elder_nadox") { } - struct boss_elder_nadoxAI : public ScriptedAI + struct boss_elder_nadoxAI : public BossAI { - boss_elder_nadoxAI(Creature* c) : ScriptedAI(c), summons(me) + boss_elder_nadoxAI(Creature* creature) : BossAI(creature, DATA_PRINCE_TALDARAM), + guardianSummoned(false), + respectYourElders(true) { - pInstance = c->GetInstanceScript(); - } - - EventMap events; - InstanceScript* pInstance; - SummonList summons; - - void SummonHelpers(bool swarm) - { - Creature* cr; - if (swarm) - { - if ((cr = me->SummonCreature(NPC_AHNKAHAR_SWARMER, 640.425f, -919.544f, 25.8701f, 2.56563f))) - summons.Summon(cr); - if ((cr = me->SummonCreature(NPC_AHNKAHAR_SWARMER, 655.891f, -930.445f, 25.6978f, 3.64774f))) - summons.Summon(cr); - } - else - { - if ((cr = me->SummonCreature(NPC_AHNKAHAR_GUARDIAN_ENTRY, 658.677f, -934.332f, 25.6978f, 3.03687f))) - summons.Summon(cr); - } } void Reset() override { - events.Reset(); - summons.DespawnAll(); + _Reset(); - if (pInstance) + // Clear eggs data + swarmEggs.clear(); + guardianEggs.clear(); + previousSwarmEgg_GUID.Clear(); + guardianSummoned = false; + respectYourElders = true; + } + + void EnterCombat(Unit * /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + + events.ScheduleEvent(EVENT_SWARMER, 10000); + events.ScheduleEvent(EVENT_CHECK_HOME, 2000); + events.ScheduleEvent(EVENT_PLAGUE, urand(5000, 8000)); + + if (IsHeroic()) { - pInstance->SetData(DATA_ELDER_NADOX_EVENT, NOT_STARTED); - pInstance->SetData(DATA_NADOX_ACHIEVEMENT, true); + events.ScheduleEvent(EVENT_BROOD_RAGE, 5000); + } + + // Cache eggs + std::list eggs; + // Swarm eggs + me->GetCreatureListWithEntryInGrid(eggs, NPC_AHNKAHAR_SWARM_EGG, 250.0f); + if (!eggs.empty()) + { + for (Creature* const egg : eggs) + { + if (egg) + { + swarmEggs.push_back(egg->GetGUID()); + } + } + } + + eggs.clear(); + + // Guardian eggs + me->GetCreatureListWithEntryInGrid(eggs, NPC_AHNKAHAR_GUARDIAN_EGG, 250.0f); + if (!eggs.empty()) + { + for (Creature* const egg : eggs) + { + if (egg) + { + guardianEggs.push_back(egg->GetGUID()); + } + } } } - void EnterCombat(Unit* /*who*/) override + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override { - Talk(SAY_AGGRO); - - events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000); - events.ScheduleEvent(EVENT_SWARMER, 10000); - events.ScheduleEvent(EVENT_CHECK_HOME, 2000); - events.ScheduleEvent(EVENT_PLAGUE, 5000 + rand() % 3000); - events.ScheduleEvent(EVENT_BROOD_RAGE, 5000); - - if (pInstance) - pInstance->SetData(DATA_ELDER_NADOX_EVENT, IN_PROGRESS); - } - - void DoAction(int32 param) override - { - if (param == ACTION_GUARDIAN_DIED) + if (summon->GetEntry() == NPC_AHNKAHAR_GUARDIAN) { - if (pInstance) - pInstance->SetData(DATA_NADOX_ACHIEVEMENT, false); + respectYourElders = false; } } void KilledUnit(Unit* victim) override { if (victim->GetTypeId() == TYPEID_PLAYER) + { Talk(SAY_SLAY); + } } void JustDied(Unit* /*killer*/) override { - events.Reset(); - summons.DespawnAll(); - + _JustDied(); Talk(SAY_DEATH); - - if (pInstance) - pInstance->SetData(DATA_ELDER_NADOX_EVENT, DONE); } - void JustSummoned(Creature* cr) override + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*school*/) override { - if (cr) + if (!guardianSummoned && me->HealthBelowPctDamaged(55, damage)) { - if (cr->GetEntry() == NPC_AHNKAHAR_GUARDIAN_ENTRY ) - Talk(SAY_EGG_SAC); - - summons.Summon(cr); + SummonHelpers(false); + guardianSummoned = true; } } + uint32 GetData(uint32 type) const override + { + if (type == DATA_RESPECT_YOUR_ELDERS) + { + return respectYourElders ? 1 : 0; + } + + return 0; + } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) + { return; + } events.Update(diff); - if( me->HasUnitState(UNIT_STATE_CASTING) ) - return; - - switch ( events.ExecuteEvent() ) + if (me->HasUnitState(UNIT_STATE_CASTING)) { - case EVENT_CHECK_HEALTH: - { - events.RepeatEvent(1000); - if (HealthBelowPct(50)) - { - events.CancelEvent(EVENT_CHECK_HEALTH); - events.ScheduleEvent(EVENT_SUMMON_GUARD, 100); - } - break; - } - case EVENT_SUMMON_GUARD: - { - Talk(EMOTE_HATCHES, me); - SummonHelpers(false); - break; - } - case EVENT_BROOD_RAGE: + return; + } + + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BROOD_RAGE: { if (Creature* pSwarmer = me->FindNearestCreature(NPC_AHNKAHAR_SWARMER, 40, true)) - me->CastSpell(pSwarmer, SPELL_BROOD_RAGE_H, true); + DoCast(pSwarmer, SPELL_BROOD_RAGE_H, true); events.RepeatEvent(10000); break; } - case EVENT_PLAGUE: + case EVENT_PLAGUE: { - me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_BROOD_PLAGUE, SPELL_BROOD_PLAGUE_H), false); - events.RepeatEvent(12000 + rand() % 5000); + DoCastVictim(SPELL_BROOD_PLAGUE, false); + events.RepeatEvent(urand(12000, 17000)); break; } - case EVENT_SWARMER: + case EVENT_SWARMER: { SummonHelpers(true); events.RepeatEvent(10000); break; } - case EVENT_CHECK_HOME: + case EVENT_CHECK_HOME: { - if (me->HasAura(SPELL_ENRAGE)) - break; - - if (me->GetPositionZ() < 24) + if (!me->HasAura(SPELL_ENRAGE) && (me->GetPositionZ() < 24.0f || !me->GetHomePosition().IsInDist(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 110.0f))) { - me->CastSpell(me, SPELL_ENRAGE, true); - break; + DoCastSelf(SPELL_ENRAGE, true); } - - events.RepeatEvent(2000); break; } + } } DoMeleeAttackIfReady(); } + + private: + GuidList swarmEggs; + GuidList guardianEggs; + ObjectGuid previousSwarmEgg_GUID; // This will prevent casting summoning spells on same egg twice + bool guardianSummoned; + bool respectYourElders; + + void SummonHelpers(bool swarm) + { + if (swarm) + { + if (swarmEggs.empty()) + { + return; + } + + // Make a copy of guid list + GuidList swarmEggs2 = swarmEggs; + + // Remove previous egg + if (previousSwarmEgg_GUID) + { + std::list::iterator itr = std::find(swarmEggs2.begin(), swarmEggs2.end(), previousSwarmEgg_GUID); + if (itr != swarmEggs2.end()) + { + swarmEggs2.erase(itr); + } + } + + if (swarmEggs2.empty()) + { + return; + } + + previousSwarmEgg_GUID = Acore::Containers::SelectRandomContainerElement(swarmEggs2); + + if (Creature* egg = ObjectAccessor::GetCreature(*me, previousSwarmEgg_GUID)) + { + egg->CastSpell(egg, SPELL_SUMMON_SWARMERS, true, nullptr, nullptr, me->GetGUID()); + } + + if (roll_chance_f(33)) + { + Talk(SAY_EGG_SAC); + } + } + else + { + if (guardianEggs.empty()) + { + return; + } + + ObjectGuid const& guardianEggGUID = Acore::Containers::SelectRandomContainerElement(guardianEggs); + if (Creature* egg = ObjectAccessor::GetCreature(*me, guardianEggGUID)) + { + egg->CastSpell(egg, SPELL_SUMMON_SWARM_GUARD, true, nullptr, nullptr, me->GetGUID()); + } + + Talk(EMOTE_HATCHES, me); + + if (roll_chance_f(33)) + { + Talk(SAY_EGG_SAC); + } + } + } }; CreatureAI* GetAI(Creature* creature) const override @@ -234,32 +303,12 @@ public: { npc_ahnkahar_nerubianAI(Creature* c) : ScriptedAI(c) { } - uint32 uiSprintTimer; void Reset() override { - if (me->GetEntry() == NPC_AHNKAHAR_GUARDIAN_ENTRY) - me->CastSpell(me, SPELL_GUARDIAN_AURA, true); - else // Swarmers - me->CastSpell(me, SPELL_SWARMER_AURA, true); - - if (me->GetEntry() == NPC_AHNKAHAR_SWARMER || me->GetEntry() == NPC_AHNKAHAR_GUARDIAN_ENTRY) - me->SetInCombatWithZone(); - + DoCastSelf(me->GetEntry() == NPC_AHNKAHAR_GUARDIAN ? SPELL_GUARDIAN_AURA : SPELL_SWARMER_AURA, true); uiSprintTimer = 10000; } - void JustDied(Unit* /*killer*/) override - { - if (me->GetEntry() == NPC_AHNKAHAR_GUARDIAN_ENTRY) - { - if (InstanceScript* pInstance = me->GetInstanceScript()) - if (Creature* nadox = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_ELDER_NADOX))) - nadox->AI()->DoAction(ACTION_GUARDIAN_DIED); - - me->RemoveAllAuras(); - } - } - void UpdateAI(uint32 diff) override { if (!UpdateVictim()) @@ -267,7 +316,7 @@ public: if (uiSprintTimer <= diff) { - me->CastSpell(me, SPELL_SPRINT, false); + DoCastSelf(SPELL_SPRINT, false); uiSprintTimer = 15000; } else @@ -275,6 +324,9 @@ public: DoMeleeAttackIfReady(); } + + private: + uint32 uiSprintTimer; }; CreatureAI* GetAI(Creature* creature) const override @@ -292,39 +344,41 @@ public: { PrepareSpellScript(spell_ahn_kahet_swarmer_aura_SpellScript) + void CountTargets(std::list& targets) + { + _targetCount = static_cast(targets.size()); + } + void HandleDummy(SpellEffIndex /*effIndex*/) { Unit* caster = GetCaster(); - std::list swarm, swarm2; - caster->GetCreaturesWithEntryInRange(swarm, 40.0f, 30338); - caster->GetCreaturesWithEntryInRange(swarm2, 40.0f, 30178); - int32 aliveCount = -1; // minus self - - std::list::const_iterator itr; - for (itr = swarm.begin(); itr != swarm.end(); ++itr) - if ((*itr)->IsAlive()) - aliveCount++; - for (itr = swarm2.begin(); itr != swarm2.end(); ++itr) - if ((*itr)->IsAlive()) - aliveCount++; - - if (Aura* aur = caster->GetAura(56281)) + if (_targetCount) { - if (aliveCount > 0) - aur->SetStackAmount(aliveCount); - else - aur->Remove(); + if (Aura *aur = caster->GetAura(SPELL_SWARM)) + { + aur->SetStackAmount(static_cast(_targetCount)); + } + else if (_targetCount) + { + // TODO: move spell id to enum + caster->CastCustomSpell(SPELL_SWARM, SPELLVALUE_AURA_STACK, _targetCount, caster, true); + if (Aura *aur = caster->GetAura(SPELL_SWARM)) + { + aur->SetStackAmount(static_cast(_targetCount)); + } + } } - else if (aliveCount > 0) + else { - caster->CastCustomSpell(caster, 56281, &aliveCount, &aliveCount, &aliveCount, true); - if (Aura* aur = caster->GetAura(56281)) - aur->SetStackAmount(aliveCount); + caster->RemoveAurasDueToSpell(SPELL_SWARM); } } + uint32 _targetCount; + void Register() override { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_ahn_kahet_swarmer_aura_SpellScript::CountTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); OnEffectHitTarget += SpellEffectFn(spell_ahn_kahet_swarmer_aura_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; @@ -335,9 +389,27 @@ public: } }; +// 7317 - Respect Your Elders (2038) +class achievement_respect_your_elders : public AchievementCriteriaScript +{ + public: + achievement_respect_your_elders() : AchievementCriteriaScript("achievement_respect_your_elders") { } + + bool OnCheck(Player* /*player*/, Unit* target, uint32 /*criteria_id*/) override + { + return target && target->GetAI()->GetData(DATA_RESPECT_YOUR_ELDERS); + } +}; + void AddSC_boss_elder_nadox() { + // Creatures new boss_elder_nadox(); new npc_ahnkahar_nerubian(); + + // Spells new spell_ahn_kahet_swarmer_aura(); + + // Achievements + new achievement_respect_your_elders(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp index a18febfe2..e23adc263 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp @@ -2,21 +2,19 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ +#include "ScriptMgr.h" +#include "ScriptedCreature.h" #include "ahnkahet.h" #include "Player.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" #include "SpellInfo.h" +#include "SpellScript.h" enum Spells { // BASIC FIGHT SPELL_MIND_FLAY = 57941, - SPELL_MIND_FLAY_H = 59974, SPELL_SHADOW_BOLT_VOLLEY = 57942, - SPELL_SHADOW_BOLT_VOLLEY_H = 59975, SPELL_SHIVER = 57949, - SPELL_SHIVER_H = 59978, // INSANITY SPELL_INSANITY = 57496, //Dummy @@ -27,169 +25,283 @@ enum Spells SPELL_INSANITY_PHASING_2 = 57509, SPELL_INSANITY_PHASING_3 = 57510, SPELL_INSANITY_PHASING_4 = 57511, - SPELL_INSANITY_PHASING_5 = 57512 + SPELL_INSANITY_PHASING_5 = 57512, + + SPELL_WHISPER_AGGRO = 60291, + SPELL_WHISPER_INSANITY = 60292, + SPELL_WHISPER_SLAY_1 = 60293, + SPELL_WHISPER_SLAY_2 = 60294, + SPELL_WHISPER_SLAY_3 = 60295, + SPELL_WHISPER_DEATH_1 = 60296, + SPELL_WHISPER_DEATH_2 = 60297 }; -enum Yells +enum Texts { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_PHASE = 3 + SAY_AGGRO = 0, + SAY_INSANITY = 1, + SAY_SLAY_1 = 2, + SAY_SLAY_2 = 3, + SAY_SLAY_3 = 4, + SAY_DEATH_1 = 5, + SAY_DEATH_2 = 6, + + WHISPER_AGGRO = 7, + WHISPER_INSANITY = 8, + WHISPER_SLAY_1 = 9, + WHISPER_SLAY_2 = 10, + WHISPER_SLAY_3 = 11, + WHISPER_DEATH_1 = 12, + WHISPER_DEATH_2 = 13 }; enum Misc { NPC_TWISTED_VISAGE = 30625, ACHIEV_QUICK_DEMISE_START_EVENT = 20382, + + MAX_INSANITY_TARGETS = 5, + DATA_SET_INSANITY_PHASE = 1, }; enum Events { EVENT_HERALD_MIND_FLAY = 1, - EVENT_HERALD_SHADOW = 2, - EVENT_HERALD_SHIVER = 3, - EVENT_HERALD_HEALTH = 4, + EVENT_HERALD_SHADOW, + EVENT_HERALD_SHIVER, }; +const std::array InsanitySpells = { SPELL_INSANITY_PHASING_1, SPELL_INSANITY_PHASING_2, SPELL_INSANITY_PHASING_3, SPELL_INSANITY_PHASING_4, SPELL_INSANITY_PHASING_5 }; + class boss_volazj : public CreatureScript { public: boss_volazj() : CreatureScript("boss_volazj") { } - struct boss_volazjAI : public ScriptedAI + struct boss_volazjAI : public BossAI { - boss_volazjAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me) + boss_volazjAI(Creature* pCreature) : BossAI(pCreature, DATA_HERALD_VOLAZJ), + insanityTimes(0), + insanityPhase(false) { - pInstance = pCreature->GetInstanceScript(); } - InstanceScript* pInstance; - EventMap events; - SummonList summons; - uint8 insanityTimes; - uint8 insanityHandled; + void InitializeAI() override + { + BossAI::InitializeAI(); + // Visible for all players in insanity + me->SetPhaseMask((1 | 16 | 32 | 64 | 128 | 256), true); + } void Reset() override { - events.Reset(); - summons.DespawnAll(); - insanityTimes = insanityHandled = 0; + _Reset(); + insanityTimes = 0; + insanityPhase = false; - // Visible for all players in insanity - me->SetPhaseMask((1 | 16 | 32 | 64 | 128 | 256), true); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetControlled(false, UNIT_STATE_STUNNED); + ResetPlayersPhaseMask(); + instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_QUICK_DEMISE_START_EVENT); + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + events.ScheduleEvent(EVENT_HERALD_MIND_FLAY, 8000); + events.ScheduleEvent(EVENT_HERALD_SHADOW, 5000); + events.ScheduleEvent(EVENT_HERALD_SHIVER, 15000); + Talk(SAY_AGGRO); + DoCastSelf(SPELL_WHISPER_AGGRO); + instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_QUICK_DEMISE_START_EVENT); + me->SetInCombatWithZone(); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetControlled(false, UNIT_STATE_STUNNED); ResetPlayersPhaseMask(); - if (pInstance) + switch (urand(0, 1)) { - pInstance->SetData(DATA_HERALD_VOLAZJ, NOT_STARTED); - pInstance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_QUICK_DEMISE_START_EVENT); + case 0: + { + Talk(SAY_DEATH_1); + DoCastSelf(SPELL_WHISPER_DEATH_1, true); + break; + } + case 1: + { + Talk(SAY_DEATH_2); + DoCastSelf(SPELL_WHISPER_DEATH_2, true); + break; + } } } - void SpellHitTarget(Unit* pTarget, const SpellInfo* spell) override + void KilledUnit(Unit* victim) override { - if (spell->Id == SPELL_INSANITY) + if (victim->GetTypeId() == TYPEID_PLAYER) { - // Not good target or too many players - if (pTarget->GetTypeId() != TYPEID_PLAYER || insanityHandled > 4) - return; - - // First target - start channel visual and set self as unnattackable - if (!insanityHandled) + switch (urand(0, 2)) { - me->RemoveAllAuras(); - me->CastSpell(me, INSANITY_VISUAL, true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetControlled(true, UNIT_STATE_STUNNED); + case 0: + { + Talk(SAY_SLAY_1); + DoCastSelf(SPELL_WHISPER_SLAY_1); + break; + } + case 1: + { + Talk(SAY_SLAY_2); + DoCastSelf(SPELL_WHISPER_SLAY_2); + break; + } + case 2: + { + Talk(SAY_SLAY_3); + DoCastSelf(SPELL_WHISPER_SLAY_3); + break; + } + } + } + } + + void SetData(uint32 type, uint32 value) override + { + if (type == DATA_SET_INSANITY_PHASE) + { + insanityPhase = (value != 0); + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + // Do not perform insanity recast if boss is casting Insanity already + if (me->FindCurrentSpellBySpellId(SPELL_INSANITY)) + { + return; + } + + // First insanity + if (insanityTimes == 0 && me->HealthBelowPctDamaged(66, damage)) + { + DoCastSelf(SPELL_INSANITY, false); + ++insanityTimes; + } + // Second insanity + else if (insanityTimes == 1 && me->HealthBelowPctDamaged(33, damage)) + { + me->InterruptNonMeleeSpells(false); + DoCastSelf(SPELL_INSANITY, false); + ++insanityTimes; + } + } + + void UpdateAI(uint32 diff) override + { + //Return since we have no target + if (!UpdateVictim()) + { + return; + } + + if (insanityPhase) + { + if (!CheckPhaseMinions()) + { + return; } - // phase mask - pTarget->CastSpell(pTarget, SPELL_INSANITY_TARGET + insanityHandled, true); + insanityPhase = false; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetControlled(false, UNIT_STATE_STUNNED); + me->RemoveAurasDueToSpell(INSANITY_VISUAL); + } - // summon twisted party members for this target - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; + } + + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) { - Player* plr = i->GetSource(); - if (!plr || !plr->IsAlive() || pTarget->GetGUID() == plr->GetGUID()) - continue; - - // Summon clone - if (Unit* summon = me->SummonCreature(NPC_TWISTED_VISAGE, plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), plr->GetOrientation(), TEMPSUMMON_CORPSE_DESPAWN, 0)) + case EVENT_HERALD_MIND_FLAY: { - summon->AddThreat(pTarget, 0.0f); - summon->SetInCombatWith(pTarget); - pTarget->SetInCombatWith(summon); + DoCastVictim(SPELL_MIND_FLAY, false); + events.RepeatEvent(20000); + break; + } + case EVENT_HERALD_SHADOW: + { + DoCastVictim(SPELL_SHADOW_BOLT_VOLLEY, false); + events.RepeatEvent(5000); + break; + } + case EVENT_HERALD_SHIVER: + { + if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + { + DoCast(pTarget, SPELL_SHIVER, false); + } - plr->CastSpell(summon, SPELL_CLONE_PLAYER, true); - summon->SetPhaseMask(1 | (1 << (4 + insanityHandled)), true); - summon->SetUInt32Value(UNIT_FIELD_MINDAMAGE, plr->GetUInt32Value(UNIT_FIELD_MINDAMAGE)); - summon->SetUInt32Value(UNIT_FIELD_MAXDAMAGE, plr->GetUInt32Value(UNIT_FIELD_MAXDAMAGE)); + events.RepeatEvent(15000); + break; } } - ++insanityHandled; + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; + } } + + DoMeleeAttackIfReady(); + } + + private: + uint8 insanityTimes; + bool insanityPhase; // Indicates if boss enter to insanity phase + + uint32 GetPlrInsanityAuraId(uint32 phaseMask) const + { + switch (phaseMask) + { + case 16: + return SPELL_INSANITY_PHASING_1; + case 32: + return SPELL_INSANITY_PHASING_2; + case 64: + return SPELL_INSANITY_PHASING_3; + case 128: + return SPELL_INSANITY_PHASING_4; + case 256: + return SPELL_INSANITY_PHASING_5; + } + + return 0; } void ResetPlayersPhaseMask() { Map::PlayerList const& players = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + for (auto const& i : players) { - if (Player* pPlayer = i->GetSource()) - if (uint32 spellId = GetSpellForPhaseMask(pPlayer->GetPhaseMask())) - pPlayer->RemoveAurasDueToSpell(spellId); + if (Player* pPlayer = i.GetSource()) + { + if (uint32 const insanityAura = GetPlrInsanityAuraId(pPlayer->GetPhaseMask())) + { + pPlayer->RemoveAurasDueToSpell(insanityAura); + } + } } } - void EnterCombat(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_HERALD_MIND_FLAY, 8000); - events.ScheduleEvent(EVENT_HERALD_SHADOW, 5000); - events.ScheduleEvent(EVENT_HERALD_SHIVER, 15000); - events.ScheduleEvent(EVENT_HERALD_HEALTH, 1000); - - Talk(SAY_AGGRO); - - if (pInstance) - { - pInstance->SetData(DATA_HERALD_VOLAZJ, IN_PROGRESS); - pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_QUICK_DEMISE_START_EVENT); - } - - me->SetInCombatWithZone(); - } - - void JustSummoned(Creature* summon) override { summons.Summon(summon); } - - uint32 GetSpellForPhaseMask(uint32 phase) - { - uint32 spell = 0; - switch (phase) - { - case 16: - spell = SPELL_INSANITY_PHASING_1; - break; - case 32: - spell = SPELL_INSANITY_PHASING_2; - break; - case 64: - spell = SPELL_INSANITY_PHASING_3; - break; - case 128: - spell = SPELL_INSANITY_PHASING_4; - break; - case 256: - spell = SPELL_INSANITY_PHASING_5; - break; - } - return spell; - } - bool CheckPhaseMinions() { summons.RemoveNotExisting(); @@ -199,105 +311,27 @@ public: return true; } - uint16 phase = 1; - for (ObjectGuid const& guid : summons) + uint32 phase = 1; + for (ObjectGuid const& summonGUID : summons) { - if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGUID)) + { phase |= summon->GetPhaseMask(); + } } Map::PlayerList const& players = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + for (auto const& i : players) { - if (Player* pPlayer = i->GetSource()) - if ((pPlayer->GetPhaseMask() & phase) == 0) - pPlayer->RemoveAurasDueToSpell(GetSpellForPhaseMask(pPlayer->GetPhaseMask())); + Player* pPlayer = i.GetSource(); + if (pPlayer && !(pPlayer->GetPhaseMask() & phase)) + { + pPlayer->RemoveAurasDueToSpell(GetPlrInsanityAuraId(pPlayer->GetPhaseMask())); + } } return false; } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - if (insanityHandled) - { - if (!CheckPhaseMinions()) - return; - - insanityHandled = 0; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetControlled(false, UNIT_STATE_STUNNED); - me->RemoveAurasDueToSpell(INSANITY_VISUAL); - } - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_HERALD_HEALTH: - { - if (insanityTimes == 0 && me->GetHealthPct() <= 66) - { - me->CastSpell(me, SPELL_INSANITY, false); - insanityTimes++; - } - else if (insanityTimes == 1 && me->GetHealthPct() <= 33) - { - me->CastSpell(me, SPELL_INSANITY, false); - insanityTimes++; - } - - events.RepeatEvent(1000); - break; - } - case EVENT_HERALD_MIND_FLAY: - { - me->CastSpell(me->GetVictim(), IsHeroic() ? SPELL_MIND_FLAY_H : SPELL_MIND_FLAY, false); - events.RepeatEvent(20000); - break; - } - case EVENT_HERALD_SHADOW: - { - me->CastSpell(me->GetVictim(), IsHeroic() ? SPELL_SHADOW_BOLT_VOLLEY_H : SPELL_SHADOW_BOLT_VOLLEY, false); - events.RepeatEvent(5000); - break; - } - case EVENT_HERALD_SHIVER: - { - if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0)) - me->CastSpell(pTarget, IsHeroic() ? SPELL_SHIVER_H : SPELL_SHIVER, false); - - events.RepeatEvent(15000); - break; - } - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - - if (pInstance) - pInstance->SetData(DATA_HERALD_VOLAZJ, DONE); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetControlled(false, UNIT_STATE_STUNNED); - summons.DespawnAll(); - ResetPlayersPhaseMask(); - } - - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_SLAY); - } }; CreatureAI* GetAI(Creature* creature) const override @@ -306,7 +340,187 @@ public: } }; +// 57496 Insanity +class spell_herald_volzaj_insanity : public SpellScriptLoader +{ +public: + spell_herald_volzaj_insanity() : SpellScriptLoader("spell_herald_volzaj_insanity") { } + + class spell_herald_volzaj_insanity_SpellScript : public SpellScript + { + PrepareSpellScript(spell_herald_volzaj_insanity_SpellScript); + + bool Load() override { return GetCaster()->GetTypeId() == TYPEID_UNIT; } + + void HandleDummyEffect(std::list& targets) + { + Unit* caster = GetCaster(); + if (!caster) + { + targets.clear(); + return; + } + + if (!targets.empty()) + { + targets.remove_if([this](WorldObject* targetObj) -> bool + { + return !targetObj || targetObj->GetTypeId() != TYPEID_PLAYER || !targetObj->ToPlayer()->IsInCombatWith(GetCaster()) || + targetObj->GetDistance(GetCaster()) >= (MAX_VISIBILITY_DISTANCE * 2); + }); + } + + if (targets.empty()) + { + return; + } + + // Start channel visual and set self as unnattackable + caster->ToCreature()->AI()->Talk(SAY_INSANITY); + caster->CastSpell(caster, SPELL_WHISPER_INSANITY, true); + caster->RemoveAllAuras(); + caster->CastSpell(caster, INSANITY_VISUAL, true); + caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + caster->SetControlled(true, UNIT_STATE_STUNNED); + + // Handle phase effect + uint32 insanityCounter = 0; + std::list::const_iterator itr = targets.begin(); + while (itr != targets.end() && insanityCounter < MAX_INSANITY_TARGETS) + { + WorldObject* targetObj = *itr; + if (!targetObj) + { + continue; + } + + Player* plrTarget = targetObj->ToPlayer(); + // This should never happen, spell has attribute SPELL_ATTR3_ONLY_TARGET_PLAYERS + if (!plrTarget) + { + continue; + } + + // phase mask + plrTarget->CastSpell(plrTarget, InsanitySpells.at(insanityCounter), true); + + // Summon clone + for (std::list::const_iterator itr2 = targets.begin(); itr2 != targets.end(); ++itr2) + { + // Should not make clone of current player target + Player const* plrClone = *itr2 ? (*itr2)->ToPlayer() : nullptr; + if (!plrClone || plrClone == plrTarget) + { + continue; + } + + if (Unit* summon = caster->SummonCreature(NPC_TWISTED_VISAGE, plrClone->GetPosition(), TEMPSUMMON_CORPSE_DESPAWN, 0)) + { + summon->AddThreat(plrTarget, 0.0f); + summon->SetInCombatWith(plrTarget); + plrTarget->SetInCombatWith(summon); + + plrTarget->CastSpell(summon, SPELL_CLONE_PLAYER, true); + summon->SetPhaseMask(1 | (1 << (4 + insanityCounter)), true); + summon->SetUInt32Value(UNIT_FIELD_MINDAMAGE, plrClone->GetUInt32Value(UNIT_FIELD_MINDAMAGE)); + summon->SetUInt32Value(UNIT_FIELD_MAXDAMAGE, plrClone->GetUInt32Value(UNIT_FIELD_MAXDAMAGE)); + } + } + + ++insanityCounter; + ++itr; + } + } + + void HandleAfterCast() + { + GetCaster()->ToCreature()->AI()->SetData(DATA_SET_INSANITY_PHASE, 1); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_herald_volzaj_insanity_SpellScript::HandleDummyEffect, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + AfterCast += SpellCastFn(spell_herald_volzaj_insanity_SpellScript::HandleAfterCast); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_herald_volzaj_insanity_SpellScript(); + } +}; + +// 60291 Volazj Whisper: Aggro +// 60292 Volazj Whisper: Insanity +// 60293 Volazj Whisper: Slay 01 +// 60294 Volazj Whisper: Slay 02 +// 60295 Volazj Whisper: Slay 03 +// 60296 Volazj Whisper: Death 01 +// 60297 Volazj Whisper: Death 02 +class spell_volazj_whisper : public SpellScriptLoader +{ +public: + spell_volazj_whisper() : SpellScriptLoader("spell_volazj_whisper") { } + + class spell_volazj_whisper_SpellScript : public SpellScript + { + PrepareSpellScript(spell_volazj_whisper_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( + { + SPELL_WHISPER_AGGRO, + SPELL_WHISPER_INSANITY, + SPELL_WHISPER_SLAY_1, + SPELL_WHISPER_SLAY_2, + SPELL_WHISPER_SLAY_3, + SPELL_WHISPER_DEATH_1, + SPELL_WHISPER_DEATH_2 + }); + } + + bool Load() override { return GetCaster()->GetTypeId() == TYPEID_UNIT; } + + void HandleScriptEffect(SpellEffIndex /* effIndex */) + { + Unit* target = GetHitPlayer(); + Creature* caster = GetCaster()->ToCreature(); + if (!target || !caster) + { + return; + } + + uint32 text = 0; + switch (GetSpellInfo()->Id) + { + case SPELL_WHISPER_AGGRO: text = WHISPER_AGGRO; break; + case SPELL_WHISPER_INSANITY: text = WHISPER_INSANITY; break; + case SPELL_WHISPER_SLAY_1: text = WHISPER_SLAY_1; break; + case SPELL_WHISPER_SLAY_2: text = WHISPER_SLAY_2; break; + case SPELL_WHISPER_SLAY_3: text = WHISPER_SLAY_3; break; + case SPELL_WHISPER_DEATH_1: text = WHISPER_DEATH_1; break; + case SPELL_WHISPER_DEATH_2: text = WHISPER_DEATH_2; break; + default: return; + } + caster->AI()->Talk(text, target); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_volazj_whisper_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_volazj_whisper_SpellScript(); + } +}; + void AddSC_boss_volazj() { new boss_volazj(); + new spell_herald_volzaj_insanity(); + new spell_volazj_whisper(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp index ed7d4270a..14327ac1e 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_jedoga_shadowseeker.cpp @@ -2,28 +2,40 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ -#include "ahnkahet.h" -#include "ScriptedCreature.h" #include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "ahnkahet.h" +#include "Containers.h" +#include "ObjectAccessor.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" +#include "TemporarySummon.h" enum Yells { - TEXT_AGGRO = 0, - TEXT_SACRIFICE_1 = 1, - TEXT_SACRIFICE_2 = 2, - TEXT_SLAY = 3, - TEXT_DEATH = 4, - TEXT_PREACHING = 5 + SAY_AGGRO = 0, + SAY_SACRIFICE_1 = 1, + SAY_SACRIFICE_2 = 2, + SAY_SLAY = 3, + SAY_DEATH = 4, + SAY_PREACHING = 5, + + // Initiate + SAY_CHOSEN = 0, + SAY_SACRIFICED = 1, }; enum Spells { // VISUALS - SPELL_PINK_SPHERE = 56075, + SPELL_SPHERE_VISUAL = 56075, SPELL_WHITE_SPHERE = 56102, SPELL_LIGHTNING_BOLTS = 56327, SPELL_ACTIVATE_INITIATE = 56868, SPELL_SACRIFICE_VISUAL = 56133, + SPELL_SACRIFICE_BEAM = 56150, + SPELL_HOVER_FALL = 56100, + SPELL_BEAM_VISUAL_JEDOGA = 56312, // FIGHT SPELL_GIFT_OF_THE_HERALD = 56219, @@ -37,33 +49,92 @@ enum Spells enum Events { + // Jedoga EVENT_JEDOGA_CYCLONE = 1, - EVENT_JEDOGA_LIGHTNING_BOLT = 2, - EVENT_JEDOGA_THUNDERSHOCK = 3, - EVENT_JEDOGA_MOVE_UP = 4, - EVENT_JEDOGA_MOVE_DOWN = 5, + EVENT_JEDOGA_LIGHTNING_BOLT, + EVENT_JEDOGA_THUNDERSHOCK, + EVENT_JEDOGA_PREPARE_RITUAL, + EVENT_JEDOGA_MOVE_UP, + EVENT_JEDOGA_MOVE_DOWN, + + // Initiate + EVENT_RITUAL_BEGIN_MOVE, }; -enum Misc +enum Creatures { - NPC_JEDOGA_CONTROLLER = 30181, - NPC_INITIATE = 30114, + NPC_TWILIGHT_INITIATE = 30114, + NPC_TWILIGHT_VOLUNTEER = 30385, +}; - ACTION_INITIATE_DIED = 1, - ACTION_ACTIVATE = 2, - ACTION_HERALD = 3, - ACTION_SACRIFICE_FAILED = 4, +enum Misc : uint32 +{ + MAX_COMBAT_INITIATES = 25, + DATA_VOLUNTEER_WORK = 1, +}; +enum SummonGroups +{ + SUMMON_GROUP_OOC = 0, + SUMMON_GROUP_OOC_TRIGGERS = 1, +}; + +enum Points +{ POINT_DOWN = 1, - POINT_UP = 2, - POINT_UP_START = 3, - POINT_RITUAL = 4, + POINT_UP, + POINT_RITUAL, + POINT_INITIAL, }; -const Position JedogaPosition[2] = +enum Phases { - {372.330994f, -705.278015f, -2.459692f, 5.628908f}, - {372.330994f, -705.278015f, -16.179716f, 5.628908f} + PHASE_NORMAL = 0x01, + PHASE_RITUAL = 0x02, +}; + +enum Actions +{ + ACTION_RITUAL_BEGIN = 1, + ACTION_SACRAFICE, +}; + +const Position JedogaPosition[3] = +{ + { 372.330994f, -705.278015f, -2.459692f }, // POINT_DOWN + { 372.330994f, -705.278015f, -16.179716f }, // POINT_UP + { 373.48f, -706.00f, -16.18f } // POINT_RITUAL and POINT_INITIAL. This positions also is used for visual trigger used for ritual +}; + +// Combat summon locations +const Position VolunteerSpotPositions[MAX_COMBAT_INITIATES][2] = +{ + // Spawn position || Move position + { { 400.7701f, -784.8928f, -31.60143f }, { 365.9514f, -719.1235f, -16.17974f } }, + { { 397.3595f, -788.5157f, -31.59679f }, { 359.7433f, -715.017f, -16.17974f } }, + { { 399.3177f, -787.2599f, -31.59631f }, { 362.0263f, -719.1036f, -16.17974f } }, + { { 460.4623f, -719.2227f, -31.58718f }, { 389.266f, -679.3693f, -16.17973f } }, + { { 456.0909f, -724.3412f, -31.58718f }, { 400.5992f, -691.7954f, -16.17973f } }, + { { 452.6613f, -726.9518f, -31.58718f }, { 400.3423f, -701.5115f, -16.17974f } }, + { { 447.8852f, -732.3298f, -31.58718f }, { 389.861f, -710.6993f, -16.17974f } }, + { { 457.562f, -721.1855f, -31.58718f }, { 395.4494f, -684.5345f, -16.17973f } }, + { { 451.7243f, -730.2181f, -31.58718f }, { 397.0945f, -708.4188f, -15.99747f } }, + { { 413.9582f, -777.132f, -31.58716f }, { 388.1394f, -723.124f, -15.9938f } }, + { { 411.5661f, -781.2356f, -31.58716f }, { 381.7102f, -730.0745f, -15.99554f } }, + { { 407.395f, -786.793f, -31.58716f }, { 366.9791f, -737.3303f, -16.17974f } }, + { { 404.9166f, -788.3472f, -31.58716f }, { 358.6124f, -735.9944f, -15.9855f } }, + { { 401.5697f, -791.2033f, -31.58717f }, { 351.9383f, -729.6436f, -16.17974f } }, + { { 410.1105f, -785.4691f, -31.58716f }, { 373.1659f, -736.2893f, -16.17974f } }, + { { 442.5644f, -730.2499f, -31.59826f }, { 390.5955f, -714.6851f, -16.17974f } }, + { { 445.5233f, -725.9542f, -31.60173f }, { 393.9694f, -708.1727f, -16.17974f } }, + { { 448.5531f, -722.5888f, -31.60066f }, { 395.2702f, -702.556f, -16.17974f } }, + { { 449.8521f, -719.7265f, -31.58849f }, { 394.5757f, -695.1004f, -16.17974f } }, + { { 453.5134f, -717.7018f, -31.59883f }, { 387.6152f, -690.1782f, -16.17974f } }, + { { 457.8564f, -711.7424f, -31.59773f }, { 378.6874f, -687.1343f, -16.17973f } }, + { { 410.0583f, -774.4119f, -31.60115f }, { 383.8151f, -723.4276f, -16.17974f } }, + { { 408.7458f, -777.955f, -31.59873f }, { 376.9857f, -725.0735f, -16.17974f } }, + { { 405.2404f, -779.6614f, -31.60512f }, { 373.3736f, -722.7498f, -16.17974f } }, + { { 404.0797f, -783.829f, -31.59497f }, { 367.8631f, -722.5212f, -16.17974f } } }; class boss_jedoga_shadowseeker : public CreatureScript @@ -71,320 +142,417 @@ class boss_jedoga_shadowseeker : public CreatureScript public: boss_jedoga_shadowseeker() : CreatureScript("boss_jedoga_shadowseeker") { } - struct boss_jedoga_shadowseekerAI : public ScriptedAI + struct boss_jedoga_shadowseekerAI : public BossAI { - boss_jedoga_shadowseekerAI(Creature* c) : ScriptedAI(c), summons(me) + boss_jedoga_shadowseekerAI(Creature* pCreature) : BossAI(pCreature, DATA_JEDOGA_SHADOWSEEKER), + sayPreachTimer(120000), + combatSummonsSummoned(false), + ritualTriggered(false), + volunteerWork(true) { - pInstance = c->GetInstanceScript(); } - InstanceScript* pInstance; - EventMap events; - SummonList summons; - - uint8 initiates; - uint32 introCheck; - bool isFlying; - bool startFly; - - void JustSummoned(Creature* cr) override { summons.Summon(cr); } - void MoveInLineOfSight(Unit*) override { } - - void SpawnInitiate(bool start) - { - summons.DespawnAll(); - if (start) - { - me->SummonCreature(NPC_INITIATE, 362.458f, -714.166f, -16.0964f, 0.977384f); - me->SummonCreature(NPC_INITIATE, 368.781f, -713.932f, -16.0964f, 1.46608f); - me->SummonCreature(NPC_INITIATE, 364.937f, -716.11f, -16.0964f, 1.25664f); - me->SummonCreature(NPC_INITIATE, 362.02f, -719.828f, -16.0964f, 1.20428f); - me->SummonCreature(NPC_INITIATE, 368.151f, -719.763f, -16.0964f, 1.53589f); - me->SummonCreature(NPC_INITIATE, 392.276f, -695.895f, -16.0964f, 3.40339f); - me->SummonCreature(NPC_INITIATE, 387.224f, -698.006f, -16.0964f, 3.36848f); - me->SummonCreature(NPC_INITIATE, 389.626f, -702.3f, -16.0964f, 3.07178f); - me->SummonCreature(NPC_INITIATE, 383.812f, -700.41f, -16.0964f, 3.15905f); - me->SummonCreature(NPC_INITIATE, 385.693f, -694.376f, -16.0964f, 3.59538f); - me->SummonCreature(NPC_INITIATE, 379.204f, -716.697f, -16.0964f, 2.1293f); - me->SummonCreature(NPC_INITIATE, 375.4f, -711.434f, -16.0964f, 2.09439f); - me->SummonCreature(NPC_INITIATE, 382.583f, -711.713f, -16.0964f, 2.53073f); - me->SummonCreature(NPC_INITIATE, 379.049f, -712.899f, -16.0964f, 2.28638f); - me->SummonCreature(NPC_INITIATE, 378.424f, -708.388f, -16.0964f, 2.58309f); - } - else - { - me->SummonCreature(NPC_INITIATE, 394.197f, -701.164f, -16.1797f, 4.09901f); - me->SummonCreature(NPC_INITIATE, 391.003f, -697.814f, -16.1797f, 4.11079f); - me->SummonCreature(NPC_INITIATE, 386.5f, -694.973f, -16.1797f, 4.12649f); - me->SummonCreature(NPC_INITIATE, 381.762f, -692.405f, -16.1797f, 4.12257f); - me->SummonCreature(NPC_INITIATE, 377.411f, -691.198f, -16.1797f, 4.6095f); - me->SummonCreature(NPC_INITIATE, 395.122f, -686.975f, -16.1797f, 2.72063f); - me->SummonCreature(NPC_INITIATE, 398.823f, -692.51f, -16.1797f, 2.72063f); - me->SummonCreature(NPC_INITIATE, 399.819f, -698.815f, -16.1797f, 2.72455f); - me->SummonCreature(NPC_INITIATE, 395.996f, -705.291f, -16.1309f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 391.505f, -710.883f, -16.0589f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 387.872f, -716.186f, -16.1797f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 383.276f, -722.431f, -16.1797f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 377.175f, -730.652f, -16.1797f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 371.625f, -735.5f, -16.1797f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 364.932f, -735.808f, -16.1797f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 358.966f, -733.199f, -16.1797f, 0.376213f); - me->SummonCreature(NPC_INITIATE, 376.348f, -725.037f, -16.1797f, 5.65409f); - me->SummonCreature(NPC_INITIATE, 371.435f, -723.892f, -16.1797f, 5.65409f); - me->SummonCreature(NPC_INITIATE, 366.861f, -721.702f, -16.1797f, 5.65409f); - me->SummonCreature(NPC_INITIATE, 362.343f, -718.019f, -16.1797f, 5.51665f); - me->SummonCreature(NPC_INITIATE, 358.906f, -714.357f, -16.1797f, 5.35957f); - } - } - - void ActivateInitiate() - { - if (!summons.size()) - return; - - uint8 rnd = urand(0, summons.size() - 1); - uint8 loop = 0; - for (GuidList::iterator i = summons.begin(); i != summons.end();) - { - Creature* summon = ObjectAccessor::GetCreature(*me, *i); - if (summon && summon->GetEntry() == NPC_INITIATE && loop >= rnd) - { - summon->AI()->DoAction(ACTION_ACTIVATE); - break; - } - - ++i; - ++loop; - } - - return; - } - - void ScheduleEvents() - { - events.RescheduleEvent(EVENT_JEDOGA_CYCLONE, 3000); - events.RescheduleEvent(EVENT_JEDOGA_LIGHTNING_BOLT, 7000); - events.RescheduleEvent(EVENT_JEDOGA_THUNDERSHOCK, 12000); - events.RescheduleEvent(EVENT_JEDOGA_MOVE_UP, urand(20000, 25000)); - } - - void DoAction(int32 param) override - { - if (param == ACTION_INITIATE_DIED) - { - // all killed - if (initiates++ > 13) - { - summons.DespawnAll(); - MoveDown(); - initiates = 0; - } - } - else if (param == ACTION_HERALD) - { - me->CastSpell(me, SPELL_GIFT_OF_THE_HERALD, true); - events.DelayEvents(1001); - events.ScheduleEvent(EVENT_JEDOGA_MOVE_DOWN, 1000); - isFlying = false; - } - else if (param == ACTION_SACRIFICE_FAILED) - { - events.DelayEvents(1001); - events.ScheduleEvent(EVENT_JEDOGA_MOVE_DOWN, 1000); - isFlying = false; - if (pInstance) - pInstance->SetData(DATA_JEDOGA_ACHIEVEMENT, false); - } - } + // Disabled events + void MoveInLineOfSight(Unit* /*who*/) override {} void Reset() override { - if (pInstance) + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC); + me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); + me->SetDisableGravity(true); + me->SetHover(true); + me->GetMotionMaster()->MovePoint(POINT_INITIAL, JedogaPosition[0], false); + + _Reset(); + events.SetPhase(PHASE_NORMAL); + + DespawnOOCSummons(); + std::list tempOOCSummons; + me->SummonCreatureGroup(SUMMON_GROUP_OOC, &tempOOCSummons); + if (!tempOOCSummons.empty()) { - pInstance->SetData(DATA_JEDOGA_SHADOWSEEKER_EVENT, NOT_STARTED); - pInstance->SetData(DATA_JEDOGA_ACHIEVEMENT, true); + for (TempSummon* summon : tempOOCSummons) + { + if (summon) + { + summon->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, false); + summon->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, false); + summon->RemoveAurasDueToSpell(SPELL_WHITE_SPHERE); + summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + summon->SetStandState(UNIT_STAND_STATE_KNEEL); + oocSummons.push_back(summon->GetGUID()); + } + } } - events.Reset(); - SpawnInitiate(true); - initiates = 0; - introCheck = 1; // leave 1 - isFlying = false; - startFly = false; + tempOOCSummons.clear(); - MoveUp(true); - me->CastSpell(me, SPELL_PINK_SPHERE, true); - me->CastSpell(me, SPELL_LIGHTNING_BOLTS, true); + me->SummonCreatureGroup(SUMMON_GROUP_OOC_TRIGGERS, &tempOOCSummons); + if (!tempOOCSummons.empty()) + { + for (TempSummon* trigger : tempOOCSummons) + { + if (trigger) + { + oocTriggers.push_back(trigger->GetGUID()); + } + } + } + + sacraficeTarget_GUID.Clear(); + sayPreachTimer = 120000; + ritualTriggered = false; + volunteerWork = true; + combatSummonsSummoned = false; } - void EnterCombat(Unit* /*who*/) override + void JustSummoned(Creature* summon) override { - if (pInstance) - pInstance->SetData(DATA_JEDOGA_SHADOWSEEKER_EVENT, IN_PROGRESS); - - Talk(TEXT_AGGRO); + if (summon->GetEntry() == NPC_JEDOGA_CONTROLLER) + { + summons.Summon(summon); + } } - void KilledUnit(Unit* Victim) override + void SummonedCreatureDies(Creature* summon, Unit* killer) override { - if (!Victim || Victim->GetTypeId() != TYPEID_PLAYER) + switch (summon->GetEntry()) + { + case NPC_TWILIGHT_INITIATE: + { + GuidList::iterator itr = std::find(oocSummons.begin(), oocSummons.end(), summon->GetGUID()); + if (itr == oocSummons.end()) + { + break; + } + + oocSummons.erase(itr); + if (!oocSummons.empty()) + { + break; + } + + DespawnOOCSummons(); + DoCastSelf(SPELL_HOVER_FALL); + me->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->MovePoint(POINT_DOWN, JedogaPosition[1], false); + + if (!combatSummonsSummoned) + { + summons.DespawnEntry(NPC_TWILIGHT_VOLUNTEER); + for (uint8 i = 0; i < MAX_COMBAT_INITIATES; ++i) + { + if (TempSummon* summon = me->SummonCreature(NPC_TWILIGHT_VOLUNTEER, VolunteerSpotPositions[i][0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) + { + summon->GetMotionMaster()->MovePoint(POINT_INITIAL, VolunteerSpotPositions[i][1]); + summon->SetReactState(REACT_PASSIVE); + summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC ); + summons.Summon(summon); + } + } + combatSummonsSummoned = true; + } + + break; + } + case NPC_TWILIGHT_VOLUNTEER: + { + if (sacraficeTarget_GUID && summon->GetGUID() != sacraficeTarget_GUID) + { + break; + } + + if (killer != me && killer->GetGUID() != sacraficeTarget_GUID) + { + volunteerWork = false; + } + else + { + DoCastSelf(SPELL_GIFT_OF_THE_HERALD, true); + } + events.ScheduleEvent(EVENT_JEDOGA_MOVE_DOWN, 1000, 0, PHASE_RITUAL); + break; + } + } + + summons.Despawn(summon); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*school*/) override + { + if (!ritualTriggered && me->HealthBelowPctDamaged(55, damage) && events.IsInPhase(PHASE_NORMAL)) + { + SetCombatMovement(false); + me->InterruptNonMeleeSpells(false); + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + events.SetPhase(PHASE_RITUAL); + events.ScheduleEvent(EVENT_JEDOGA_PREPARE_RITUAL, 1000, 0, PHASE_RITUAL); + ritualTriggered = true; return; + } - Talk(TEXT_SLAY); + if (events.IsInPhase(PHASE_RITUAL)) + { + damage = 0; + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_SACRAFICE) + { + if (Creature* target = ObjectAccessor::GetCreature(*me, sacraficeTarget_GUID)) + { + Unit::Kill(me, target); + } + } + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + ReschedulleCombatEvents(); + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() != TYPEID_PLAYER) + { + return; + } + + Talk(SAY_SLAY); } void JustDied(Unit* /*Killer*/) override { - Talk(TEXT_DEATH); - if (pInstance) - pInstance->SetData(DATA_JEDOGA_SHADOWSEEKER_EVENT, DONE); - - summons.DespawnAll(); + _JustDied(); + DespawnOOCSummons(); + Talk(SAY_DEATH); } - void MoveDown() + void MovementInform(uint32 type, uint32 pointId) override { - me->GetMotionMaster()->MoveIdle(); - me->GetMotionMaster()->MovePoint(POINT_DOWN, JedogaPosition[1]); - isFlying = false; - } - - void MoveUp(bool start) - { - isFlying = true; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - me->AttackStop(); - me->GetMotionMaster()->Clear(true); - me->SetFacingTo(5.66f); - me->GetMotionMaster()->MovePoint((start ? POINT_UP_START : POINT_UP), JedogaPosition[0]); - - me->SetDisableGravity(true); - } - - void MovementInform(uint32 Type, uint32 PointId) override - { - if (Type != POINT_MOTION_TYPE) - return; - - if (PointId == POINT_DOWN) + if (!(type == POINT_MOTION_TYPE || type == EFFECT_MOTION_TYPE)) { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - me->RemoveAurasDueToSpell(SPELL_PINK_SPHERE); - me->RemoveAurasDueToSpell(SPELL_LIGHTNING_BOLTS); + return; + } - isFlying = false; - me->SetInCombatWithZone(); - me->SetDisableGravity(false); - if (!summons.HasEntry(NPC_INITIATE)) - SpawnInitiate(false); - - if (UpdateVictim()) + switch (pointId) + { + case POINT_DOWN: { - me->StopMoving(); - ScheduleEvents(); - AttackStart(me->GetVictim()); - me->GetMotionMaster()->MoveChase(me->GetVictim()); + me->ClearUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); + ReschedulleCombatEvents(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_AGGRESSIVE); + + me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); + me->RemoveAurasDueToSpell(SPELL_LIGHTNING_BOLTS); + me->RemoveAurasDueToSpell(SPELL_HOVER_FALL); + SetCombatMovement(true); + + me->SetDisableGravity(false); + me->SetHover(false); + + me->SetInCombatWithZone(); + if (Unit* victim = me->GetVictim()) + { + me->StopMoving(); + AttackStart(victim); + } + break; + } + case POINT_UP: + { + me->SetFacingTo(5.66f); + if (!summons.empty()) + { + sacraficeTarget_GUID = Acore::Containers::SelectRandomContainerElement(summons); + if (Creature* volunteer = ObjectAccessor::GetCreature(*me, sacraficeTarget_GUID)) + { + Talk(SAY_SACRIFICE_1); + sacraficeTarget_GUID = volunteer->GetGUID(); + volunteer->AI()->DoAction(ACTION_RITUAL_BEGIN); + } + // Something failed, let players continue but do not grant achievement + else + { + volunteerWork = false; + me->GetMotionMaster()->Clear(); + DoCastSelf(SPELL_HOVER_FALL); + me->GetMotionMaster()->MovePoint(POINT_DOWN, JedogaPosition[1], false); + } + } + break; + } + case POINT_RITUAL: + { + me->SetFacingTo(5.66f); + DoCastSelf(SPELL_HOVER_FALL); + events.ScheduleEvent(EVENT_JEDOGA_MOVE_UP, 1000, 0, PHASE_RITUAL); + break; + } + case POINT_INITIAL: + { + me->SetFacingTo(5.66f); + DoCastSelf(SPELL_SPHERE_VISUAL, true); + DoCastSelf(SPELL_LIGHTNING_BOLTS, true); + if (!oocTriggers.empty()) + { + for (ObjectGuid const& guid : oocTriggers) + { + if (Creature* trigger = ObjectAccessor::GetCreature(*me, guid)) + { + trigger->CastSpell(nullptr, SPELL_BEAM_VISUAL_JEDOGA); + } + } + } + break; } } - else if (PointId == POINT_UP) - ActivateInitiate(); - else if (PointId == POINT_RITUAL) - startFly = true; } void UpdateAI(uint32 diff) override { - // Start text - if (introCheck) + if (!UpdateVictim()) { - introCheck += diff; - if (introCheck >= 2000) + if (instance->GetBossState(DATA_PRINCE_TALDARAM) == DONE) { - if (SelectTargetFromPlayerList(80.0f)) + if (sayPreachTimer <= diff) { - introCheck = 0; - Talk(TEXT_PREACHING); + Talk(SAY_PREACHING); + sayPreachTimer = 120000; // 2 min } else - introCheck = 1; + { + sayPreachTimer -= diff; + } } - return; } - if (startFly) + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) { - startFly = false; - MoveUp(false); - } - - if (isFlying && !SelectTargetFromPlayerList(80.0f)) - { - EnterEvadeMode(); return; } - if (!isFlying) + while (uint32 const eventId = events.ExecuteEvent()) { - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) + switch (eventId) { + // Normal phase case EVENT_JEDOGA_CYCLONE: - { - me->CastSpell(me, IsHeroic() ? SPELL_CYCLONE_STRIKE_H : SPELL_CYCLONE_STRIKE, false); - events.RepeatEvent(urand(10000, 14000)); - break; - } + { + DoCastSelf(DUNGEON_MODE(SPELL_CYCLONE_STRIKE, SPELL_CYCLONE_STRIKE_H), false); + events.RepeatEvent(urand(10000, 14000)); + break; + } case EVENT_JEDOGA_LIGHTNING_BOLT: + { + if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) { - if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - me->CastSpell(pTarget, IsHeroic() ? SPELL_LIGHTNING_BOLT_H : SPELL_LIGHTNING_BOLT, false); - - events.RepeatEvent(urand(11000, 15000)); - break; + DoCast(pTarget, DUNGEON_MODE(SPELL_LIGHTNING_BOLT, SPELL_LIGHTNING_BOLT_H), false); } + events.RepeatEvent(urand(11000, 15000)); + break; + } case EVENT_JEDOGA_THUNDERSHOCK: + { + if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) { - if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - me->CastSpell(pTarget, IsHeroic() ? SPELL_THUNDERSHOCK_H : SPELL_THUNDERSHOCK, false); - - events.RepeatEvent(urand(16000, 22000)); - break; + DoCast(pTarget, DUNGEON_MODE(SPELL_THUNDERSHOCK, SPELL_THUNDERSHOCK_H), false); } + + events.RepeatEvent(urand(16000, 22000)); + break; + } + // Ritual phase + case EVENT_JEDOGA_PREPARE_RITUAL: + { + me->GetMotionMaster()->Clear(true); + me->GetMotionMaster()->MovePoint(POINT_RITUAL, JedogaPosition[1]); + break; + } case EVENT_JEDOGA_MOVE_UP: - { - if (!summons.HasEntry(NPC_INITIATE)) - break; - - if (Creature* cr = me->SummonCreature(NPC_JEDOGA_CONTROLLER, 373.48f, -706.00f, -16.18f)) - { - cr->CastSpell(cr, SPELL_SACRIFICE_VISUAL, true); - summons.Summon(cr); - } - - Talk(TEXT_SACRIFICE_1); - - isFlying = true; - me->GetMotionMaster()->Clear(true); - me->GetMotionMaster()->MovePoint(POINT_RITUAL, JedogaPosition[1]); - break; - } + { + me->GetMotionMaster()->Clear(true); + me->SetDisableGravity(true); + me->SetHover(true); + me->GetMotionMaster()->MoveTakeoff(POINT_UP, JedogaPosition[0], 7.0f); + break; + } case EVENT_JEDOGA_MOVE_DOWN: - { - Talk(TEXT_SACRIFICE_2); - summons.DespawnEntry(NPC_JEDOGA_CONTROLLER); - MoveDown(); - break; - } + { + summons.DespawnEntry(NPC_JEDOGA_CONTROLLER); + DoCastSelf(SPELL_HOVER_FALL); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(POINT_DOWN, JedogaPosition[1], false); + break; + } } + } - DoMeleeAttackIfReady(); + DoMeleeAttackIfReady(); + } + + uint32 GetData(uint32 type) const override + { + if (type == DATA_VOLUNTEER_WORK) + { + return volunteerWork ? 1 : 0; + } + + return 0; + } + + private: + GuidList oocSummons; + GuidList oocTriggers; + ObjectGuid sacraficeTarget_GUID; + uint32 sayPreachTimer; + bool combatSummonsSummoned; + bool ritualTriggered; + bool volunteerWork; // true = success, false = failed + + void ReschedulleCombatEvents() + { + events.SetPhase(PHASE_NORMAL); + events.RescheduleEvent(EVENT_JEDOGA_CYCLONE, 3000, 0, PHASE_NORMAL); + events.RescheduleEvent(EVENT_JEDOGA_LIGHTNING_BOLT, 7000, 0, PHASE_NORMAL); + events.RescheduleEvent(EVENT_JEDOGA_THUNDERSHOCK, 12000, 0, PHASE_NORMAL); + } + + void DespawnOOCSummons() + { + if (!oocTriggers.empty()) + { + for (ObjectGuid const& guid : oocTriggers) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + { + summon->DespawnOrUnsummon(); + } + } + oocTriggers.clear(); + } + + if (!oocSummons.empty()) + { + for (ObjectGuid const& guid : oocSummons) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + { + summon->DespawnOrUnsummon(); + } + } + oocSummons.clear(); } } }; @@ -395,138 +563,213 @@ public: } }; -class npc_jedoga_initiand : public CreatureScript +class npc_twilight_volunteer : public CreatureScript { public: - npc_jedoga_initiand() : CreatureScript("npc_jedoga_initiand") { } + npc_twilight_volunteer() : CreatureScript("npc_twilight_volunteer") { } - struct npc_jedoga_initiandAI : public ScriptedAI + struct npc_twilight_volunteerAI : public ScriptedAI { - npc_jedoga_initiandAI(Creature* c) : ScriptedAI(c) + npc_twilight_volunteerAI(Creature* pCreature) : ScriptedAI(pCreature), + pInstance(pCreature->GetInstanceScript()), + isSacraficeTarget(false) { - pInstance = c->GetInstanceScript(); } - InstanceScript* pInstance; - int32 Timer; + void DoAction(int32 action) override + { + if (action == ACTION_RITUAL_BEGIN) + { + isSacraficeTarget = true; + me->SetRegeneratingHealth(false); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + DoCastSelf(SPELL_ACTIVATE_INITIATE, true); + me->RemoveAurasDueToSpell(SPELL_WHITE_SPHERE); + me->SetControlled(false, UNIT_STATE_STUNNED); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC); + + Talk(SAY_CHOSEN); + me->SetStandState(UNIT_STAND_STATE_STAND); + + events.ScheduleEvent(EVENT_RITUAL_BEGIN_MOVE, 1500); + } + } + + void EnterEvadeMode() override + { + if (!isSacraficeTarget) + { + ScriptedAI::EnterEvadeMode(); + } + } void AttackStart(Unit* who) override { - if (!Timer) + if (!isSacraficeTarget) + { ScriptedAI::AttackStart(who); - } - - void MoveInLineOfSight(Unit* who) override - { - if (!Timer) - ScriptedAI::MoveInLineOfSight(who); - } - - void Reset() override - { - Timer = 0; - - if (!pInstance) - return; - - if (pInstance->GetData(DATA_JEDOGA_SHADOWSEEKER_EVENT) != IN_PROGRESS) - { - me->SetControlled(false, UNIT_STATE_STUNNED); - me->RemoveAurasDueToSpell(SPELL_WHITE_SPHERE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); } - else + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) { - me->SetOrientation(me->GetAngle(372.6f, -705.12f)); + return; + } + + if (id == POINT_INITIAL) + { + me->SetFacingTo(me->GetAngle(&JedogaPosition[0])); me->SendMovementFlagUpdate(); - me->CastSpell(me, SPELL_WHITE_SPHERE, false); + DoCastSelf(SPELL_WHITE_SPHERE, false); me->SetControlled(true, UNIT_STATE_STUNNED); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); + me->SetStandState(UNIT_STAND_STATE_KNEEL); } - } - - void JustDied(Unit* Killer) override - { - if (!pInstance || Killer == me) - return; - - Creature* boss = me->GetMap()->GetCreature(pInstance->GetGuidData(DATA_JEDOGA_SHADOWSEEKER)); - if (boss) + else if (id == POINT_RITUAL) { - if (Timer) - boss->AI()->DoAction(ACTION_SACRIFICE_FAILED); - else - boss->AI()->DoAction(ACTION_INITIATE_DIED); - } - } + if (Creature* jedoga = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_JEDOGA_SHADOWSEEKER))) + { + jedoga->AI()->Talk(SAY_SACRIFICE_2); + jedoga->CastSpell(nullptr, SPELL_SACRIFICE_BEAM); - void DoAction(int32 param) override - { - if (param == ACTION_ACTIVATE) - { - Timer = 1500; - me->CastSpell(me, SPELL_ACTIVATE_INITIATE, true); - } - } + if (Creature* ritualTrigger = jedoga->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaPosition[2], TEMPSUMMON_TIMED_DESPAWN, 5000)) + { + ritualTrigger->CastSpell(ritualTrigger, SPELL_SACRIFICE_VISUAL); + } + } - void MovementInform(uint32 Type, uint32 PointId) override - { - if (Type == POINT_MOTION_TYPE && PointId == POINT_RITUAL) - { - Unit::Kill(me, me); - me->DespawnOrUnsummon(5000); - Creature* boss = me->GetMap()->GetCreature(pInstance->GetGuidData(DATA_JEDOGA_SHADOWSEEKER)); - if (boss) - boss->AI()->DoAction(ACTION_HERALD); + Talk(SAY_SACRIFICED); + me->SetStandState(UNIT_STAND_STATE_KNEEL); } } void UpdateAI(uint32 diff) override { - if (Timer) + if (!events.Empty()) { - Timer -= diff; - if (Timer <= 0) + events.Update(diff); + if (events.ExecuteEvent() == EVENT_RITUAL_BEGIN_MOVE) { - me->CombatStop(); - me->SetControlled(false, UNIT_STATE_STUNNED); - me->RemoveAurasDueToSpell(SPELL_WHITE_SPHERE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + me->GetMotionMaster()->Clear(); + me->SetHomePosition(JedogaPosition[2]); me->SetWalk(true); - - float distance = me->GetDistance(JedogaPosition[1]); - - if (distance < 9.0f) - me->SetSpeed(MOVE_WALK, 0.5f, true); - else if (distance < 15.0f) - me->SetSpeed(MOVE_WALK, 0.75f, true); - else if (distance < 20.0f) - me->SetSpeed(MOVE_WALK, 1.0f, true); - - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MovePoint(POINT_RITUAL, 373.48f, -706.00f, -16.18f); - - Timer = 10000000; + me->GetMotionMaster()->MovePoint(POINT_RITUAL, JedogaPosition[2], false); } - - return; } - if (!UpdateVictim()) - return; + if (!isSacraficeTarget && UpdateVictim()) + { + DoMeleeAttackIfReady(); + } + } - DoMeleeAttackIfReady(); + private: + InstanceScript* pInstance; + EventMap events; + bool isSacraficeTarget; + }; + + CreatureAI *GetAI(Creature *creature) const override + { + return GetAhnkahetAI(creature); + } +}; + +// 56328 - Random Lightning Visual Effect +class spell_random_lightning_visual_effect : public SpellScriptLoader +{ + public: + spell_random_lightning_visual_effect() : SpellScriptLoader("spell_random_lightning_visual_effect") { } + + class spell_random_lightning_visual_effect_SpellScript : public SpellScript + { + PrepareSpellScript(spell_random_lightning_visual_effect_SpellScript); + + void ModDestHeight(SpellDestination& dest) + { + Position const offset = { frand(-15.0f, 15.0f), frand(-15.0f, 15.0f), -19.0f, 0.0f }; + dest.RelocateOffset(offset); + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_random_lightning_visual_effect_SpellScript::ModDestHeight, EFFECT_0, TARGET_DEST_CASTER_RANDOM); } }; - CreatureAI* GetAI(Creature* creature) const override + SpellScript* GetSpellScript() const override { - return GetAhnkahetAI(creature); + return new spell_random_lightning_visual_effect_SpellScript(); } }; +// 56150 - Sacrifice Beam +class spell_jedoga_sacrafice_beam : public SpellScriptLoader +{ + public: + spell_jedoga_sacrafice_beam() : SpellScriptLoader("spell_jedoga_sacrafice_beam") { } + + class spell_jedoga_sacrafice_beam_AuraScript : public AuraScript + { + PrepareAuraScript(spell_jedoga_sacrafice_beam_AuraScript); + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void HandleRemoval(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + AuraRemoveMode const removeMode = GetTargetApplication()->GetRemoveMode(); + if (removeMode == AURA_REMOVE_BY_DEFAULT || removeMode == AURA_REMOVE_BY_EXPIRE) + { + GetCaster()->ToCreature()->AI()->DoAction(ACTION_SACRAFICE); + } + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_jedoga_sacrafice_beam_AuraScript::HandleRemoval, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_jedoga_sacrafice_beam_AuraScript(); + } +}; + +// CriteriaID 7359, Volunteer Work (2056) +class achievement_volunteer_work : public AchievementCriteriaScript +{ + public: + achievement_volunteer_work() : AchievementCriteriaScript("achievement_volunteer_work") + { + } + + bool OnCheck(Player* /*player*/, Unit* target, uint32 /*criteria_id*/) override + { + if (Creature const* jedoga = target ? target->ToCreature() : nullptr) + { + return jedoga->AI()->GetData(DATA_VOLUNTEER_WORK) == 1; + } + + return false; + } +}; + void AddSC_boss_jedoga_shadowseeker() { + // Creatures new boss_jedoga_shadowseeker(); - new npc_jedoga_initiand(); + new npc_twilight_volunteer(); + + // Spells + new spell_random_lightning_visual_effect(); + new spell_jedoga_sacrafice_beam(); + + // Achievements + new achievement_volunteer_work(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp index 8e34bb8b0..2e96bbbfe 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp @@ -2,256 +2,494 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ -#include "ahnkahet.h" -#include "ScriptedCreature.h" #include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "ahnkahet.h" #include "SpellInfo.h" +#include "SpellScript.h" +#include "Player.h" +#include "PassiveAI.h" enum Spells { SPELL_BLOODTHIRST = 55968, //Trigger Spell + add aura SPELL_CONJURE_FLAME_SPHERE = 55931, SPELL_FLAME_SPHERE_SPAWN_EFFECT = 55891, + SPELL_FLAME_SPHERE_SUMMON_1 = 55895, // 1x 30106 + SPELL_FLAME_SPHERE_SUMMON_2 = 59511, // 1x 31686 + SPELL_FLAME_SPHERE_SUMMON_3 = 59512, // 1x 31687 SPELL_FLAME_SPHERE_VISUAL = 55928, SPELL_FLAME_SPHERE_PERIODIC = 55926, - SPELL_FLAME_SPHERE_PERIODIC_H = 59508, SPELL_FLAME_SPHERE_DEATH_EFFECT = 55947, SPELL_BEAM_VISUAL = 60342, - SPELL_EMBRACE_OF_THE_VAMPYR = 55959, - SPELL_EMBRACE_OF_THE_VAMPYR_H = 59513, SPELL_VANISH = 55964, - CREATURE_FLAME_SPHERE = 30106, - CREATURE_FLAME_SPHERE_1 = 31686, - CREATURE_FLAME_SPHERE_2 = 31687, + SPELL_SHADOWSTEP = 55966, + SPELL_HOVER_FALL = 60425 }; + +#define SPELL_EMBRACE_OF_THE_VAMPYR DUNGEON_MODE(55959, 59513) + +enum Spheres +{ + NPC_FLAME_SPHERE_1 = 30106, + NPC_FLAME_SPHERE_2 = 31686, + NPC_FLAME_SPHERE_3 = 31687, +}; + enum Misc { - DATA_EMBRACE_DMG = 20000, - DATA_EMBRACE_DMG_H = 40000, - DATA_SPHERE_DISTANCE = 30, - ACTION_FREE = 1, - ACTION_SPHERE = 2, + MAX_EMBRACE_DMG = 20000, + MAX_EMBRACE_DMG_H = 40000, + + SUMMON_GROUP_TRIGGERS = 0, +}; + +enum Actions +{ + ACTION_REMOVE_PRISON_AT_RESET = 1, + ACTION_SPHERE, }; enum Event { EVENT_PRINCE_FLAME_SPHERES = 1, - EVENT_PRINCE_VANISH = 2, - EVENT_PRINCE_BLOODTHIRST = 3, - EVENT_PRINCE_VANISH_RUN = 4, - EVENT_PRINCE_RESCHEDULE = 5, + EVENT_PRINCE_VANISH, + EVENT_PRINCE_BLOODTHIRST, + EVENT_PRINCE_VANISH_RUN, + EVENT_PRINCE_RESCHEDULE, }; -#define DATA_GROUND_POSITION_Z 11.4f - enum Yells { - SAY_1 = 0, - SAY_WARNING = 1, - SAY_AGGRO = 2, - SAY_SLAY = 3, - SAY_DEATH = 4, - SAY_FEED = 5, - SAY_VANISH = 6, + SAY_SPHERE_ACTIVATED = 0, + SAY_REMOVE_PRISON = 1, + SAY_AGGRO = 2, + SAY_SLAY = 3, + SAY_DEATH = 4, + SAY_FEED = 5, + SAY_VANISH = 6, +}; + +enum Points +{ + POINT_LAND = 1, + POINT_ORB, +}; + +constexpr float DATA_GROUND_POSITION_Z = 11.308135f; +constexpr float DATA_SPHERE_DISTANCE = 25.0f; +#define DATA_SPHERE_ANGLE_OFFSET float(M_PI) / 2.0f + +class npc_taldaram_flamesphere : public CreatureScript +{ +public: + npc_taldaram_flamesphere() : CreatureScript("npc_taldaram_flamesphere") { } + + struct npc_taldaram_flamesphereAI : public NullCreatureAI + { + npc_taldaram_flamesphereAI(Creature *pCreature) : NullCreatureAI(pCreature), + instance(pCreature->GetInstanceScript()), + uiDespawnTimer(13000), + moveTimer(0) + { + pCreature->SetReactState(REACT_PASSIVE); + } + + void DoAction(int32 action) override + { + if (action == ACTION_SPHERE) + { + moveTimer = 3000; + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == POINT_ORB) + { + me->DisappearAndDie(); + } + } + + void IsSummonedBy(Unit* /*summoner*/) override + { + // Replace sphere instantly if sphere is summoned after prince death + if (instance->GetBossState(DATA_PRINCE_TALDARAM) != IN_PROGRESS) + { + me->DespawnOrUnsummon(); + return; + } + + DoCastSelf(SPELL_FLAME_SPHERE_SPAWN_EFFECT); + DoCastSelf(SPELL_FLAME_SPHERE_VISUAL); + + // TODO: replace with DespawnOrUnsummon + uiDespawnTimer = 13000; + } + + void JustDied(Unit* /*who*/) override + { + DoCastSelf(SPELL_FLAME_SPHERE_DEATH_EFFECT); + } + + void UpdateAI(uint32 diff) override + { + if (moveTimer) + { + if (moveTimer <= diff) + { + DoCastSelf(SPELL_FLAME_SPHERE_PERIODIC); + float angleOffset = 0.0f; + + switch (me->GetEntry()) + { + case NPC_FLAME_SPHERE_1: + break; + case NPC_FLAME_SPHERE_2: + angleOffset = DATA_SPHERE_ANGLE_OFFSET; + break; + case NPC_FLAME_SPHERE_3: + angleOffset = -DATA_SPHERE_ANGLE_OFFSET; + break; + default: + return; + } + + float angle = me->GetAngle(&victimPos) + angleOffset; + float x = me->GetPositionX() + DATA_SPHERE_DISTANCE * cos(angle); + float y = me->GetPositionY() + DATA_SPHERE_DISTANCE * sin(angle); + me->GetMotionMaster()->MovePoint(POINT_ORB, x, y, me->GetPositionZ()); + + moveTimer = 0; + } + else + { + moveTimer -= diff; + } + } + + if (uiDespawnTimer) + { + if (uiDespawnTimer <= diff) + { + me->DisappearAndDie(); + uiDespawnTimer = 0; + } + else + uiDespawnTimer -= diff; + } + } + + void SetVictimPos(Position const& pos) + { + victimPos.Relocate(pos); + } + + private: + Position victimPos; + InstanceScript* instance; + uint32 uiDespawnTimer; + uint32 moveTimer; + }; + + CreatureAI *GetAI(Creature *creature) const override + { + return GetAhnkahetAI(creature); + } }; class boss_taldaram : public CreatureScript { public: - boss_taldaram() : CreatureScript("boss_taldaram") { } - - struct boss_taldaramAI : public ScriptedAI + boss_taldaram() : CreatureScript("boss_taldaram") { - boss_taldaramAI(Creature* c) : ScriptedAI(c), summons(me) + } + + struct boss_taldaramAI : public BossAI + { + boss_taldaramAI(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM), + vanishDamage(0) { - pInstance = c->GetInstanceScript(); } - InstanceScript* pInstance; - EventMap events; - SummonList summons; - ObjectGuid vanishTarget; - uint32 vanishDamage; + void InitializeAI() override + { + BossAI::InitializeAI(); + + // Event not started + if (instance->GetData(DATA_TELDRAM_SPHERE1) != DONE || instance->GetData(DATA_TELDRAM_SPHERE2) != DONE) + { + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + me->SetDisableGravity(true); + me->SetHover(true); + if (!me->HasAura(SPELL_BEAM_VISUAL)) + { + DoCastSelf(SPELL_BEAM_VISUAL, true); + } + + me->SummonCreatureGroup(SUMMON_GROUP_TRIGGERS); + return; + } + + if (instance->GetData(DATA_TELDRAM_SPHERE1) == DONE && instance->GetData(DATA_TELDRAM_SPHERE2) == DONE) + { + DoAction(ACTION_REMOVE_PRISON_AT_RESET); + } + } void Reset() override { - if (me->GetPositionZ() > 15.0f) - me->CastSpell(me, SPELL_BEAM_VISUAL, true); + _Reset(); - events.Reset(); - summons.DespawnAll(); vanishDamage = 0; - vanishTarget.Clear(); - - if (pInstance) - { - pInstance->SetData(DATA_PRINCE_TALDARAM_EVENT, NOT_STARTED); - - // Event not started - if (pInstance->GetData(DATA_SPHERE_EVENT) == DONE) - DoAction(ACTION_FREE); - } + vanishTarget_GUID.Clear(); } - void DoAction(int32 param) override + void DoAction(int32 action) override { - if (param == ACTION_FREE) + if (action == ACTION_REMOVE_PRISON || action == ACTION_REMOVE_PRISON_AT_RESET) { - me->RemoveAllAuras(); - me->SetUnitMovementFlags(MOVEMENTFLAG_WALKING); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), DATA_GROUND_POSITION_Z, me->GetOrientation()); - me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), DATA_GROUND_POSITION_Z, me->GetOrientation(), true); + instance->HandleGameObject(instance->GetGuidData(DATA_PRINCE_TALDARAM_PLATFORM), true); - if (pInstance) - pInstance->HandleGameObject(pInstance->GetGuidData(DATA_PRINCE_TALDARAM_PLATFORM), true); - } - } - - void EnterCombat(Unit* /*who*/) override - { - if (pInstance) - pInstance->SetData(DATA_PRINCE_TALDARAM_EVENT, IN_PROGRESS); - - Talk(SAY_AGGRO); - ScheduleEvents(); - - me->RemoveAllAuras(); - me->InterruptNonMeleeSpells(true); - } - - void ScheduleEvents() - { - events.Reset(); - events.ScheduleEvent(EVENT_PRINCE_FLAME_SPHERES, 10000); - events.ScheduleEvent(EVENT_PRINCE_BLOODTHIRST, 10000); - vanishTarget.Clear(); - vanishDamage = 0; - } - - void SpellHitTarget(Unit*, const SpellInfo* spellInfo) override - { - if (spellInfo->Id == SPELL_CONJURE_FLAME_SPHERE) - summons.DoAction(ACTION_SPHERE); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_PRINCE_BLOODTHIRST: - { - me->CastSpell(me->GetVictim(), SPELL_BLOODTHIRST, false); - events.RepeatEvent(10000); - break; - } - case EVENT_PRINCE_FLAME_SPHERES: - { - me->CastSpell(me->GetVictim(), SPELL_CONJURE_FLAME_SPHERE, false); - events.RescheduleEvent(EVENT_PRINCE_VANISH, 14000); - Creature* cr; - if ((cr = me->SummonCreature(CREATURE_FLAME_SPHERE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 5.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10 * IN_MILLISECONDS))) - summons.Summon(cr); - - if (me->GetMap()->IsHeroic()) - { - if ((cr = me->SummonCreature(CREATURE_FLAME_SPHERE_1, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 5.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10 * IN_MILLISECONDS))) - summons.Summon(cr); - - if ((cr = me->SummonCreature(CREATURE_FLAME_SPHERE_2, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 5.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10 * IN_MILLISECONDS))) - summons.Summon(cr); - } - events.RepeatEvent(15000); - break; - } - case EVENT_PRINCE_VANISH: - { - //Count alive players - uint8 count = 0; - Unit* pTarget; - std::list t_list = me->getThreatManager().getThreatList(); - for (std::list::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) - { - pTarget = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER && pTarget->IsAlive()) - count++; - } - //He only vanishes if there are 3 or more alive players - if (count > 2) - { - Talk(SAY_VANISH); - me->CastSpell(me, SPELL_VANISH, false); - - events.CancelEvent(EVENT_PRINCE_FLAME_SPHERES); - events.CancelEvent(EVENT_PRINCE_BLOODTHIRST); - events.ScheduleEvent(EVENT_PRINCE_VANISH_RUN, 2499); - if (Unit* pEmbraceTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - vanishTarget = pEmbraceTarget->GetGUID(); - } - break; - } - case EVENT_PRINCE_VANISH_RUN: - { - if (Unit* vT = ObjectAccessor::GetUnit(*me, vanishTarget)) - { - me->UpdatePosition(vT->GetPositionX(), vT->GetPositionY(), vT->GetPositionZ(), me->GetAngle(vT), true); - me->CastSpell(vT, SPELL_EMBRACE_OF_THE_VAMPYR, false); - me->RemoveAura(SPELL_VANISH); - } - - events.ScheduleEvent(EVENT_PRINCE_RESCHEDULE, 20000); - break; - } - case EVENT_PRINCE_RESCHEDULE: - { - ScheduleEvents(); - break; - } - } - - if (me->IsVisible()) - DoMeleeAttackIfReady(); - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (vanishTarget) - { - vanishDamage += damage; - if (vanishDamage > (uint32) DUNGEON_MODE(DATA_EMBRACE_DMG, DATA_EMBRACE_DMG_H)) + if (action == ACTION_REMOVE_PRISON) { - ScheduleEvents(); - me->CastStop(); + DoCastSelf(SPELL_HOVER_FALL); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveLand(POINT_LAND, me->GetHomePosition(), 8.0f); + Talk(SAY_REMOVE_PRISON); + } + // Teleport instantly + else + { + me->SetDisableGravity(false); + me->SetHover(false); + me->RemoveAllAuras(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NOT_SELECTABLE); + me->UpdatePosition(me->GetHomePosition(), true); + } + summons.DespawnEntry(NPC_JEDOGA_CONTROLLER); + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == EFFECT_MOTION_TYPE && id == POINT_LAND) + { + me->SetDisableGravity(false); + me->SetHover(false); + me->RemoveAllAuras(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NOT_SELECTABLE); + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*school*/) override + { + if (vanishTarget_GUID) + { + if (me->FindCurrentSpellBySpellId(SPELL_EMBRACE_OF_THE_VAMPYR)) + { + vanishDamage += damage; + if (vanishDamage >= DUNGEON_MODE(MAX_EMBRACE_DMG, MAX_EMBRACE_DMG_H)) + { + ScheduleCombatEvents(); + me->CastStop(); + vanishTarget_GUID.Clear(); + vanishDamage = 0; + } } } } void JustDied(Unit* /*killer*/) override { - summons.DespawnAll(); + _JustDied(); Talk(SAY_DEATH); - - if (pInstance) - pInstance->SetData(DATA_PRINCE_TALDARAM_EVENT, DONE); } void KilledUnit(Unit* victim) override { - if (urand(0, 1)) + if (victim->GetTypeId() != TYPEID_PLAYER) + { return; - - if (vanishTarget && victim->GetGUID() == vanishTarget) - ScheduleEvents(); + } Talk(SAY_SLAY); + + if (vanishTarget_GUID && victim->GetGUID() == vanishTarget_GUID) + { + vanishTarget_GUID.Clear(); + vanishDamage = 0; + } + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + ScheduleCombatEvents(); + + me->RemoveAllAuras(); + me->InterruptNonMeleeSpells(true); + } + + void SpellHitTarget(Unit* /*target*/, const SpellInfo *spellInfo) override + { + if (spellInfo->Id == SPELL_CONJURE_FLAME_SPHERE) + { + summons.DoAction(ACTION_SPHERE); + } + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + switch (summon->GetEntry()) + { + case NPC_FLAME_SPHERE_1: + case NPC_FLAME_SPHERE_2: + case NPC_FLAME_SPHERE_3: + { + if (npc_taldaram_flamesphere::npc_taldaram_flamesphereAI* summonAI = dynamic_cast(summon->AI())) + { + summonAI->SetVictimPos(victimSperePos); + } + + break; + } + case NPC_JEDOGA_CONTROLLER: + { + summon->CastSpell(nullptr, SPELL_BEAM_VISUAL); + break; + } + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + return; + } + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; + } + + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_PRINCE_BLOODTHIRST: + { + DoCastSelf(SPELL_BLOODTHIRST); + events.RepeatEvent(10000); + break; + } + case EVENT_PRINCE_FLAME_SPHERES: + { + if (Unit* victim = me->GetVictim()) + { + DoCast(victim, SPELL_CONJURE_FLAME_SPHERE); + victimSperePos = *victim; + } + + if (!events.GetNextEventTime(EVENT_PRINCE_VANISH)) + { + events.RescheduleEvent(EVENT_PRINCE_VANISH, 14000); + } + else + { + // Make sure that Vanish won't get triggered at same time as sphere summon + events.DelayEvents(4000); + } + + events.RepeatEvent(15000); + break; + } + case EVENT_PRINCE_VANISH: + { + //Count alive players + uint8 count = 0; + std::list const t_list = me->getThreatManager().getThreatList(); + if (!t_list.empty()) + { + for (HostileReference const* reference : t_list) + { + if (reference) + { + Unit const* pTarget = ObjectAccessor::GetUnit(*me, reference->getUnitGuid()); + if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER && pTarget->IsAlive()) + { + ++count; + } + } + } + } + + // He only vanishes if there are 3 or more alive players + if (count > 2) + { + Talk(SAY_VANISH); + DoCastSelf(SPELL_VANISH, false); + if (Unit* pEmbraceTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + { + vanishTarget_GUID = pEmbraceTarget->GetGUID(); + } + + events.CancelEvent(EVENT_PRINCE_FLAME_SPHERES); + events.CancelEvent(EVENT_PRINCE_BLOODTHIRST); + events.ScheduleEvent(EVENT_PRINCE_VANISH_RUN, 2499); + } + break; + } + case EVENT_PRINCE_VANISH_RUN: + { + if (Unit* _vanishTarget = ObjectAccessor::GetUnit(*me, vanishTarget_GUID)) + { + vanishDamage = 0; + DoCast(_vanishTarget, SPELL_SHADOWSTEP); + me->CastSpell(_vanishTarget, SPELL_EMBRACE_OF_THE_VAMPYR, false); + me->RemoveAura(SPELL_VANISH); + } + + events.ScheduleEvent(EVENT_PRINCE_RESCHEDULE, 20000); + break; + } + case EVENT_PRINCE_RESCHEDULE: + { + ScheduleCombatEvents(); + break; + } + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; + } + } + + if (me->IsVisible()) + { + DoMeleeAttackIfReady(); + } + } + + private: + Position victimSperePos; + ObjectGuid vanishTarget_GUID; + uint32 vanishDamage; + + void ScheduleCombatEvents() + { + events.Reset(); + events.RescheduleEvent(EVENT_PRINCE_FLAME_SPHERES, 10000); + events.RescheduleEvent(EVENT_PRINCE_BLOODTHIRST, 10000); + vanishTarget_GUID.Clear(); + vanishDamage = 0; } }; @@ -261,101 +499,121 @@ public: } }; -class npc_taldaram_flamesphere : public CreatureScript -{ -public: - npc_taldaram_flamesphere() : CreatureScript("npc_taldaram_flamesphere") { } - - struct npc_taldaram_flamesphereAI : public ScriptedAI - { - npc_taldaram_flamesphereAI(Creature* c) : ScriptedAI(c) - { - } - - uint32 uiDespawnTimer; - - void DoAction(int32 param) override - { - if (param == ACTION_SPHERE) - { - me->CastSpell(me, me->GetMap()->IsHeroic() ? SPELL_FLAME_SPHERE_PERIODIC_H : SPELL_FLAME_SPHERE_PERIODIC, true); - - float angle = rand_norm() * 2 * M_PI; - float x = me->GetPositionX() + static_cast(DATA_SPHERE_DISTANCE) * cos(angle); - float y = me->GetPositionY() + static_cast(DATA_SPHERE_DISTANCE) * sin(angle); - me->GetMotionMaster()->MovePoint(0, x, y, me->GetPositionZ()); - } - } - - void MovementInform(uint32 /*type*/, uint32 id) override - { - if (id == 0) - me->DisappearAndDie(); - } - - void Reset() override - { - me->CastSpell(me, SPELL_FLAME_SPHERE_SPAWN_EFFECT, true); - me->CastSpell(me, SPELL_FLAME_SPHERE_VISUAL, true); - uiDespawnTimer = 13 * IN_MILLISECONDS; - } - - void EnterCombat(Unit* /*who*/) override {} - void MoveInLineOfSight(Unit* /*who*/) override {} - - void JustDied(Unit* /*who*/) override - { - me->CastSpell(me, SPELL_FLAME_SPHERE_DEATH_EFFECT, true); - } - - void UpdateAI(uint32 diff) override - { - if (uiDespawnTimer <= diff) - me->DisappearAndDie(); - else - uiDespawnTimer -= diff; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetAhnkahetAI(creature); - } -}; - class go_prince_taldaram_sphere : public GameObjectScript { public: go_prince_taldaram_sphere() : GameObjectScript("go_prince_taldaram_sphere") { } - bool OnGossipHello(Player* /*pPlayer*/, GameObject* go) override + bool OnGossipHello(Player* pPlayer, GameObject *go) override { - InstanceScript* pInstance = go->GetInstanceScript(); - if (!pInstance) - return false; - - Creature* pPrinceTaldaram = ObjectAccessor::GetCreature(*go, pInstance->GetGuidData(DATA_PRINCE_TALDARAM)); - if (pPrinceTaldaram && pPrinceTaldaram->IsAlive()) + if (pPlayer && pPlayer->IsInCombat()) { - go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - go->SetGoState(GO_STATE_ACTIVE); + return true; + } - if (pInstance->GetData(DATA_SPHERE_EVENT) == NOT_STARTED) + InstanceScript *pInstance = go->GetInstanceScript(); + if (!pInstance) + { + return true; + } + + go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + go->SetGoState(GO_STATE_ACTIVE); + + uint32 const objectIndex = go->GetEntry() == GO_TELDARAM_SPHERE1 ? DATA_TELDRAM_SPHERE1 : DATA_TELDRAM_SPHERE2; + if (pInstance->GetData(objectIndex) == NOT_STARTED) + { + Creature* taldaram = ObjectAccessor::GetCreature(*go, pInstance->GetGuidData(DATA_PRINCE_TALDARAM)); + if (taldaram && taldaram->IsAlive()) { - pInstance->SetData(DATA_SPHERE_EVENT, DONE); - return true; + taldaram->AI()->Talk(SAY_SPHERE_ACTIVATED); } - pPrinceTaldaram->AI()->DoAction(ACTION_FREE); + pInstance->SetData(objectIndex, DONE); } return true; } }; +// 55931 - Conjure Flame Sphere +class spell_prince_taldaram_conjure_flame_sphere : public SpellScriptLoader +{ +public: + spell_prince_taldaram_conjure_flame_sphere() : SpellScriptLoader("spell_prince_taldaram_conjure_flame_sphere") { } + + class spell_prince_taldaram_conjure_flame_sphere_SpellScript : public SpellScript + { + PrepareSpellScript(spell_prince_taldaram_conjure_flame_sphere_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({SPELL_FLAME_SPHERE_SUMMON_1, SPELL_FLAME_SPHERE_SUMMON_2, SPELL_FLAME_SPHERE_SUMMON_3}); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (!caster || caster->isDead()) + { + return; + } + + caster->CastSpell(caster, SPELL_FLAME_SPHERE_SUMMON_1, false, nullptr, nullptr, caster->GetGUID()); + + if (caster->GetMap()->IsHeroic()) + { + caster->CastSpell(caster, SPELL_FLAME_SPHERE_SUMMON_2, false, nullptr, nullptr, caster->GetGUID()); + caster->CastSpell(caster, SPELL_FLAME_SPHERE_SUMMON_3, false, nullptr, nullptr, caster->GetGUID()); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_prince_taldaram_conjure_flame_sphere_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_prince_taldaram_conjure_flame_sphere_SpellScript(); + } +}; + +// 55895, 59511, 59512 - Flame Sphere Summon +class spell_prince_taldaram_flame_sphere_summon : public SpellScriptLoader +{ + public: + spell_prince_taldaram_flame_sphere_summon() : SpellScriptLoader("spell_prince_taldaram_flame_sphere_summon") { } + + class spell_prince_taldaram_flame_sphere_summon_SpellScript : public SpellScript + { + PrepareSpellScript(spell_prince_taldaram_flame_sphere_summon_SpellScript); + + void SetDest(SpellDestination& dest) + { + dest._position.m_positionZ = DATA_GROUND_POSITION_Z + 5.5f; + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_prince_taldaram_flame_sphere_summon_SpellScript::SetDest, EFFECT_0, TARGET_DEST_CASTER); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_prince_taldaram_flame_sphere_summon_SpellScript(); + } +}; + void AddSC_boss_taldaram() { - new boss_taldaram(); new npc_taldaram_flamesphere(); + new boss_taldaram(); new go_prince_taldaram_sphere(); + + // Spells + new spell_prince_taldaram_conjure_flame_sphere(); + new spell_prince_taldaram_flame_sphere_summon(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/def_ahnkahet.h b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/def_ahnkahet.h deleted file mode 100644 index 492420bfd..000000000 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/def_ahnkahet.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2 - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef DEF_AHNKAHET_H -#define DEF_AHNKAHET_H - -#define DATA_ELDER_NADOX 1 -#define DATA_PRINCE_TALDARAM 2 -#define DATA_JEDOGA_SHADOWSEEKER 3 -#define DATA_HERALD_VOLAZJ 4 -#define DATA_AMANITAR 5 - -#define DATA_ELDER_NADOX_EVENT 6 -#define DATA_PRINCE_TALDARAM_EVENT 7 -#define DATA_JEDOGA_SHADOWSEEKER_EVENT 8 -#define DATA_HERALD_VOLAZJ_EVENT 9 -#define DATA_AMANITAR_EVENT 10 - -#define DATA_SPHERE1 11 -#define DATA_SPHERE2 12 -#define DATA_SPHERE1_EVENT 13 -#define DATA_SPHERE2_EVENT 14 -#define DATA_PRINCE_TALDARAM_PLATFORM 15 - -#endif diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/instance_ahnkahet.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/instance_ahnkahet.cpp index b720d5506..df4e51d01 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/instance_ahnkahet.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/instance_ahnkahet.cpp @@ -2,205 +2,173 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ +#include "ScriptMgr.h" +#include "ScriptedCreature.h" #include "ahnkahet.h" #include "Player.h" -#include "ScriptedCreature.h" -#include "ScriptMgr.h" #include "SpellScript.h" +#include class instance_ahnkahet : public InstanceMapScript { public: - instance_ahnkahet() : InstanceMapScript("instance_ahnkahet", 619) { } + instance_ahnkahet() : InstanceMapScript(AhnahetScriptName, 619) { } struct instance_ahnkahet_InstanceScript : public InstanceScript { - instance_ahnkahet_InstanceScript(Map* pMap) : InstanceScript(pMap) {Initialize();}; - - ObjectGuid Elder_Nadox; - ObjectGuid Prince_Taldaram; - ObjectGuid Jedoga_Shadowseeker; - ObjectGuid Herald_Volazj; - ObjectGuid Amanitar; - - ObjectGuid Prince_TaldaramPlatform; - ObjectGuid Prince_TaldaramGate; - - uint32 m_auiEncounter[MAX_ENCOUNTER]; - uint32 spheres; - - bool nadoxAchievement; - bool jedogaAchievement; - - void Initialize() override + instance_ahnkahet_InstanceScript(Map* pMap) : InstanceScript(pMap), canSaveBossStates(false) { - memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); - - spheres = NOT_STARTED; - - nadoxAchievement = false; - jedogaAchievement = false; - } - - bool IsEncounterInProgress() const override - { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) return true; - - return false; + SetBossNumber(MAX_ENCOUNTER); + teldaramSpheres.fill(NOT_STARTED); } void OnCreatureCreate(Creature* pCreature) override { - switch(pCreature->GetEntry()) + switch (pCreature->GetEntry()) { case NPC_ELDER_NADOX: - Elder_Nadox = pCreature->GetGUID(); + elderNadox_GUID = pCreature->GetGUID(); break; case NPC_PRINCE_TALDARAM: - Prince_Taldaram = pCreature->GetGUID(); + princeTaldaram_GUID = pCreature->GetGUID(); break; case NPC_JEDOGA_SHADOWSEEKER: - Jedoga_Shadowseeker = pCreature->GetGUID(); + jedogaShadowseeker_GUID = pCreature->GetGUID(); break; case NPC_HERALD_JOLAZJ: - Herald_Volazj = pCreature->GetGUID(); + heraldVolazj_GUID = pCreature->GetGUID(); break; case NPC_AMANITAR: - Amanitar = pCreature->GetGUID(); + amanitar_GUID = pCreature->GetGUID(); break; } } void OnGameObjectCreate(GameObject* pGo) override { - switch(pGo->GetEntry()) + switch (pGo->GetEntry()) { - case 193564: - Prince_TaldaramPlatform = pGo->GetGUID(); - if (m_auiEncounter[1] == DONE) + case GO_TELDARAM_PLATFORM: + { + taldaramPlatform_GUID = pGo->GetGUID(); + if (IsAllSpheresActivated() || GetBossState(DATA_PRINCE_TALDARAM) == DONE) + { HandleGameObject(ObjectGuid::Empty, true, pGo); + } break; - case 193093: - if (spheres == DONE) + } + case GO_TELDARAM_SPHERE1: + case GO_TELDARAM_SPHERE2: + { + if (teldaramSpheres.at(pGo->GetEntry() == GO_TELDARAM_SPHERE1 ? 0 : 1) == DONE || GetBossState(DATA_PRINCE_TALDARAM) == DONE) { pGo->SetGoState(GO_STATE_ACTIVE); pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); } else - pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - - break; - case 193094: - if (spheres == DONE) { - pGo->SetGoState(GO_STATE_ACTIVE); - pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - } - else pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + } break; - case 192236: - Prince_TaldaramGate = pGo->GetGUID(); // Web gate past Prince Taldaram - if (m_auiEncounter[1] == DONE) + } + case GO_TELDARAM_DOOR: + { + taldaramGate_GUID = pGo->GetGUID(); // Web gate past Prince Taldaram + if (GetBossState(DATA_PRINCE_TALDARAM) == DONE) + { HandleGameObject(ObjectGuid::Empty, true, pGo); + } break; + } } } - ObjectGuid GetGuidData(uint32 identifier) const override + bool SetBossState(uint32 type, EncounterState state) override { - switch(identifier) + if (!InstanceScript::SetBossState(type, state)) { - case DATA_ELDER_NADOX: - return Elder_Nadox; - case DATA_PRINCE_TALDARAM: - return Prince_Taldaram; - case DATA_JEDOGA_SHADOWSEEKER: - return Jedoga_Shadowseeker; - case DATA_HERALD_VOLAZJ: - return Herald_Volazj; - case DATA_AMANITAR: - return Amanitar; - case DATA_PRINCE_TALDARAM_PLATFORM: - return Prince_TaldaramPlatform; + return false; } - return ObjectGuid::Empty; - } - - bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override - { - switch(criteria_id) + if (type == DATA_PRINCE_TALDARAM && state == DONE) { - case 7317: // Respect Your Elders (2038) - return nadoxAchievement; - case 7359: // Volunteer Work (2056) - return jedogaAchievement; + HandleGameObject(taldaramGate_GUID, true); } - return false; + + if (canSaveBossStates) + { + SaveToDB(); + } + + return true; } void SetData(uint32 type, uint32 data) override { - switch(type) + if (type == DATA_TELDRAM_SPHERE1 || type == DATA_TELDRAM_SPHERE2) { - case DATA_HERALD_VOLAZJ_EVENT: - case DATA_AMANITAR_EVENT: - case DATA_ELDER_NADOX_EVENT: - case DATA_JEDOGA_SHADOWSEEKER_EVENT: - m_auiEncounter[type] = data; - break; - case DATA_PRINCE_TALDARAM_EVENT: - if (data == DONE) - HandleGameObject(Prince_TaldaramGate, true); - m_auiEncounter[type] = data; - break; - case DATA_SPHERE_EVENT: - spheres = data; - break; - case DATA_NADOX_ACHIEVEMENT: - nadoxAchievement = (bool)data; - return; - case DATA_JEDOGA_ACHIEVEMENT: - jedogaAchievement = (bool)data; - return; - } - - if (data == DONE) + teldaramSpheres[type == DATA_TELDRAM_SPHERE1 ? 0 : 1] = data; SaveToDB(); + + if (IsAllSpheresActivated()) + { + HandleGameObject(taldaramPlatform_GUID, true, nullptr); + + Creature* teldaram = instance->GetCreature(princeTaldaram_GUID); + if (teldaram && teldaram->IsAlive()) + { + teldaram->AI()->DoAction(ACTION_REMOVE_PRISON); + } + } + } } uint32 GetData(uint32 type) const override { - switch(type) + switch (type) { - case DATA_ELDER_NADOX_EVENT: - case DATA_PRINCE_TALDARAM_EVENT: - case DATA_JEDOGA_SHADOWSEEKER_EVENT: - case DATA_HERALD_VOLAZJ: - case DATA_AMANITAR_EVENT: - return m_auiEncounter[type]; - - case DATA_SPHERE_EVENT: - return spheres; + case DATA_TELDRAM_SPHERE1: + return teldaramSpheres.at(0); + case DATA_TELDRAM_SPHERE2: + return teldaramSpheres.at(1); } return 0; } + ObjectGuid GetGuidData(uint32 type) const override + { + switch (type) + { + case DATA_ELDER_NADOX: + return elderNadox_GUID; + case DATA_PRINCE_TALDARAM: + return princeTaldaram_GUID; + case DATA_JEDOGA_SHADOWSEEKER: + return jedogaShadowseeker_GUID; + case DATA_HERALD_VOLAZJ: + return heraldVolazj_GUID; + case DATA_AMANITAR: + return amanitar_GUID; + } + + return ObjectGuid::Empty; + } + std::string GetSaveData() override { OUT_SAVE_INST_DATA; std::ostringstream saveStream; - saveStream << "A K " << m_auiEncounter[0] << ' ' << m_auiEncounter[1] << ' ' - << m_auiEncounter[2] << ' ' << m_auiEncounter[3] << ' ' << m_auiEncounter[4] << ' ' - << spheres; + // Encounter states + saveStream << "A K " << GetBossSaveData(); + + // Extra data + saveStream << teldaramSpheres[0] << ' ' << teldaramSpheres[1]; OUT_SAVE_INST_DATA_COMPLETE; return saveStream.str(); @@ -217,29 +185,55 @@ public: OUT_LOAD_INST_DATA(in); char dataHead1, dataHead2; - uint32 data0, data1, data2, data3, data4, data5; std::istringstream loadStream(in); - loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4 >> data5; + loadStream >> dataHead1 >> dataHead2; if (dataHead1 == 'A' && dataHead2 == 'K') { - m_auiEncounter[0] = data0; - m_auiEncounter[1] = data1; - m_auiEncounter[2] = data2; - m_auiEncounter[3] = data3; - m_auiEncounter[4] = data4; - + // Encounter states for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) - m_auiEncounter[i] = NOT_STARTED; + { + uint32 tmpState; + loadStream >> tmpState; + if (tmpState == IN_PROGRESS || tmpState > SPECIAL) + { + tmpState = NOT_STARTED; + } - spheres = data5; + SetBossState(i, EncounterState(tmpState)); + } + + // Extra data + loadStream >> teldaramSpheres[0] >> teldaramSpheres[1]; + } + else + { + OUT_LOAD_INST_DATA_FAIL; + return; } - else OUT_LOAD_INST_DATA_FAIL; + canSaveBossStates = true; OUT_LOAD_INST_DATA_COMPLETE; } + + private: + ObjectGuid elderNadox_GUID; + ObjectGuid princeTaldaram_GUID; + ObjectGuid jedogaShadowseeker_GUID; + ObjectGuid heraldVolazj_GUID; + ObjectGuid amanitar_GUID; + + // Teldaram related + ObjectGuid taldaramPlatform_GUID; + ObjectGuid taldaramGate_GUID; + std::array teldaramSpheres; // Used to identify activation status for sphere activation + bool canSaveBossStates; // Indicates that it is safe to trigger SaveToDB call in SetBossState + + bool IsAllSpheresActivated() const + { + return teldaramSpheres.at(0) == DONE && teldaramSpheres.at(1) == DONE; + } }; InstanceScript* GetInstanceScript(InstanceMap* map) const override @@ -248,6 +242,8 @@ public: } }; +// 56702 Shadow Sickle +// 59103 Shadow Sickle class spell_shadow_sickle_periodic_damage : public SpellScriptLoader { public: @@ -259,22 +255,7 @@ public: void HandlePeriodic(AuraEffect const* /*aurEff*/) { - PreventDefaultAction(); - - if (Unit* caster = GetCaster()) - { - std::list PlayerList; - PlayerList.clear(); - - Map::PlayerList const& players = caster->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - if (Player* player = itr->GetSource()->ToPlayer()) - if (player->IsWithinDist(caster, 40.0f) && player->IsAlive()) // SPELL_SHADOW_SICKLE_H & SPELL_SHADOW_SICKLE range is 40 yards - PlayerList.push_back(player); - - if (!PlayerList.empty()) - caster->CastSpell(Acore::Containers::SelectRandomContainerElement(PlayerList), caster->GetMap()->IsHeroic() ? SPELL_SHADOW_SICKLE_H : SPELL_SHADOW_SICKLE, true); - } + GetCaster()->CastSpell(nullptr, SPELL_SHADOW_SICKLE); } void Register() override