diff options
author | Treeston <treeston.mmoc@gmail.com> | 2016-09-26 12:49:32 +0200 |
---|---|---|
committer | joschiwald <joschiwald.trinity@gmail.com> | 2017-03-04 00:05:55 +0100 |
commit | f02ff3bd6b8b1243c411afc764626f3fb9994869 (patch) | |
tree | 1feae19f828e88f461bf828510f76e46213936e2 | |
parent | 24c755f4847259f07d33d5c562da301896d7a0e7 (diff) |
[3.3.5] Azjol-Nerub rewrite
* Scripts/AzjolNerub: Complete rewrite.
- Gatewatcher:
- Trash now actually engages properly one by one
- Fix trash spell casting
- Add missing quotes
- Hadronox:
- Everything. Literally.
- Anub'arak:
- Fix add spawns
- Fix impale
- OK this might as well be "fix everything with every boss". Because that's pretty much what happened.
- General hack cleanup
* Update and rename 9999_99_99_99_AZJOLNERUB.sql to 2016_09_25_01_world.sql
* Update and rename 2016_09_25_01_world.sql to 2016_09_25_02_world.sql
* Rename 2016_09_25_02_world.sql to 2016_09_26_02_world.sql
(cherry picked from commit 4b990eb7d7fda3951e640d84a5c18c90366cd26b)
# Conflicts:
# src/server/game/Spells/SpellMgr.cpp
# src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h
# src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
Update boss_hadronox.cpp
(cherry picked from commit 04ec868768d99c97f1cc4d064892bdeee2cbfb17)
Update boss_hadronox.cpp
I hate travis, learn from buildbot
(cherry picked from commit aeb20951bbac5453d5a4db22d23516ba8f948275)
I think I got all of them this time
No idea how this one got past travis.
(cherry picked from commit ae163197a765b294c0a325b6e893d216e1c5d050)
Fix nonPCH.
(cherry picked from commit e121d66fbb8edd68fcf027a05d47324757656228)
...for real this time (seriously - what happened to test builds before merging, Aok?)
(cherry picked from commit dbe0789eba03deb444f0f72e7e79e3be3763662a)
.....hail PCH
(cherry picked from commit 2528194cc1e481a6f002cc0663b12652e7e3de33)
and you get a header, and everyone gets a header...
(cherry picked from commit 5e5fe37a7ef2a76d7bd59250a7702af052a03d20)
8 files changed, 2581 insertions, 640 deletions
diff --git a/sql/updates/world/master/2017_03_03_17_world_2016_09_26_02_world.sql b/sql/updates/world/master/2017_03_03_17_world_2016_09_26_02_world.sql new file mode 100644 index 00000000000..d260efaef5b --- /dev/null +++ b/sql/updates/world/master/2017_03_03_17_world_2016_09_26_02_world.sql @@ -0,0 +1,188 @@ +-- +SET @CGUID = 87530; -- creature GUIDs (14 creatures) + +SET @IMMUNEMASK = (1|2|4|16|32|64|128|256|512|2048|4096|8192|65536|131072|524288|4194304|8388608|67108864|536870912); +-- Correct immunity mask (all bosses) +UPDATE `creature_template` SET `mechanic_immune_mask` = @IMMUNEMASK WHERE `entry` IN (28684,31612); +UPDATE `creature_template` SET `mechanic_immune_mask` = (@IMMUNEMASK|33554432) WHERE `entry` IN (28921,29120,31611,31610); + +-- ------------------------- -- +-- Krik'thir the Gatewatcher -- +-- ------------------------- -- +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (52438,52449,52343); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`Comment`) VALUES +(13,1,52438,0,0,31,0,3,22515,0,0,"Summon Skittering Swarmer - Target World Trigger"), +(13,1,52449,0,0,31,0,3,22515,0,0,"Summon Skittering Infector - Target World Trigger"), +(13,1,52343,0,0,31,0,3,28729,0,0,"Krik'thir Subboss Aggro Trigger - Target Watcher Narjil"), +(13,1,52343,0,1,31,0,3,28730,0,0,"Krik'thir Subboss Aggro Trigger - Target Watcher Garsha"), +(13,1,52343,0,2,31,0,3,28731,0,0,"Krik'thir Subboss Aggro Trigger - Target Watcher Silthik"); + +-- Remove static spawn for Krik'thir's pets +DELETE FROM `creature` WHERE `guid` IN (127234,127232,127227,127226,127231,127226,127225,127228,127233,127236); +DELETE FROM `creature_addon` WHERE `guid` IN (127234,127232,127227,127226,127231,127226,127225,127228,127233,127236); +DELETE FROM `linked_respawn` WHERE `guid` IN (127234,127232,127227,127226,127231,127226,127225,127228,127233,127236); +-- And replace it with creature_summon_groups which allows us to easily respawn them on demand +DELETE FROM `creature_summon_groups` WHERE `summonerId`=28684 AND `summonerType`=0; +INSERT INTO `creature_summon_groups` (`summonerId`,`summonerType`,`groupId`,`entry`,`position_x`,`position_y`,`position_z`,`orientation`,`summonType`,`summonTime`) VALUES +(28684,0,1,28729,511.8097,666.4934,776.2781,0.9773844,6,45000), +(28684,0,1,28732,506.5164,664.3796,776.9759,0.9250245,6,30000), +(28684,0,1,28733,506.1783,669.9266,776.3057,0.8552113,6,30000), +(28684,0,2,28730,526.6636,663.6053,775.8052,1.239184 ,6,45000), +(28684,0,2,28732,531.0308,658.1731,776.2396,1.343904 ,6,30000), +(28684,0,2,28734,521.8148,659.4708,776.3131,1.186824 ,6,30000), +(28684,0,3,28731,543.8261,665.1234,776.2453,1.553343 ,6,45000), +(28684,0,3,28734,550.0461,668.131 ,776.2789,1.658063 ,6,30000), +(28684,0,3,28733,549.6929,662.0067,776.9927,1.64061 ,6,30000); + +-- Small minions are now owned by the World Triggers in the room, so they need AI to aggro properly +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_skittering_swarmer" WHERE `entry`=28735; +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_skittering_infector" WHERE `entry`=28736; + +-- Krik'thir: Subboss Aggro Trigger +DELETE FROM `spell_script_names` WHERE `scriptname` = "spell_gatewatcher_subboss_trigger"; +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES (52343,"spell_gatewatcher_subboss_trigger"); + +-- Anub'ar Skirmisher: Fixtate +DELETE FROM `spell_script_names` WHERE `scriptname` = "spell_anub_ar_skirmisher_fixtate"; +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES (52536,"spell_anub_ar_skirmisher_fixtate"); + +-- Watchers: Web Wrap +DELETE FROM `spell_script_names` WHERE `scriptname` = "spell_gatewatcher_web_wrap"; +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES (52086,"spell_gatewatcher_web_wrap"); +UPDATE `creature_template` SET `AIName`='',`ScriptName`="npc_gatewatcher_web_wrap" WHERE `entry`=28619; + +-- -------- -- +-- Hadronox -- +-- -------- -- +UPDATE `creature` SET `position_x`=515.5848,`position_y`=544.2007,`position_z`=673.6272,`orientation`=5.740286 WHERE `map`=601 AND `id`=28921; +-- Remove existing world trigger spawns +DELETE FROM `creature` WHERE `guid` BETWEEN 127376 AND 127378; +DELETE FROM `creature_addon` WHERE `guid` BETWEEN 127376 AND 127378; +DELETE FROM `creature` WHERE `guid` BETWEEN @CGUID+0 AND @CGUID+5; +DELETE FROM `creature_addon` WHERE `guid` BETWEEN @CGUID+0 AND @CGUID+5; +-- And re-spawn them +INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`PhaseId`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`movementtype`) VALUES +(@CGUID+00,23472,601,6,0,530.495 ,597.0955,777.2634,5.986479 ,0,0,0), +(@CGUID+01,23472,601,6,0,530.6029,596.997 ,777.2633,5.427974 ,0,0,0), +(@CGUID+02,23472,601,6,0,530.7466,596.745 ,777.2631,2.303835 ,0,0,0), +(@CGUID+03,23472,601,6,0,583.0908,617.371 ,771.5508,0.6457718,0,0,0), +(@CGUID+04,23472,601,6,0,480.0325,615.5892,773.3531,4.101524 ,0,0,0), +(@CGUID+05,23472,601,6,0,581.4475,608.8407,739.4051,1.727876 ,0,0,0); +INSERT INTO `creature_addon` (`guid`,`auras`) VALUES +(@CGUID+03,"53035 53036 53037"), +(@CGUID+04,"53035 53036 53037"), +(@CGUID+05,"53035 53036 53037"); +-- Crusher groups are now spawn groups on Hadronox +DELETE FROM `creature` WHERE `guid` IN (127379,127380,127402); +DELETE FROM `creature_addon` WHERE `guid` IN (127379,127380,127402); +DELETE FROM `linked_respawn` WHERE `guid` IN (127379,127380,127402); +DELETE FROM `creature_summon_groups` WHERE `summonerId`=28921 AND `summonerType`=0; +INSERT INTO `creature_summon_groups` (`summonerId`,`summonerType`,`groupId`,`entry`,`position_x`,`position_y`,`position_z`,`orientation`,`summonType`,`summonTime`) VALUES +(28921,0,1,28922,529.6913,547.1257,731.9155,4.79965 ,6,45000), +(28921,0,1,29117,539.2076,549.7539,732.8668,4.55531 ,6,30000), +(28921,0,1,29118,520.3911,548.7895,732.0118,5.0091 ,6,30000), +(28921,0,2,28922,493.4772,603.3438,760.5628,5.440244,6,45000), +(28921,0,2,29117,490.4421,604.335 ,763.1821,5.625595,6,30000), +(28921,0,2,29119,488.8254,609.2819,767.5876,5.590288,6,30000), +(28921,0,3,28922,566.9789,602.5711,759.6418,3.885966,6,45000), +(28921,0,3,29118,569.348 ,604.9987,763.2137,4.179827,6,30000), +(28921,0,3,29119,572.4737,607.4109,767.1777,3.944169,6,30000); +-- Periodic spawn triggers: Spell scripts +DELETE FROM `spell_script_names` WHERE `scriptname` IN ("spell_hadronox_periodic_summon_champion","spell_hadronox_periodic_summon_crypt_fiend","spell_hadronox_periodic_summon_necromancer"); +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES +(53035,"spell_hadronox_periodic_summon_champion"), +(53037,"spell_hadronox_periodic_summon_crypt_fiend"), +(53036,"spell_hadronox_periodic_summon_necromancer"); +-- Assign custom AI to the Anub'ar foes +DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid` IN (29062,29063,29064,29096,29097,29098); +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anub_ar_champion" WHERE `entry` IN (29062,29096); +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anub_ar_crypt_fiend" WHERE `entry` IN (29063,29097); +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anub_ar_necromancer" WHERE `entry` IN (29064,29098); +-- And crusher pack creatures +DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid` IN (28922,29117,29118,29119); +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anub_ar_crusher" WHERE `entry` = 28922; +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anub_ar_crusher_champion" WHERE `entry` = 29117; +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anub_ar_crusher_crypt_fiend" WHERE `entry` = 29118; +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anub_ar_crusher_necromancer" WHERE `entry` = 29119; +-- Hadronox: Leeching Poison +DELETE FROM `spell_script_names` WHERE `scriptname` = "spell_hadronox_leeching_poison"; +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES (53030,"spell_hadronox_leeching_poison"),(59417,"spell_hadronox_leeching_poison"); +-- Hadronox: Web Side Doors / Web Front Doors +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (53177,53185); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionTarget`,`ElseGroup`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`Comment`) VALUES +(13,1,53177,31,0,0,3,23472,@CGUID+3,"Web Front Door - Target Large AOI World Trigger Top Door 1"), +(13,1,53177,31,0,1,3,23472,@CGUID+4,"Web Front Door - Target Large AOI World Trigger Top Door 2"), +(13,1,53185,31,0,0,3,23472,@CGUID+5,"Web Side Door - Target Large AOI World Trigger Side Door"); +DELETE FROM `spell_script_names` WHERE `scriptname`="spell_hadronox_web_doors"; +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES +(53177,"spell_hadronox_web_doors"), +(53185,"spell_hadronox_web_doors"); +-- Spell difficulty data +-- DELETE FROM `spelldifficulty_dbc` WHERE `id` IN (53406,57731,53318); +-- INSERT INTO `spelldifficulty_dbc` (`id`,`spellid0`,`spellid1`) VALUES +-- (53406,53406,59420), -- Hadronox - Web Grab +-- (53318,53318,59346); -- Crusher - Smash +-- Creature texts +DELETE FROM `creature_text` WHERE `entry` IN (28921,28922); +INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`probability`,`sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(28921,1,0,"%s moves up the tunnel!",41,100,0,29852,3,"Hadronox HADRONOX_EMOTE_MOVE"), +(28922,1,0,"The gate has been breached! Quickly, divert forces to deal with these invaders!",14,100,13941,29473,3,"Anub'ar Crusher CRUSHER_SAY_AGGRO"), +(28922,2,0,"%s goes into a frenzy!",41,100,0,10645,3,"Anub'ar Crusher CRUSHER_EMOTE_FRENZY"); +-- Achievement: Hadronox Denied +DELETE FROM `criteria_data` WHERE `criteria_id`=4244; +INSERT INTO `criteria_data` (`criteria_id`,`type`,`ScriptName`) VALUES +(4244,11,"achievement_hadronox_denied"); +DELETE FROM `disables` WHERE `sourceType`=4 AND `entry`=4244; + +-- --------- -- +-- Anub'arak -- +-- --------- -- +-- Pound +DELETE FROM `spell_script_names` WHERE `scriptname`="spell_anubarak_pound"; +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES +(53472,"spell_anubarak_pound"), +(59433,"spell_anubarak_pound"); +-- Summon Carrion Beetles +DELETE FROM `spell_script_names` WHERE `scriptname`="spell_anubarak_carrion_beetles"; +INSERT INTO `spell_script_names` (`spell_id`,`scriptname`) VALUES +(53520,"spell_anubarak_carrion_beetles"); +-- New pet AI +DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid` IN (29213,29214,29216,29217,29184); +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anubarak_anub_ar_darter" WHERE `entry` = 29213; +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anubarak_anub_ar_assassin" WHERE `entry` = 29214; +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anubarak_anub_ar_guardian" WHERE `entry` = 29216; +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anubarak_anub_ar_venomancer" WHERE `entry` = 29217; +-- Impale +UPDATE `creature_template` SET `AIName`="",`ScriptName`="npc_anubarak_impale_target" WHERE `entry`=29184; +-- World triggers for Assassin spawns +DELETE FROM `creature` WHERE `guid` BETWEEN @CGUID+06 AND @CGUID+13; +INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`PhaseId`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`movementtype`) VALUES +(@CGUID+06,22515,601,6,0,478.7393,252.8498,250.5435,0.05235988,0,0,0), +(@CGUID+07,22515,601,6,0,621.319 ,268.4819,250.5435,3.333579 ,0,0,0), +(@CGUID+08,22515,601,6,0,622.9042,252.945 ,250.5435,3.124139 ,0,0,0), +(@CGUID+09,22515,601,6,0,478.1486,269.0094,250.5435,6.126106 ,0,0,0), +(@CGUID+10,22515,601,6,0,478.5473,297.0447,250.5435,5.7944933 ,0,0,0), +(@CGUID+11,22515,601,6,0,478.2909,224.8265,250.2346,0.4014257 ,0,0,0), +(@CGUID+12,22515,601,6,0,620.6218,298.2625,250.5435,3.700098 ,0,0,0), +(@CGUID+13,22515,601,6,0,620.7037,224.5618,250.2321,2.530727 ,0,0,0); +-- Guardian spawn trigger +DELETE FROM `creature_summon_groups` WHERE `summonerId`=29120 AND `summonerType`=0; +INSERT INTO `creature_summon_groups` (`summonerId`,`summonerType`,`groupId`,`entry`,`position_x`,`position_y`,`position_z`,`orientation`,`summonType`,`summonTime`) VALUES +(29120,0,1,22515,549.6223,352.0468,240.899,3.455752,8,0); +-- Targeting info for Assassin spawns +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=53609; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionTarget`,`ElseGroup`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`Comment`) VALUES +(13,1,53609,31,0,0,3,22515,@CGUID+06,"Summon Anub'ar Assassin - Target World Trigger"), +(13,1,53609,31,0,1,3,22515,@CGUID+07,"Summon Anub'ar Assassin - Target World Trigger"), +(13,1,53609,31,0,2,3,22515,@CGUID+08,"Summon Anub'ar Assassin - Target World Trigger"), +(13,1,53609,31,0,3,3,22515,@CGUID+09,"Summon Anub'ar Assassin - Target World Trigger"), +(13,1,53609,31,0,4,3,22515,@CGUID+10,"Summon Anub'ar Assassin - Target World Trigger"), +(13,1,53609,31,0,5,3,22515,@CGUID+11,"Summon Anub'ar Assassin - Target World Trigger"), +(13,1,53609,31,0,6,3,22515,@CGUID+12,"Summon Anub'ar Assassin - Target World Trigger"), +(13,1,53609,31,0,7,3,22515,@CGUID+13,"Summon Anub'ar Assassin - Target World Trigger"); +-- Difficulty data +-- DELETE FROM `spelldifficulty_dbc` WHERE `id` IN (53454,53472,53509); +-- INSERT INTO `spelldifficulty_dbc` (`id`,`spellid0`,`spellid1`) VALUES +-- (53454,53454,59446), -- Anub'arak: Impale +-- (53472,53472,59433), -- Anub'arak: Pound (Stun) +-- (53509,53509,59432); -- Anub'arak: Pound (Damage) diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 7639f45ead8..a0cd1e7daab 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -89,6 +89,12 @@ public: return storage_.size(); } + // Clear the underlying storage. This does NOT despawn the creatures - use DespawnAll for that! + void clear() + { + storage_.clear(); + } + void Summon(Creature const* summon) { storage_.push_back(summon->GetGUID()); } void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); } void DespawnEntry(uint32 entry); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 858665453fa..1b5b481f458 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3031,14 +3031,18 @@ void SpellMgr::LoadSpellInfoCorrections() case 45027: // Revitalize case 45976: // Muru Portal Channel case 52124: // Sky Darkener Assault - case 53096: // Quetz'lun's Judgment - case 70743: // AoD Special - case 70614: // AoD Special - Vegard - case 4020: // Safirdrang's Chill case 52479: // Gift of the Harvester case 61588: // Blazing Harpoon case 55479: // Force Obedience case 28560: // Summon Blizzard (Sapphiron) + case 53096: // Quetz'lun's Judgment + case 70743: // AoD Special + case 70614: // AoD Special - Vegard + case 4020: // Safirdrang's Chill + case 52438: // Summon Skittering Swarmer (Force Cast) + case 52449: // Summon Skittering Infector (Force Cast) + case 53609: // Summon Anub'ar Assassin (Force Cast) + case 53457: // Summon Impale Trigger (AoE) spellInfo->MaxAffectedTargets = 1; break; case 36384: // Skartax Purple Beam diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h index cc2e6a275f0..59429a4d6f6 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h @@ -31,9 +31,10 @@ enum ANDataTypes DATA_ANUBARAK = 2, // Additional Data - DATA_WATCHER_GASHRA = 3, - DATA_WATCHER_SILTHIK = 4, - DATA_WATCHER_NARJIL = 5 + DATA_WATCHER_NARJIL, + DATA_WATCHER_GASHRA, + DATA_WATCHER_SILTHIK, + DATA_ANUBARAK_WALL }; enum ANCreatureIds @@ -55,6 +56,12 @@ enum ANGameObjectIds GO_ANUBARAK_DOOR_3 = 192398 }; +// These are passed as -action to AI's DoAction to differentiate between them and boss scripts' own actions +enum ANInstanceActions +{ + ACTION_GATEWATCHER_GREET = 1 +}; + template<class AI> AI* GetAzjolNerubAI(Creature* creature) { diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp index 55bf1867797..74e89d66d4c 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp @@ -17,65 +17,90 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "PassiveAI.h" #include "azjol_nerub.h" enum Spells { - SPELL_CARRION_BEETLES = 53520, - SPELL_SUMMON_CARRION_BEETLES = 53521, - SPELL_LEECHING_SWARM = 53467, - SPELL_POUND = 53472, - SPELL_SUBMERGE = 53421, - SPELL_IMPALE_DMG = 53454, - SPELL_IMPALE_SHAKEGROUND = 53455, - SPELL_IMPALE_SPIKE = 53539, //this is not the correct visual effect - //SPELL_IMPALE_TARGET = 53458, + SPELL_EMERGE = 53500, + SPELL_SUBMERGE = 53421, + SPELL_IMPALE_AURA = 53456, + SPELL_IMPALE_VISUAL = 53455, + SPELL_IMPALE_DAMAGE = 53454, + SPELL_LEECHING_SWARM = 53467, + SPELL_POUND = 59433, + SPELL_POUND_DAMAGE = 59432, + SPELL_CARRION_BEETLES = 53520, + SPELL_CARRION_BEETLE = 53521, + + SPELL_SUMMON_DARTER = 53599, + SPELL_SUMMON_ASSASSIN = 53609, + SPELL_SUMMON_GUARDIAN = 53614, + SPELL_SUMMON_VENOMANCER = 53615, + + SPELL_DART = 59349, + SPELL_BACKSTAB = 52540, + SPELL_ASSASSIN_VISUAL = 53611, + SPELL_SUNDER_ARMOR = 53618, + SPELL_POISON_BOLT = 53617 }; enum Creatures { - CREATURE_GUARDIAN = 29216, - CREATURE_VENOMANCER = 29217, - CREATURE_DATTER = 29213, - CREATURE_IMPALE_TARGET = 89, - DISPLAY_INVISIBLE = 11686 + NPC_WORLD_TRIGGER = 22515, }; -// not in db enum Yells { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_LOCUST = 3, - SAY_SUBMERGE = 4, - SAY_INTRO = 5 + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_LOCUST = 3, + SAY_SUBMERGE = 4, + SAY_INTRO = 5 +}; + +enum Events +{ + EVENT_POUND = 1, + EVENT_IMPALE, + EVENT_LEECHING_SWARM, + EVENT_CARRION_BEETLES, + EVENT_SUBMERGE, // use event for this so we don't submerge mid-cast + EVENT_DARTER, + EVENT_ASSASSIN, + EVENT_GUARDIAN, + EVENT_VENOMANCER, + EVENT_CLOSE_DOOR +}; + +enum Actions +{ + ACTION_PET_DIED = 1, + ACTION_PET_EVADE }; enum Misc { - ACHIEV_TIMED_START_EVENT = 20381, + ACHIEV_GOTTA_GO_START_EVENT = 20381, }; enum Phases { - PHASE_MELEE = 0, - PHASE_UNDERGROUND = 1, - IMPALE_PHASE_TARGET = 0, - IMPALE_PHASE_ATTACK = 1, - IMPALE_PHASE_DMG = 2 + PHASE_EMERGE = 1, + PHASE_SUBMERGE }; -const Position SpawnPoint[2] = +enum GUIDTypes { - { 550.7f, 282.8f, 224.3f, 0.0f }, - { 551.1f, 229.4f, 224.3f, 0.0f }, + GUID_TYPE_PET = 0, + GUID_TYPE_IMPALE }; -const Position SpawnPointGuardian[2] = +enum SummonGroups { - { 550.348633f, 316.006805f, 234.2947f, 0.0f }, - { 550.188660f, 324.264557f, 237.7412f, 0.0f }, + SUMMON_GROUP_WORLD_TRIGGER_GUARDIAN = 1 }; class boss_anub_arak : public CreatureScript @@ -83,97 +108,58 @@ class boss_anub_arak : public CreatureScript public: boss_anub_arak() : CreatureScript("boss_anub_arak") { } - struct boss_anub_arakAI : public ScriptedAI + struct boss_anub_arakAI : public BossAI { - boss_anub_arakAI(Creature* creature) : ScriptedAI(creature), Summons(me) - { - Initialize(); - instance = creature->GetInstanceScript(); - GuardianSummoned = false; - VenomancerSummoned = false; - DatterSummoned = false; - UndergroundTimer = 0; - VenomancerTimer = 0; - DatterTimer = 0; - DelayTimer = 0; - } - - void Initialize() - { - CarrionBeetlesTimer = 8 * IN_MILLISECONDS; - LeechingSwarmTimer = 20 * IN_MILLISECONDS; - ImpaleTimer = 9 * IN_MILLISECONDS; - PoundTimer = 15 * IN_MILLISECONDS; - - Phase = PHASE_MELEE; - UndergroundPhase = 0; - Channeling = false; - ImpalePhase = IMPALE_PHASE_TARGET; - ImpaleTarget.Clear(); - } - - InstanceScript* instance; - - bool Channeling; - bool GuardianSummoned; - bool VenomancerSummoned; - bool DatterSummoned; - uint8 Phase; - uint32 UndergroundPhase; - uint32 CarrionBeetlesTimer; - uint32 LeechingSwarmTimer; - uint32 PoundTimer; - uint32 UndergroundTimer; - uint32 VenomancerTimer; - uint32 DatterTimer; - uint32 DelayTimer; - - uint32 ImpaleTimer; - uint32 ImpalePhase; - ObjectGuid ImpaleTarget; - - SummonList Summons; + boss_anub_arakAI(Creature* creature) : BossAI(creature, DATA_ANUBARAK), _nextSubmerge(0), _petCount(0), _assassinCount(0), _guardianCount(0), _venomancerCount(0) { } void Reset() override { - Initialize(); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->RemoveAura(SPELL_SUBMERGE); - - Summons.DespawnAll(); - - instance->SetBossState(DATA_ANUBARAK, NOT_STARTED); - instance->DoStopCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + BossAI::Reset(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + instance->DoStopCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT); + _nextSubmerge = 75; + _petCount = 0; } - Creature* DoSummonImpaleTarget(Unit* target) + void EnterCombat(Unit* who) override { - Position targetPos = target->GetPosition(); + BossAI::EnterCombat(who); - if (TempSummon* impaleTarget = me->SummonCreature(CREATURE_IMPALE_TARGET, targetPos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 6*IN_MILLISECONDS)) - { - ImpaleTarget = impaleTarget->GetGUID(); - impaleTarget->SetReactState(REACT_PASSIVE); - impaleTarget->SetDisplayId(DISPLAY_INVISIBLE); - impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - impaleTarget->SetControlled(true, UNIT_STATE_ROOT); - return impaleTarget; - } - - return NULL; - } + if (GameObject* door = instance->GetGameObject(DATA_ANUBARAK_WALL)) + door->SetGoState(GO_STATE_ACTIVE); // open door for now - void EnterCombat(Unit* /*who*/) override - { Talk(SAY_AGGRO); - DelayTimer = 0; - instance->DoStartCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + instance->DoStartCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT); + + events.SetPhase(PHASE_EMERGE); + events.ScheduleEvent(EVENT_CLOSE_DOOR, Seconds(5)); + events.ScheduleEvent(EVENT_POUND, randtime(Seconds(2), Seconds(4)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_LEECHING_SWARM, randtime(Seconds(5), Seconds(7)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_CARRION_BEETLES, randtime(Seconds(14), Seconds(17)), 0, PHASE_EMERGE); + + // set up world triggers + std::list<TempSummon*> summoned; + me->SummonCreatureGroup(SUMMON_GROUP_WORLD_TRIGGER_GUARDIAN, &summoned); + if (summoned.empty()) // something went wrong + { + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } + _guardianTrigger = (*summoned.begin())->GetGUID(); + + if (Creature* trigger = DoSummon(NPC_WORLD_TRIGGER, me->GetPosition(), 0u, TEMPSUMMON_MANUAL_DESPAWN)) + _assassinTrigger = trigger->GetGUID(); + else + { + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } } - void DelayEventStart() + void EnterEvadeMode(EvadeReason /*why*/) override { - instance->SetBossState(DATA_ANUBARAK, IN_PROGRESS); + summons.DespawnAll(); + _DespawnAtEvade(); } void UpdateAI(uint32 diff) override @@ -181,192 +167,549 @@ public: if (!UpdateVictim()) return; - if (DelayTimer && DelayTimer > 5000) - DelayEventStart(); - else DelayTimer+=diff; + events.Update(diff); - switch (Phase) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - case PHASE_UNDERGROUND: - if (ImpaleTimer <= diff) + switch (eventId) { - switch (ImpalePhase) + case EVENT_CLOSE_DOOR: + if (GameObject* door = instance->GetGameObject(DATA_ANUBARAK_WALL)) + door->SetGoState(GO_STATE_READY); + break; + case EVENT_POUND: + DoCastVictim(SPELL_POUND); + events.Repeat(randtime(Seconds(26), Seconds(32))); + break; + case EVENT_LEECHING_SWARM: + Talk(SAY_LOCUST); + DoCastAOE(SPELL_LEECHING_SWARM); + events.Repeat(randtime(Seconds(25), Seconds(28))); + break; + case EVENT_CARRION_BEETLES: + DoCastAOE(SPELL_CARRION_BEETLES); + events.Repeat(randtime(Seconds(24), Seconds(27))); + break; + case EVENT_IMPALE: + if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, _impaleTarget)) + DoCast(impaleTarget, SPELL_IMPALE_DAMAGE, true); + break; + case EVENT_SUBMERGE: + Talk(SAY_SUBMERGE); + DoCastSelf(SPELL_SUBMERGE); + break; + case EVENT_DARTER: { - case IMPALE_PHASE_TARGET: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + std::list<Creature*> triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_WORLD_TRIGGER); + if (!triggers.empty()) { - if (Creature* impaleTarget = DoSummonImpaleTarget(target)) - impaleTarget->CastSpell(impaleTarget, SPELL_IMPALE_SHAKEGROUND, true); - ImpaleTimer = 3*IN_MILLISECONDS; - ImpalePhase = IMPALE_PHASE_ATTACK; + std::list<Creature*>::iterator it = triggers.begin(); + std::advance(it, urand(0, triggers.size()-1)); + (*it)->CastSpell(*it, SPELL_SUMMON_DARTER, true); + events.Repeat(Seconds(11)); } + else + EnterEvadeMode(EVADE_REASON_OTHER); break; - case IMPALE_PHASE_ATTACK: - if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, ImpaleTarget)) + } + case EVENT_ASSASSIN: + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _assassinTrigger)) { - impaleTarget->CastSpell(impaleTarget, SPELL_IMPALE_SPIKE, false); - impaleTarget->RemoveAurasDueToSpell(SPELL_IMPALE_SHAKEGROUND); + trigger->CastSpell(trigger, SPELL_SUMMON_ASSASSIN, true); + trigger->CastSpell(trigger, SPELL_SUMMON_ASSASSIN, true); + if (_assassinCount > 2) + { + _assassinCount -= 2; + events.Repeat(Seconds(20)); + } + else + _assassinCount = 0; } - ImpalePhase = IMPALE_PHASE_DMG; - ImpaleTimer = 1*IN_MILLISECONDS; - break; - case IMPALE_PHASE_DMG: - if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, ImpaleTarget)) - me->CastSpell(impaleTarget, SPELL_IMPALE_DMG, true); - ImpalePhase = IMPALE_PHASE_TARGET; - ImpaleTimer = 9*IN_MILLISECONDS; + else // something went wrong + EnterEvadeMode(EVADE_REASON_OTHER); break; - } - } else ImpaleTimer -= diff; - - if (!GuardianSummoned) - { - for (uint8 i = 0; i < 2; ++i) - { - if (Creature* Guardian = me->SummonCreature(CREATURE_GUARDIAN, SpawnPointGuardian[i], TEMPSUMMON_CORPSE_DESPAWN, 0)) + case EVENT_GUARDIAN: + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _guardianTrigger)) { - Guardian->AddThreat(me->GetVictim(), 0.0f); - DoZoneInCombat(Guardian); + trigger->CastSpell(trigger, SPELL_SUMMON_GUARDIAN, true); + trigger->CastSpell(trigger, SPELL_SUMMON_GUARDIAN, true); + if (_guardianCount > 2) + { + _guardianCount -= 2; + events.Repeat(Seconds(20)); + } + else + _guardianCount = 0; } - } - GuardianSummoned = true; - } - - if (!VenomancerSummoned) - { - if (VenomancerTimer <= diff) - { - if (UndergroundPhase > 1) + else + EnterEvadeMode(EVADE_REASON_OTHER); + break; + case EVENT_VENOMANCER: + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _guardianTrigger)) { - for (uint8 i = 0; i < 2; ++i) + trigger->CastSpell(trigger, SPELL_SUMMON_VENOMANCER, true); + trigger->CastSpell(trigger, SPELL_SUMMON_VENOMANCER, true); + if (_venomancerCount > 2) { - if (Creature* Venomancer = me->SummonCreature(CREATURE_VENOMANCER, SpawnPoint[i], TEMPSUMMON_CORPSE_DESPAWN, 0)) - { - Venomancer->AddThreat(me->GetVictim(), 0.0f); - DoZoneInCombat(Venomancer); - } + _venomancerCount -= 2; + events.Repeat(Seconds(20)); } - VenomancerSummoned = true; + else + _venomancerCount = 0; } - } else VenomancerTimer -= diff; + else + EnterEvadeMode(EVADE_REASON_OTHER); + break; + default: + break; } - if (!DatterSummoned) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void SetGUID(ObjectGuid guid, int32 type) override + { + switch (type) + { + case GUID_TYPE_PET: { - if (DatterTimer <= diff) + if (Creature* creature = ObjectAccessor::GetCreature(*me, guid)) + JustSummoned(creature); + else // something has gone horribly wrong + EnterEvadeMode(EVADE_REASON_OTHER); + break; + } + case GUID_TYPE_IMPALE: + _impaleTarget = guid; + events.ScheduleEvent(EVENT_IMPALE, Seconds(4)); + break; + } + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_PET_DIED: + if (!_petCount) // underflow check - something has gone horribly wrong { - if (UndergroundPhase > 2) - { - for (uint8 i = 0; i < 2; ++i) - { - if (Creature* Datter = me->SummonCreature(CREATURE_DATTER, SpawnPoint[i], TEMPSUMMON_CORPSE_DESPAWN, 0)) - { - Datter->AddThreat(me->GetVictim(), 0.0f); - DoZoneInCombat(Datter); - } - } - DatterSummoned = true; - } - } else DatterTimer -= diff; + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } + if (!--_petCount) // last pet died, emerge + { + me->RemoveAurasDueToSpell(SPELL_SUBMERGE); + me->RemoveAurasDueToSpell(SPELL_IMPALE_AURA); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + DoCastSelf(SPELL_EMERGE); + events.SetPhase(PHASE_EMERGE); + events.ScheduleEvent(EVENT_POUND, randtime(Seconds(13), Seconds(18)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_LEECHING_SWARM, randtime(Seconds(3), Seconds(7)), 0, PHASE_EMERGE); + events.ScheduleEvent(EVENT_CARRION_BEETLES, randtime(Seconds(10), Seconds(15)), 0, PHASE_EMERGE); + } + break; + case ACTION_PET_EVADE: + EnterEvadeMode(EVADE_REASON_OTHER); + break; + } + } - if (me->HasAura(SPELL_LEECHING_SWARM)) - me->RemoveAurasDueToSpell(SPELL_LEECHING_SWARM); + void DamageTaken(Unit* /*source*/, uint32& damage) override + { + if (me->HasAura(SPELL_SUBMERGE)) + damage = 0; + else + if (_nextSubmerge && me->HealthBelowPctDamaged(_nextSubmerge, damage)) + { + events.CancelEvent(EVENT_SUBMERGE); + events.ScheduleEvent(EVENT_SUBMERGE, 0, 0, PHASE_EMERGE); + _nextSubmerge = _nextSubmerge-25; } + } - if (UndergroundTimer <= diff) - { - me->RemoveAura(SPELL_SUBMERGE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - Phase = PHASE_MELEE; - } else UndergroundTimer -= diff; - break; - - case PHASE_MELEE: - if (((UndergroundPhase == 0 && HealthBelowPct(75)) - || (UndergroundPhase == 1 && HealthBelowPct(50)) - || (UndergroundPhase == 2 && HealthBelowPct(25))) - && !me->HasUnitState(UNIT_STATE_CASTING)) + void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SUBMERGE) + { + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasDueToSpell(SPELL_LEECHING_SWARM); + DoCastSelf(SPELL_IMPALE_AURA, true); + + events.SetPhase(PHASE_SUBMERGE); + switch (_nextSubmerge) { - GuardianSummoned = false; - VenomancerSummoned = false; - DatterSummoned = false; + case 50: // first submerge phase + _assassinCount = 4; + _guardianCount = 2; + _venomancerCount = 0; + break; + case 25: // second submerge phase + _assassinCount = 6; + _guardianCount = 2; + _venomancerCount = 2; + break; + case 0: // third submerge phase + _assassinCount = 6; + _guardianCount = 2; + _venomancerCount = 2; + events.ScheduleEvent(EVENT_DARTER, Seconds(0), 0, PHASE_SUBMERGE); + break; + } + _petCount = _guardianCount + _venomancerCount; + if (_assassinCount) + events.ScheduleEvent(EVENT_ASSASSIN, Seconds(0), 0, PHASE_SUBMERGE); + if (_guardianCount) + events.ScheduleEvent(EVENT_GUARDIAN, Seconds(4), 0, PHASE_SUBMERGE); + if (_venomancerCount) + events.ScheduleEvent(EVENT_VENOMANCER, Seconds(20), 0, PHASE_SUBMERGE); + } + } + + private: + ObjectGuid _impaleTarget; + uint32 _nextSubmerge; + uint32 _petCount; + ObjectGuid _guardianTrigger; + ObjectGuid _assassinTrigger; + uint8 _assassinCount; + uint8 _guardianCount; + uint8 _venomancerCount; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<boss_anub_arakAI>(creature); + } +}; - UndergroundTimer = 40*IN_MILLISECONDS; - VenomancerTimer = 25*IN_MILLISECONDS; - DatterTimer = 32*IN_MILLISECONDS; +class npc_anubarak_pet_template : public ScriptedAI +{ + public: + npc_anubarak_pet_template(Creature* creature, bool isLarge) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _isLarge(isLarge) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->SetGUID(me->GetGUID(), GUID_TYPE_PET); + else + me->DespawnOrUnsummon(); + } - ImpalePhase = 0; - ImpaleTimer = 9*IN_MILLISECONDS; + void JustDied(Unit* killer) override + { + ScriptedAI::JustDied(killer); + if (_isLarge) + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->DoAction(ACTION_PET_DIED); + } - DoCast(me, SPELL_SUBMERGE, false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->DoAction(ACTION_PET_EVADE); + else + me->DespawnOrUnsummon(); + } - Phase = PHASE_UNDERGROUND; - ++UndergroundPhase; - } + protected: + InstanceScript* _instance; + private: + bool const _isLarge; +}; - if (Channeling == true) +class npc_anubarak_anub_ar_darter : public CreatureScript +{ + public: + npc_anubarak_anub_ar_darter() : CreatureScript("npc_anubarak_anub_ar_darter") { } + + struct npc_anubarak_anub_ar_darterAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_darterAI(Creature* creature) : npc_anubarak_pet_template(creature, false) { } + + void InitializeAI() override + { + npc_anubarak_pet_template::InitializeAI(); + DoCastAOE(SPELL_DART); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anubarak_anub_ar_darterAI>(creature); + } +}; + +class npc_anubarak_anub_ar_assassin : public CreatureScript +{ + public: + npc_anubarak_anub_ar_assassin() : CreatureScript("npc_anubarak_anub_ar_assassin") { } + + struct npc_anubarak_anub_ar_assassinAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_assassinAI(Creature* creature) : npc_anubarak_pet_template(creature, false), _backstabTimer(6 * IN_MILLISECONDS) { } + + bool IsInBounds(Position const& jumpTo, CreatureBoundary const* boundary) + { + if (!boundary) + return true; + for (CreatureBoundary::const_iterator it = boundary->cbegin(); it != boundary->cend(); ++it) + if (!(*it)->IsWithinBoundary(&jumpTo)) + return false; + return true; + } + Position GetRandomPositionAround(Creature* anubarak) + { + static float DISTANCE_MIN = 10.0f; + static float DISTANCE_MAX = 30.0f; + double angle = rand_norm() * 2.0 * M_PI; + return { anubarak->GetPositionX() + (float)(frand(DISTANCE_MIN, DISTANCE_MAX)*std::sin(angle)), anubarak->GetPositionY() + (float)(frand(DISTANCE_MIN, DISTANCE_MAX)*std::cos(angle)), anubarak->GetPositionZ() }; + } + void InitializeAI() override + { + npc_anubarak_pet_template::InitializeAI(); + CreatureBoundary const* boundary = _instance->GetBossBoundary(DATA_ANUBARAK); + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) { - for (uint8 i = 0; i < 8; ++i) - DoCastVictim(SPELL_SUMMON_CARRION_BEETLES, true); - Channeling = false; + Position jumpTo; + do + jumpTo = GetRandomPositionAround(anubarak); + while (!IsInBounds(jumpTo, boundary)); + me->GetMotionMaster()->MoveJump(jumpTo, 40.0f, 40.0f); + DoCastSelf(SPELL_ASSASSIN_VISUAL, true); } - else if (CarrionBeetlesTimer <= diff) - { - Channeling = true; - DoCastVictim(SPELL_CARRION_BEETLES); - CarrionBeetlesTimer = 25*IN_MILLISECONDS; - } else CarrionBeetlesTimer -= diff; + } - if (LeechingSwarmTimer <= diff) - { - DoCast(me, SPELL_LEECHING_SWARM, true); - LeechingSwarmTimer = 19*IN_MILLISECONDS; - } else LeechingSwarmTimer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (PoundTimer <= diff) + if (diff >= _backstabTimer) { - if (Unit* target = me->GetVictim()) - { - if (Creature* pImpaleTarget = DoSummonImpaleTarget(target)) - me->CastSpell(pImpaleTarget, SPELL_POUND, false); - } - PoundTimer = 16500; - } else PoundTimer -= diff; + if (me->GetVictim() && me->GetVictim()->isInBack(me)) + DoCastVictim(SPELL_BACKSTAB); + _backstabTimer = 6 * IN_MILLISECONDS; + } + else + _backstabTimer -= diff; DoMeleeAttackIfReady(); - break; } - } - void JustDied(Unit* /*killer*/) override + void MovementInform(uint32 /*type*/, uint32 id) override + { + if (id == EVENT_JUMP) + { + me->RemoveAurasDueToSpell(SPELL_ASSASSIN_VISUAL); + DoZoneInCombat(); + } + } + + private: + uint32 _backstabTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override { - Talk(SAY_DEATH); - Summons.DespawnAll(); - instance->SetBossState(DATA_ANUBARAK, DONE); + return GetAzjolNerubAI<npc_anubarak_anub_ar_assassinAI>(creature); } +}; - void KilledUnit(Unit* victim) override +class npc_anubarak_anub_ar_guardian : public CreatureScript +{ + public: + npc_anubarak_anub_ar_guardian() : CreatureScript("npc_anubarak_anub_ar_guardian") { } + + struct npc_anubarak_anub_ar_guardianAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_guardianAI(Creature* creature) : npc_anubarak_pet_template(creature, true), _sunderTimer(6 * IN_MILLISECONDS) { } + + void UpdateAI(uint32 diff) override { - if (victim->GetTypeId() != TYPEID_PLAYER) + if (!UpdateVictim()) return; - Talk(SAY_SLAY); + if (diff >= _sunderTimer) + { + DoCastVictim(SPELL_SUNDER_ARMOR); + _sunderTimer = 12 * IN_MILLISECONDS; + } + else + _sunderTimer -= diff; + + DoMeleeAttackIfReady(); } - void JustSummoned(Creature* summon) override + private: + uint32 _sunderTimer; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anubarak_anub_ar_guardianAI>(creature); + } +}; + +class npc_anubarak_anub_ar_venomancer : public CreatureScript +{ + public: + npc_anubarak_anub_ar_venomancer() : CreatureScript("npc_anubarak_anub_ar_venomancer") { } + + struct npc_anubarak_anub_ar_venomancerAI : public npc_anubarak_pet_template + { + npc_anubarak_anub_ar_venomancerAI(Creature* creature) : npc_anubarak_pet_template(creature, true), _boltTimer(5 * IN_MILLISECONDS) { } + + void UpdateAI(uint32 diff) override { - Summons.Summon(summon); + if (!UpdateVictim()) + return; + + if (diff >= _boltTimer) + { + DoCastVictim(SPELL_POISON_BOLT); + _boltTimer = urandms(2, 3); + } + else + _boltTimer -= diff; + + DoMeleeAttackIfReady(); } + + private: + uint32 _boltTimer; }; CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<boss_anub_arakAI>(creature); + return GetAzjolNerubAI<npc_anubarak_anub_ar_venomancerAI>(creature); } }; +class npc_anubarak_impale_target : public CreatureScript +{ + public: + npc_anubarak_impale_target() : CreatureScript("npc_anubarak_impale_target") { } + + struct npc_anubarak_impale_targetAI : public NullCreatureAI + { + npc_anubarak_impale_targetAI(Creature* creature) : NullCreatureAI(creature) { } + + void InitializeAI() override + { + if (Creature* anubarak = me->GetInstanceScript()->GetCreature(DATA_ANUBARAK)) + { + DoCastSelf(SPELL_IMPALE_VISUAL); + me->DespawnOrUnsummon(Seconds(6)); + anubarak->AI()->SetGUID(me->GetGUID(), GUID_TYPE_IMPALE); + } + else + me->DespawnOrUnsummon(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anubarak_impale_targetAI>(creature); + } +}; + +class spell_anubarak_pound : public SpellScriptLoader +{ + public: + spell_anubarak_pound() : SpellScriptLoader("spell_anubarak_pound") { } + + class spell_anubarak_pound_SpellScript : public SpellScript + { + PrepareSpellScript(spell_anubarak_pound_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_POUND_DAMAGE) != nullptr; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + GetCaster()->CastSpell(target, SPELL_POUND_DAMAGE, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_anubarak_pound_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_anubarak_pound_SpellScript(); + } +}; + +class spell_anubarak_carrion_beetles : public SpellScriptLoader +{ + public: + spell_anubarak_carrion_beetles() : SpellScriptLoader("spell_anubarak_carrion_beetles") { } + + class spell_anubarak_carrion_beetles_AuraScript : public AuraScript + { + public: + PrepareAuraScript(spell_anubarak_carrion_beetles_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return (sSpellMgr->GetSpellInfo(SPELL_CARRION_BEETLE) != nullptr); + } + + void HandlePeriodic(AuraEffect const* /*eff*/) + { + GetCaster()->CastSpell(GetCaster(), SPELL_CARRION_BEETLE, true); + GetCaster()->CastSpell(GetCaster(), SPELL_CARRION_BEETLE, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_anubarak_carrion_beetles_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_anubarak_carrion_beetles_AuraScript(); + } +}; + void AddSC_boss_anub_arak() { new boss_anub_arak(); + + new npc_anubarak_anub_ar_darter(); + new npc_anubarak_anub_ar_assassin(); + new npc_anubarak_anub_ar_guardian(); + new npc_anubarak_anub_ar_venomancer(); + new npc_anubarak_impale_target(); + + new spell_anubarak_pound(); + new spell_anubarak_carrion_beetles(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index 45e984f8936..ac840c58f55 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -15,34 +15,136 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* -* Comment: No Waves atm and the doors spells are crazy... -* -* When your group enters the main room (the one after the bridge), you will notice a group of 3 Nerubians. -* When you engage them, 2 more groups like this one spawn behind the first one - it is important to pull the first group back, -* so you don't aggro all 3. Hadronox will be under you, fighting Nerubians. -* -* This is the timed gauntlet - waves of non-elite spiders -* will spawn from the 3 doors located a little above the main room, and will then head down to fight Hadronox. After clearing the -* main room, it is recommended to just stay in it, kill the occasional non-elites that will attack you instead of the boss, and wait for -* Hadronox to make his way to you. When Hadronox enters the main room, she will web the doors, and no more non-elites will spawn. -*/ - #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" #include "azjol_nerub.h" +enum Events +{ + // Hadronox + EVENT_LEECH_POISON = 1, + EVENT_ACID_CLOUD, + EVENT_WEB_GRAB, + EVENT_PIERCE_ARMOR, + EVENT_PLAYER_CHECK, + + // Anub'ar Crusher + EVENT_SMASH, + + // Anub'ar foes - Shared + EVENT_TAUNT, + + // Anub'ar Champion + EVENT_REND, + EVENT_PUMMEL, + + // Anub'ar Crypt Guard + EVENT_CRUSHING_WEBS, + EVENT_INFECTED_WOUND, + + // Anub'ar Necromancer + EVENT_SHADOW_BOLT, + EVENT_ANIMATE_BONES +}; + enum Spells { - SPELL_ACID_CLOUD = 53400, // Victim - SPELL_LEECH_POISON = 53030, // Victim - SPELL_PIERCE_ARMOR = 53418, // Victim - SPELL_WEB_GRAB = 57731, // Victim - SPELL_WEB_FRONT_DOORS = 53177, // Self - SPELL_WEB_SIDE_DOORS = 53185, // Self - H_SPELL_ACID_CLOUD = 59419, - H_SPELL_LEECH_POISON = 59417, - H_SPELL_WEB_GRAB = 59421 + // Hadronox + SPELL_WEB_FRONT_DOORS = 53177, + SPELL_WEB_SIDE_DOORS = 53185, + SPELL_LEECH_POISON = 53030, + SPELL_LEECH_POISON_HEAL = 53800, + SPELL_ACID_CLOUD = 53400, + SPELL_WEB_GRAB = 57731, + SPELL_PIERCE_ARMOR = 53418, + + // Anub'ar opponent summoning spells + SPELL_SUMMON_CHAMPION_PERIODIC = 53035, + SPELL_SUMMON_CRYPT_FIEND_PERIODIC = 53037, + SPELL_SUMMON_NECROMANCER_PERIODIC = 53036, + SPELL_SUMMON_CHAMPION_TOP = 53064, + SPELL_SUMMON_CRYPT_FIEND_TOP = 53065, + SPELL_SUMMON_NECROMANCER_TOP = 53066, + SPELL_SUMMON_CHAMPION_BOTTOM = 53090, + SPELL_SUMMON_CRYPT_FIEND_BOTTOM = 53091, + SPELL_SUMMON_NECROMANCER_BOTTOM = 53092, + + // Anub'ar Crusher + SPELL_SMASH = 53318, + SPELL_FRENZY = 53801, + + // Anub'ar foes - Shared + SPELL_TAUNT = 53798, + + // Anub'ar Champion + SPELL_REND = 59343, + SPELL_PUMMEL = 59344, + + // Anub'ar Crypt Guard + SPELL_CRUSHING_WEBS = 59347, + SPELL_INFECTED_WOUND = 59348, + + // Anub'ar Necromancer + SPELL_SHADOW_BOLT = 53333, + SPELL_ANIMATE_BONES_1 = 53334, + SPELL_ANIMATE_BONES_2 = 53336, +}; + +enum SummonGroups +{ + SUMMON_GROUP_CRUSHER_1 = 1, + SUMMON_GROUP_CRUSHER_2 = 2, + SUMMON_GROUP_CRUSHER_3 = 3 +}; + +enum Actions +{ + ACTION_HADRONOX_MOVE = 1, + ACTION_CRUSHER_ENGAGED, + ACTION_PACK_WALK +}; + +enum Data +{ + DATA_CRUSHER_PACK_ID = 1, + DATA_HADRONOX_ENTERED_COMBAT, + DATA_HADRONOX_WEBBED_DOORS +}; + +enum Creatures +{ + NPC_CRUSHER = 28922, + NPC_WORLDTRIGGER_LARGE = 23472 +}; + +enum Talk +{ + CRUSHER_SAY_AGGRO = 1, + CRUSHER_EMOTE_FRENZY = 2, + HADRONOX_EMOTE_MOVE = 1 +}; + +// Movement IDs used by the permanently spawning Anub'ar opponents - they are done in sequence, as one finishes, the next one starts +enum Movements +{ + MOVE_NONE = 0, + MOVE_OUTSIDE, + MOVE_DOWNSTAIRS, + MOVE_DOWNSTAIRS_2, + MOVE_HADRONOX, // this one might have us take a detour to avoid pathfinding "through" the floor... + MOVE_HADRONOX_REAL // while this one will always make us movechase +}; + +static const uint8 NUM_STEPS = 4; +static const Position hadronoxStep[NUM_STEPS] = +{ + { 515.5848f, 544.2007f, 673.6272f }, + { 562.191f , 514.068f , 696.4448f }, + { 610.3828f, 518.6407f, 695.9385f }, + { 530.42f , 560.003f, 733.0308f } }; class boss_hadronox : public CreatureScript @@ -50,157 +152,1014 @@ class boss_hadronox : public CreatureScript public: boss_hadronox() : CreatureScript("boss_hadronox") { } - struct boss_hadronoxAI : public ScriptedAI + struct boss_hadronoxAI : public BossAI { - boss_hadronoxAI(Creature* creature) : ScriptedAI(creature) + boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX), _enteredCombat(false), _doorsWebbed(false), _lastPlayerCombatState(false), _step(0) { } + + bool IsInCombatWithPlayer() const { - Initialize(); - instance = creature->GetInstanceScript(); - fMaxDistance = 50.0f; - bFirstTime = true; + std::list<HostileReference*> const& refs = me->getThreatManager().getThreatList(); + for (HostileReference const* hostileRef : refs) + { + if (Unit const* target = hostileRef->getTarget()) + if (target->IsControlledByPlayer()) + return true; + } + return false; } - void Initialize() + void SetStep(uint8 step) { - uiAcidTimer = urand(10 * IN_MILLISECONDS, 14 * IN_MILLISECONDS); - uiLeechTimer = urand(3 * IN_MILLISECONDS, 9 * IN_MILLISECONDS); - uiPierceTimer = urand(1 * IN_MILLISECONDS, 3 * IN_MILLISECONDS); - uiGrabTimer = urand(15 * IN_MILLISECONDS, 19 * IN_MILLISECONDS); - uiDoorsTimer = urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); - uiCheckDistanceTimer = 2 * IN_MILLISECONDS; + if (_lastPlayerCombatState) + return; + + _step = step; + me->SetHomePosition(hadronoxStep[step]); + me->GetMotionMaster()->Clear(); + me->AttackStop(); + SetCombatMovement(false); + me->GetMotionMaster()->MovePoint(0, hadronoxStep[step]); } - InstanceScript* instance; + void SummonCrusherPack(SummonGroups group) + { + std::list<TempSummon*> summoned; + me->SummonCreatureGroup(group, &summoned); + for (TempSummon* summon : summoned) + { + summon->AI()->SetData(DATA_CRUSHER_PACK_ID, group); + summon->AI()->DoAction(ACTION_PACK_WALK); + } + } + + void MovementInform(uint32 type, uint32 /*id*/) override + { + if (type != POINT_MOTION_TYPE) + return; + SetCombatMovement(true); + AttackStart(me->GetVictim()); + if (_step < NUM_STEPS-1) + return; + DoCastAOE(SPELL_WEB_FRONT_DOORS); + DoCastAOE(SPELL_WEB_SIDE_DOORS); + _doorsWebbed = true; + DoZoneInCombat(); + } - uint32 uiAcidTimer; - uint32 uiLeechTimer; - uint32 uiPierceTimer; - uint32 uiGrabTimer; - uint32 uiDoorsTimer; - uint32 uiCheckDistanceTimer; + uint32 GetData(uint32 data) const override + { + if (data == DATA_HADRONOX_ENTERED_COMBAT) + return _enteredCombat ? 1 : 0; + if (data == DATA_HADRONOX_WEBBED_DOORS) + return _doorsWebbed ? 1 : 0; + return 0; + } - bool bFirstTime; + bool CanAIAttack(Unit const* target) const override + { + // Prevent Hadronox from going too far from her current home position + if (!target->IsControlledByPlayer() && target->GetDistance(me->GetHomePosition()) > 20.0f) + return false; + return BossAI::CanAIAttack(target); + } - float fMaxDistance; + void EnterCombat(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_LEECH_POISON, randtime(Seconds(5), Seconds(7))); + events.ScheduleEvent(EVENT_ACID_CLOUD, randtime(Seconds(7), Seconds(13))); + events.ScheduleEvent(EVENT_WEB_GRAB, randtime(Seconds(13), Seconds(19))); + events.ScheduleEvent(EVENT_PIERCE_ARMOR, randtime(Seconds(4), Seconds(7))); + events.ScheduleEvent(EVENT_PLAYER_CHECK, Seconds(1)); + me->setActive(true); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_CRUSHER_ENGAGED: + if (_enteredCombat) + break; + instance->SetBossState(DATA_HADRONOX, IN_PROGRESS); + _enteredCombat = true; + SummonCrusherPack(SUMMON_GROUP_CRUSHER_2); + SummonCrusherPack(SUMMON_GROUP_CRUSHER_3); + break; + case ACTION_HADRONOX_MOVE: + if (_step < NUM_STEPS-1) + { + SetStep(_step + 1); + Talk(HADRONOX_EMOTE_MOVE); + } + break; + } + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + std::list<Creature*> triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_WORLDTRIGGER_LARGE); + for (Creature* trigger : triggers) + if (trigger->HasAura(SPELL_SUMMON_CHAMPION_PERIODIC) || trigger->HasAura(SPELL_WEB_FRONT_DOORS) || trigger->HasAura(SPELL_WEB_SIDE_DOORS)) + _DespawnAtEvade(25, trigger); + _DespawnAtEvade(25); + summons.DespawnAll(); + for (ObjectGuid gNerubian : _anubar) + if (Creature* nerubian = ObjectAccessor::GetCreature(*me, gNerubian)) + nerubian->DespawnOrUnsummon(); + } + + void SetGUID(ObjectGuid guid, int32 /*what*/) override + { + _anubar.push_back(guid); + } - void Reset() override + void Initialize() { me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 9.0f); me->SetFloatValue(UNIT_FIELD_COMBATREACH, 9.0f); + _enteredCombat = false; + _doorsWebbed = false; + _lastPlayerCombatState = false; + SetStep(0); + SetCombatMovement(true); + SummonCrusherPack(SUMMON_GROUP_CRUSHER_1); + } + + void InitializeAI() override + { + BossAI::InitializeAI(); + if (me->IsAlive()) + Initialize(); + } + void JustRespawned() override + { + BossAI::JustRespawned(); Initialize(); + } - if (instance->GetBossState(DATA_HADRONOX) != DONE && !bFirstTime) - instance->SetBossState(DATA_HADRONOX, FAIL); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - bFirstTime = false; + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_LEECH_POISON: + DoCastAOE(SPELL_LEECH_POISON); + events.Repeat(randtime(Seconds(7), Seconds(9))); + break; + case EVENT_ACID_CLOUD: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f)) + DoCast(target, SPELL_ACID_CLOUD); + events.Repeat(randtime(Seconds(16), Seconds(23))); + break; + case EVENT_WEB_GRAB: + DoCastAOE(SPELL_WEB_GRAB); + events.Repeat(randtime(Seconds(20), Seconds(25))); + break; + case EVENT_PIERCE_ARMOR: + DoCastVictim(SPELL_PIERCE_ARMOR); + events.Repeat(randtime(Seconds(10), Seconds(15))); + break; + case EVENT_PLAYER_CHECK: + if (IsInCombatWithPlayer() != _lastPlayerCombatState) + { + _lastPlayerCombatState = !_lastPlayerCombatState; + if (_lastPlayerCombatState) // we are now in combat with players + { + if (!instance->CheckRequiredBosses(DATA_HADRONOX)) + { + EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK); + return; + } + // cancel current point movement if engaged by players + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) + { + me->GetMotionMaster()->Clear(); + SetCombatMovement(true); + AttackStart(me->GetVictim()); + } + } + else // we are no longer in combat with players - reset the encounter + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + events.Repeat(Seconds(1)); + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); } - //when Hadronox kills any enemy (that includes a party member) she will regain 10% of her HP if the target had Leech Poison on - void KilledUnit(Unit* Victim) override + // Safeguard to prevent Hadronox dying to NPCs + void DamageTaken(Unit* who, uint32& damage) override { - // not sure if this aura check is correct, I think it is though - if (!Victim || !Victim->HasAura(DUNGEON_MODE(SPELL_LEECH_POISON, H_SPELL_LEECH_POISON)) || !me->IsAlive()) - return; + if (!who->IsControlledByPlayer() && me->HealthBelowPct(70)) + { + if (me->HealthBelowPctDamaged(5, damage)) + damage = 0; + else + damage *= (me->GetHealthPct()-5.0f)/ 65.0f; + } + } - me->ModifyHealth(int32(me->CountPctFromMaxHealth(10))); + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + // Do not enter combat with zone } + + private: + bool _enteredCombat; // has a player entered combat with the first crusher pack? (talk and spawn two more packs) + bool _doorsWebbed; // obvious - have we reached the top and webbed the doors shut? (trigger for hadronox denied achievement) + bool _lastPlayerCombatState; // was there a player in our threat list the last time we checked (we check every second) + uint8 _step; + std::list<ObjectGuid> _anubar; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<boss_hadronoxAI>(creature); + } +}; + +struct npc_hadronox_crusherPackAI : public ScriptedAI +{ + npc_hadronox_crusherPackAI(Creature* creature, Position const* positions) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _positions(positions), _myPack(SummonGroups(0)), _doFacing(false) { } - void JustDied(Unit* /*killer*/) override + void DoAction(int32 action) override + { + if (action == ACTION_PACK_WALK) { - instance->SetBossState(DATA_HADRONOX, DONE); + switch (_myPack) + { + case SUMMON_GROUP_CRUSHER_1: + case SUMMON_GROUP_CRUSHER_2: + case SUMMON_GROUP_CRUSHER_3: + me->GetMotionMaster()->MovePoint(ACTION_PACK_WALK, _positions[_myPack - SUMMON_GROUP_CRUSHER_1]); + break; + default: + break; + } } + } - void EnterCombat(Unit* /*who*/) override + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == ACTION_PACK_WALK) + _doFacing = true; + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + hadronox->AI()->EnterEvadeMode(EVADE_REASON_OTHER); + } + + uint32 GetData(uint32 data) const override + { + if (data == DATA_CRUSHER_PACK_ID) + return _myPack; + return 0; + } + + void SetData(uint32 data, uint32 value) override + { + if (data == DATA_CRUSHER_PACK_ID) { - instance->SetBossState(DATA_HADRONOX, IN_PROGRESS); - me->SetInCombatWithZone(); + _myPack = SummonGroups(value); + me->SetReactState(_myPack ? REACT_PASSIVE : REACT_AGGRESSIVE); } + } - void CheckDistance(float dist, const uint32 uiDiff) + void EnterCombat(Unit* who) override + { + if (me->HasReactState(REACT_PASSIVE)) { - if (!me->IsInCombat()) - return; + std::list<Creature*> others; + me->GetCreatureListWithEntryInGrid(others, 0, 40.0f); + for (Creature* other : others) + if (other->AI()->GetData(DATA_CRUSHER_PACK_ID) == _myPack) + { + other->SetReactState(REACT_AGGRESSIVE); + other->AI()->AttackStart(who); + } + } + _EnterCombat(); + ScriptedAI::EnterCombat(who); + } - float x=0.0f, y=0.0f, z=0.0f; - me->GetRespawnPosition(x, y, z); + virtual void _EnterCombat() = 0; + virtual void DoEvent(uint32 /*eventId*/) = 0; - if (uiCheckDistanceTimer <= uiDiff) - uiCheckDistanceTimer = 5*IN_MILLISECONDS; - else + void MoveInLineOfSight(Unit* who) override + { + if (!me->HasReactState(REACT_PASSIVE)) + { + ScriptedAI::MoveInLineOfSight(who); + return; + } + + if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance)) + EnterCombat(who); + } + + void UpdateAI(uint32 diff) override + { + if (_doFacing) + { + _doFacing = false; + me->SetFacingTo(_positions[_myPack - SUMMON_GROUP_CRUSHER_1].GetOrientation()); + } + + if (!UpdateVictim()) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + DoEvent(eventId); + + DoMeleeAttackIfReady(); + } + + protected: + InstanceScript* const _instance; + EventMap _events; + Position const* const _positions; + SummonGroups _myPack; + bool _doFacing; + +}; + +static const Position crusherWaypoints[] = +{ + { 529.6913f, 547.1257f, 731.9155f, 4.799650f }, + { 517.51f , 561.439f , 734.0306f, 4.520403f }, + { 543.414f , 551.728f , 732.0522f, 3.996804f } +}; +class npc_anub_ar_crusher : public CreatureScript +{ + public: + npc_anub_ar_crusher() : CreatureScript("npc_anub_ar_crusher") { } + + struct npc_anub_ar_crusherAI : public npc_hadronox_crusherPackAI + { + npc_anub_ar_crusherAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, crusherWaypoints), _hadFrenzy(false) { } + + void _EnterCombat() override { - uiCheckDistanceTimer -= uiDiff; - return; + _events.ScheduleEvent(EVENT_SMASH, randtime(Seconds(8), Seconds(12))); + + if (_myPack != SUMMON_GROUP_CRUSHER_1) + return; + + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + { + if (hadronox->AI()->GetData(DATA_HADRONOX_ENTERED_COMBAT)) + return; + hadronox->AI()->DoAction(ACTION_CRUSHER_ENGAGED); + } + + Talk(CRUSHER_SAY_AGGRO); } - if (me->IsInEvadeMode() || !me->GetVictim()) - return; - if (me->GetDistance(x, y, z) > dist) - EnterEvadeMode(); + + void DamageTaken(Unit* /*source*/, uint32& damage) override + { + if (_hadFrenzy || !me->HealthBelowPctDamaged(25, damage)) + return; + _hadFrenzy = true; + Talk(CRUSHER_EMOTE_FRENZY); + DoCastSelf(SPELL_FRENZY); + } + + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_SMASH: + DoCastVictim(SPELL_SMASH); + _events.Repeat(randtime(Seconds(13), Seconds(21))); + break; + } + } + + void JustDied(Unit* killer) override + { + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + hadronox->AI()->DoAction(ACTION_HADRONOX_MOVE); + ScriptedAI::JustDied(killer); + } + + private: + bool _hadFrenzy; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anub_ar_crusherAI>(creature); } +}; - void UpdateAI(uint32 diff) override +static const Position championWaypoints[] = +{ + { 539.2076f, 549.7539f, 732.8668f, 4.55531f }, + { 527.3098f, 559.5197f, 732.9407f, 4.742493f }, + { } +}; +class npc_anub_ar_crusher_champion : public CreatureScript +{ + public: + npc_anub_ar_crusher_champion() : CreatureScript("npc_anub_ar_crusher_champion") { } + + struct npc_anub_ar_crusher_championAI : public npc_hadronox_crusherPackAI { - //Return since we have no target - if (!UpdateVictim()) - return; + npc_anub_ar_crusher_championAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, championWaypoints) { } + + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_REND: + DoCastVictim(SPELL_REND); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_PUMMEL: + DoCastVictim(SPELL_PUMMEL); + _events.Repeat(randtime(Seconds(12), Seconds(17))); + break; + } + } + + void _EnterCombat() override + { + _events.ScheduleEvent(EVENT_REND, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_PUMMEL, randtime(Seconds(15), Seconds(19))); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anub_ar_crusher_championAI>(creature); + } +}; + +static const Position cryptFiendWaypoints[] = +{ + { 520.3911f, 548.7895f, 732.0118f, 5.0091f }, + { }, + { 550.9611f, 545.1674f, 731.9031f, 3.996804f } +}; +class npc_anub_ar_crusher_crypt_fiend : public CreatureScript +{ + public: + npc_anub_ar_crusher_crypt_fiend() : CreatureScript("npc_anub_ar_crusher_crypt_fiend") { } + + struct npc_anub_ar_crusher_crypt_fiendAI : public npc_hadronox_crusherPackAI + { + npc_anub_ar_crusher_crypt_fiendAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, cryptFiendWaypoints) { } - // Without he comes up through the air to players on the bridge after krikthir if players crossing this bridge! - CheckDistance(fMaxDistance, diff); + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_CRUSHING_WEBS: + DoCastVictim(SPELL_CRUSHING_WEBS); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_INFECTED_WOUND: + DoCastVictim(SPELL_INFECTED_WOUND); + _events.Repeat(randtime(Seconds(16), Seconds(25))); + break; + } + } - if (me->HasAura(SPELL_WEB_FRONT_DOORS) || me->HasAura(SPELL_WEB_SIDE_DOORS)) + void _EnterCombat() override { - if (IsCombatMovementAllowed()) - SetCombatMovement(false); + _events.ScheduleEvent(EVENT_CRUSHING_WEBS, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_INFECTED_WOUND, randtime(Seconds(15), Seconds(19))); } - else if (!IsCombatMovementAllowed()) - SetCombatMovement(true); + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anub_ar_crusher_crypt_fiendAI>(creature); + } +}; + +static const Position necromancerWaypoints[] = +{ + { }, + { 507.6937f, 563.3471f, 734.8986f, 4.520403f }, + { 535.1049f, 552.8961f, 732.8441f, 3.996804f }, +}; +class npc_anub_ar_crusher_necromancer : public CreatureScript +{ + public: + npc_anub_ar_crusher_necromancer() : CreatureScript("npc_anub_ar_crusher_necromancer") { } + + struct npc_anub_ar_crusher_necromancerAI : public npc_hadronox_crusherPackAI + { + npc_anub_ar_crusher_necromancerAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, necromancerWaypoints) { } - if (uiPierceTimer <= diff) + void DoEvent(uint32 eventId) override { - DoCastVictim(SPELL_PIERCE_ARMOR); - uiPierceTimer = 8*IN_MILLISECONDS; - } else uiPierceTimer -= diff; + switch (eventId) + { + case EVENT_SHADOW_BOLT: + DoCastVictim(SPELL_SHADOW_BOLT); + _events.Repeat(randtime(Seconds(2), Seconds(5))); + break; + case EVENT_ANIMATE_BONES: + DoCastVictim(urand(0, 1) ? SPELL_ANIMATE_BONES_2 : SPELL_ANIMATE_BONES_1); + _events.Repeat(randtime(Seconds(35), Seconds(50))); + break; + } + } - if (uiAcidTimer <= diff) + void _EnterCombat() override { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_ACID_CLOUD); + _events.ScheduleEvent(EVENT_SHADOW_BOLT, randtime(Seconds(2), Seconds(4))); + _events.ScheduleEvent(EVENT_ANIMATE_BONES, randtime(Seconds(37), Seconds(45))); + } + }; - uiAcidTimer = urand(20*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } else uiAcidTimer -= diff; + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anub_ar_crusher_necromancerAI>(creature); + } +}; - if (uiLeechTimer <= diff) +static const uint8 NUM_SPAWNS = 3; +static const Position initialMoves[NUM_SPAWNS] = +{ + { 485.314606f, 611.418640f, 771.428406f }, + { 575.760437f, 611.516418f, 771.427368f }, + { 588.930725f, 598.233276f, 739.142151f } +}; +static const Position downstairsMoves[NUM_SPAWNS] = +{ + { 513.574341f, 587.022156f, 736.229065f }, + { 537.920410f, 580.436157f, 732.796692f }, + { 601.289246f, 583.259644f, 725.443054f }, +}; +static const Position downstairsMoves2[NUM_SPAWNS] = +{ + { 571.498718f, 576.978333f, 727.582947f }, + { 571.498718f, 576.978333f, 727.582947f }, + { } +}; +struct npc_hadronox_foeAI : public ScriptedAI +{ + npc_hadronox_foeAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _nextMovement(MOVE_OUTSIDE), _mySpawn(0) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX)) + hadronox->AI()->SetGUID(me->GetGUID()); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE) + _nextMovement = Movements(id+1); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + me->DespawnOrUnsummon(); + } + + virtual void DoEvent(uint32 /*eventId*/) = 0; + + void UpdateAI(uint32 diff) override + { + if (_nextMovement) + { + switch (_nextMovement) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_LEECH_POISON); + case MOVE_OUTSIDE: + { + float dist = HUGE_VALF; + for (uint8 spawn = 0; spawn < NUM_SPAWNS; ++spawn) + { + float thisDist = initialMoves[spawn].GetExactDistSq(me); + if (thisDist < dist) + { + _mySpawn = spawn; + dist = thisDist; + } + } + me->GetMotionMaster()->MovePoint(MOVE_OUTSIDE, initialMoves[_mySpawn], false); // do not pathfind here, we have to pass through a "wall" of webbing + break; + } + case MOVE_DOWNSTAIRS: + me->GetMotionMaster()->MovePoint(MOVE_DOWNSTAIRS, downstairsMoves[_mySpawn]); + break; + case MOVE_DOWNSTAIRS_2: + if (downstairsMoves2[_mySpawn].GetPositionX() > 0.0f) // might be unset for this spawn - if yes, skip + { + me->GetMotionMaster()->MovePoint(MOVE_DOWNSTAIRS_2, downstairsMoves2[_mySpawn]); + break; + } + // intentional missing break + case MOVE_HADRONOX: + case MOVE_HADRONOX_REAL: + { + static const float zCutoff = 702.0f; + Creature* hadronox = _instance->GetCreature(DATA_HADRONOX); + if (hadronox && hadronox->IsAlive()) + { + if (_nextMovement != MOVE_HADRONOX_REAL) + if (hadronox->GetPositionZ() < zCutoff) + { + me->GetMotionMaster()->MovePoint(MOVE_HADRONOX, hadronoxStep[2]); + break; + } + me->GetMotionMaster()->MoveChase(hadronox); + } + break; + } + default: + break; + } + _nextMovement = MOVE_NONE; + } + + if (!UpdateVictim()) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + DoEvent(eventId); + + DoMeleeAttackIfReady(); + } - uiLeechTimer = urand(11*IN_MILLISECONDS, 14*IN_MILLISECONDS); - } else uiLeechTimer -= diff; + protected: + EventMap _events; + InstanceScript* const _instance; - if (uiGrabTimer <= diff) + private: + Movements _nextMovement; + uint8 _mySpawn; +}; + +class npc_anub_ar_champion : public CreatureScript +{ + public: + npc_anub_ar_champion() : CreatureScript("npc_anub_ar_champion") { } + + struct npc_anub_ar_championAI : public npc_hadronox_foeAI + { + npc_anub_ar_championAI(Creature* creature) : npc_hadronox_foeAI(creature) { } + + void DoEvent(uint32 eventId) override { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) // Draws all players (and attacking Mobs) to itself. - DoCast(target, SPELL_WEB_GRAB); + switch (eventId) + { + case EVENT_REND: + DoCastVictim(SPELL_REND); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_PUMMEL: + DoCastVictim(SPELL_PUMMEL); + _events.Repeat(randtime(Seconds(12), Seconds(17))); + break; + case EVENT_TAUNT: + DoCastVictim(SPELL_TAUNT); + _events.Repeat(randtime(Seconds(15), Seconds(50))); + break; + } + } - uiGrabTimer = urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } else uiGrabTimer -= diff; + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_REND, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_PUMMEL, randtime(Seconds(15), Seconds(19))); + _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50))); + } + }; - if (uiDoorsTimer <= diff) + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anub_ar_championAI>(creature); + } +}; + +class npc_anub_ar_crypt_fiend : public CreatureScript +{ + public: + npc_anub_ar_crypt_fiend() : CreatureScript("npc_anub_ar_crypt_fiend") { } + + struct npc_anub_ar_crypt_fiendAI : public npc_hadronox_foeAI + { + npc_anub_ar_crypt_fiendAI(Creature* creature) : npc_hadronox_foeAI(creature) { } + + void DoEvent(uint32 eventId) override + { + switch (eventId) { - uiDoorsTimer = urand(30*IN_MILLISECONDS, 60*IN_MILLISECONDS); - } else uiDoorsTimer -= diff; + case EVENT_CRUSHING_WEBS: + DoCastVictim(SPELL_CRUSHING_WEBS); + _events.Repeat(randtime(Seconds(12), Seconds(16))); + break; + case EVENT_INFECTED_WOUND: + DoCastVictim(SPELL_INFECTED_WOUND); + _events.Repeat(randtime(Seconds(16), Seconds(25))); + break; + case EVENT_TAUNT: + DoCastVictim(SPELL_TAUNT); + _events.Repeat(randtime(Seconds(15), Seconds(50))); + break; + } + } - DoMeleeAttackIfReady(); + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_CRUSHING_WEBS, randtime(Seconds(4), Seconds(8))); + _events.ScheduleEvent(EVENT_INFECTED_WOUND, randtime(Seconds(15), Seconds(19))); + _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50))); } }; CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<boss_hadronoxAI>(creature); + return GetAzjolNerubAI<npc_anub_ar_crypt_fiendAI>(creature); + } +}; + +class npc_anub_ar_necromancer : public CreatureScript +{ + public: + npc_anub_ar_necromancer() : CreatureScript("npc_anub_ar_necromancer") { } + + struct npc_anub_ar_necromancerAI : public npc_hadronox_foeAI + { + npc_anub_ar_necromancerAI(Creature* creature) : npc_hadronox_foeAI(creature) { } + + void DoEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_SHADOW_BOLT: + DoCastVictim(SPELL_SHADOW_BOLT); + _events.Repeat(randtime(Seconds(2), Seconds(5))); + break; + case EVENT_ANIMATE_BONES: + DoCastVictim(urand(0,1) ? SPELL_ANIMATE_BONES_2 : SPELL_ANIMATE_BONES_1); + _events.Repeat(randtime(Seconds(35), Seconds(50))); + break; + case EVENT_TAUNT: + DoCastVictim(SPELL_TAUNT); + _events.Repeat(randtime(Seconds(15), Seconds(50))); + break; + } + } + + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_SHADOW_BOLT, randtime(Seconds(2), Seconds(4))); + _events.ScheduleEvent(EVENT_ANIMATE_BONES, randtime(Seconds(37), Seconds(45))); + _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50))); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anub_ar_necromancerAI>(creature); + } +}; + +class spell_hadronox_periodic_summon_template_AuraScript : public AuraScript +{ + public: + spell_hadronox_periodic_summon_template_AuraScript(uint32 topSpellId, uint32 bottomSpellId) : AuraScript(), _topSpellId(topSpellId), _bottomSpellId(bottomSpellId) { } + PrepareAuraScript(spell_hadronox_periodic_summon_template_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return + (sSpellMgr->GetSpellInfo(_topSpellId) != nullptr) && + (sSpellMgr->GetSpellInfo(_bottomSpellId) != nullptr); + } + + void HandleApply(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/) + { + if (AuraEffect* effect = GetAura()->GetEffect(EFFECT_0)) + effect->SetPeriodicTimer(urandms(2, 17)); + } + + void HandlePeriodic(AuraEffect const* /*eff*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + InstanceScript* instance = caster->GetInstanceScript(); + if (!instance) + return; + if (instance->GetBossState(DATA_HADRONOX) == DONE) + GetAura()->Remove(); + else + { + if (caster->GetPositionZ() >= 750.0f) + caster->CastSpell(caster, _topSpellId, true); + else + caster->CastSpell(caster, _bottomSpellId, true); + } + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_hadronox_periodic_summon_template_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_hadronox_periodic_summon_template_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + + private: + uint32 _topSpellId; + uint32 _bottomSpellId; +}; + +class spell_hadronox_periodic_summon_champion : public SpellScriptLoader +{ + public: + spell_hadronox_periodic_summon_champion() : SpellScriptLoader("spell_hadronox_periodic_summon_champion") { } + + class spell_hadronox_periodic_summon_champion_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript + { + public: + spell_hadronox_periodic_summon_champion_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_CHAMPION_TOP, SPELL_SUMMON_CHAMPION_BOTTOM) { } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_periodic_summon_champion_AuraScript(); + } +}; + +class spell_hadronox_periodic_summon_crypt_fiend : public SpellScriptLoader +{ + public: + spell_hadronox_periodic_summon_crypt_fiend() : SpellScriptLoader("spell_hadronox_periodic_summon_crypt_fiend") { } + + class spell_hadronox_periodic_summon_crypt_fiend_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript + { + public: + spell_hadronox_periodic_summon_crypt_fiend_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_CRYPT_FIEND_TOP, SPELL_SUMMON_CRYPT_FIEND_BOTTOM) { } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_periodic_summon_crypt_fiend_AuraScript(); + } +}; + +class spell_hadronox_periodic_summon_necromancer : public SpellScriptLoader +{ + public: + spell_hadronox_periodic_summon_necromancer() : SpellScriptLoader("spell_hadronox_periodic_summon_necromancer") { } + + class spell_hadronox_periodic_summon_necromancer_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript + { + public: + spell_hadronox_periodic_summon_necromancer_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_NECROMANCER_TOP, SPELL_SUMMON_NECROMANCER_BOTTOM) { } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_periodic_summon_necromancer_AuraScript(); + } +}; + +class spell_hadronox_leeching_poison : public SpellScriptLoader +{ + public: + spell_hadronox_leeching_poison() : SpellScriptLoader("spell_hadronox_leeching_poison") { } + + class spell_hadronox_leeching_poison_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hadronox_leeching_poison_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_LEECH_POISON_HEAL) != nullptr; + } + + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH) + return; + + if (GetTarget()->IsGuardian()) + return; + + if (Unit* caster = GetCaster()) + caster->CastSpell(caster, SPELL_LEECH_POISON_HEAL, true); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_hadronox_leeching_poison_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_LEECH, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hadronox_leeching_poison_AuraScript(); } }; +class spell_hadronox_web_doors : public SpellScriptLoader +{ + public: + spell_hadronox_web_doors() : SpellScriptLoader("spell_hadronox_web_doors") { } + + class spell_hadronox_web_doors_SpellScript : public SpellScript + { + PrepareSpellScript(spell_hadronox_web_doors_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ( + sSpellMgr->GetSpellInfo(SPELL_SUMMON_CHAMPION_PERIODIC) && + sSpellMgr->GetSpellInfo(SPELL_SUMMON_CRYPT_FIEND_PERIODIC) && + sSpellMgr->GetSpellInfo(SPELL_SUMMON_NECROMANCER_PERIODIC) + ); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + { + target->RemoveAurasDueToSpell(SPELL_SUMMON_CHAMPION_PERIODIC); + target->RemoveAurasDueToSpell(SPELL_SUMMON_CRYPT_FIEND_PERIODIC); + target->RemoveAurasDueToSpell(SPELL_SUMMON_NECROMANCER_PERIODIC); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hadronox_web_doors_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_hadronox_web_doors_SpellScript(); + } +}; + +class achievement_hadronox_denied : public AchievementCriteriaScript +{ + public: + achievement_hadronox_denied() : AchievementCriteriaScript("achievement_hadronox_denied") { } + + bool OnCheck(Player* /*player*/, Unit* target) override + { + if (!target) + return false; + + if (Creature* cTarget = target->ToCreature()) + if (!cTarget->AI()->GetData(DATA_HADRONOX_WEBBED_DOORS)) + return true; + + return false; + } +}; + void AddSC_boss_hadronox() { new boss_hadronox(); + + new npc_anub_ar_crusher(); + new npc_anub_ar_crusher_champion(); + new npc_anub_ar_crusher_crypt_fiend(); + new npc_anub_ar_crusher_necromancer(); + + new npc_anub_ar_champion(); + new npc_anub_ar_crypt_fiend(); + new npc_anub_ar_necromancer(); + + new spell_hadronox_periodic_summon_champion(); + new spell_hadronox_periodic_summon_crypt_fiend(); + new spell_hadronox_periodic_summon_necromancer(); + + new spell_hadronox_leeching_poison(); + new spell_hadronox_web_doors(); + + new achievement_hadronox_denied(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp index 5a6d8be1ec2..a06d915e594 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp @@ -21,63 +21,105 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "PassiveAI.h" +#include "SpellAuras.h" #include "azjol_nerub.h" -enum Spells +enum Events { - SPELL_MIND_FLAY = 52586, - SPELL_CURSE_OF_FATIGUE = 52592, - SPELL_FRENZY = 28747, //maybe 53361 - SPELL_SUMMON_SKITTERING_SWARMER = 52438, //AOE Effect 140, maybe 52439 - SPELL_SUMMON_SKITTERING_SWARMER_1 = 52439, //Summon 3x 28735 - SPELL_ACID_SPLASH = 52446, - SPELL_CHARGE = 16979, //maybe is another spell - SPELL_BACKSTAB = 52540, - SPELL_SHADOW_BOLT = 52534, - SPELL_SHADOW_NOVA = 52535, - SPELL_STRIKE = 52532, - SPELL_CLEAVE = 49806, - SPELL_ENRAGE = 52470, - SPELL_INFECTED_BITE = 52469, - SPELL_WEB_WRAP = 52086, //the spell is not working properly - SPELL_BLINDING_WEBS = 52524, - SPELL_POSION_SPRAY = 52493 + // Krik'thir the Gatewatcher + EVENT_SEND_GROUP = 1, + EVENT_SWARM, + EVENT_MIND_FLAY, + EVENT_FRENZY, + + // Watchers - Shared + EVENT_WEB_WRAP, + EVENT_INFECTED_BITE, + + // Watcher Gashra + EVENT_ENRAGE, + // Watcher Narjil + EVENT_BLINDING_WEBS, + // Watcher Silthik + EVENT_POISON_SPRAY, + + // Anubar Skirmisher + EVENT_ANUBAR_CHARGE, + EVENT_BACKSTAB, + + // Anubar Shadowcaster + EVENT_SHADOW_BOLT, + EVENT_SHADOW_NOVA, + + // Anubar Warrior + EVENT_STRIKE, + EVENT_CLEAVE }; -enum Mobs +enum Spells { - NPC_SKITTERING_SWARMER = 28735, - NPC_SKITTERING_SWARMER_CONTROLLER = 32593, - NPC_SKITTERING_INFECTIOR = 28736 + // Krik'thir the Gatewatcher + SPELL_SUBBOSS_AGGRO_TRIGGER = 52343, + SPELL_SWARM = 52440, + SPELL_MIND_FLAY = 52586, + SPELL_CURSE_OF_FATIGUE = 52592, + SPELL_FRENZY = 28747, + + // Watchers - Shared + SPELL_WEB_WRAP = 52086, + SPELL_WEB_WRAP_WRAPPED = 52087, + SPELL_INFECTED_BITE = 52469, + + // Watcher Gashra + SPELL_ENRAGE = 52470, + // Watcher Narjil + SPELL_BLINDING_WEBS = 52524, + // Watcher Silthik + SPELL_POISON_SPRAY = 52493, + + // Anub'ar Warrior + SPELL_CLEAVE = 49806, + SPELL_STRIKE = 52532, + + // Anub'ar Skirmisher + SPELL_CHARGE = 52538, + SPELL_BACKSTAB = 52540, + SPELL_FIXTATE_TRIGGER = 52536, + SPELL_FIXTATE_TRIGGERED = 52537, + + // Anub'ar Shadowcaster + SPELL_SHADOW_BOLT = 52534, + SPELL_SHADOW_NOVA = 52535, + + // Skittering Infector + SPELL_ACID_SPLASH = 52446 }; -enum Yells +enum Data { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_SWARM = 3, - SAY_PREFIGHT = 4, - SAY_SEND_GROUP = 5 + DATA_PET_GROUP }; -Position const SpawnPoint[] = +enum Actions { - { 566.164f, 682.087f, 769.079f, 2.21657f }, - { 529.042f, 706.941f, 777.298f, 1.0821f }, - { 489.975f, 671.239f, 772.131f, 0.261799f }, - { 488.556f, 692.95f, 771.764f, 4.88692f }, - { 553.34f, 640.387f, 777.419f, 1.20428f }, - { 517.486f, 706.398f, 777.335f, 5.35816f }, - { 504.01f, 637.693f, 777.479f, 0.506145f }, - { 552.625f, 706.408f, 777.177f, 3.4383f } + ACTION_GASHRA_DIED, + ACTION_NARJIL_DIED, + ACTION_SILTHIK_DIED, + ACTION_WATCHER_ENGAGED, + ACTION_PET_ENGAGED, + ACTION_PET_EVADE }; -enum Events +enum Yells { - EVENT_SUMMON = 1, - EVENT_MIND_FLAY, - EVENT_CURSE_FATIGUE + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_SWARM = 3, + SAY_PREFIGHT = 4, + SAY_SEND_GROUP = 5 }; class boss_krik_thir : public CreatureScript @@ -87,37 +129,128 @@ class boss_krik_thir : public CreatureScript struct boss_krik_thirAI : public BossAI { - boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR_THE_GATEWATCHER) { } + boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR_THE_GATEWATCHER), _hadGreet(false), _hadFrenzy(false), _petsInCombat(false), _watchersActive(0) { } + + void SummonAdds() + { + if (instance->GetBossState(DATA_KRIKTHIR_THE_GATEWATCHER) == DONE) + return; + + for (uint8 i = 1; i <= 3; ++i) + { + std::list<TempSummon*> summons; + me->SummonCreatureGroup(i, &summons); + for (TempSummon* summon : summons) + summon->AI()->SetData(DATA_PET_GROUP, i); + } + } void Reset() override { - _Reset(); + BossAI::Reset(); + _hadFrenzy = false; + _petsInCombat = false; + _watchersActive = 0; + me->SetReactState(REACT_PASSIVE); } - void EnterCombat(Unit* /*who*/) override + void InitializeAI() override { - Talk(SAY_AGGRO); - _EnterCombat(); - Summon(); + BossAI::InitializeAI(); + SummonAdds(); + } - events.ScheduleEvent(EVENT_SUMMON, 15000); - events.ScheduleEvent(EVENT_MIND_FLAY, 15000); - events.ScheduleEvent(EVENT_CURSE_FATIGUE, 12000); + void JustRespawned() override + { + BossAI::JustRespawned(); + SummonAdds(); } - void Summon() + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void JustDied(Unit* killer) override + { + summons.clear(); + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void EnterCombat(Unit* who) override { - for (uint8 i = 0; i < 8; i++) + _petsInCombat = false; + me->SetReactState(REACT_AGGRESSIVE); + summons.DoZoneInCombat(); + + events.CancelEvent(EVENT_SEND_GROUP); + events.ScheduleEvent(EVENT_SWARM, Seconds(5)); + events.ScheduleEvent(EVENT_MIND_FLAY, randtime(Seconds(1), Seconds(3))); + + BossAI::EnterCombat(who); + } + + void MoveInLineOfSight(Unit* who) override + { + if (!me->HasReactState(REACT_PASSIVE)) { - me->SummonCreature(NPC_SKITTERING_SWARMER, SpawnPoint[i], TEMPSUMMON_TIMED_DESPAWN, 25000); - uint32 summon_entry = (i == 4 || i == 5 || i == 6) ? NPC_SKITTERING_INFECTIOR : NPC_SKITTERING_SWARMER; - me->SummonCreature(summon_entry, SpawnPoint[i], TEMPSUMMON_TIMED_DESPAWN, 25000); + ScriptedAI::MoveInLineOfSight(who); + return; + } + + if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance)) + EnterCombat(who); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + summons.DespawnAll(); + _DespawnAtEvade(); + } + + void DoAction(int32 action) override + { + switch (action) + { + case -ACTION_GATEWATCHER_GREET: + if (!_hadGreet && me->IsAlive() && !me->IsInCombat() && !_petsInCombat) + { + _hadGreet = true; + Talk(SAY_PREFIGHT); + } + break; + case ACTION_GASHRA_DIED: + case ACTION_NARJIL_DIED: + case ACTION_SILTHIK_DIED: + if (!_watchersActive) // something is wrong + { + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } + if (!--_watchersActive) // if there are no watchers currently in combat... + events.RescheduleEvent(EVENT_SEND_GROUP, Seconds(5)); // ...send the next watcher after the targets sooner + break; + case ACTION_WATCHER_ENGAGED: + ++_watchersActive; + break; + case ACTION_PET_ENGAGED: + if (_petsInCombat || me->IsInCombat()) + break; + _petsInCombat = true; + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_SEND_GROUP, Seconds(70)); + break; + case ACTION_PET_EVADE: + EnterEvadeMode(EVADE_REASON_OTHER); + break; } } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) + if (!UpdateVictim() && !_petsInCombat) return; events.Update(diff); @@ -125,52 +258,62 @@ class boss_krik_thir : public CreatureScript if (me->HasUnitState(UNIT_STATE_CASTING)) return; + if (me->HealthBelowPct(10) && !_hadFrenzy) + { + _hadFrenzy = true; + events.ScheduleEvent(EVENT_FRENZY, Seconds(1)); + } + while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { - case EVENT_SUMMON: - Summon(); - events.ScheduleEvent(EVENT_SUMMON, 15000); + case EVENT_SEND_GROUP: + DoCastAOE(SPELL_SUBBOSS_AGGRO_TRIGGER, true); + events.Repeat(Seconds(70)); break; + + case EVENT_SWARM: + DoCastAOE(SPELL_SWARM); + Talk(SAY_SWARM); + break; + case EVENT_MIND_FLAY: DoCastVictim(SPELL_MIND_FLAY); - events.ScheduleEvent(EVENT_MIND_FLAY, 15000); + events.Repeat(randtime(Seconds(9), Seconds(11))); break; - case EVENT_CURSE_FATIGUE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_CURSE_OF_FATIGUE); - events.ScheduleEvent(EVENT_CURSE_FATIGUE, 10000); - break; - default: + + case EVENT_FRENZY: + DoCastSelf(SPELL_FRENZY); + DoCastAOE(SPELL_CURSE_OF_FATIGUE); + events.Repeat(Seconds(15)); break; } - } - if (!me->HasAura(SPELL_FRENZY) && HealthBelowPct(10)) - DoCast(me, SPELL_FRENZY, true); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } DoMeleeAttackIfReady(); } - void JustDied(Unit* /*killer*/) override + void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override { - Talk(SAY_DEATH); - _JustDied(); + if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER) + DoZoneInCombat(); } - void KilledUnit(Unit* victim) override + void SpellHitTarget(Unit* /*who*/, SpellInfo const* spell) override { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - - Talk(SAY_SLAY); + if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER) + Talk(SAY_SEND_GROUP); } - void JustSummoned(Creature* summoned) override - { - summoned->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); - } + private: + bool _hadGreet; + bool _hadFrenzy; + bool _petsInCombat; + uint8 _watchersActive; }; CreatureAI* GetAI(Creature* creature) const override @@ -179,74 +322,123 @@ class boss_krik_thir : public CreatureScript } }; -class npc_skittering_infector : public CreatureScript +struct npc_gatewatcher_petAI : public ScriptedAI { - public: - npc_skittering_infector() : CreatureScript("npc_skittering_infector") { } + npc_gatewatcher_petAI(Creature* creature, bool isWatcher) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _petGroup(0), _isWatcher(isWatcher) { } - struct npc_skittering_infectorAI : public ScriptedAI + virtual void _EnterCombat() = 0; + void EnterCombat(Unit* who) override + { + if (_isWatcher) { - npc_skittering_infectorAI(Creature* creature) : ScriptedAI(creature) { } - - void JustDied(Unit* /*killer*/) override - { - DoCastAOE(SPELL_ACID_SPLASH); - } - }; + _isWatcher = false; + if (TempSummon* meSummon = me->ToTempSummon()) + if (Creature* summoner = meSummon->GetSummonerCreatureBase()) + summoner->AI()->DoAction(ACTION_WATCHER_ENGAGED); + } - CreatureAI* GetAI(Creature* creature) const override + if (me->HasReactState(REACT_PASSIVE)) { - return GetAzjolNerubAI<npc_skittering_infectorAI>(creature); - } -}; + std::list<Creature*> others; + me->GetCreatureListWithEntryInGrid(others, 0, 40.0f); + for (Creature* other : others) + if (other->AI()->GetData(DATA_PET_GROUP) == _petGroup) + { + other->SetReactState(REACT_AGGRESSIVE); + other->AI()->AttackStart(who); + } -enum TrashEvents -{ - // Anubar Skrimisher - EVENT_ANUBAR_CHARGE = 1, - EVENT_BACKSTAB, + if (TempSummon* meSummon = me->ToTempSummon()) + if (Creature* summoner = meSummon->GetSummonerCreatureBase()) + summoner->AI()->DoAction(ACTION_PET_ENGAGED); + } + _EnterCombat(); + ScriptedAI::EnterCombat(who); + } - // Anubar Shadowcaster - EVENT_SHADOW_BOLT, - EVENT_SHADOW_NOVA, + void SetData(uint32 data, uint32 value) override + { + if (data == DATA_PET_GROUP) + { + _petGroup = value; + me->SetReactState(_petGroup ? REACT_PASSIVE : REACT_AGGRESSIVE); + } + } + + uint32 GetData(uint32 data) const override + { + if (data == DATA_PET_GROUP) + return _petGroup; + return 0; + } + + void MoveInLineOfSight(Unit* who) override + { + if (!me->HasReactState(REACT_PASSIVE)) + { + ScriptedAI::MoveInLineOfSight(who); + return; + } - // Anubar Warrior - EVENT_STRIKE, - EVENT_CLEAVE, + if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance)) + EnterCombat(who); + } - // Watcher Gashra - EVENT_WEB_WRAP_GASHRA, - EVENT_INFECTED_BITE_GASHRA, + void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER) + DoZoneInCombat(); + } - // Watcher Narjil - EVENT_WEB_WRAP_NARJIL, - EVENT_INFECTED_BITE_NARJIL, - EVENT_BINDING_WEBS, + void EnterEvadeMode(EvadeReason why) override + { + if (TempSummon* meSummon = me->ToTempSummon()) + { + if (Creature* summoner = meSummon->GetSummonerCreatureBase()) + summoner->AI()->DoAction(ACTION_PET_EVADE); + else + me->DespawnOrUnsummon(); + return; + } + ScriptedAI::EnterEvadeMode(why); + } - // Watcher Silthik - EVENT_WEB_WRAP_SILTHIK, - EVENT_INFECTED_BITE_SILTHIK, - EVENT_POISON_SPRAY + EventMap _events; + InstanceScript* _instance; + uint32 _petGroup; + bool _isWatcher; }; -class npc_anub_ar_skirmisher : public CreatureScript +class npc_watcher_gashra : public CreatureScript { public: - npc_anub_ar_skirmisher() : CreatureScript("npc_anub_ar_skirmisher") { } + npc_watcher_gashra() : CreatureScript("npc_watcher_gashra") { } - struct npc_anub_ar_skirmisherAI : public ScriptedAI + struct npc_watcher_gashraAI : public npc_gatewatcher_petAI { - npc_anub_ar_skirmisherAI(Creature* creature) : ScriptedAI(creature) { } + npc_watcher_gashraAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) + { + _instance = creature->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + } void Reset() override { - events.Reset(); + _events.Reset(); } - void EnterCombat(Unit* /*who*/) override + void _EnterCombat() override { - events.ScheduleEvent(EVENT_ANUBAR_CHARGE, 11000); - events.ScheduleEvent(EVENT_BACKSTAB, 7000); + _events.ScheduleEvent(EVENT_ENRAGE, randtime(Seconds(3), Seconds(5))); + _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(16), Seconds(19))); + _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(7),Seconds(11))); + } + + void JustDied(Unit* /*killer*/) override + { + Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); + if (krikthir && krikthir->IsAlive()) + krikthir->AI()->DoAction(ACTION_GASHRA_DIED); } void UpdateAI(uint32 diff) override @@ -254,64 +446,79 @@ class npc_anub_ar_skirmisher : public CreatureScript if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_ANUBAR_CHARGE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - { - DoResetThreat(); - me->AddThreat(target, 1.0f); - DoCast(target, SPELL_CHARGE, true); - } - events.ScheduleEvent(EVENT_ANUBAR_CHARGE, 15000); + case EVENT_ENRAGE: + DoCastSelf(SPELL_ENRAGE); + _events.Repeat(randtime(Seconds(12), Seconds(20))); break; - case EVENT_BACKSTAB: - DoCastVictim(SPELL_BACKSTAB); - events.ScheduleEvent(EVENT_BACKSTAB, 12000); + case EVENT_WEB_WRAP: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f)) + DoCast(target, SPELL_WEB_WRAP); + _events.Repeat(randtime(Seconds(13), Seconds(19))); + break; + case EVENT_INFECTED_BITE: + DoCastVictim(SPELL_INFECTED_BITE); + _events.Repeat(randtime(Seconds(23), Seconds(27))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } private: - EventMap events; + EventMap _events; + InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_anub_ar_skirmisherAI>(creature); + return GetAzjolNerubAI<npc_watcher_gashraAI>(creature); } }; -class npc_anub_ar_shadowcaster : public CreatureScript +class npc_watcher_narjil : public CreatureScript { public: - npc_anub_ar_shadowcaster() : CreatureScript("npc_anub_ar_shadowcaster") { } + npc_watcher_narjil() : CreatureScript("npc_watcher_narjil") { } - struct npc_anub_ar_shadowcasterAI : public ScriptedAI + struct npc_watcher_narjilAI : public npc_gatewatcher_petAI { - npc_anub_ar_shadowcasterAI(Creature* creature) : ScriptedAI(creature) { } + npc_watcher_narjilAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) + { + _instance = creature->GetInstanceScript(); + } void Reset() override { - events.Reset(); + _events.Reset(); } - void EnterCombat(Unit* /*who*/) override + void _EnterCombat() override { - events.ScheduleEvent(EVENT_SHADOW_BOLT, 6000); - events.ScheduleEvent(EVENT_SHADOW_NOVA, 15000); + _events.ScheduleEvent(EVENT_BLINDING_WEBS, randtime(Seconds(13), Seconds(18))); + _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(3), Seconds(5))); + _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(7), Seconds(11))); + } + + void JustDied(Unit* /*killer*/) override + { + Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); + if (krikthir && krikthir->IsAlive()) + krikthir->AI()->DoAction(ACTION_NARJIL_DIED); } void UpdateAI(uint32 diff) override @@ -319,60 +526,79 @@ class npc_anub_ar_shadowcaster : public CreatureScript if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_SHADOW_BOLT: + case EVENT_BLINDING_WEBS: + DoCastVictim(SPELL_BLINDING_WEBS); + _events.Repeat(randtime(Seconds(23), Seconds(27))); + break; + case EVENT_WEB_WRAP: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_SHADOW_BOLT, true); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 15000); + DoCast(target, SPELL_WEB_WRAP); + _events.Repeat(randtime(Seconds(13), Seconds(19))); break; - case EVENT_SHADOW_NOVA: - DoCastVictim(SPELL_SHADOW_NOVA, true); - events.ScheduleEvent(EVENT_SHADOW_NOVA, 17000); + case EVENT_INFECTED_BITE: + DoCastVictim(SPELL_INFECTED_BITE); + _events.Repeat(randtime(Seconds(20), Seconds(25))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } private: - EventMap events; + EventMap _events; + InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_anub_ar_shadowcasterAI>(creature); + return GetAzjolNerubAI<npc_watcher_narjilAI>(creature); } }; -class npc_anub_ar_warrior : public CreatureScript +class npc_watcher_silthik : public CreatureScript { public: - npc_anub_ar_warrior() : CreatureScript("npc_anub_ar_warrior") { } + npc_watcher_silthik() : CreatureScript("npc_watcher_silthik") { } - struct npc_anub_ar_warriorAI : public ScriptedAI + struct npc_watcher_silthikAI : public npc_gatewatcher_petAI { - npc_anub_ar_warriorAI(Creature* creature) : ScriptedAI(creature) { } + npc_watcher_silthikAI(Creature* creature) : npc_gatewatcher_petAI(creature, true) + { + _instance = creature->GetInstanceScript(); + } void Reset() override { - events.Reset(); + _events.Reset(); } - void EnterCombat(Unit* /*who*/) override + void _EnterCombat() override { - events.ScheduleEvent(EVENT_CLEAVE, 11000); - events.ScheduleEvent(EVENT_STRIKE, 6000); + _events.ScheduleEvent(EVENT_POISON_SPRAY, randtime(Seconds(16), Seconds(19))); + _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(7), Seconds(11))); + _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(3), Seconds(5))); + } + + void JustDied(Unit* /*killer*/) override + { + Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); + if (krikthir && krikthir->IsAlive()) + krikthir->AI()->DoAction(ACTION_SILTHIK_DIED); } void UpdateAI(uint32 diff) override @@ -380,70 +606,68 @@ class npc_anub_ar_warrior : public CreatureScript if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_CLEAVE: - DoCastVictim(SPELL_STRIKE, true); - events.ScheduleEvent(EVENT_CLEAVE, 15000); + case EVENT_POISON_SPRAY: + DoCastVictim(SPELL_POISON_SPRAY); + _events.Repeat(randtime(Seconds(13), Seconds(19))); break; - case EVENT_STRIKE: - DoCastVictim(SPELL_CLEAVE, true); - events.ScheduleEvent(EVENT_STRIKE, 17000); + case EVENT_WEB_WRAP: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + DoCast(target, SPELL_WEB_WRAP); + _events.Repeat(randtime(Seconds(13), Seconds(17))); + break; + case EVENT_INFECTED_BITE: + DoCastVictim(SPELL_INFECTED_BITE); + _events.Repeat(randtime(Seconds(20), Seconds(24))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } private: - EventMap events; + EventMap _events; + InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_anub_ar_warriorAI>(creature); + return GetAzjolNerubAI<npc_watcher_silthikAI>(creature); } }; -class npc_watcher_gashra : public CreatureScript +class npc_anub_ar_warrior : public CreatureScript { public: - npc_watcher_gashra() : CreatureScript("npc_watcher_gashra") { } + npc_anub_ar_warrior() : CreatureScript("npc_anub_ar_warrior") { } - struct npc_watcher_gashraAI : public ScriptedAI + struct npc_anub_ar_warriorAI : public npc_gatewatcher_petAI { - npc_watcher_gashraAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + npc_anub_ar_warriorAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { } void Reset() override { _events.Reset(); } - void EnterCombat(Unit* /*who*/) override + void _EnterCombat() override { - DoCast(me, SPELL_ENRAGE, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_GASHRA, 11000); - _events.ScheduleEvent(EVENT_INFECTED_BITE_GASHRA, 4000); - } - - void JustDied(Unit* /*killer*/) override - { - Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); - if (krikthir && krikthir->IsAlive()) - krikthir->AI()->Talk(SAY_PREFIGHT); + _events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(7), Seconds(9))); + _events.ScheduleEvent(EVENT_STRIKE, randtime(Seconds(5), Seconds(10))); } void UpdateAI(uint32 diff) override @@ -460,63 +684,50 @@ class npc_watcher_gashra : public CreatureScript { switch (eventId) { - case EVENT_WEB_WRAP_GASHRA: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_WEB_WRAP, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_GASHRA, 17000); + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + _events.Repeat(randtime(Seconds(10), Seconds(16))); break; - case EVENT_INFECTED_BITE_GASHRA: - DoCastVictim(SPELL_INFECTED_BITE, true); - _events.ScheduleEvent(EVENT_INFECTED_BITE_GASHRA, 15000); + case EVENT_STRIKE: + DoCastVictim(SPELL_STRIKE); + _events.Repeat(randtime(Seconds(15), Seconds(19))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } - - private: - EventMap _events; - InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_watcher_gashraAI>(creature); + return GetAzjolNerubAI<npc_anub_ar_warriorAI>(creature); } }; -class npc_watcher_narjil : public CreatureScript +class npc_anub_ar_skirmisher : public CreatureScript { public: - npc_watcher_narjil() : CreatureScript("npc_watcher_narjil") { } + npc_anub_ar_skirmisher() : CreatureScript("npc_anub_ar_skirmisher") { } - struct npc_watcher_narjilAI : public ScriptedAI + struct npc_anub_ar_skirmisherAI : public npc_gatewatcher_petAI { - npc_watcher_narjilAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + npc_anub_ar_skirmisherAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { } void Reset() override { _events.Reset(); } - void EnterCombat(Unit* /*who*/) override + void _EnterCombat() override { - _events.ScheduleEvent(EVENT_WEB_WRAP_NARJIL, 11000); - _events.ScheduleEvent(EVENT_INFECTED_BITE_NARJIL, 4000); - _events.ScheduleEvent(EVENT_BINDING_WEBS, 17000); - } - - void JustDied(Unit* /*killer*/) override - { - Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); - if (krikthir && krikthir->IsAlive()) - krikthir->AI()->Talk(SAY_PREFIGHT); + _events.ScheduleEvent(EVENT_ANUBAR_CHARGE, randtime(Seconds(6), Seconds(8))); + _events.ScheduleEvent(EVENT_BACKSTAB, randtime(Seconds(7), Seconds(9))); } void UpdateAI(uint32 diff) override @@ -533,67 +744,58 @@ class npc_watcher_narjil : public CreatureScript { switch (eventId) { - case EVENT_WEB_WRAP_NARJIL: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_WEB_WRAP, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_NARJIL, 15000); - break; - case EVENT_INFECTED_BITE_NARJIL: - DoCastVictim(SPELL_INFECTED_BITE, true); - _events.ScheduleEvent(EVENT_INFECTED_BITE_NARJIL, 11000); + case EVENT_ANUBAR_CHARGE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_CHARGE); + _events.Repeat(randtime(Seconds(20), Seconds(25))); break; - case EVENT_BINDING_WEBS: - DoCastVictim(SPELL_BLINDING_WEBS, true); - _events.ScheduleEvent(EVENT_BINDING_WEBS, 17000); + case EVENT_BACKSTAB: + if (me->GetVictim() && me->GetVictim()->isInBack(me)) + DoCastVictim(SPELL_BACKSTAB); + _events.Repeat(randtime(Seconds(10), Seconds(13))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } - private: - EventMap _events; - InstanceScript* _instance; + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (spell->Id == SPELL_CHARGE && target) + DoCast(target, SPELL_FIXTATE_TRIGGER); + } }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_watcher_narjilAI>(creature); + return GetAzjolNerubAI<npc_anub_ar_skirmisherAI>(creature); } }; -class npc_watcher_silthik : public CreatureScript +class npc_anub_ar_shadowcaster : public CreatureScript { public: - npc_watcher_silthik() : CreatureScript("npc_watcher_silthik") { } + npc_anub_ar_shadowcaster() : CreatureScript("npc_anub_ar_shadowcaster") { } - struct npc_watcher_silthikAI : public ScriptedAI + struct npc_anub_ar_shadowcasterAI : public npc_gatewatcher_petAI { - npc_watcher_silthikAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + npc_anub_ar_shadowcasterAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { } void Reset() override { _events.Reset(); } - void EnterCombat(Unit* /*who*/) override + void _EnterCombat() override { - _events.ScheduleEvent(EVENT_WEB_WRAP_SILTHIK, 11000); - _events.ScheduleEvent(EVENT_INFECTED_BITE_SILTHIK, 4000); - _events.ScheduleEvent(EVENT_POISON_SPRAY, 15000); - } - - void JustDied(Unit* /*killer*/) override - { - Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER); - if (krikthir && krikthir->IsAlive()) - krikthir->AI()->Talk(SAY_PREFIGHT); + _events.ScheduleEvent(EVENT_SHADOW_BOLT, Seconds(4)); + _events.ScheduleEvent(EVENT_SHADOW_NOVA, randtime(Seconds(10), Seconds(14))); } void UpdateAI(uint32 diff) override @@ -610,35 +812,230 @@ class npc_watcher_silthik : public CreatureScript { switch (eventId) { - case EVENT_WEB_WRAP_SILTHIK: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_WEB_WRAP, true); - _events.ScheduleEvent(EVENT_WEB_WRAP_SILTHIK, 15000); - break; - case EVENT_INFECTED_BITE_SILTHIK: - DoCastVictim(SPELL_INFECTED_BITE, true); - _events.ScheduleEvent(EVENT_INFECTED_BITE_SILTHIK, 11000); + case EVENT_SHADOW_BOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_SHADOW_BOLT); + _events.Repeat(randtime(Seconds(2), Seconds(4))); break; - case EVENT_POISON_SPRAY: - DoCastVictim(SPELL_POSION_SPRAY, true); - _events.ScheduleEvent(EVENT_POISON_SPRAY, 17000); + case EVENT_SHADOW_NOVA: + DoCastVictim(SPELL_SHADOW_NOVA); + _events.Repeat(randtime(Seconds(10), Seconds(16))); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } + }; - private: - EventMap _events; - InstanceScript* _instance; + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_anub_ar_shadowcasterAI>(creature); + } +}; + +class npc_skittering_swarmer : public CreatureScript +{ + public: + npc_skittering_swarmer() : CreatureScript("npc_skittering_swarmer") { } + + struct npc_skittering_swarmerAI : public ScriptedAI + { + npc_skittering_swarmerAI(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER)) + { + if (Unit* target = gatewatcher->getAttackerForHelper()) + AttackStart(target); + gatewatcher->AI()->JustSummoned(me); + } + } }; CreatureAI* GetAI(Creature* creature) const override { - return GetAzjolNerubAI<npc_watcher_silthikAI>(creature); + return GetAzjolNerubAI<npc_skittering_swarmerAI>(creature); + } +}; + +class npc_skittering_infector : public CreatureScript +{ + public: + npc_skittering_infector() : CreatureScript("npc_skittering_infector") { } + + struct npc_skittering_infectorAI : public ScriptedAI + { + npc_skittering_infectorAI(Creature* creature) : ScriptedAI(creature) { } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER)) + { + if (Unit* target = gatewatcher->getAttackerForHelper()) + AttackStart(target); + gatewatcher->AI()->JustSummoned(me); + } + } + + void JustDied(Unit* killer) override + { + DoCastAOE(SPELL_ACID_SPLASH); + ScriptedAI::JustDied(killer); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_skittering_infectorAI>(creature); + } +}; + +class npc_gatewatcher_web_wrap : public CreatureScript +{ + public: + npc_gatewatcher_web_wrap() : CreatureScript("npc_gatewatcher_web_wrap") { } + + struct npc_gatewatcher_web_wrapAI : public NullCreatureAI + { + npc_gatewatcher_web_wrapAI(Creature* creature) : NullCreatureAI(creature) { } + + void JustDied(Unit* /*killer*/) override + { + if (TempSummon* meSummon = me->ToTempSummon()) + if (Unit* summoner = meSummon->GetSummoner()) + summoner->RemoveAurasDueToSpell(SPELL_WEB_WRAP_WRAPPED); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetAzjolNerubAI<npc_gatewatcher_web_wrapAI>(creature); + } +}; + +class spell_gatewatcher_subboss_trigger : public SpellScriptLoader +{ + public: + spell_gatewatcher_subboss_trigger() : SpellScriptLoader("spell_gatewatcher_subboss_trigger") { } + + class spell_gatewatcher_subboss_trigger_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gatewatcher_subboss_trigger_SpellScript); + + void HandleTargets(std::list<WorldObject*>& targetList) + { + // Remove any Watchers that are already in combat + for (std::list<WorldObject*>::iterator it = targetList.begin(); it != targetList.end(); ++it) + { + if (Creature* creature = (*it)->ToCreature()) + if (creature->IsAlive() && !creature->IsInCombat()) + continue; + it = targetList.erase(it); + } + + // Default to Krik'thir himself if he isn't engaged + WorldObject* target = nullptr; + if (GetCaster() && !GetCaster()->IsInCombat()) + target = GetCaster(); + // Unless there are Watchers that aren't engaged yet + if (!targetList.empty()) + { + // If there are, pick one of them at random + std::list<WorldObject*>::iterator it = targetList.begin(); + std::advance(it, urand(0, targetList.size() - 1)); + target = *it; + } + // And hit only that one + targetList.clear(); + if (target) + targetList.push_back(target); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gatewatcher_subboss_trigger_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_gatewatcher_subboss_trigger_SpellScript(); + } +}; + +class spell_anub_ar_skirmisher_fixtate : public SpellScriptLoader +{ + public: + spell_anub_ar_skirmisher_fixtate() : SpellScriptLoader("spell_anub_ar_skirmisher_fixtate") { } + + class spell_anub_ar_skirmisher_fixtate_SpellScript : public SpellScript + { + PrepareSpellScript(spell_anub_ar_skirmisher_fixtate_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_FIXTATE_TRIGGERED) != nullptr; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + target->CastSpell(GetCaster(), SPELL_FIXTATE_TRIGGERED, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_anub_ar_skirmisher_fixtate_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_anub_ar_skirmisher_fixtate_SpellScript(); + } +}; + +class spell_gatewatcher_web_wrap : public SpellScriptLoader +{ + public: + spell_gatewatcher_web_wrap() : SpellScriptLoader("spell_gatewatcher_web_wrap") { } + + class spell_gatewatcher_web_wrap_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gatewatcher_web_wrap_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + return sSpellMgr->GetSpellInfo(SPELL_WEB_WRAP_WRAPPED) != nullptr; + } + + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) + return; + + if (Unit* target = GetTarget()) + target->CastSpell(target, SPELL_WEB_WRAP_WRAPPED, true); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_gatewatcher_web_wrap_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_ROOT, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gatewatcher_web_wrap_AuraScript(); } }; @@ -656,11 +1053,12 @@ class achievement_watch_him_die : public AchievementCriteriaScript if (!instance) return false; - for (uint8 n = 0; n < 3; ++n) + for (ANDataTypes watcherData : {DATA_WATCHER_GASHRA, DATA_WATCHER_NARJIL, DATA_WATCHER_SILTHIK}) { - if (Creature* watcher = instance->GetCreature(DATA_WATCHER_GASHRA + n)) - if (!watcher->IsAlive()) - return false; + if (Creature* watcher = instance->GetCreature(watcherData)) + if (watcher->IsAlive()) + continue; + return false; } return true; @@ -670,12 +1068,22 @@ class achievement_watch_him_die : public AchievementCriteriaScript void AddSC_boss_krik_thir() { new boss_krik_thir(); - new npc_skittering_infector(); - new npc_anub_ar_skirmisher(); - new npc_anub_ar_shadowcaster(); + new npc_watcher_gashra(); - new npc_anub_ar_warrior(); - new npc_watcher_silthik(); new npc_watcher_narjil(); + new npc_watcher_silthik(); + + new npc_anub_ar_warrior(); + new npc_anub_ar_skirmisher(); + new npc_anub_ar_shadowcaster(); + + new npc_skittering_swarmer(); + new npc_skittering_infector(); + new npc_gatewatcher_web_wrap(); + + new spell_gatewatcher_subboss_trigger(); + new spell_anub_ar_skirmisher_fixtate(); + new spell_gatewatcher_web_wrap(); + new achievement_watch_him_die(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp index bba3a87efcf..7ebf309c74f 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp @@ -31,12 +31,27 @@ DoorData const doorData[] = ObjectData const creatureData[] = { { NPC_KRIKTHIR, DATA_KRIKTHIR_THE_GATEWATCHER }, + { NPC_HADRONOX, DATA_HADRONOX }, + { NPC_ANUBARAK, DATA_ANUBARAK }, { NPC_WATCHER_NARJIL, DATA_WATCHER_GASHRA }, { NPC_WATCHER_GASHRA, DATA_WATCHER_SILTHIK }, { NPC_WATCHER_SILTHIK, DATA_WATCHER_NARJIL }, { 0, 0 } // END }; +ObjectData const gameobjectData[] = +{ + { GO_ANUBARAK_DOOR_3, DATA_ANUBARAK_WALL }, + { 0, 0 } // END +}; + +BossBoundaryData const boundaries = +{ + { DATA_KRIKTHIR_THE_GATEWATCHER, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) }, + { DATA_HADRONOX, new ZRangeBoundary(666.0f, 776.0f) }, + { DATA_ANUBARAK, new CircleBoundary(Position(550.6178f, 253.5917f), 26.0f) } +}; + class instance_azjol_nerub : public InstanceMapScript { public: @@ -48,8 +63,19 @@ class instance_azjol_nerub : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(EncounterCount); + LoadBossBoundaries(boundaries); LoadDoorData(doorData); - LoadObjectData(creatureData, nullptr); + LoadObjectData(creatureData, gameobjectData); + } + + void OnUnitDeath(Unit* who) override + { + InstanceScript::OnUnitDeath(who); + Creature* creature = who->ToCreature(); + if (!creature || creature->IsCritter() || creature->IsControlledByPlayer()) + return; + if (Creature* gatewatcher = GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER)) + gatewatcher->AI()->DoAction(-ACTION_GATEWATCHER_GREET); } }; |