aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2011-10-05 20:48:34 +0200
committerShauren <shauren.trinity@gmail.com>2011-10-05 20:48:34 +0200
commit2ff1c28c4c3a4808f70e7e75833e5b354a3b1e34 (patch)
treef7dda332d916bee305a5b2aa8f0b1d667c3fb6cf /src
parent34678d8d39fccd0fa63c16874f20a23208a3e197 (diff)
Scripts/Icecrown Citadel
* The Lich King
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp11
-rwxr-xr-xsrc/server/game/Scripting/ScriptLoader.cpp2
-rwxr-xr-xsrc/server/game/Spells/SpellMgr.cpp89
-rw-r--r--src/server/scripts/Northrend/CMakeLists.txt1
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp3213
-rwxr-xr-xsrc/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h63
-rwxr-xr-xsrc/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp204
7 files changed, 3547 insertions, 36 deletions
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 305821095fa..2322e88d105 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -52,8 +52,7 @@ void SummonList::DespawnEntry(uint32 entry)
else if (summon->GetEntry() == entry)
{
erase(i++);
- summon->setDeathState(JUST_DIED);
- summon->RemoveCorpse();
+ summon->DespawnOrUnsummon();
}
else
++i;
@@ -70,13 +69,7 @@ void SummonList::DespawnAll()
else
{
erase(begin());
- if (TempSummon* summ = summon->ToTempSummon())
- {
- summon->DestroyForNearbyPlayers();
- summ->UnSummon();
- }
- else
- summon->DisappearAndDie();
+ summon->DespawnOrUnsummon();
}
}
}
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 64c92470fd7..26d703458ac 100755
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -479,6 +479,7 @@ void AddSC_boss_blood_prince_council();
void AddSC_boss_blood_queen_lana_thel();
void AddSC_boss_valithria_dreamwalker();
void AddSC_boss_sindragosa();
+void AddSC_boss_the_lich_king();
void AddSC_icecrown_citadel_teleport();
void AddSC_instance_icecrown_citadel();
void AddSC_icecrown_citadel();
@@ -1181,6 +1182,7 @@ void AddNorthrendScripts()
AddSC_boss_blood_queen_lana_thel();
AddSC_boss_valithria_dreamwalker();
AddSC_boss_sindragosa();
+ AddSC_boss_the_lich_king();
AddSC_icecrown_citadel_teleport();
AddSC_instance_icecrown_citadel();
AddSC_icecrown_citadel();
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index c80a3123739..2a37a55d830 100755
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -2803,6 +2803,10 @@ void SpellMgr::LoadSpellCustomAttr()
case 43140: // Flame Breath
case 43215: // Flame Breath
case 70461: // Coldflame Trap
+ case 72133: // Pain and Suffering
+ case 73788: // Pain and Suffering
+ case 73789: // Pain and Suffering
+ case 73790: // Pain and Suffering
spellInfo->AttributesCu |= SPELL_ATTR0_CU_CONE_LINE;
break;
case 24340: // Meteor
@@ -3327,6 +3331,91 @@ void SpellMgr::LoadDbcDataCorrections()
spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_TARGET_ANY;
spellInfo->Effect[1] = 0;
break;
+ case 71614: // Ice Lock
+ spellInfo->Mechanic = MECHANIC_STUN;
+ break;
+ case 72762: // Defile
+ spellInfo->DurationIndex = 559; // 53 seconds
+ break;
+ case 72743: // Defile
+ spellInfo->DurationIndex = 22; // 45 seconds
+ break;
+ case 72754: // Defile
+ case 73708: // Defile
+ case 73709: // Defile
+ case 73710: // Defile
+ spellInfo->EffectRadiusIndex[0] = 22; // 200yd
+ spellInfo->EffectRadiusIndex[1] = 22; // 200yd
+ break;
+ case 69030: // Val'kyr Target Search
+ spellInfo->EffectRadiusIndex[0] = 22; // 200yd
+ spellInfo->EffectRadiusIndex[1] = 22; // 200yd
+ break;
+ case 69198: // Raging Spirit Visual
+ spellInfo->rangeIndex = 13; // 50000yd
+ break;
+ case 73654: // Harvest Souls
+ case 74295: // Harvest Souls
+ case 74296: // Harvest Souls
+ case 74297: // Harvest Souls
+ spellInfo->EffectRadiusIndex[0] = 28; // 50000yd
+ spellInfo->EffectRadiusIndex[1] = 28; // 50000yd
+ spellInfo->EffectRadiusIndex[2] = 28; // 50000yd
+ break;
+ case 73655: // Harvest Soul
+ spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS;
+ break;
+ case 73540: // Summon Shadow Trap
+ spellInfo->DurationIndex = 23; // 90 seconds
+ break;
+ case 73530: // Shadow Trap (visual)
+ spellInfo->DurationIndex = 28; // 5 seconds
+ break;
+ case 73529: // Shadow Trap
+ spellInfo->EffectRadiusIndex[1] = 13; // 10yd
+ break;
+ case 74282: // Shadow Trap (searcher)
+ spellInfo->EffectRadiusIndex[0] = 15; // 3yd
+ break;
+ case 72595: // Restore Soul
+ case 73650: // Restore Soul
+ spellInfo->EffectRadiusIndex[0] = 22; // 200yd
+ break;
+ case 74086: // Destroy Soul
+ spellInfo->EffectRadiusIndex[0] = 22; // 200yd
+ break;
+ case 74302: // Summon Spirit Bomb
+ case 74342: // Summon Spirit Bomb
+ spellInfo->EffectRadiusIndex[0] = 22; // 200yd
+ spellInfo->MaxAffectedTargets = 1;
+ break;
+ case 74341: // Summon Spirit Bomb
+ case 74343: // Summon Spirit Bomb
+ spellInfo->EffectRadiusIndex[0] = 22; // 200yd
+ spellInfo->MaxAffectedTargets = 3;
+ break;
+ case 73579: // Summon Spirit Bomb
+ spellInfo->EffectRadiusIndex[0] = 20; // 25yd
+ break;
+ case 72350: // Fury of Frostmourne
+ spellInfo->EffectRadiusIndex[0] = 28; // 50000yd
+ spellInfo->EffectRadiusIndex[1] = 28; // 50000yd
+ break;
+ case 75127: // Kill Frostmourne Players
+ case 72351: // Fury of Frostmourne
+ case 72429: // Mass Resurrection
+ case 73159: // Play Movie
+ case 73582: // Trigger Vile Spirit (Inside, Heroic)
+ spellInfo->EffectRadiusIndex[0] = 28; // 50000yd
+ break;
+ case 72376: // Raise Dead
+ spellInfo->MaxAffectedTargets = 3;
+ spellInfo->EffectRadiusIndex[0] = 28; // 50000yd
+ break;
+ case 71809: // Jump
+ spellInfo->rangeIndex = 3; // 20yd
+ spellInfo->EffectRadiusIndex[0] = 20; // 25yd
+ break;
default:
break;
}
diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt
index 3e1f500ebce..63e714d17aa 100644
--- a/src/server/scripts/Northrend/CMakeLists.txt
+++ b/src/server/scripts/Northrend/CMakeLists.txt
@@ -173,6 +173,7 @@ set(scripts_STAT_SRCS
Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp
Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp
Northrend/IcecrownCitadel/boss_sindragosa.cpp
+ Northrend/IcecrownCitadel/boss_the_lich_king.cpp
Northrend/zuldrak.cpp
Northrend/icecrown.cpp
Northrend/Gundrak/boss_slad_ran.cpp
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
new file mode 100644
index 00000000000..5dbdf38ba7f
--- /dev/null
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
@@ -0,0 +1,3213 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ObjectMgr.h"
+#include "ScriptMgr.h"
+#include "ScriptedCreature.h"
+#include "SpellScript.h"
+#include "SpellAuraEffects.h"
+#include "Spell.h"
+#include "Vehicle.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "icecrown_citadel.h"
+
+// TODO:
+// test outro
+
+enum Texts
+{
+ // The Lich King
+ SAY_LK_INTRO_1 = 0,
+ SAY_LK_INTRO_2 = 1,
+ SAY_LK_INTRO_3 = 2,
+ SAY_LK_REMORSELESS_WINTER = 4,
+ SAY_LK_QUAKE = 5,
+ SAY_LK_SUMMON_VALKYR = 6,
+ SAY_LK_HARVEST_SOUL = 7,
+ SAY_LK_FROSTMOURNE_ESCAPE = 8, // not said on heroic
+ SAY_LK_FROSTMOURNE_KILL = 9, // not said on heroic
+ SAY_LK_KILL = 10,
+ SAY_LK_BERSERK = 11,
+ EMOTE_DEFILE_WARNING = 12,
+ EMOTE_NECROTIC_PLAGUE_WARNING = 13,
+ SAY_LK_OUTRO_1 = 14,
+ SAY_LK_OUTRO_2 = 15,
+ SAY_LK_OUTRO_3 = 16,
+ SAY_LK_OUTRO_4 = 17,
+ SAY_LK_OUTRO_5 = 18,
+ SAY_LK_OUTRO_6 = 19,
+ SAY_LK_OUTRO_7 = 20,
+ SAY_LK_OUTRO_8 = 21,
+
+ // Highlord Tirion Fordring
+ SAY_TIRION_INTRO_1 = 0,
+ SAY_TIRION_INTRO_2 = 1,
+ SAY_TIRION_OUTRO_1 = 2,
+ SAY_TIRION_OUTRO_2 = 3,
+
+ // Terenas Menethil (outro)
+ SAY_TERENAS_OUTRO_1 = 0,
+ SAY_TERENAS_OUTRO_2 = 1,
+
+ // Terenas Menethil (Frostmourne)
+ SAY_TERENAS_INTRO_1 = 0,
+ SAY_TERENAS_INTRO_2 = 1,
+ SAY_TERENAS_INTRO_3 = 2,
+};
+
+enum Spells
+{
+ // The Lich King
+ SPELL_PLAGUE_AVOIDANCE = 72846, // raging spirits also get it
+ SPELL_EMOTE_SIT_NO_SHEATH = 73220,
+ SPELL_BOSS_HITTIN_YA = 73878,
+ SPELL_EMOTE_SHOUT_NO_SHEATH = 73213,
+ SPELL_ICE_LOCK = 71614,
+
+ // Phase 1
+ SPELL_SUMMON_SHAMBLING_HORROR = 70372,
+ SPELL_RISEN_WITCH_DOCTOR_SPAWN = 69639,
+ SPELL_SUMMON_DRUDGE_GHOULS = 70358,
+ SPELL_INFEST = 70541,
+ SPELL_NECROTIC_PLAGUE = 70337,
+ SPELL_NECROTIC_PLAGUE_JUMP = 70338,
+ SPELL_PLAGUE_SIPHON = 74074,
+ SPELL_SHADOW_TRAP = 73539,
+ SPELL_SHADOW_TRAP_AURA = 73525,
+ SPELL_SHADOW_TRAP_KNOCKBACK = 73529,
+
+ // Phase Transition
+ SPELL_REMORSELESS_WINTER_1 = 68981,
+ SPELL_REMORSELESS_WINTER_2 = 72259,
+ SPELL_PAIN_AND_SUFFERING = 72133,
+ SPELL_SUMMON_ICE_SPHERE = 69104,
+ SPELL_ICE_SPHERE = 69090,
+ SPELL_ICE_BURST_TARGET_SEARCH = 69109,
+ SPELL_ICE_PULSE = 69091,
+ SPELL_ICE_BURST = 69108,
+ SPELL_RAGING_SPIRIT = 69200,
+ SPELL_RAGING_SPIRIT_VISUAL = 69197,
+ SPELL_RAGING_SPIRIT_VISUAL_CLONE = 69198,
+ SPELL_SOUL_SHRIEK = 69242,
+ SPELL_QUAKE = 72262,
+
+ // Phase 2
+ SPELL_DEFILE = 72762,
+ SPELL_DEFILE_AURA = 72743,
+ SPELL_DEFILE_GROW = 72756,
+ SPELL_SUMMON_VALKYR = 69037,
+ SPELL_SUMMON_VALKYR_PERIODIC = 74361,
+ SPELL_HARVEST_SOUL_VALKYR = 68985, // Val'kyr Shadowguard vehicle aura
+ SPELL_SOUL_REAPER = 69409,
+ SPELL_SOUL_REAPER_BUFF = 69410,
+ SPELL_WINGS_OF_THE_DAMNED = 74352,
+ SPELL_VALKYR_TARGET_SEARCH = 69030,
+ SPELL_CHARGE = 74399, // cast on selected target
+ SPELL_VALKYR_CARRY = 74445, // removes unselectable flag
+ SPELL_LIFE_SIPHON = 73488,
+ SPELL_LIFE_SIPHON_HEAL = 73489,
+ SPELL_EJECT_ALL_PASSENGERS = 68576,
+
+ // Phase 3
+ SPELL_VILE_SPIRITS = 70498,
+ SPELL_VILE_SPIRIT_MOVE_SEARCH = 70501,
+ SPELL_VILE_SPIRIT_DAMAGE_SEARCH = 70502,
+ SPELL_SPIRIT_BURST = 70503,
+ SPELL_HARVEST_SOUL = 68980,
+ SPELL_HARVEST_SOULS = 73654, // Heroic version, weird because it has all 4 difficulties just like above spell
+ SPELL_HARVEST_SOUL_VEHICLE = 68984,
+ SPELL_HARVEST_SOUL_VISUAL = 71372,
+ SPELL_HARVEST_SOUL_TELEPORT = 72546,
+ SPELL_HARVEST_SOULS_TELEPORT = 73655,
+ SPELL_HARVEST_SOUL_TELEPORT_BACK = 72597,
+ SPELL_IN_FROSTMOURNE_ROOM = 74276,
+ SPELL_KILL_FROSTMOURNE_PLAYERS = 75127,
+ SPELL_HARVESTED_SOUL = 72679,
+ SPELL_TRIGGER_VILE_SPIRIT_HEROIC = 73582,
+
+ // Frostmourne
+ SPELL_LIGHTS_FAVOR = 69382,
+ SPELL_RESTORE_SOUL = 72595,
+ SPELL_RESTORE_SOULS = 73650, // Heroic
+ SPELL_DARK_HUNGER = 69383, // Passive proc healing
+ SPELL_DESTROY_SOUL = 74086, // Used when Terenas Menethil dies
+ SPELL_SOUL_RIP = 69397, // Deals increasing damage
+ SPELL_SOUL_RIP_DAMAGE = 69398,
+ SPELL_TERENAS_LOSES_INSIDE = 72572,
+ SPELL_SUMMON_SPIRIT_BOMB_1 = 73581, // (Heroic)
+ SPELL_SUMMON_SPIRIT_BOMB_2 = 74299, // (Heroic)
+ SPELL_EXPLOSION = 73576, // Spirit Bomb (Heroic)
+
+ // Outro
+ SPELL_FURY_OF_FROSTMOURNE = 72350,
+ SPELL_FURY_OF_FROSTMOURNE_NO_REZ = 72351,
+ SPELL_EMOTE_QUESTION_NO_SHEATH = 73330,
+ SPELL_RAISE_DEAD = 71769,
+ SPELL_LIGHTS_BLESSING = 71797,
+ SPELL_JUMP = 71809,
+ SPELL_JUMP_TRIGGERED = 71811,
+ SPELL_JUMP_2 = 72431,
+ SPELL_SUMMON_BROKEN_FROSTMOURNE = 74081, // visual
+ SPELL_SUMMON_BROKEN_FROSTMOURNE_2 = 72406, // animation
+ SPELL_SUMMON_BROKEN_FROSTMOURNE_3 = 73017, // real summon
+ SPELL_BROKEN_FROSTMOURNE = 72398,
+ SPELL_BROKEN_FROSTMOURNE_KNOCK = 72405,
+ SPELL_SOUL_BARRAGE = 72305,
+ SPELL_SUMMON_TERENAS = 72420,
+ SPELL_MASS_RESURRECTION = 72429,
+ SPELL_MASS_RESURRECTION_REAL = 72423,
+ SPELL_PLAY_MOVIE = 73159,
+
+ // Shambling Horror
+ SPELL_SHOCKWAVE = 72149,
+ SPELL_ENRAGE = 72143,
+ SPELL_FRENZY = 28747,
+};
+
+#define NECROTIC_PLAGUE_LK RAID_MODE<uint32>(70337, 73912, 73913, 73914)
+#define NECROTIC_PLAGUE_PLR RAID_MODE<uint32>(70338, 73785, 73786, 73787)
+#define REMORSELESS_WINTER_1 RAID_MODE<uint32>(68981, 74270, 74271, 74272)
+#define REMORSELESS_WINTER_2 RAID_MODE<uint32>(72259, 74273, 74274, 74275)
+#define SUMMON_VALKYR RAID_MODE<uint32>(69037, 74361, 69037, 74361)
+#define HARVEST_SOUL RAID_MODE<uint32>(68980, 74325, 74296, 74297)
+
+enum Events
+{
+ // The Lich King
+ // intro events
+ EVENT_INTRO_MOVE_1 = 1,
+ EVENT_INTRO_MOVE_2 = 2,
+ EVENT_INTRO_MOVE_3 = 3,
+ EVENT_INTRO_TALK_1 = 4,
+ EVENT_EMOTE_CAST_SHOUT = 5,
+ EVENT_INTRO_EMOTE_1 = 6,
+ EVENT_INTRO_CHARGE = 7,
+ EVENT_INTRO_CAST_FREEZE = 8,
+ EVENT_FINISH_INTRO = 9,
+
+ // combat events
+ EVENT_SUMMON_SHAMBLING_HORROR = 10,
+ EVENT_SUMMON_DRUDGE_GHOUL = 11,
+ EVENT_INFEST = 12,
+ EVENT_NECROTIC_PLAGUE = 13,
+ EVENT_SHADOW_TRAP = 14, // heroic only
+ EVENT_SOUL_REAPER = 15,
+ EVENT_DEFILE = 16,
+ EVENT_HARVEST_SOUL = 17, // normal mode only
+ EVENT_PAIN_AND_SUFFERING = 18,
+ EVENT_SUMMON_ICE_SPHERE = 19,
+ EVENT_SUMMON_RAGING_SPIRIT = 20,
+ EVENT_QUAKE = 21,
+ EVENT_SUMMON_VALKYR = 22,
+ EVENT_GRAB_PLAYER = 23,
+ EVENT_MOVE_TO_DROP_POS = 24,
+ EVENT_LIFE_SIPHON = 25, // heroic only
+ EVENT_START_ATTACK = 26,
+ EVENT_QUAKE_2 = 27,
+ EVENT_VILE_SPIRITS = 28,
+ EVENT_HARVEST_SOULS = 29, // heroic only
+ EVENT_WICKED_SPIRITS = 30,
+ EVENT_SOUL_RIP = 31,
+ EVENT_DESTROY_SOUL = 32,
+ EVENT_FROSTMOURNE_TALK_1 = 33,
+ EVENT_FROSTMOURNE_TALK_2 = 34,
+ EVENT_FROSTMOURNE_TALK_3 = 35,
+ EVENT_TELEPORT_BACK = 36,
+ EVENT_FROSTMOURNE_HEROIC = 37,
+ EVENT_OUTRO_TALK_1 = 38,
+ EVENT_OUTRO_TALK_2 = 39,
+ EVENT_OUTRO_EMOTE_TALK = 40,
+ EVENT_OUTRO_TALK_3 = 41,
+ EVENT_OUTRO_MOVE_CENTER = 42,
+ EVENT_OUTRO_TALK_4 = 43,
+ EVENT_OUTRO_RAISE_DEAD = 44,
+ EVENT_OUTRO_TALK_5 = 45,
+ EVENT_OUTRO_BLESS = 46,
+ EVENT_OUTRO_REMOVE_ICE = 47,
+ EVENT_OUTRO_MOVE_1 = 48,
+ EVENT_OUTRO_JUMP = 49,
+ EVENT_OUTRO_TALK_6 = 50,
+ EVENT_OUTRO_KNOCK_BACK = 51,
+ EVENT_OUTRO_SOUL_BARRAGE = 52,
+ EVENT_OUTRO_SUMMON_TERENAS = 53,
+ EVENT_OUTRO_TERENAS_TALK_1 = 54,
+ EVENT_OUTRO_TERENAS_TALK_2 = 55,
+ EVENT_OUTRO_TALK_7 = 56,
+ EVENT_OUTRO_TALK_8 = 57,
+ EVENT_BERSERK = 58,
+
+ // Shambling Horror
+ EVENT_SHOCKWAVE = 59,
+ EVENT_ENRAGE = 60,
+
+ // Raging Spirit
+ EVENT_SOUL_SHRIEK = 61,
+
+ // Strangulate Vehicle (Harvest Soul)
+ EVENT_TELEPORT = 62,
+ EVENT_MOVE_TO_LICH_KING = 63,
+ EVENT_DESPAWN_SELF = 64,
+};
+
+enum EventGroups
+{
+ EVENT_GROUP_BERSERK = 1,
+ EVENT_GROUP_VILE_SPIRITS = 2,
+};
+
+enum Phases
+{
+ PHASE_INTRO = 1,
+ PHASE_ONE = 2,
+ PHASE_TWO = 3,
+ PHASE_THREE = 4,
+ PHASE_TRANSITION = 5,
+ PHASE_FROSTMOURNE = 6, // only set on heroic mode when all players are sent into frostmourne
+ PHASE_OUTRO = 7,
+
+ PHASE_MASK_INTRO = 1 << PHASE_INTRO,
+ PHASE_MASK_ONE = 1 << PHASE_ONE,
+ PHASE_MASK_TWO = 1 << PHASE_TWO,
+ PHASE_MASK_THREE = 1 << PHASE_THREE,
+ PHASE_MASK_TRANSITION = 1 << PHASE_TRANSITION,
+ PHASE_MASK_NO_CAST_CHECK = (1 << PHASE_TRANSITION) | (1 << PHASE_FROSTMOURNE) | (1 << PHASE_OUTRO),
+ PHASE_MASK_FROSTMOURNE = 1 << PHASE_FROSTMOURNE,
+ PHASE_MASK_OUTRO = 1 << PHASE_OUTRO,
+ PHASE_MASK_NO_VICTIM = (1 << PHASE_INTRO) | (1 << PHASE_OUTRO) | (1 << PHASE_FROSTMOURNE),
+};
+
+#define PHASE_TWO_THREE (events.GetPhaseMask() & PHASE_MASK_TWO ? PHASE_TWO : PHASE_THREE)
+
+Position const CenterPosition = {503.6282f, -2124.655f, 840.8569f, 0.0f};
+Position const TirionIntro = {489.2970f, -2124.840f, 840.8569f, 0.0f};
+Position const TirionCharge = {482.9019f, -2124.479f, 840.8570f, 0.0f};
+Position const LichKingIntro[3] =
+{
+ {432.0851f, -2123.673f, 864.6582f, 0.0f},
+ {457.8351f, -2123.423f, 841.1582f, 0.0f},
+ {465.0730f, -2123.470f, 840.8569f, 0.0f},
+};
+Position const OutroPosition1 = {493.6286f, -2124.569f, 840.8569f, 0.0f};
+Position const OutroFlying = {508.9897f, -2124.561f, 845.3565f, 0.0f};
+Position const TerenasSpawn = {495.5542f, -2517.012f, 1050.000f, 4.6993f};
+Position const TerenasSpawnHeroic = {495.7080f, -2523.760f, 1050.000f, 0.0f};
+Position const SpiritWardenSpawn = {495.3406f, -2529.983f, 1050.000f, 1.5592f};
+
+enum MovePoints
+{
+ POINT_CENTER_1 = 1,
+ POINT_CENTER_2 = 2,
+ POINT_TIRION_INTRO = 3,
+ POINT_LK_INTRO_1 = 4,
+ POINT_LK_INTRO_2 = 5,
+ POINT_LK_INTRO_3 = 6,
+ POINT_TIRION_CHARGE = 7,
+ POINT_DROP_PLAYER = 8,
+ POINT_LK_OUTRO_1 = 9,
+ POINT_TIRION_OUTRO_1 = 10,
+ POINT_OUTRO_JUMP = 11,
+ POINT_LK_OUTRO_2 = 12,
+ POINT_GROUND = 13,
+ POINT_CHARGE = 1003, // globally used number for charge spell effects
+};
+
+enum EncounterActions
+{
+ ACTION_START_ENCOUNTER = 0,
+ ACTION_CONTINUE_INTRO = 1,
+ ACTION_START_ATTACK = 2,
+ ACTION_OUTRO = 3,
+ ACTION_PLAY_MUSIC = 4,
+ ACTION_BREAK_FROSTMOURNE = 5,
+ ACTION_SUMMON_TERENAS = 6,
+ ACTION_FINISH_OUTRO = 7,
+ ACTION_TELEPORT_BACK = 8,
+};
+
+enum MiscData
+{
+ LIGHT_SNOWSTORM = 2490,
+ LIGHT_SOULSTORM = 2508,
+
+ MUSIC_FROZEN_THRONE = 17457,
+ MUSIC_SPECIAL = 17458, // Summon Shambling Horror, Remorseless Winter, Quake, Summon Val'kyr Periodic, Harvest Soul, Vile Spirits
+ MUSIC_FURY_OF_FROSTMOURNE = 17459,
+ MUSIC_FINAL = 17460, // Raise Dead, Light's Blessing
+
+ SOUND_PAIN = 17360, // separate sound, not attached to any text
+
+ EQUIP_ASHBRINGER_GLOWING = 50442,
+ EQUIP_BROKEN_FROSTMOURNE = 50840,
+
+ MOVIE_FALL_OF_THE_LICH_KING = 16,
+};
+
+#define DATA_PLAGUE_STACK 70337
+#define DATA_VILE 45814622
+
+class NecroticPlagueTargetCheck : public std::unary_function<Unit*, bool>
+{
+ public:
+ NecroticPlagueTargetCheck(Unit const* obj, uint32 notAura1 = 0, uint32 notAura2 = 0)
+ : _sourceObj(obj), _notAura1(notAura1), _notAura2(notAura2)
+ {
+ }
+
+ bool operator()(Unit* unit) const
+ {
+ if (!unit || unit == _sourceObj || !unit->isTargetableForAttack() || unit->isTotem() || unit->HasAura(SPELL_PLAGUE_AVOIDANCE))
+ return false;
+ if ((_notAura1 && unit->HasAura(_notAura1)) || (_notAura2 && unit->HasAura(_notAura2)))
+ return false;
+ return true;
+ }
+
+ private:
+ Unit const* _sourceObj;
+ uint32 _notAura1;
+ uint32 _notAura2;
+};
+
+class HeightDifferenceCheck
+{
+ public:
+ HeightDifferenceCheck(GameObject* go, float diff, bool reverse)
+ : _baseObject(go), _difference(diff), _reverse(reverse)
+ {
+ }
+
+ bool operator()(Unit* unit) const
+ {
+ return (unit->GetPositionZ() - _baseObject->GetPositionZ() > _difference) != _reverse;
+ }
+
+ private:
+ GameObject* _baseObject;
+ float _difference;
+ bool _reverse;
+};
+
+class FrozenThroneResetWorker
+{
+ public:
+ FrozenThroneResetWorker() { }
+
+ bool operator()(GameObject* go)
+ {
+ switch (go->GetEntry())
+ {
+ case GO_ARTHAS_PLATFORM:
+ go->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING);
+ break;
+ case GO_DOODAD_ICECROWN_THRONEFROSTYWIND01:
+ go->SetGoState(GO_STATE_ACTIVE);
+ break;
+ case GO_DOODAD_ICECROWN_THRONEFROSTYEDGE01:
+ go->SetGoState(GO_STATE_READY);
+ break;
+ case GO_DOODAD_ICESHARD_STANDING02:
+ case GO_DOODAD_ICESHARD_STANDING01:
+ case GO_DOODAD_ICESHARD_STANDING03:
+ case GO_DOODAD_ICESHARD_STANDING04:
+ go->ResetDoorOrButton();
+ break;
+ default:
+ break;
+ }
+
+ return false;
+ }
+};
+
+class StartMovementEvent : public BasicEvent
+{
+ public:
+ StartMovementEvent(Creature* summoner, Creature* owner)
+ : _summoner(summoner), _owner(owner)
+ {
+ }
+
+ bool Execute(uint64 /*time*/, uint32 /*diff*/)
+ {
+ _owner->SetReactState(REACT_AGGRESSIVE);
+ if (Unit* target = _summoner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(_summoner)))
+ _owner->AI()->AttackStart(target);
+ return true;
+ }
+
+ private:
+ Creature* _summoner;
+ Creature* _owner;
+};
+
+class VileSpiritActivateEvent : public BasicEvent
+{
+ public:
+ explicit VileSpiritActivateEvent(Creature* owner)
+ : _owner(owner)
+ {
+ }
+
+ bool Execute(uint64 /*time*/, uint32 /*diff*/)
+ {
+ _owner->SetReactState(REACT_AGGRESSIVE);
+ _owner->CastSpell(_owner, SPELL_VILE_SPIRIT_MOVE_SEARCH, true);
+ _owner->CastSpell((Unit*)NULL, SPELL_VILE_SPIRIT_DAMAGE_SEARCH, true);
+ return true;
+ }
+
+ private:
+ Creature* _owner;
+};
+
+class boss_the_lich_king : public CreatureScript
+{
+ public:
+ boss_the_lich_king() : CreatureScript("boss_the_lich_king") { }
+
+ struct boss_the_lich_kingAI : public BossAI
+ {
+ boss_the_lich_kingAI(Creature* creature) : BossAI(creature, DATA_THE_LICH_KING)
+ {
+ }
+
+ void Reset()
+ {
+ _Reset();
+ me->SetReactState(REACT_PASSIVE);
+ events.SetPhase(PHASE_INTRO);
+ _necroticPlagueStack = 0;
+ _vileSpiritExplosions = 0;
+ SetEquipmentSlots(true);
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
+ DoCastAOE(SPELL_PLAY_MOVIE, false);
+ }
+
+ void EnterCombat(Unit* target)
+ {
+ //if (!instance->CheckRequiredBosses(DATA_THE_LICH_KING, target->ToPlayer()))
+ //{
+ // EnterEvadeMode();
+ // instance->DoCastSpellOnPlayers(LIGHT_S_HAMMER_TELEPORT);
+ // return;
+ //}
+
+ me->setActive(true);
+ DoZoneInCombat();
+
+ events.SetPhase(PHASE_ONE);
+ events.ScheduleEvent(EVENT_SUMMON_SHAMBLING_HORROR, 20000, 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_SUMMON_DRUDGE_GHOUL, 10000, 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_INFEST, 5000, 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_NECROTIC_PLAGUE, urand(30000, 33000), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_BERSERK, 900000, EVENT_GROUP_BERSERK);
+ if (IsHeroic())
+ events.ScheduleEvent(EVENT_SHADOW_TRAP, 15500, 0, PHASE_ONE);
+ }
+
+ void JustReachedHome()
+ {
+ _JustReachedHome();
+ instance->SetBossState(DATA_THE_LICH_KING, NOT_STARTED);
+
+ // Reset The Frozen Throne gameobjects
+ FrozenThroneResetWorker reset;
+ Trinity::GameObjectWorker<FrozenThroneResetWorker> worker(me, reset);
+ me->VisitNearbyGridObject(333.0f, worker);
+
+ // Reset any light override
+ SendLightOverride(0, 5000);
+ }
+
+ bool CanAIAttack(Unit const* target) const
+ {
+ // The Lich King must not select targets in frostmourne room if he killed everyone outside
+ return !target->HasAura(SPELL_IN_FROSTMOURNE_ROOM);
+ }
+
+ void EnterEvadeMode()
+ {
+ instance->SetBossState(DATA_THE_LICH_KING, FAIL);
+ BossAI::EnterEvadeMode();
+ if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HIGHLORD_TIRION_FORDRING)))
+ tirion->AI()->EnterEvadeMode();
+ DoCastAOE(SPELL_KILL_FROSTMOURNE_PLAYERS);
+ summons.DoAction(NPC_STRANGULATE_VEHICLE, ACTION_TELEPORT_BACK);
+ }
+
+ void KilledUnit(Unit* victim)
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER && !me->IsInEvadeMode() && !(events.GetPhaseMask() & PHASE_MASK_OUTRO))
+ Talk(SAY_LK_KILL);
+ }
+
+ void DoAction(int32 const action)
+ {
+ switch (action)
+ {
+ case ACTION_START_ENCOUNTER:
+ instance->SetBossState(DATA_THE_LICH_KING, IN_PROGRESS);
+ Talk(SAY_LK_INTRO_1);
+ SendMusicToPlayers(MUSIC_FROZEN_THRONE);
+ // schedule talks
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ events.ScheduleEvent(EVENT_INTRO_MOVE_1, 4000);
+ break;
+ case ACTION_START_ATTACK:
+ events.ScheduleEvent(EVENT_START_ATTACK, 5000);
+ break;
+ case ACTION_PLAY_MUSIC:
+ SendMusicToPlayers(MUSIC_FINAL);
+ break;
+ case ACTION_RESTORE_LIGHT:
+ SendLightOverride(0, 5000);
+ break;
+ case ACTION_BREAK_FROSTMOURNE:
+ DoCastAOE(SPELL_SUMMON_BROKEN_FROSTMOURNE);
+ DoCastAOE(SPELL_SUMMON_BROKEN_FROSTMOURNE_2);
+ SetEquipmentSlots(false, EQUIP_BROKEN_FROSTMOURNE);
+ events.ScheduleEvent(EVENT_OUTRO_TALK_6, 2500, 0, PHASE_OUTRO);
+ break;
+ case ACTION_FINISH_OUTRO:
+ events.ScheduleEvent(EVENT_OUTRO_TALK_7, 7000, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_TALK_8, 17000, 0, PHASE_OUTRO);
+ break;
+ case ACTION_TELEPORT_BACK:
+ summons.DoAction(NPC_STRANGULATE_VEHICLE, ACTION_TELEPORT_BACK);
+ if (!IsHeroic())
+ Talk(SAY_LK_FROSTMOURNE_ESCAPE);
+ else
+ DoCastAOE(SPELL_TRIGGER_VILE_SPIRIT_HEROIC);
+ break;
+ default:
+ break;
+ }
+ }
+
+ uint32 GetData(uint32 type)
+ {
+ switch (type)
+ {
+ case DATA_PLAGUE_STACK:
+ return _necroticPlagueStack;
+ case DATA_VILE:
+ return _vileSpiritExplosions;
+ default:
+ break;
+ }
+
+ return 0;
+ }
+
+ void SetData(uint32 type, uint32 value)
+ {
+ switch (type)
+ {
+ case DATA_PLAGUE_STACK:
+ _necroticPlagueStack = std::max(value, _necroticPlagueStack);
+ break;
+ case DATA_VILE:
+ _vileSpiritExplosions += value;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/)
+ {
+ //if (events.GetPhaseMask() & PHASE_MASK_ONE && !HealthAbovePct(70))
+ //{
+ // events.SetPhase(PHASE_TRANSITION);
+ // me->GetMotionMaster()->MovePoint(POINT_CENTER_1, CenterPosition);
+ // return;
+ //}
+
+ if (events.GetPhaseMask() & (PHASE_MASK_ONE | PHASE_MASK_TWO) && !HealthAbovePct(40))
+ {
+ events.SetPhase(PHASE_TRANSITION);
+ me->GetMotionMaster()->MovePoint(POINT_CENTER_2, CenterPosition);
+ return;
+ }
+
+ if (events.GetPhaseMask() & PHASE_MASK_THREE && !HealthAbovePct(10))
+ {
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ events.Reset();
+ events.SetPhase(PHASE_OUTRO);
+ summons.DespawnAll();
+ SendMusicToPlayers(MUSIC_FURY_OF_FROSTMOURNE);
+ DoCastAOE(SPELL_FURY_OF_FROSTMOURNE);
+ me->AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ events.ScheduleEvent(EVENT_OUTRO_TALK_1, 2600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_EMOTE_TALK, 6600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_EMOTE_TALK, 17600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_EMOTE_TALK, 27600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_TALK_2, 34600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_TALK_3, 43600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_EMOTE_CAST_SHOUT, 54600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_EMOTE_TALK, 58600, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_MOVE_CENTER, 69600, 0, PHASE_OUTRO);
+ // stop here. rest will get scheduled from MovementInform
+ return;
+ }
+ }
+
+ void JustSummoned(Creature* summon)
+ {
+ switch (summon->GetEntry())
+ {
+ case NPC_SHAMBLING_HORROR:
+ case NPC_DRUDGE_GHOUL:
+ summon->CastSpell(summon, SPELL_RISEN_WITCH_DOCTOR_SPAWN, true);
+ summon->SetReactState(REACT_PASSIVE);
+ summon->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE);
+ summon->m_Events.AddEvent(new StartMovementEvent(me, summon), summon->m_Events.CalculateTime(5000));
+ break;
+ case NPC_ICE_SPHERE:
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
+ {
+ summon->SetReactState(REACT_PASSIVE);
+ summon->CastSpell(summon, SPELL_ICE_SPHERE, false);
+ summon->CastSpell(summon, SPELL_ICE_BURST_TARGET_SEARCH, false);
+ summon->CastSpell(target, SPELL_ICE_PULSE, false);
+ summon->ClearUnitState(UNIT_STAT_CASTING);
+ summon->GetMotionMaster()->MoveFollow(target, 0.0f, 0.0f);
+ }
+ else
+ summon->DespawnOrUnsummon();
+ break;
+ }
+ case NPC_DEFILE:
+ summon->SetReactState(REACT_PASSIVE);
+ summon->CastSpell(summon, SPELL_DEFILE_AURA, false);
+ break;
+ case NPC_VALKYR_SHADOWGUARD:
+ summon->CastSpell(summon, SPELL_WINGS_OF_THE_DAMNED, true);
+ summon->CastSpell(summon, SPELL_VALKYR_TARGET_SEARCH, true);
+ break;
+ case NPC_FROSTMOURNE_TRIGGER:
+ {
+ summons.Summon(summon);
+ summon->CastSpell((Unit*)NULL, SPELL_BROKEN_FROSTMOURNE, true);
+
+ SendLightOverride(LIGHT_SOULSTORM, 10000);
+ SendWeather(WEATHER_STATE_BLACKSNOW);
+
+ events.ScheduleEvent(EVENT_OUTRO_SOUL_BARRAGE, 5000, 0, PHASE_OUTRO);
+ return;
+ }
+ case NPC_VILE_SPIRIT:
+ {
+ summons.Summon(summon);
+ if (events.GetPhaseMask() & PHASE_MASK_FROSTMOURNE)
+ {
+ Position offset;
+ me->GetPositionOffsetTo(*summon, offset);
+ offset.m_positionZ += 5.0f;
+ Position dest = TerenasSpawnHeroic;
+ dest.RelocateOffset(offset);
+ summon->NearTeleportTo(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.GetOrientation());
+ summon->UpdateEntry(NPC_WICKED_SPIRIT);
+ summon->SetSpeed(MOVE_FLIGHT, 0.5f);
+ summon->CastSpell(summon, SPELL_VILE_SPIRIT_MOVE_SEARCH, true);
+ summon->CastSpell((Unit*)NULL, SPELL_VILE_SPIRIT_DAMAGE_SEARCH, true);
+ return;
+ }
+
+ summon->SetReactState(REACT_PASSIVE);
+ summon->SetSpeed(MOVE_FLIGHT, 0.5f);
+ summon->GetMotionMaster()->MoveRandom(10.0f);
+ summon->m_Events.AddEvent(new VileSpiritActivateEvent(summon), summon->m_Events.CalculateTime(15000));
+ return;
+ }
+ case NPC_STRANGULATE_VEHICLE:
+ summons.Summon(summon);
+ return;
+ default:
+ break;
+ }
+
+ BossAI::JustSummoned(summon);
+ }
+
+ void SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
+ {
+ switch (summon->GetEntry())
+ {
+ case NPC_SHAMBLING_HORROR:
+ case NPC_DRUDGE_GHOUL:
+ case NPC_ICE_SPHERE:
+ case NPC_VALKYR_SHADOWGUARD:
+ case NPC_RAGING_SPIRIT:
+ case NPC_VILE_SPIRIT:
+ case NPC_WICKED_SPIRIT:
+ summon->ToTempSummon()->SetTempSummonType(TEMPSUMMON_CORPSE_DESPAWN);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SpellHit(Unit* /*caster*/, SpellInfo const* spell)
+ {
+ if (spell->Id == SPELL_HARVESTED_SOUL && me->isInCombat() && !IsHeroic())
+ Talk(SAY_LK_FROSTMOURNE_KILL);
+ }
+
+ void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell)
+ {
+ if (spell->Id == REMORSELESS_WINTER_1 || spell->Id == REMORSELESS_WINTER_2)
+ {
+ SendLightOverride(LIGHT_SNOWSTORM, 5000);
+ SendWeather(WEATHER_STATE_LIGHT_SNOW);
+ }
+ }
+
+ void MovementInform(uint32 type, uint32 pointId)
+ {
+ if (type != POINT_MOTION_TYPE)
+ return;
+
+ switch (pointId)
+ {
+ case POINT_LK_INTRO_1:
+ // schedule for next update cycle, current update must finalize movement
+ events.ScheduleEvent(EVENT_INTRO_MOVE_2, 1, 0, PHASE_INTRO);
+ break;
+ case POINT_LK_INTRO_2:
+ events.ScheduleEvent(EVENT_INTRO_MOVE_3, 1, 0, PHASE_INTRO);
+ break;
+ case POINT_LK_INTRO_3:
+ if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HIGHLORD_TIRION_FORDRING)))
+ tirion->AI()->DoAction(ACTION_CONTINUE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_TALK_1, 9000, 0, PHASE_INTRO);
+ break;
+ case POINT_CENTER_1:
+ me->SetFacing(0.0f);
+ Talk(SAY_LK_REMORSELESS_WINTER);
+ SendMusicToPlayers(MUSIC_SPECIAL);
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ DoCast(me, SPELL_REMORSELESS_WINTER_1);
+ events.DelayEvents(62500, EVENT_GROUP_BERSERK); // delay berserk timer, its not ticking during phase transitions
+ events.ScheduleEvent(EVENT_QUAKE, 62500, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_PAIN_AND_SUFFERING, 4000, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_SUMMON_ICE_SPHERE, 8000, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_SUMMON_RAGING_SPIRIT, 3000, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_SUMMON_VALKYR, 78000, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_INFEST, 70000, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_DEFILE, 97000, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_SOUL_REAPER, 94000, 0, PHASE_TWO);
+ break;
+ case POINT_CENTER_2:
+ me->SetFacing(0.0f);
+ Talk(SAY_LK_REMORSELESS_WINTER);
+ SendMusicToPlayers(MUSIC_SPECIAL);
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ DoCast(me, SPELL_REMORSELESS_WINTER_2);
+ summons.DespawnEntry(NPC_VALKYR_SHADOWGUARD);
+ events.DelayEvents(62500, EVENT_GROUP_BERSERK); // delay berserk timer, its not ticking during phase transitions
+ events.ScheduleEvent(EVENT_QUAKE_2, 62500, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_PAIN_AND_SUFFERING, 6000, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_SUMMON_ICE_SPHERE, 8000, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_SUMMON_RAGING_SPIRIT, 5000, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_DEFILE, 95500, 0, PHASE_THREE);
+ events.ScheduleEvent(EVENT_SOUL_REAPER, 99500, 0, PHASE_THREE);
+ events.ScheduleEvent(EVENT_VILE_SPIRITS, 79500, EVENT_GROUP_VILE_SPIRITS, PHASE_THREE);
+ events.ScheduleEvent(IsHeroic() ? EVENT_HARVEST_SOULS : EVENT_HARVEST_SOUL, 73500, 0, PHASE_THREE);
+ break;
+ case POINT_LK_OUTRO_1:
+ events.ScheduleEvent(EVENT_OUTRO_TALK_4, 1, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_RAISE_DEAD, 1000, 0, PHASE_OUTRO);
+ events.ScheduleEvent(EVENT_OUTRO_TALK_5, 29000, 0, PHASE_OUTRO);
+ break;
+ case POINT_LK_OUTRO_2:
+ if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HIGHLORD_TIRION_FORDRING)))
+ tirion->AI()->Talk(SAY_TIRION_OUTRO_2);
+ if (Creature* frostmourne = me->FindNearestCreature(NPC_FROSTMOURNE_TRIGGER, 50.0f))
+ frostmourne->AI()->DoAction(ACTION_SUMMON_TERENAS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ // check phase first to prevent updating victim and entering evade mode when not wanted
+ if (!(events.GetPhaseMask() & PHASE_MASK_NO_VICTIM))
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ // during Remorseless Winter phases The Lich King is channeling a spell, but we must continue casting other spells
+ if (me->HasUnitState(UNIT_STAT_CASTING) && !(events.GetPhaseMask() & PHASE_MASK_NO_CAST_CHECK))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_INTRO_MOVE_1:
+ me->SetSheath(SHEATH_STATE_MELEE);
+ me->RemoveAurasDueToSpell(SPELL_EMOTE_SIT_NO_SHEATH);
+ me->AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ me->GetMotionMaster()->MovePoint(POINT_LK_INTRO_1, LichKingIntro[0]);
+ break;
+ case EVENT_INTRO_MOVE_2:
+ me->GetMotionMaster()->MovePoint(POINT_LK_INTRO_2, LichKingIntro[1]);
+ break;
+ case EVENT_INTRO_MOVE_3:
+ me->GetMotionMaster()->MovePoint(POINT_LK_INTRO_3, LichKingIntro[2]);
+ break;
+ case EVENT_INTRO_TALK_1:
+ Talk(SAY_LK_INTRO_2);
+ // for some reason blizz sends 2 emotes in row here so (we handle one in Talk)
+ me->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NOSHEATHE);
+ events.ScheduleEvent(EVENT_EMOTE_CAST_SHOUT, 7000, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_EMOTE_1, 13000, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_EMOTE_CAST_SHOUT, 18000, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_CAST_FREEZE, 31000, 0, PHASE_INTRO);
+ break;
+ case EVENT_EMOTE_CAST_SHOUT:
+ DoCast(me, SPELL_EMOTE_SHOUT_NO_SHEATH, false);
+ break;
+ case EVENT_INTRO_EMOTE_1:
+ me->HandleEmoteCommand(EMOTE_ONESHOT_POINT_NOSHEATHE);
+ break;
+ case EVENT_INTRO_CAST_FREEZE:
+ Talk(SAY_LK_INTRO_3);
+ DoCastAOE(SPELL_ICE_LOCK, false);
+ events.ScheduleEvent(EVENT_FINISH_INTRO, 1000, 0, PHASE_INTRO);
+ break;
+ case EVENT_FINISH_INTRO:
+ me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
+ me->SetReactState(REACT_AGGRESSIVE);
+ events.SetPhase(PHASE_ONE);
+ break;
+ case EVENT_SUMMON_SHAMBLING_HORROR:
+ DoCast(me, SPELL_SUMMON_SHAMBLING_HORROR);
+ SendMusicToPlayers(MUSIC_SPECIAL);
+ events.ScheduleEvent(EVENT_SUMMON_SHAMBLING_HORROR, 60000, 0, PHASE_ONE);
+ break;
+ case EVENT_SUMMON_DRUDGE_GHOUL:
+ DoCast(me, SPELL_SUMMON_DRUDGE_GHOULS);
+ events.ScheduleEvent(EVENT_SUMMON_DRUDGE_GHOUL, 30000, 0, PHASE_ONE);
+ break;
+ case EVENT_INFEST:
+ DoCast(me, SPELL_INFEST);
+ events.ScheduleEvent(EVENT_INFEST, urand(21000, 24000), 0, (events.GetPhaseMask() & PHASE_MASK_ONE) ? PHASE_ONE : PHASE_TWO);
+ break;
+ case EVENT_NECROTIC_PLAGUE:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, NecroticPlagueTargetCheck(me, NECROTIC_PLAGUE_LK, NECROTIC_PLAGUE_PLR)))
+ {
+ Talk(EMOTE_NECROTIC_PLAGUE_WARNING, target->GetGUID());
+ DoCast(target, SPELL_NECROTIC_PLAGUE);
+ }
+ events.ScheduleEvent(EVENT_NECROTIC_PLAGUE, urand(30000, 33000), 0, PHASE_ONE);
+ break;
+ case EVENT_SHADOW_TRAP:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me)))
+ DoCast(target, SPELL_SHADOW_TRAP);
+ events.ScheduleEvent(EVENT_SHADOW_TRAP, 15500, 0, PHASE_ONE);
+ break;
+ case EVENT_SOUL_REAPER:
+ DoCastVictim(SPELL_SOUL_REAPER);
+ events.ScheduleEvent(EVENT_SOUL_REAPER, urand(33000, 35000), 0, PHASE_TWO_THREE);
+ break;
+ case EVENT_DEFILE:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_HARVEST_SOUL_VALKYR))
+ {
+ Talk(EMOTE_DEFILE_WARNING);
+ DoCast(target, SPELL_DEFILE);
+ }
+ events.ScheduleEvent(EVENT_DEFILE, urand(32000, 35000), 0, PHASE_TWO_THREE);
+ break;
+ case EVENT_HARVEST_SOUL:
+ Talk(SAY_LK_HARVEST_SOUL);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, SpellTargetSelector(me, SPELL_HARVEST_SOUL)))
+ DoCast(target, SPELL_HARVEST_SOUL);
+ events.ScheduleEvent(EVENT_HARVEST_SOUL, 75000, 0, PHASE_THREE);
+ break;
+ case EVENT_PAIN_AND_SUFFERING:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
+ me->CastSpell(target, SPELL_PAIN_AND_SUFFERING, TRIGGERED_NONE);
+ events.ScheduleEvent(EVENT_PAIN_AND_SUFFERING, urand(1500, 4000), 0, PHASE_TRANSITION);
+ break;
+ case EVENT_SUMMON_ICE_SPHERE:
+ DoCastAOE(SPELL_SUMMON_ICE_SPHERE);
+ events.ScheduleEvent(EVENT_SUMMON_ICE_SPHERE, urand(7500, 8500), 0, PHASE_TRANSITION);
+ break;
+ case EVENT_SUMMON_RAGING_SPIRIT:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
+ me->CastSpell(target, SPELL_RAGING_SPIRIT, TRIGGERED_NONE);
+ events.ScheduleEvent(EVENT_SUMMON_RAGING_SPIRIT, urand(22000, 23000), 0, PHASE_TRANSITION);
+ break;
+ case EVENT_QUAKE:
+ events.SetPhase(PHASE_TWO);
+ me->ClearUnitState(UNIT_STAT_CASTING); // clear state to ensure check in DoCastAOE passes
+ DoCastAOE(SPELL_QUAKE);
+ SendMusicToPlayers(MUSIC_SPECIAL);
+ Talk(SAY_LK_QUAKE);
+ break;
+ case EVENT_QUAKE_2:
+ events.SetPhase(PHASE_THREE);
+ me->ClearUnitState(UNIT_STAT_CASTING); // clear state to ensure check in DoCastAOE passes
+ DoCastAOE(SPELL_QUAKE);
+ SendMusicToPlayers(MUSIC_SPECIAL);
+ Talk(SAY_LK_QUAKE);
+ break;
+ case EVENT_SUMMON_VALKYR:
+ SendMusicToPlayers(MUSIC_SPECIAL);
+ Talk(SAY_LK_SUMMON_VALKYR);
+ DoCastAOE(SUMMON_VALKYR);
+ events.ScheduleEvent(EVENT_SUMMON_VALKYR, urand(45000, 50000), 0, PHASE_TWO);
+ break;
+ case EVENT_START_ATTACK:
+ me->SetReactState(REACT_AGGRESSIVE);
+ if (events.GetPhaseMask() & PHASE_MASK_FROSTMOURNE)
+ events.SetPhase(PHASE_THREE);
+ break;
+ case EVENT_VILE_SPIRITS:
+ SendMusicToPlayers(MUSIC_SPECIAL);
+ DoCastAOE(SPELL_VILE_SPIRITS);
+ events.ScheduleEvent(EVENT_VILE_SPIRITS, urand(35000, 40000), EVENT_GROUP_VILE_SPIRITS, PHASE_THREE);
+ break;
+ case EVENT_WICKED_SPIRITS:
+ DoCastAOE(SPELL_VILE_SPIRITS);
+ events.ScheduleEvent(EVENT_WICKED_SPIRITS, urand(35000, 40000), 0, PHASE_FROSTMOURNE);
+ break;
+ case EVENT_HARVEST_SOULS:
+ Talk(SAY_LK_HARVEST_SOUL);
+ DoCastAOE(SPELL_HARVEST_SOULS);
+ events.ScheduleEvent(EVENT_HARVEST_SOULS, urand(100000, 110000), 0, PHASE_THREE);
+ events.SetPhase(PHASE_FROSTMOURNE); // will stop running UpdateVictim (no evading)
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ events.ScheduleEvent(EVENT_WICKED_SPIRITS, events.GetNextEventTime(EVENT_VILE_SPIRITS) - events.GetTimer(), 0, PHASE_FROSTMOURNE);
+ events.DelayEvents(50000, EVENT_GROUP_VILE_SPIRITS);
+ events.RescheduleEvent(EVENT_DEFILE, 50000, 0, PHASE_THREE);
+ events.RescheduleEvent(EVENT_SOUL_REAPER, urand(57000, 62000), 0, PHASE_THREE);
+ events.ScheduleEvent(EVENT_START_ATTACK, 49000);
+ events.ScheduleEvent(EVENT_FROSTMOURNE_HEROIC, 6500);
+ break;
+ case EVENT_FROSTMOURNE_HEROIC:
+ if (TempSummon* terenas = me->GetMap()->SummonCreature(NPC_TERENAS_MENETHIL_FROSTMOURNE_H, TerenasSpawnHeroic, NULL, 50000))
+ {
+ terenas->AI()->DoAction(ACTION_FROSTMOURNE_INTRO);
+ std::list<Creature*> triggers;
+ GetCreatureListWithEntryInGrid(triggers, terenas, NPC_WORLD_TRIGGER_INFINITE_AOI, 100.0f);
+ if (!triggers.empty())
+ {
+ triggers.sort(Trinity::ObjectDistanceOrderPred(terenas, true));
+ Unit* spawner = triggers.front();
+ spawner->CastSpell(spawner, SPELL_SUMMON_SPIRIT_BOMB_1, true); // summons bombs randomly
+ spawner->CastSpell(spawner, SPELL_SUMMON_SPIRIT_BOMB_2, true); // summons bombs on players
+ }
+ }
+ break;
+ case EVENT_OUTRO_TALK_1:
+ Talk(SAY_LK_OUTRO_1);
+ DoCastAOE(SPELL_FURY_OF_FROSTMOURNE_NO_REZ, true);
+ break;
+ case EVENT_OUTRO_TALK_2:
+ Talk(SAY_LK_OUTRO_2);
+ DoCastAOE(SPELL_EMOTE_QUESTION_NO_SHEATH);
+ break;
+ case EVENT_OUTRO_EMOTE_TALK:
+ me->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NOSHEATHE);
+ break;
+ case EVENT_OUTRO_TALK_3:
+ if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HIGHLORD_TIRION_FORDRING)))
+ me->SetFacing(0.0f, tirion);
+ Talk(SAY_LK_OUTRO_3);
+ break;
+ case EVENT_OUTRO_MOVE_CENTER:
+ me->GetMotionMaster()->MovePoint(POINT_LK_OUTRO_1, CenterPosition);
+ break;
+ case EVENT_OUTRO_TALK_4:
+ me->SetFacing(0.01745329f);
+ Talk(SAY_LK_OUTRO_4);
+ break;
+ case EVENT_OUTRO_RAISE_DEAD:
+ DoCastAOE(SPELL_RAISE_DEAD);
+ me->ClearUnitState(UNIT_STAT_CASTING);
+ SendMusicToPlayers(MUSIC_FINAL);
+ break;
+ case EVENT_OUTRO_TALK_5:
+ Talk(SAY_LK_OUTRO_5);
+ if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HIGHLORD_TIRION_FORDRING)))
+ tirion->AI()->DoAction(ACTION_OUTRO);
+ break;
+ case EVENT_OUTRO_TALK_6:
+ Talk(SAY_LK_OUTRO_6);
+ if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HIGHLORD_TIRION_FORDRING)))
+ tirion->SetFacing(0.0f, me);
+ me->ClearUnitState(UNIT_STAT_CASTING);
+ DoCastAOE(SPELL_SUMMON_BROKEN_FROSTMOURNE_3);
+ SetEquipmentSlots(false, EQUIP_UNEQUIP);
+ break;
+ case EVENT_OUTRO_SOUL_BARRAGE:
+ DoCastAOE(SPELL_SOUL_BARRAGE);
+ sCreatureTextMgr->SendSound(me, SOUND_PAIN, CHAT_MSG_MONSTER_YELL, 0, TEXT_RANGE_NORMAL, TEAM_OTHER, false);
+ // set flight
+ me->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
+ me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0x03);
+ me->GetMotionMaster()->MovePoint(POINT_LK_OUTRO_2, OutroFlying);
+ break;
+ case EVENT_OUTRO_TALK_7:
+ Talk(SAY_LK_OUTRO_7);
+ break;
+ case EVENT_OUTRO_TALK_8:
+ Talk(SAY_LK_OUTRO_8);
+ break;
+ case EVENT_BERSERK:
+ Talk(SAY_LK_BERSERK);
+ DoCast(me, SPELL_BERSERK2);
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+
+ void SendMusicToPlayers(uint32 musicId) const
+ {
+ WorldPacket data(SMSG_PLAY_MUSIC, 4);
+ data << uint32(musicId);
+ SendPacketToPlayers(&data);
+ }
+
+ void SendLightOverride(uint32 overrideId, uint32 fadeInTime) const
+ {
+ WorldPacket data(SMSG_OVERRIDE_LIGHT, 12);
+ data << uint32(2488); // Light.dbc entry (map default)
+ data << uint32(overrideId); // Light.dbc entry (override)
+ data << uint32(fadeInTime);
+ SendPacketToPlayers(&data);
+ }
+
+ void SendWeather(WeatherState weather) const
+ {
+ WorldPacket data(SMSG_WEATHER, 9);
+ data << uint32(weather);
+ data << float(0.5f);
+ data << uint8(0);
+ SendPacketToPlayers(&data);
+ }
+
+ // Send packet to all players in The Frozen Throne
+ void SendPacketToPlayers(WorldPacket const* data) const
+ {
+ Map::PlayerList const& players = me->GetMap()->GetPlayers();
+ if (!players.isEmpty())
+ for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ if (Player* player = itr->getSource())
+ if (player->GetAreaId() == AREA_THE_FROZEN_THRONE)
+ player->GetSession()->SendPacket(data);
+ }
+
+ uint32 _necroticPlagueStack;
+ uint32 _vileSpiritExplosions;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<boss_the_lich_kingAI>(creature);
+ }
+};
+
+class npc_tirion_fordring_tft : public CreatureScript
+{
+ public:
+ npc_tirion_fordring_tft() : CreatureScript("npc_tirion_fordring_tft") { }
+
+ struct npc_tirion_fordringAI : public ScriptedAI
+ {
+ npc_tirion_fordringAI(Creature* creature) : ScriptedAI(creature),
+ _instance(creature->GetInstanceScript())
+ {
+ }
+
+ void Reset()
+ {
+ _events.Reset();
+ }
+
+ void MovementInform(uint32 type, uint32 id)
+ {
+ if (type != POINT_MOTION_TYPE)
+ return;
+
+ switch (id)
+ {
+ case POINT_TIRION_INTRO:
+ me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
+ if (Creature* theLichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ theLichKing->AI()->DoAction(ACTION_START_ENCOUNTER);
+ break;
+ case POINT_TIRION_OUTRO_1:
+ _events.ScheduleEvent(EVENT_OUTRO_JUMP, 1, 0, PHASE_OUTRO);
+ break;
+ }
+ }
+
+ void DoAction(int32 const action)
+ {
+ switch (action)
+ {
+ case ACTION_CONTINUE_INTRO:
+ Talk(SAY_TIRION_INTRO_1);
+ _events.ScheduleEvent(EVENT_INTRO_TALK_1, 34000, 0, PHASE_INTRO);
+ break;
+ case ACTION_OUTRO:
+ _events.SetPhase(PHASE_OUTRO);
+ _events.ScheduleEvent(EVENT_OUTRO_TALK_1, 7000, 0, PHASE_OUTRO);
+ _events.ScheduleEvent(EVENT_OUTRO_BLESS, 18000, 0, PHASE_OUTRO);
+ _events.ScheduleEvent(EVENT_OUTRO_REMOVE_ICE, 23000, 0, PHASE_OUTRO);
+ _events.ScheduleEvent(EVENT_OUTRO_MOVE_1, 25000, 0, PHASE_OUTRO);
+ break;
+ }
+ }
+
+ void SpellHit(Unit* /*caster*/, SpellInfo const* spell)
+ {
+ if (spell->Id == SPELL_ICE_LOCK)
+ me->SetFacing(3.085098f);
+ else if (spell->Id == SPELL_BROKEN_FROSTMOURNE_KNOCK)
+ SetEquipmentSlots(true); // remove glow on ashbringer
+ }
+
+ void sGossipSelect(Player* player, uint32 sender, uint32 action)
+ {
+ if (me->GetCreatureInfo()->GossipMenuId == sender && !action)
+ {
+ _events.SetPhase(PHASE_INTRO);
+ me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ me->AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ me->GetMotionMaster()->MovePoint(POINT_TIRION_INTRO, TirionIntro);
+ }
+ }
+
+ void JustReachedHome()
+ {
+ me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
+ me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim() && !(_events.GetPhaseMask() & (PHASE_MASK_INTRO | PHASE_MASK_OUTRO)))
+ return;
+
+ _events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_INTRO_TALK_1:
+ Talk(SAY_TIRION_INTRO_2);
+ _events.ScheduleEvent(EVENT_INTRO_EMOTE_1, 2000, 0, PHASE_INTRO);
+ _events.ScheduleEvent(EVENT_INTRO_CHARGE, 5000, 0, PHASE_INTRO);
+ break;
+ case EVENT_INTRO_EMOTE_1:
+ me->HandleEmoteCommand(EMOTE_ONESHOT_POINT_NOSHEATHE);
+ break;
+ case EVENT_INTRO_CHARGE:
+ me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ me->GetMotionMaster()->MovePoint(POINT_TIRION_CHARGE, TirionCharge);
+ break;
+ case EVENT_OUTRO_TALK_1:
+ Talk(SAY_TIRION_OUTRO_1);
+ break;
+ case EVENT_OUTRO_BLESS:
+ DoCast(me, SPELL_LIGHTS_BLESSING);
+ break;
+ case EVENT_OUTRO_REMOVE_ICE:
+ me->RemoveAurasDueToSpell(SPELL_ICE_LOCK);
+ SetEquipmentSlots(false, EQUIP_ASHBRINGER_GLOWING);
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ {
+ me->SetFacing(0.0f, lichKing);
+ lichKing->AI()->DoAction(ACTION_PLAY_MUSIC);
+ }
+ break;
+ case EVENT_OUTRO_MOVE_1:
+ me->GetMotionMaster()->MovePoint(POINT_TIRION_OUTRO_1, OutroPosition1);
+ break;
+ case EVENT_OUTRO_JUMP:
+ DoCastAOE(SPELL_JUMP);
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap _events;
+ InstanceScript* _instance;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_tirion_fordringAI>(creature);
+ }
+};
+
+class npc_shambling_horror_icc : public CreatureScript
+{
+ public:
+ npc_shambling_horror_icc() : CreatureScript("npc_shambling_horror_icc") { }
+
+ struct npc_shambling_horror_iccAI : public ScriptedAI
+ {
+ npc_shambling_horror_iccAI(Creature* creature) : ScriptedAI(creature)
+ {
+ _frenzied = false;
+ }
+
+ void Reset()
+ {
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_SHOCKWAVE, urand(20000, 25000));
+ _events.ScheduleEvent(EVENT_ENRAGE, urand(11000, 14000));
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage)
+ {
+ if (IsHeroic() && me->HealthBelowPctDamaged(20, damage))
+ {
+ _frenzied = true;
+ DoCast(me, SPELL_FRENZY, true);
+ }
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SHOCKWAVE:
+ DoCast(me, SPELL_SHOCKWAVE);
+ _events.ScheduleEvent(EVENT_SHOCKWAVE, urand(20000, 25000));
+ break;
+ case EVENT_ENRAGE:
+ DoCast(me, SPELL_ENRAGE);
+ _events.ScheduleEvent(EVENT_ENRAGE, urand(20000, 25000));
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap _events;
+ bool _frenzied;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_shambling_horror_iccAI>(creature);
+ }
+};
+
+class npc_raging_spirit : public CreatureScript
+{
+ public:
+ npc_raging_spirit() : CreatureScript("npc_raging_spirit") { }
+
+ struct npc_raging_spiritAI : public ScriptedAI
+ {
+ npc_raging_spiritAI(Creature* creature) : ScriptedAI(creature),
+ _instance(creature->GetInstanceScript())
+ {
+ }
+
+ void Reset()
+ {
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_SOUL_SHRIEK, urand(12000, 15000));
+ DoCast(me, SPELL_PLAGUE_AVOIDANCE, true);
+ DoCast(me, SPELL_RAGING_SPIRIT_VISUAL, true);
+ if (TempSummon* summon = me->ToTempSummon())
+ if (Unit* summoner = summon->GetSummoner())
+ summoner->CastSpell(me, SPELL_RAGING_SPIRIT_VISUAL_CLONE, true);
+ DoCast(me, SPELL_BOSS_HITTIN_YA, true);
+ }
+
+ void IsSummonedBy(Unit* summoner)
+ {
+ // player is the spellcaster so register summon manually
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ lichKing->AI()->JustSummoned(me);
+ }
+
+ void JustDied(Unit* killer)
+ {
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ lichKing->AI()->SummonedCreatureDespawn(me);
+ if (TempSummon* summon = me->ToTempSummon())
+ summon->SetTempSummonType(TEMPSUMMON_CORPSE_DESPAWN);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SOUL_SHRIEK:
+ DoCastAOE(SPELL_SOUL_SHRIEK);
+ _events.ScheduleEvent(EVENT_SOUL_SHRIEK, urand(12000, 15000));
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap _events;
+ InstanceScript* _instance;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_raging_spiritAI>(creature);
+ }
+};
+
+class npc_valkyr_shadowguard : public CreatureScript
+{
+ public:
+ npc_valkyr_shadowguard() : CreatureScript("npc_valkyr_shadowguard") { }
+
+ struct npc_valkyr_shadowguardAI : public ScriptedAI
+ {
+ npc_valkyr_shadowguardAI(Creature* creature) : ScriptedAI(creature),
+ _grabbedPlayer(0), _instance(creature->GetInstanceScript())
+ {
+ }
+
+ void Reset()
+ {
+ _events.Reset();
+ me->SetReactState(REACT_PASSIVE);
+ me->SetSpeed(MOVE_FLIGHT, 0.642857f, true);
+ }
+
+ void IsSummonedBy(Unit* /*summoner*/)
+ {
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_GRAB_PLAYER, 2500);
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage)
+ {
+ if (!IsHeroic())
+ return;
+
+ if (!me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE))
+ return;
+
+ if (me->HealthBelowPctDamaged(50, damage))
+ {
+ _events.Reset();
+ DoCastAOE(SPELL_EJECT_ALL_PASSENGERS);
+ me->GetMotionMaster()->MoveTargetedHome();
+ me->ClearUnitState(UNIT_STAT_EVADE);
+ }
+ }
+
+ void JustReachedHome()
+ {
+ // schedule siphon life event (heroic only)
+ DoZoneInCombat();
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_LIFE_SIPHON, 2000);
+ }
+
+ void AttackStart(Unit* /*target*/)
+ {
+ }
+
+ void MovementInform(uint32 type, uint32 id)
+ {
+ if (type != POINT_MOTION_TYPE)
+ return;
+
+ switch (id)
+ {
+ case POINT_DROP_PLAYER:
+ DoCastAOE(SPELL_EJECT_ALL_PASSENGERS);
+ me->DespawnOrUnsummon(1000);
+ break;
+ case POINT_CHARGE:
+ if (Player* target = ObjectAccessor::GetPlayer(*me, _grabbedPlayer))
+ {
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ if (GameObject* platform = ObjectAccessor::GetGameObject(*me, _instance->GetData64(DATA_ARTHAS_PLATFORM)))
+ {
+ std::list<Creature*> triggers;
+ GetCreatureListWithEntryInGrid(triggers, me, NPC_WORLD_TRIGGER, 150.0f);
+ triggers.remove_if(HeightDifferenceCheck(platform, 5.0f, true));
+ if (triggers.empty())
+ return;
+
+ triggers.sort(Trinity::ObjectDistanceOrderPred(me));
+ DoCast(target, SPELL_VALKYR_CARRY);
+ _dropPoint.Relocate(triggers.front());
+ _events.ScheduleEvent(EVENT_MOVE_TO_DROP_POS, 1500);
+
+ }
+ }
+ else
+ me->DespawnOrUnsummon();
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SetGUID(uint64 guid, int32 /* = 0*/)
+ {
+ _grabbedPlayer = guid;
+ _events.Reset();
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_GRAB_PLAYER:
+ DoCastAOE(SPELL_VALKYR_TARGET_SEARCH);
+ _events.ScheduleEvent(EVENT_GRAB_PLAYER, 2000);
+ break;
+ case EVENT_MOVE_TO_DROP_POS:
+ me->GetMotionMaster()->MovePoint(POINT_DROP_PLAYER, _dropPoint);
+ break;
+ case EVENT_LIFE_SIPHON:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1))
+ DoCast(target, SPELL_LIFE_SIPHON);
+ _events.ScheduleEvent(EVENT_LIFE_SIPHON, 2500);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // no melee attacks
+ }
+
+ private:
+ EventMap _events;
+ Position _dropPoint;
+ uint64 _grabbedPlayer;
+ InstanceScript* _instance;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_valkyr_shadowguardAI>(creature);
+ }
+};
+
+class npc_strangulate_vehicle : public CreatureScript
+{
+ public:
+ npc_strangulate_vehicle() : CreatureScript("npc_strangulate_vehicle") { }
+
+ struct npc_strangulate_vehicleAI : public ScriptedAI
+ {
+ npc_strangulate_vehicleAI(Creature* creature) : ScriptedAI(creature),
+ _instance(creature->GetInstanceScript())
+ {
+ }
+
+ void IsSummonedBy(Unit* summoner)
+ {
+ me->SetFacing(0.0f, summoner);
+ DoCast(summoner, SPELL_HARVEST_SOUL_VEHICLE);
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_MOVE_TO_LICH_KING, 2000);
+ _events.ScheduleEvent(EVENT_TELEPORT, 6000);
+
+ // this will let us easily access all creatures of this entry on heroic mode when its time to teleport back
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ lichKing->AI()->JustSummoned(me);
+ }
+
+ void DoAction(int32 const action)
+ {
+ if (action != ACTION_TELEPORT_BACK)
+ return;
+
+ if (TempSummon* summ = me->ToTempSummon())
+ if (Unit* summoner = summ->GetSummoner())
+ DoCast(summoner, SPELL_HARVEST_SOUL_TELEPORT_BACK);
+
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ lichKing->AI()->SummonedCreatureDespawn(me);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ UpdateVictim();
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_TELEPORT:
+ me->GetMotionMaster()->MoveIdle(MOTION_SLOT_ACTIVE);
+ if (TempSummon* summ = me->ToTempSummon())
+ {
+ if (Unit* summoner = summ->GetSummoner())
+ {
+ summoner->CastSpell((Unit*)NULL, SPELL_HARVEST_SOUL_VISUAL, true);
+ summoner->ExitVehicle(summoner);
+ if (!IsHeroic())
+ summoner->CastSpell(summoner, SPELL_HARVEST_SOUL_TELEPORT, true);
+ else
+ {
+ summoner->CastSpell(summoner, SPELL_HARVEST_SOULS_TELEPORT, true);
+ summoner->RemoveAurasDueToSpell(HARVEST_SOUL, 0, 0, AURA_REMOVE_BY_EXPIRE);
+ }
+ }
+ }
+
+ _events.ScheduleEvent(EVENT_DESPAWN_SELF, 65000);
+ break;
+ case EVENT_MOVE_TO_LICH_KING:
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ {
+ if (me->GetExactDist(lichKing) > 10.0f)
+ {
+ Position pos;
+ lichKing->GetNearPosition(pos, float(rand_norm()) * 5.0f + 7.5f, lichKing->GetAngle(me));
+ me->GetMotionMaster()->MovePoint(0, pos);
+ }
+ }
+ break;
+ case EVENT_DESPAWN_SELF:
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ lichKing->AI()->SummonedCreatureDespawn(me);
+ me->DespawnOrUnsummon(1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private:
+ EventMap _events;
+ InstanceScript* _instance;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_strangulate_vehicleAI>(creature);
+ }
+};
+
+class npc_terenas_menethil : public CreatureScript
+{
+ public:
+ npc_terenas_menethil() : CreatureScript("npc_terenas_menethil") { }
+
+ struct npc_terenas_menethilAI : public ScriptedAI
+ {
+ npc_terenas_menethilAI(Creature* creature) : ScriptedAI(creature),
+ _instance(creature->GetInstanceScript())
+ {
+ }
+
+ bool CanAIAttack(Unit const* target) const
+ {
+ return target->GetEntry() != NPC_THE_LICH_KING;
+ }
+
+ void DoAction(int32 const action)
+ {
+ switch (action)
+ {
+ case ACTION_FROSTMOURNE_INTRO:
+ me->setActive(true);
+ DoCast(me, SPELL_LIGHTS_FAVOR);
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_FROSTMOURNE_TALK_1, 2000, PHASE_FROSTMOURNE);
+ _events.ScheduleEvent(EVENT_FROSTMOURNE_TALK_2, 11000, PHASE_FROSTMOURNE);
+ if (!IsHeroic())
+ {
+ _events.ScheduleEvent(EVENT_DESTROY_SOUL, 60000, PHASE_FROSTMOURNE);
+ _events.ScheduleEvent(EVENT_FROSTMOURNE_TALK_3, 25000);
+ }
+ break;
+ case ACTION_TELEPORT_BACK:
+ me->CastSpell((Unit*)NULL, SPELL_RESTORE_SOUL, TRIGGERED_NONE);
+ me->DespawnOrUnsummon(3000);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void EnterEvadeMode()
+ {
+ // no running back home
+ if (!me->isAlive())
+ return;
+
+ me->DeleteThreatList();
+ me->CombatStop(false);
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage)
+ {
+ if (damage >= me->GetHealth())
+ {
+ damage = me->GetHealth() - 1;
+ if (!me->HasAura(SPELL_TERENAS_LOSES_INSIDE) && !IsHeroic())
+ {
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ DoCast(SPELL_TERENAS_LOSES_INSIDE);
+ _events.ScheduleEvent(EVENT_TELEPORT_BACK, 1000);
+ if (Creature* warden = me->FindNearestCreature(NPC_SPIRIT_WARDEN, 20.0f))
+ {
+ warden->CastSpell((Unit*)NULL, SPELL_DESTROY_SOUL, TRIGGERED_NONE);
+ warden->DespawnOrUnsummon(2000);
+ }
+
+ me->DespawnOrUnsummon(2000);
+ }
+ }
+ }
+
+ void IsSummonedBy(Unit* /*summoner*/)
+ {
+ _events.Reset();
+ _events.SetPhase(PHASE_OUTRO);
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ me->SetFacing(0.0f, lichKing);
+
+ _events.ScheduleEvent(EVENT_OUTRO_TERENAS_TALK_1, 2000, 0, PHASE_OUTRO);
+ _events.ScheduleEvent(EVENT_OUTRO_TERENAS_TALK_2, 14000, 0, PHASE_OUTRO);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ UpdateVictim();
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_FROSTMOURNE_TALK_1:
+ Talk(SAY_TERENAS_INTRO_1);
+ if (IsHeroic())
+ DoCastAOE(SPELL_RESTORE_SOULS);
+ break;
+ case EVENT_FROSTMOURNE_TALK_2:
+ Talk(SAY_TERENAS_INTRO_2);
+ break;
+ case EVENT_FROSTMOURNE_TALK_3:
+ Talk(SAY_TERENAS_INTRO_3);
+ break;
+ case EVENT_OUTRO_TERENAS_TALK_1:
+ Talk(SAY_TERENAS_OUTRO_1);
+ break;
+ case EVENT_OUTRO_TERENAS_TALK_2:
+ Talk(SAY_TERENAS_OUTRO_2);
+ DoCastAOE(SPELL_MASS_RESURRECTION);
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ {
+ lichKing->AI()->DoAction(ACTION_FINISH_OUTRO);
+ lichKing->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE);
+ if (Creature* tirion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HIGHLORD_TIRION_FORDRING)))
+ tirion->AI()->AttackStart(lichKing);
+ }
+ break;
+ case EVENT_DESTROY_SOUL:
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ if (Creature* warden = me->FindNearestCreature(NPC_SPIRIT_WARDEN, 20.0f))
+ warden->CastSpell((Unit*)NULL, SPELL_DESTROY_SOUL, TRIGGERED_NONE);
+ DoCast(SPELL_TERENAS_LOSES_INSIDE);
+ _events.ScheduleEvent(EVENT_TELEPORT_BACK, 1000);
+ break;
+ case EVENT_TELEPORT_BACK:
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_THE_LICH_KING)))
+ lichKing->AI()->DoAction(ACTION_TELEPORT_BACK);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // fighting Spirit Warden
+ if (me->isInCombat())
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap _events;
+ InstanceScript* _instance;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_terenas_menethilAI>(creature);
+ }
+};
+
+class npc_spirit_warden : public CreatureScript
+{
+ public:
+ npc_spirit_warden() : CreatureScript("npc_spirit_warden") { }
+
+ struct npc_spirit_wardenAI : public ScriptedAI
+ {
+ npc_spirit_wardenAI(Creature* creature) : ScriptedAI(creature),
+ _instance(creature->GetInstanceScript())
+ {
+ }
+
+ void Reset()
+ {
+ _events.Reset();
+ _events.ScheduleEvent(EVENT_SOUL_RIP, urand(12000, 15000));
+ DoCast(SPELL_DARK_HUNGER);
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ if (Creature* terenas = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TERENAS_MENETHIL)))
+ terenas->AI()->DoAction(ACTION_TELEPORT_BACK);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SOUL_RIP:
+ DoCastVictim(SPELL_SOUL_RIP);
+ _events.ScheduleEvent(EVENT_SOUL_RIP, urand(23000, 27000));
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap _events;
+ InstanceScript* _instance;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_spirit_wardenAI>(creature);
+ }
+};
+
+class npc_spirit_bomb : public CreatureScript
+{
+ public:
+ npc_spirit_bomb() : CreatureScript("npc_spirit_bomb") { }
+
+ struct npc_spirit_bombAI : public CreatureAI
+ {
+ npc_spirit_bombAI(Creature* creature) : CreatureAI(creature)
+ {
+ }
+
+ void IsSummonedBy(Unit* summoner)
+ {
+ float destX, destY, destZ;
+ me->GetPosition(destX, destY);
+ destZ = 1055.0f; // approximation, gets more precise later
+ me->UpdateGroundPositionZ(destX, destY, destZ);
+ me->GetMotionMaster()->MovePoint(POINT_GROUND, destX, destY, destZ);
+ }
+
+ void MovementInform(uint32 type, uint32 point)
+ {
+ if (type != POINT_MOTION_TYPE || point != POINT_GROUND)
+ return;
+
+ me->RemoveAllAuras();
+ DoCastAOE(SPELL_EXPLOSION);
+ me->DespawnOrUnsummon(1000);
+ }
+
+ void AttackStart(Unit* /*victim*/)
+ {
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ UpdateVictim();
+ // no melee attacks
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_spirit_bombAI>(creature);
+ }
+};
+
+class npc_broken_frostmourne : public CreatureScript
+{
+ public:
+ npc_broken_frostmourne() : CreatureScript("npc_broken_frostmourne") { }
+
+ struct npc_broken_frostmourneAI : public CreatureAI
+ {
+ npc_broken_frostmourneAI(Creature* creature) : CreatureAI(creature)
+ {
+ }
+
+ void Reset()
+ {
+ _events.Reset();
+ }
+
+ void IsSummonedBy(Unit* summoner)
+ {
+ _events.SetPhase(PHASE_OUTRO);
+ _events.ScheduleEvent(EVENT_OUTRO_KNOCK_BACK, 3000, 0, PHASE_OUTRO);
+ }
+
+ void DoAction(int32 const action)
+ {
+ if (action == ACTION_SUMMON_TERENAS)
+ _events.ScheduleEvent(EVENT_OUTRO_SUMMON_TERENAS, 6000, 0, PHASE_OUTRO);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ UpdateVictim();
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_OUTRO_KNOCK_BACK:
+ DoCastAOE(SPELL_BROKEN_FROSTMOURNE_KNOCK);
+ break;
+ case EVENT_OUTRO_SUMMON_TERENAS:
+ DoCastAOE(SPELL_SUMMON_TERENAS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // no melee attacks
+ }
+
+ private:
+ EventMap _events;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetIcecrownCitadelAI<npc_broken_frostmourneAI>(creature);
+ }
+};
+
+class spell_the_lich_king_infest : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_infest() : SpellScriptLoader("spell_the_lich_king_infest") { }
+
+ class spell_the_lich_king_infest_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_infest_AuraScript);
+
+ void OnPeriodic(AuraEffect const* /*aurEff*/)
+ {
+ if (GetUnitOwner()->HealthAbovePct(90))
+ {
+ PreventDefaultAction();
+ Remove(AURA_REMOVE_BY_ENEMY_SPELL);
+ }
+ }
+
+ void OnUpdate(AuraEffect* aurEff)
+ {
+ // multiply, starting from 2nd tick
+ if (aurEff->GetTickNumber() == 1)
+ return;
+
+ aurEff->SetAmount(int32(aurEff->GetAmount() * 1.15f));
+ }
+
+ void Register()
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_the_lich_king_infest_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_the_lich_king_infest_AuraScript::OnUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_infest_AuraScript();
+ }
+};
+
+class spell_the_lich_king_necrotic_plague : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_necrotic_plague() : SpellScriptLoader("spell_the_lich_king_necrotic_plague") { }
+
+ class spell_the_lich_king_necrotic_plague_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_necrotic_plague_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_NECROTIC_PLAGUE_JUMP))
+ return false;
+ return true;
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* newCaster = GetTarget();
+ switch (GetTargetApplication()->GetRemoveMode())
+ {
+ case AURA_REMOVE_BY_ENEMY_SPELL:
+ case AURA_REMOVE_BY_EXPIRE:
+ case AURA_REMOVE_BY_DEATH:
+ break;
+ default:
+ return;
+ }
+
+ CustomSpellValues values;
+ //values.AddSpellMod(SPELLVALUE_AURA_STACK, 2);
+ values.AddSpellMod(SPELLVALUE_MAX_TARGETS, 1);
+ GetTarget()->CastCustomSpell(SPELL_NECROTIC_PLAGUE_JUMP, values, NULL, true, NULL, NULL, GetCasterGUID());
+ if (Unit* caster = GetCaster())
+ caster->CastSpell(caster, SPELL_PLAGUE_SIPHON, true);
+ }
+
+ void Register()
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_the_lich_king_necrotic_plague_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_necrotic_plague_AuraScript();
+ }
+};
+
+class spell_the_lich_king_necrotic_plague_jump : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_necrotic_plague_jump() : SpellScriptLoader("spell_the_lich_king_necrotic_plague_jump") { }
+
+ class spell_the_lich_king_necrotic_plague_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_necrotic_plague_SpellScript);
+
+ bool Load()
+ {
+ _hadAura = false;
+ return true;
+ }
+
+ void SelectTarget(std::list<Unit*>& targets)
+ {
+ targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster()));
+ if (targets.size() < 2)
+ return;
+
+ targets.resize(1);
+ }
+
+ void CheckAura()
+ {
+ if (GetHitUnit()->HasAura(GetSpellInfo()->Id))
+ _hadAura = true;
+ }
+
+ void AddMissingStack()
+ {
+ if (!_hadAura && GetSpellValue()->EffectBasePoints[EFFECT_1] != AURA_REMOVE_BY_ENEMY_SPELL)
+ GetHitAura()->ModStackAmount(1);
+ }
+
+ void Register()
+ {
+ BeforeHit += SpellHitFn(spell_the_lich_king_necrotic_plague_SpellScript::CheckAura);
+ OnHit += SpellHitFn(spell_the_lich_king_necrotic_plague_SpellScript::AddMissingStack);
+ }
+
+ bool _hadAura;
+ };
+
+ class spell_the_lich_king_necrotic_plague_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_necrotic_plague_AuraScript);
+
+ bool Load()
+ {
+ _lastAmount = 0;
+ return true;
+ }
+
+ void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Unit* caster = GetCaster())
+ caster->GetAI()->SetData(DATA_PLAGUE_STACK, GetStackAmount());
+ }
+
+ void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ _lastAmount = aurEff->GetAmount();
+ switch (GetTargetApplication()->GetRemoveMode())
+ {
+ case AURA_REMOVE_BY_EXPIRE:
+ case AURA_REMOVE_BY_DEATH:
+ break;
+ default:
+ return;
+ }
+
+ CustomSpellValues values;
+ values.AddSpellMod(SPELLVALUE_AURA_STACK, GetStackAmount());
+ GetTarget()->CastCustomSpell(SPELL_NECROTIC_PLAGUE_JUMP, values, NULL, true, NULL, NULL, GetCasterGUID());
+ if (Unit* caster = GetCaster())
+ caster->CastSpell(caster, SPELL_PLAGUE_SIPHON, true);
+ }
+
+ void OnDispel(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ _lastAmount = aurEff->GetAmount();
+ }
+
+ void AfterDispel(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ // this means the stack increased so don't process as if dispelled
+ if (aurEff->GetAmount() > _lastAmount)
+ return;
+
+ CustomSpellValues values;
+ values.AddSpellMod(SPELLVALUE_AURA_STACK, GetStackAmount());
+ values.AddSpellMod(SPELLVALUE_BASE_POINT1, AURA_REMOVE_BY_ENEMY_SPELL); // add as marker (spell has no effect 1)
+ GetTarget()->CastCustomSpell(SPELL_NECROTIC_PLAGUE_JUMP, values, NULL, true, NULL, NULL, GetCasterGUID());
+ if (Unit* caster = GetCaster())
+ caster->CastSpell(caster, SPELL_PLAGUE_SIPHON, true);
+
+ Remove(AURA_REMOVE_BY_ENEMY_SPELL);
+ }
+
+ void Register()
+ {
+ OnEffectApply += AuraEffectApplyFn(spell_the_lich_king_necrotic_plague_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_the_lich_king_necrotic_plague_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_the_lich_king_necrotic_plague_AuraScript::OnDispel, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAPPLY);
+ AfterEffectApply += AuraEffectRemoveFn(spell_the_lich_king_necrotic_plague_AuraScript::AfterDispel, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAPPLY);
+ }
+
+ int32 _lastAmount;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_necrotic_plague_SpellScript();
+ }
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_necrotic_plague_AuraScript();
+ }
+};
+
+class spell_the_lich_king_shadow_trap_visual : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_shadow_trap_visual() : SpellScriptLoader("spell_the_lich_king_shadow_trap_visual") { }
+
+ class spell_the_lich_king_shadow_trap_visual_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_shadow_trap_visual_AuraScript);
+
+ void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes mode)
+ {
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
+ GetTarget()->CastSpell(GetTarget(), SPELL_SHADOW_TRAP_AURA, TRIGGERED_NONE);
+ }
+
+ void Register()
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_the_lich_king_shadow_trap_visual_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_shadow_trap_visual_AuraScript();
+ }
+};
+
+class spell_the_lich_king_shadow_trap_periodic : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_shadow_trap_periodic() : SpellScriptLoader("spell_the_lich_king_shadow_trap_periodic") { }
+
+ class spell_the_lich_king_shadow_trap_periodic_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_shadow_trap_periodic_SpellScript);
+
+ void CheckTargetCount(std::list<Unit*>& targets)
+ {
+ if (targets.empty())
+ return;
+
+ GetCaster()->CastSpell((Unit*)NULL, SPELL_SHADOW_TRAP_KNOCKBACK, true);
+ }
+
+ void Register()
+ {
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_shadow_trap_periodic_SpellScript::CheckTargetCount, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_shadow_trap_periodic_SpellScript();
+ }
+};
+
+class spell_the_lich_king_quake : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_quake() : SpellScriptLoader("spell_the_lich_king_quake") { }
+
+ class spell_the_lich_king_quake_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_quake_SpellScript);
+
+ bool Load()
+ {
+ return GetCaster()->GetInstanceScript() != NULL;
+ }
+
+ void FilterTargets(std::list<Unit*>& unitList)
+ {
+ if (GameObject* platform = ObjectAccessor::GetGameObject(*GetCaster(), GetCaster()->GetInstanceScript()->GetData64(DATA_ARTHAS_PLATFORM)))
+ unitList.remove_if(HeightDifferenceCheck(platform, 5.0f, false));
+ }
+
+ void HandleSendEvent(SpellEffIndex /*effIndex*/)
+ {
+ if (GetCaster()->IsAIEnabled)
+ GetCaster()->GetAI()->DoAction(ACTION_START_ATTACK);
+ }
+
+ void Register()
+ {
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_quake_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
+ OnEffectHit += SpellEffectFn(spell_the_lich_king_quake_SpellScript::HandleSendEvent, EFFECT_1, SPELL_EFFECT_SEND_EVENT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_quake_SpellScript();
+ }
+};
+
+class spell_the_lich_king_ice_burst_target_search : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_ice_burst_target_search() : SpellScriptLoader("spell_the_lich_king_ice_burst_target_search") { }
+
+ class spell_the_lich_king_ice_burst_target_search_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_ice_burst_target_search_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ICE_BURST))
+ return false;
+ return true;
+ }
+
+ void CheckTargetCount(std::list<Unit*>& unitList)
+ {
+ if (unitList.empty())
+ return;
+
+ // if there is at least one affected target cast the explosion
+ GetCaster()->CastSpell(GetCaster(), SPELL_ICE_BURST, true);
+ if (GetCaster()->GetTypeId() == TYPEID_UNIT)
+ GetCaster()->ToCreature()->DespawnOrUnsummon(500);
+ }
+
+ void Register()
+ {
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_ice_burst_target_search_SpellScript::CheckTargetCount, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_ice_burst_target_search_SpellScript();
+ }
+};
+
+class spell_the_lich_king_raging_spirit : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_raging_spirit() : SpellScriptLoader("spell_the_lich_king_raging_spirit") { }
+
+ class spell_the_lich_king_raging_spirit_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_raging_spirit_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_LIFE_SIPHON_HEAL))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ GetHitUnit()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), true);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_raging_spirit_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_raging_spirit_SpellScript();
+ }
+};
+
+class ExactDistanceCheck
+{
+ public:
+ ExactDistanceCheck(Unit* source, float dist) : _source(source), _dist(dist) {}
+
+ bool operator()(Unit* unit)
+ {
+ return _source->GetExactDist2d(unit) > _dist;
+ }
+
+ private:
+ Unit* _source;
+ float _dist;
+};
+
+class spell_the_lich_king_defile : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_defile() : SpellScriptLoader("spell_the_lich_king_defile") { }
+
+ class spell_the_lich_king_defile_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_defile_SpellScript);
+
+ void CorrectRange(std::list<Unit*>& targets)
+ {
+ targets.remove_if(ExactDistanceCheck(GetCaster(), 10.0f * GetCaster()->GetFloatValue(OBJECT_FIELD_SCALE_X)));
+ }
+
+ void ChangeDamageAndGrow()
+ {
+ SetHitDamage(int32(GetHitDamage() * GetCaster()->GetFloatValue(OBJECT_FIELD_SCALE_X)));
+ // HACK: target player should cast this spell on defile
+ // however with current aura handling auras cast by different units
+ // cannot stack on the same aura object increasing the stack count
+ GetCaster()->CastSpell(GetCaster(), SPELL_DEFILE_GROW, true);
+ }
+
+ void Register()
+ {
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_defile_SpellScript::CorrectRange, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_defile_SpellScript::CorrectRange, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnHit += SpellHitFn(spell_the_lich_king_defile_SpellScript::ChangeDamageAndGrow);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_defile_SpellScript();
+ }
+};
+
+class spell_the_lich_king_summon_into_air : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_summon_into_air() : SpellScriptLoader("spell_the_lich_king_summon_into_air") { }
+
+ class spell_the_lich_king_summon_into_air_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_summon_into_air_SpellScript);
+
+ void ModDestHeight(SpellEffIndex effIndex)
+ {
+ static Position const offset = {0.0f, 0.0f, 15.0f, 0.0f};
+ WorldLocation* dest = const_cast<WorldLocation*>(GetTargetDest());
+ dest->RelocateOffset(offset);
+ // spirit bombs get higher
+ if (GetSpellInfo()->Effects[effIndex].MiscValue == NPC_SPIRIT_BOMB)
+ dest->RelocateOffset(offset);
+ }
+
+ void Register()
+ {
+ OnEffectLaunch += SpellEffectFn(spell_the_lich_king_summon_into_air_SpellScript::ModDestHeight, EFFECT_0, SPELL_EFFECT_SUMMON);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_summon_into_air_SpellScript();
+ }
+};
+
+class spell_the_lich_king_soul_reaper : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_soul_reaper() : SpellScriptLoader("spell_the_lich_king_soul_reaper") { }
+
+ class spell_the_lich_king_soul_reaper_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_soul_reaper_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SOUL_REAPER_BUFF))
+ return false;
+ return true;
+ }
+
+ void OnPeriodic(AuraEffect const* /*aurEff*/)
+ {
+ if (Unit* caster = GetCaster())
+ GetTarget()->CastSpell(caster, SPELL_SOUL_REAPER_BUFF, true);
+ }
+
+ void Register()
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_the_lich_king_soul_reaper_AuraScript::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_soul_reaper_AuraScript();
+ }
+};
+
+class spell_the_lich_king_valkyr_target_search : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_valkyr_target_search() : SpellScriptLoader("spell_the_lich_king_valkyr_target_search") { }
+
+ class spell_the_lich_king_valkyr_target_search_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_valkyr_target_search_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ICE_BURST))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ _target = NULL;
+ return true;
+ }
+
+ void SelectTarget(std::list<Unit*>& unitList)
+ {
+ if (unitList.empty())
+ return;
+
+ unitList.remove_if(Trinity::UnitAuraCheck(true, GetSpellInfo()->Id));
+ if (unitList.empty())
+ return;
+
+ _target = SelectRandomContainerElement(unitList);
+ unitList.clear();
+ unitList.push_back(_target);
+ GetCaster()->GetAI()->SetGUID(_target->GetGUID());
+ }
+
+ void ReplaceTarget(std::list<Unit*>& unitList)
+ {
+ unitList.clear();
+ if (_target)
+ unitList.push_back(_target);
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_CHARGE, true);
+ }
+
+ void Register()
+ {
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_valkyr_target_search_SpellScript::SelectTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_valkyr_target_search_SpellScript::ReplaceTarget, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_valkyr_target_search_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+
+ Unit* _target;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_valkyr_target_search_SpellScript();
+ }
+};
+
+class spell_the_lich_king_eject_all_passengers : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_eject_all_passengers() : SpellScriptLoader("spell_the_lich_king_eject_all_passengers") { }
+
+ class spell_the_lich_king_eject_all_passengers_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_eject_all_passengers_SpellScript);
+
+ bool Load()
+ {
+ return GetCaster()->IsVehicle();
+ }
+
+ void HandleDummy(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ GetCaster()->GetVehicleKit()->RemoveAllPassengers();
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_eject_all_passengers_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_eject_all_passengers_SpellScript();
+ }
+};
+
+class spell_the_lich_king_cast_back_to_caster : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_cast_back_to_caster() : SpellScriptLoader("spell_the_lich_king_cast_back_to_caster") { }
+
+ class spell_the_lich_king_cast_back_to_caster_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_cast_back_to_caster_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ GetHitUnit()->CastSpell(GetCaster(), uint32(GetEffectValue()), true);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_cast_back_to_caster_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_cast_back_to_caster_SpellScript();
+ }
+};
+
+class spell_the_lich_king_life_siphon : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_life_siphon() : SpellScriptLoader("spell_the_lich_king_life_siphon") { }
+
+ class spell_the_lich_king_life_siphon_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_life_siphon_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_LIFE_SIPHON_HEAL))
+ return false;
+ return true;
+ }
+
+ void TriggerHeal()
+ {
+ GetHitUnit()->CastCustomSpell(SPELL_LIFE_SIPHON_HEAL, SPELLVALUE_BASE_POINT0, GetHitDamage() * 10, GetCaster(), true);
+ }
+
+ void Register()
+ {
+ AfterHit += SpellHitFn(spell_the_lich_king_life_siphon_SpellScript::TriggerHeal);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_life_siphon_SpellScript();
+ }
+};
+
+class spell_the_lich_king_vile_spirits : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_vile_spirits() : SpellScriptLoader("spell_the_lich_king_vile_spirits") { }
+
+ class spell_the_lich_king_vile_spirits_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_vile_spirits_AuraScript);
+
+ bool Load()
+ {
+ _is25Man = GetUnitOwner()->GetMap()->Is25ManRaid();
+ return true;
+ }
+
+ void OnPeriodic(AuraEffect const* aurEff)
+ {
+ if (_is25Man || ((aurEff->GetTickNumber() - 1) % 5))
+ GetTarget()->CastSpell((Unit*)NULL, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true, NULL, aurEff, GetCasterGUID());
+ }
+
+ void Register()
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_the_lich_king_vile_spirits_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+
+ bool _is25Man;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_vile_spirits_AuraScript();
+ }
+};
+
+class spell_the_lich_king_vile_spirits_visual : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_vile_spirits_visual() : SpellScriptLoader("spell_the_lich_king_vile_spirits_visual") { }
+
+ class spell_the_lich_king_vile_spirits_visual_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_vile_spirits_visual_SpellScript);
+
+ void ModDestHeight(SpellEffIndex /*effIndex*/)
+ {
+ Position offset = {0.0f, 0.0f, 15.0f, 0.0f};
+ const_cast<WorldLocation*>(GetTargetDest())->RelocateOffset(offset);
+ }
+
+ void Register()
+ {
+ OnEffectLaunch += SpellEffectFn(spell_the_lich_king_vile_spirits_visual_SpellScript::ModDestHeight, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_vile_spirits_visual_SpellScript();
+ }
+};
+
+class spell_the_lich_king_vile_spirit_move_target_search : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_vile_spirit_move_target_search() : SpellScriptLoader("spell_the_lich_king_vile_spirit_move_target_search") { }
+
+ class spell_the_lich_king_vile_spirit_move_target_search_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_vile_spirit_move_target_search_SpellScript);
+
+ bool Load()
+ {
+ _target = NULL;
+ return GetCaster()->GetTypeId() == TYPEID_UNIT;
+ }
+
+ void SelectTarget(std::list<Unit*>& targets)
+ {
+ if (targets.empty())
+ return;
+
+ _target = SelectRandomContainerElement(targets);
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ // for this spell, all units are in target map, however it should select one to attack
+ if (GetHitUnit() != _target)
+ return;
+
+ GetCaster()->ToCreature()->AI()->AttackStart(GetHitUnit());
+ GetCaster()->AddThreat(GetHitUnit(), 100000.0f);
+ }
+
+ void Register()
+ {
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_vile_spirit_move_target_search_SpellScript::SelectTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_vile_spirit_move_target_search_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+
+ Unit* _target;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_vile_spirit_move_target_search_SpellScript();
+ }
+};
+
+class spell_the_lich_king_vile_spirit_damage_target_search : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_vile_spirit_damage_target_search() : SpellScriptLoader("spell_the_lich_king_vile_spirit_damage_target_search") { }
+
+ class spell_the_lich_king_vile_spirit_damage_target_search_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_vile_spirit_damage_target_search_SpellScript);
+
+ bool Load()
+ {
+ return GetCaster()->GetTypeId() == TYPEID_UNIT;
+ }
+
+ void CheckTargetCount(std::list<Unit*>& targets)
+ {
+ if (targets.empty())
+ return;
+
+ // this spell has SPELL_AURA_BLOCK_SPELL_FAMILY so every next cast of this
+ // searcher spell will be blocked
+ GetCaster()->GetAI()->SetData(DATA_VILE, 1);
+ GetCaster()->CastSpell((Unit*)NULL, SPELL_SPIRIT_BURST, true);
+ GetCaster()->ToCreature()->DespawnOrUnsummon(3000);
+ GetCaster()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ }
+
+ void Register()
+ {
+ OnUnitTargetSelect += SpellUnitTargetFn(spell_the_lich_king_vile_spirit_damage_target_search_SpellScript::CheckTargetCount, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ }
+
+ Unit* _target;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_vile_spirit_damage_target_search_SpellScript();
+ }
+};
+
+class spell_the_lich_king_harvest_soul : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_harvest_soul() : SpellScriptLoader("spell_the_lich_king_harvest_soul") { }
+
+ class spell_the_lich_king_harvest_soul_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_harvest_soul_AuraScript);
+
+ bool Load()
+ {
+ return GetOwner()->GetInstanceScript() != NULL;
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ // m_originalCaster to allow stacking from different casters, meh
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH)
+ GetTarget()->CastSpell((Unit*)NULL, SPELL_HARVESTED_SOUL, true, NULL, NULL, GetTarget()->GetInstanceScript()->GetData64(DATA_THE_LICH_KING));
+ }
+
+ void Register()
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_the_lich_king_harvest_soul_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_harvest_soul_AuraScript();
+ }
+};
+
+class spell_the_lich_king_lights_favor : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_lights_favor() : SpellScriptLoader("spell_the_lich_king_lights_favor") { }
+
+ class spell_the_lich_king_lights_favor_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_lights_favor_AuraScript);
+
+ void OnPeriodic(AuraEffect const* /*aurEff*/)
+ {
+ if (Unit* caster = GetCaster())
+ if (AuraEffect* effect = GetAura()->GetEffect(EFFECT_1))
+ effect->RecalculateAmount(caster);
+ }
+
+ void CalculateBonus(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = true;
+ amount = 0;
+ if (Unit* caster = GetCaster())
+ amount = int32(caster->GetHealthPct());
+ }
+
+ void Register()
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_the_lich_king_lights_favor_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_HEAL);
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_the_lich_king_lights_favor_AuraScript::CalculateBonus, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_lights_favor_AuraScript();
+ }
+};
+
+class spell_the_lich_king_soul_rip : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_soul_rip() : SpellScriptLoader("spell_the_lich_king_soul_rip") { }
+
+ class spell_the_lich_king_soul_rip_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_soul_rip_AuraScript);
+
+ void OnPeriodic(AuraEffect const* aurEff)
+ {
+ PreventDefaultAction();
+ // shouldn't be needed, this is channeled
+ if (Unit* caster = GetCaster())
+ caster->CastCustomSpell(SPELL_SOUL_RIP_DAMAGE, SPELLVALUE_BASE_POINT0, 5000 * aurEff->GetTickNumber(), GetTarget(), true, NULL, aurEff, GetCasterGUID());
+ }
+
+ void Register()
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_the_lich_king_soul_rip_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_soul_rip_AuraScript();
+ }
+};
+
+class spell_the_lich_king_restore_soul : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_restore_soul() : SpellScriptLoader("spell_the_lich_king_restore_soul") { }
+
+ class spell_the_lich_king_restore_soul_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_restore_soul_SpellScript);
+
+ bool Load()
+ {
+ _instance = GetCaster()->GetInstanceScript();
+ return _instance != NULL;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*GetCaster(), _instance->GetData64(DATA_THE_LICH_KING)))
+ lichKing->AI()->DoAction(ACTION_TELEPORT_BACK);
+ if (Creature* spawner = GetCaster()->FindNearestCreature(NPC_WORLD_TRIGGER_INFINITE_AOI, 50.0f))
+ spawner->RemoveAllAuras();
+ }
+
+ void RemoveAura()
+ {
+ if (Unit* target = GetHitUnit())
+ target->RemoveAurasDueToSpell(target->GetMap()->IsHeroic() ? SPELL_HARVEST_SOULS_TELEPORT : SPELL_HARVEST_SOUL_TELEPORT);
+ }
+
+ void Register()
+ {
+ OnEffectHit += SpellEffectFn(spell_the_lich_king_restore_soul_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
+ BeforeHit += SpellHitFn(spell_the_lich_king_restore_soul_SpellScript::RemoveAura);
+ }
+
+ InstanceScript* _instance;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_restore_soul_SpellScript();
+ }
+};
+
+class spell_the_lich_king_in_frostmourne_room : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_in_frostmourne_room() : SpellScriptLoader("spell_the_lich_king_in_frostmourne_room") { }
+
+ class spell_the_lich_king_in_frostmourne_room_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_the_lich_king_in_frostmourne_room_AuraScript);
+
+ bool Load()
+ {
+ return GetOwner()->GetInstanceScript() != NULL;
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ // m_originalCaster to allow stacking from different casters, meh
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH)
+ GetTarget()->CastSpell((Unit*)NULL, SPELL_HARVESTED_SOUL, true, NULL, NULL, GetTarget()->GetInstanceScript()->GetData64(DATA_THE_LICH_KING));
+ }
+
+ void Register()
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_the_lich_king_in_frostmourne_room_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_the_lich_king_in_frostmourne_room_AuraScript();
+ }
+};
+
+class spell_the_lich_king_summon_spirit_bomb : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_summon_spirit_bomb() : SpellScriptLoader("spell_the_lich_king_summon_spirit_bomb") { }
+
+ class spell_the_lich_king_summon_spirit_bomb_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_summon_spirit_bomb_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ GetHitUnit()->CastSpell((Unit*)NULL, uint32(GetEffectValue()), true);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_summon_spirit_bomb_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_summon_spirit_bomb_SpellScript();
+ }
+};
+
+class spell_the_lich_king_trigger_vile_spirit : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_trigger_vile_spirit() : SpellScriptLoader("spell_the_lich_king_trigger_vile_spirit") { }
+
+ class spell_the_lich_king_trigger_vile_spirit_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_trigger_vile_spirit_SpellScript);
+
+ void TeleportOutside()
+ {
+ Creature* target = GetHitCreature();
+ if (!target)
+ return;
+
+ Position dest;
+ Position offset;
+ TerenasSpawnHeroic.GetPositionOffsetTo(*target, offset);
+ GetCaster()->GetPosition(&dest);
+ dest.RelocateOffset(offset);
+ target->NearTeleportTo(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.GetOrientation());
+ }
+
+ void Register()
+ {
+ OnHit += SpellHitFn(spell_the_lich_king_trigger_vile_spirit_SpellScript::TeleportOutside);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_trigger_vile_spirit_SpellScript();
+ }
+};
+
+class spell_the_lich_king_jump : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_jump() : SpellScriptLoader("spell_the_lich_king_jump") { }
+
+ class spell_the_lich_king_jump_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_jump_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ GetHitUnit()->RemoveAurasDueToSpell(SPELL_RAISE_DEAD);
+ GetHitUnit()->CastSpell((Unit*)NULL, SPELL_JUMP_2, true);
+ if (Creature* creature = GetHitCreature())
+ creature->AI()->DoAction(ACTION_BREAK_FROSTMOURNE);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_jump_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_jump_SpellScript();
+ }
+};
+
+class spell_the_lich_king_jump_remove_aura : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_jump_remove_aura() : SpellScriptLoader("spell_the_lich_king_jump_remove_aura") { }
+
+ class spell_the_lich_king_jump_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_jump_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ GetHitUnit()->RemoveAurasDueToSpell(uint32(GetEffectValue()));
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_jump_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_jump_SpellScript();
+ }
+};
+
+class spell_the_lich_king_play_movie : public SpellScriptLoader
+{
+ public:
+ spell_the_lich_king_play_movie() : SpellScriptLoader("spell_the_lich_king_play_movie") { }
+
+ class spell_the_lich_king_play_movie_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_the_lich_king_play_movie_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sMovieStore.LookupEntry(MOVIE_FALL_OF_THE_LICH_KING))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ if (Player* player = GetHitPlayer())
+ player->SendMovieStart(MOVIE_FALL_OF_THE_LICH_KING);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_the_lich_king_play_movie_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_the_lich_king_play_movie_SpellScript();
+ }
+};
+
+class achievement_been_waiting_long_time : public AchievementCriteriaScript
+{
+ public:
+ achievement_been_waiting_long_time() : AchievementCriteriaScript("achievement_been_waiting_long_time") { }
+
+ bool OnCheck(Player* /*source*/, Unit* target)
+ {
+ if (!target)
+ return false;
+
+ return target->GetAI()->GetData(DATA_PLAGUE_STACK) >= 30;
+ }
+};
+
+class achievement_neck_deep_in_vile : public AchievementCriteriaScript
+{
+ public:
+ achievement_neck_deep_in_vile() : AchievementCriteriaScript("achievement_neck_deep_in_vile") { }
+
+ bool OnCheck(Player* /*source*/, Unit* target)
+ {
+ if (!target)
+ return false;
+
+ return !target->GetAI()->GetData(DATA_VILE);
+ }
+};
+
+void AddSC_boss_the_lich_king()
+{
+ new boss_the_lich_king();
+ new npc_tirion_fordring_tft();
+ new npc_shambling_horror_icc();
+ new npc_raging_spirit();
+ new npc_valkyr_shadowguard();
+ new npc_strangulate_vehicle();
+ new npc_terenas_menethil();
+ new npc_spirit_warden();
+ new npc_spirit_bomb();
+ new npc_broken_frostmourne();
+ new spell_the_lich_king_infest();
+ new spell_the_lich_king_necrotic_plague();
+ new spell_the_lich_king_necrotic_plague_jump();
+ new spell_the_lich_king_shadow_trap_visual();
+ new spell_the_lich_king_shadow_trap_periodic();
+ new spell_the_lich_king_quake();
+ new spell_the_lich_king_ice_burst_target_search();
+ new spell_the_lich_king_raging_spirit();
+ new spell_the_lich_king_defile();
+ new spell_the_lich_king_summon_into_air();
+ new spell_the_lich_king_soul_reaper();
+ new spell_the_lich_king_valkyr_target_search();
+ new spell_the_lich_king_eject_all_passengers();
+ new spell_the_lich_king_cast_back_to_caster();
+ new spell_the_lich_king_life_siphon();
+ new spell_the_lich_king_vile_spirits();
+ new spell_the_lich_king_vile_spirits_visual();
+ new spell_the_lich_king_vile_spirit_move_target_search();
+ new spell_the_lich_king_vile_spirit_damage_target_search();
+ new spell_the_lich_king_harvest_soul();
+ new spell_the_lich_king_lights_favor();
+ new spell_the_lich_king_soul_rip();
+ new spell_the_lich_king_restore_soul();
+ new spell_the_lich_king_in_frostmourne_room();
+ new spell_the_lich_king_summon_spirit_bomb();
+ new spell_the_lich_king_trigger_vile_spirit();
+ new spell_the_lich_king_jump();
+ new spell_the_lich_king_jump_remove_aura();
+ new spell_trigger_spell_from_caster("spell_the_lich_king_mass_resurrection", SPELL_MASS_RESURRECTION_REAL);
+ new spell_the_lich_king_play_movie();
+ new achievement_been_waiting_long_time();
+ new achievement_neck_deep_in_vile();
+}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h
index 26b2546fc50..ece966fcda5 100755
--- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h
+++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h
@@ -28,22 +28,30 @@ uint32 const EncounterCount = 13;
uint32 const WeeklyNPCs = 9;
uint32 const MaxHeroicAttempts = 50;
+// Defined in boss_valithria_dreamwalker.cpp
extern Position const ValithriaSpawnPos;
// Defined in boss_sindragosa.cpp
extern Position const SindragosaSpawnPos;
+// Defined in boss_the_lich_king.cpp
+extern Position const TerenasSpawn;
+extern Position const TerenasSpawnHeroic;
+extern Position const SpiritWardenSpawn;
// Shared spells used by more than one script
enum SharedSpells
{
- SPELL_BERSERK = 26662,
- SPELL_BERSERK2 = 47008,
+ SPELL_BERSERK = 26662,
+ SPELL_BERSERK2 = 47008,
// Deathbound Ward
- SPELL_STONEFORM = 70733,
+ SPELL_STONEFORM = 70733,
// Residue Rendezvous
- SPELL_ORANGE_BLIGHT_RESIDUE = 72144,
- SPELL_GREEN_BLIGHT_RESIDUE = 72145,
+ SPELL_ORANGE_BLIGHT_RESIDUE = 72144,
+ SPELL_GREEN_BLIGHT_RESIDUE = 72145,
+
+ // The Lich King
+ SPELL_FROSTMOURNE_TELEPORT_VISUAL = 73078,
};
enum TeleporterSpells
@@ -99,6 +107,9 @@ enum DataTypes
DATA_CAPTAIN_RUPERT = 34,
DATA_VALITHRIA_TRIGGER = 35,
DATA_VALITHRIA_LICH_KING = 36,
+ DATA_HIGHLORD_TIRION_FORDRING = 37,
+ DATA_ARTHAS_PLATFORM = 38,
+ DATA_TERENAS_MENETHIL = 39,
};
enum CreaturesIds
@@ -247,7 +258,23 @@ enum CreaturesIds
// The Lich King
NPC_THE_LICH_KING = 36597,
+ NPC_HIGHLORD_TIRION_FORDRING_LK = 38995,
+ NPC_TERENAS_MENETHIL_FROSTMOURNE = 36823,
+ NPC_SPIRIT_WARDEN = 36824,
+ NPC_TERENAS_MENETHIL_FROSTMOURNE_H = 39217,
+ NPC_SHAMBLING_HORROR = 37698,
+ NPC_DRUDGE_GHOUL = 37695,
+ NPC_ICE_SPHERE = 36633,
+ NPC_RAGING_SPIRIT = 36701,
+ NPC_DEFILE = 38757,
+ NPC_VALKYR_SHADOWGUARD = 36609,
+ NPC_VILE_SPIRIT = 37799,
+ NPC_WICKED_SPIRIT = 39190,
+ NPC_STRANGULATE_VEHICLE = 36598,
NPC_WORLD_TRIGGER = 22515,
+ NPC_WORLD_TRIGGER_INFINITE_AOI = 36171,
+ NPC_SPIRIT_BOMB = 39189,
+ NPC_FROSTMOURNE_TRIGGER = 38584,
};
enum GameObjectsIds
@@ -312,6 +339,20 @@ enum GameObjectsIds
GO_ICE_WALL = 202396,
GO_ICE_BLOCK = 201722,
GO_SIGIL_OF_THE_FROSTWING = 202181,
+
+ // The Lich King
+ GO_ARTHAS_PLATFORM = 202161,
+ GO_ARTHAS_PRECIPICE = 202078,
+ GO_DOODAD_ICECROWN_THRONEFROSTYWIND01 = 202188,
+ GO_DOODAD_ICECROWN_THRONEFROSTYEDGE01 = 202189,
+ GO_DOODAD_ICESHARD_STANDING02 = 202141,
+ GO_DOODAD_ICESHARD_STANDING01 = 202142,
+ GO_DOODAD_ICESHARD_STANDING03 = 202143,
+ GO_DOODAD_ICESHARD_STANDING04 = 202144,
+ GO_DOODAD_ICECROWN_SNOWEDGEWARNING01 = 202190,
+ GO_FROZEN_LAVAMAN = 202436,
+ GO_LAVAMAN_PILLARS_CHAINED = 202437,
+ GO_LAVAMAN_PILLARS_UNCHAINED = 202438,
};
enum AchievementCriteriaIds
@@ -351,9 +392,6 @@ enum AchievementCriteriaIds
enum SharedActions
{
- // Coldflame Traps
- ACTION_STOP_TRAPS = -377440,
-
// Festergut
ACTION_FESTERGUT_COMBAT = -366260,
ACTION_FESTERGUT_GAS = -366261,
@@ -374,6 +412,10 @@ enum SharedActions
// Sindragosa
ACTION_START_FROSTWYRM = -368530,
ACTION_TRIGGER_ASPHYXIATION = -368531,
+
+ // The Lich King
+ ACTION_RESTORE_LIGHT = -72262,
+ ACTION_FROSTMOURNE_INTRO = -36823,
};
enum WeekliesICC
@@ -399,6 +441,11 @@ enum WorldStatesICC
WORLDSTATE_ATTEMPTS_MAX = 4942,
};
+enum AreaIds
+{
+ AREA_THE_FROZEN_THRONE = 4859,
+};
+
class spell_trigger_spell_from_caster : public SpellScriptLoader
{
public:
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index d38af614bc0..90078e44d10 100755
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -23,6 +23,20 @@
#include "PoolMgr.h"
#include "icecrown_citadel.h"
+enum EventIds
+{
+ EVENT_QUAKE = 23437,
+ EVENT_SECOND_REMORSELESS_WINTER = 23507,
+ EVENT_TELEPORT_TO_FROSMOURNE = 23617,
+};
+
+enum TimedEvents
+{
+ EVENT_UPDATE_EXECUTION_TIME = 1,
+ EVENT_QUAKE_SHATTER = 2,
+ EVENT_REBUILD_PLATFORM = 3,
+};
+
DoorData const doorData[] =
{
{GO_LORD_MARROWGAR_S_ENTRANCE, DATA_LORD_MARROWGAR, DOOR_TYPE_ROOM, BOUNDARY_N },
@@ -116,6 +130,13 @@ class instance_icecrown_citadel : public InstanceMapScript
SpinestalkerGUID = 0;
RimefangGUID = 0;
TheLichKingGUID = 0;
+ HighlordTirionFordringGUID = 0;
+ TerenasMenethilGUID = 0;
+ ArthasPlatformGUID = 0;
+ ArthasPrecipiceGUID = 0;
+ FrozenThroneEdgeGUID = 0;
+ FrozenThroneWindGUID = 0;
+ FrozenThroneWarningGUID = 0;
FrostwyrmCount = 0;
SpinestalkerTrashCount = 0;
RimefangTrashCount = 0;
@@ -125,7 +146,6 @@ class instance_icecrown_citadel : public InstanceMapScript
IsOrbWhispererEligible = true;
ColdflameJetsState = NOT_STARTED;
BloodQuickeningState = NOT_STARTED;
- BloodQuickeningTimer = 0;
BloodQuickeningMinutes = 0;
}
@@ -269,6 +289,13 @@ class instance_icecrown_citadel : public InstanceMapScript
case NPC_THE_LICH_KING:
TheLichKingGUID = creature->GetGUID();
break;
+ case NPC_HIGHLORD_TIRION_FORDRING_LK:
+ HighlordTirionFordringGUID = creature->GetGUID();
+ break;
+ case NPC_TERENAS_MENETHIL_FROSTMOURNE:
+ case NPC_TERENAS_MENETHIL_FROSTMOURNE_H:
+ TerenasMenethilGUID = creature->GetGUID();
+ break;
default:
break;
}
@@ -424,6 +451,40 @@ class instance_icecrown_citadel : public InstanceMapScript
case GO_DRINK_ME:
PutricideTableGUID = go->GetGUID();
break;
+ case GO_ARTHAS_PLATFORM:
+ // this enables movement at The Frozen Throne, when printed this value is 0.000000f
+ // however, when represented as integer client will accept only this value
+ go->SetUInt32Value(GAMEOBJECT_PARENTROTATION, 5535469);
+ ArthasPlatformGUID = go->GetGUID();
+ break;
+ case GO_ARTHAS_PRECIPICE:
+ go->SetUInt32Value(GAMEOBJECT_PARENTROTATION, 4178312);
+ ArthasPrecipiceGUID = go->GetGUID();
+ break;
+ case GO_DOODAD_ICECROWN_THRONEFROSTYEDGE01:
+ FrozenThroneEdgeGUID = go->GetGUID();
+ break;
+ case GO_DOODAD_ICECROWN_THRONEFROSTYWIND01:
+ FrozenThroneWindGUID = go->GetGUID();
+ break;
+ case GO_DOODAD_ICECROWN_SNOWEDGEWARNING01:
+ FrozenThroneWarningGUID = go->GetGUID();
+ break;
+ case GO_FROZEN_LAVAMAN:
+ FrozenBolvarGUID = go->GetGUID();
+ if (GetBossState(DATA_THE_LICH_KING) == DONE)
+ go->SetRespawnTime(7 * DAY);
+ break;
+ case GO_LAVAMAN_PILLARS_CHAINED:
+ PillarsChainedGUID = go->GetGUID();
+ if (GetBossState(DATA_THE_LICH_KING) == DONE)
+ go->SetRespawnTime(7 * DAY);
+ break;
+ case GO_LAVAMAN_PILLARS_UNCHAINED:
+ PillarsUnchainedGUID = go->GetGUID();
+ if (GetBossState(DATA_THE_LICH_KING) == DONE)
+ go->SetRespawnTime(7 * DAY);
+ break;
default:
break;
}
@@ -541,6 +602,12 @@ class instance_icecrown_citadel : public InstanceMapScript
return RimefangGUID;
case DATA_THE_LICH_KING:
return TheLichKingGUID;
+ case DATA_HIGHLORD_TIRION_FORDRING:
+ return HighlordTirionFordringGUID;
+ case DATA_ARTHAS_PLATFORM:
+ return ArthasPlatformGUID;
+ case DATA_TERENAS_MENETHIL:
+ return TerenasMenethilGUID;
default:
break;
}
@@ -661,6 +728,14 @@ class instance_icecrown_citadel : public InstanceMapScript
}
break;
case DATA_THE_LICH_KING:
+ {
+ // set the platform as active object to dramatically increase visibility range
+ // note: "active" gameobjects do not block grid unloading
+ if (GameObject* precipice = instance->GetGameObject(ArthasPrecipiceGUID))
+ precipice->setActive(state == IN_PROGRESS);
+ if (GameObject* platform = instance->GetGameObject(ArthasPlatformGUID))
+ platform->setActive(state == IN_PROGRESS);
+
if (instance->IsHeroic())
{
if (state == FAIL && HeroicAttempts)
@@ -672,7 +747,18 @@ class instance_icecrown_citadel : public InstanceMapScript
theLichKing->DespawnOrUnsummon();
}
}
+
+ if (state == DONE)
+ {
+ if (GameObject* bolvar = instance->GetGameObject(FrozenBolvarGUID))
+ bolvar->SetRespawnTime(7 * DAY);
+ if (GameObject* pillars = instance->GetGameObject(PillarsChainedGUID))
+ pillars->SetRespawnTime(7 * DAY);
+ if (GameObject* pillars = instance->GetGameObject(PillarsUnchainedGUID))
+ pillars->SetRespawnTime(7 * DAY);
+ }
break;
+ }
default:
break;
}
@@ -798,13 +884,13 @@ class instance_icecrown_citadel : public InstanceMapScript
switch (data)
{
case IN_PROGRESS:
- BloodQuickeningTimer = 60000;
+ Events.ScheduleEvent(EVENT_UPDATE_EXECUTION_TIME, 60000);
BloodQuickeningMinutes = 30;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, BloodQuickeningMinutes);
break;
case DONE:
- BloodQuickeningTimer = 0;
+ Events.CancelEvent(EVENT_UPDATE_EXECUTION_TIME);
BloodQuickeningMinutes = 0;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 0);
break;
@@ -1050,32 +1136,103 @@ class instance_icecrown_citadel : public InstanceMapScript
void Update(uint32 diff)
{
- if (BloodQuickeningState == IN_PROGRESS)
+ if (BloodQuickeningState != IN_PROGRESS && GetBossState(DATA_THE_LICH_KING) != IN_PROGRESS)
+ return;
+
+ Events.Update(diff);
+
+ while (uint32 eventId = Events.ExecuteEvent())
{
- if (BloodQuickeningTimer <= diff)
+ switch (eventId)
{
- --BloodQuickeningMinutes;
- BloodQuickeningTimer = 60000;
- if (BloodQuickeningMinutes)
+ case EVENT_UPDATE_EXECUTION_TIME:
{
- DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
- DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, BloodQuickeningMinutes);
+ --BloodQuickeningMinutes;
+ if (BloodQuickeningMinutes)
+ {
+ Events.ScheduleEvent(EVENT_UPDATE_EXECUTION_TIME, 60000);
+ DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
+ DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, BloodQuickeningMinutes);
+ }
+ else
+ {
+ BloodQuickeningState = DONE;
+ DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 0);
+ if (Creature* bq = instance->GetCreature(BloodQueenLanaThelGUID))
+ bq->AI()->DoAction(ACTION_KILL_MINCHAR);
+ }
+ SaveToDB();
+ break;
}
- else
+ case EVENT_QUAKE_SHATTER:
{
- BloodQuickeningState = DONE;
- DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 0);
- if (Creature* bq = instance->GetCreature(BloodQueenLanaThelGUID))
- bq->AI()->DoAction(ACTION_KILL_MINCHAR);
+ if (GameObject* platform = instance->GetGameObject(ArthasPlatformGUID))
+ platform->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED);
+ if (GameObject* edge = instance->GetGameObject(FrozenThroneEdgeGUID))
+ edge->SetGoState(GO_STATE_ACTIVE);
+ if (GameObject* wind = instance->GetGameObject(FrozenThroneWindGUID))
+ wind->SetGoState(GO_STATE_READY);
+ if (GameObject* warning = instance->GetGameObject(FrozenThroneWarningGUID))
+ warning->SetGoState(GO_STATE_READY);
+ if (Creature* theLichKing = instance->GetCreature(TheLichKingGUID))
+ theLichKing->AI()->DoAction(ACTION_RESTORE_LIGHT);
+ break;
}
- SaveToDB();
+ case EVENT_REBUILD_PLATFORM:
+ if (GameObject* platform = instance->GetGameObject(ArthasPlatformGUID))
+ platform->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING);
+ if (GameObject* edge = instance->GetGameObject(FrozenThroneEdgeGUID))
+ edge->SetGoState(GO_STATE_READY);
+ if (GameObject* wind = instance->GetGameObject(FrozenThroneWindGUID))
+ wind->SetGoState(GO_STATE_ACTIVE);
+ break;
+ default:
+ break;
}
- else
- BloodQuickeningTimer -= diff;
+ }
+ }
+
+ void ProcessEvent(WorldObject* /*source*/, uint32 eventId)
+ {
+ switch (eventId)
+ {
+ case EVENT_QUAKE:
+ if (GameObject* warning = instance->GetGameObject(FrozenThroneWarningGUID))
+ warning->SetGoState(GO_STATE_ACTIVE);
+ Events.ScheduleEvent(EVENT_QUAKE_SHATTER, 5000);
+ break;
+ case EVENT_SECOND_REMORSELESS_WINTER:
+ if (GameObject* platform = instance->GetGameObject(ArthasPlatformGUID))
+ {
+ platform->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED);
+ Events.ScheduleEvent(EVENT_REBUILD_PLATFORM, 1500);
+ }
+ break;
+ case EVENT_TELEPORT_TO_FROSMOURNE: // Harvest Soul (normal mode)
+ if (Creature* terenas = instance->SummonCreature(NPC_TERENAS_MENETHIL_FROSTMOURNE, TerenasSpawn, NULL, 63000))
+ {
+ terenas->AI()->DoAction(ACTION_FROSTMOURNE_INTRO);
+ std::list<Creature*> triggers;
+ GetCreatureListWithEntryInGrid(triggers, terenas, NPC_WORLD_TRIGGER_INFINITE_AOI, 100.0f);
+ if (!triggers.empty())
+ {
+ triggers.sort(Trinity::ObjectDistanceOrderPred(terenas, false));
+ Unit* visual = triggers.front();
+ visual->CastSpell(visual, SPELL_FROSTMOURNE_TELEPORT_VISUAL, true);
+ }
+
+ if (Creature* warden = instance->SummonCreature(NPC_SPIRIT_WARDEN, SpiritWardenSpawn, NULL, 63000))
+ {
+ terenas->AI()->AttackStart(warden);
+ warden->AddThreat(terenas, 300000.0f);
+ }
+ }
+ break;
}
}
protected:
+ EventMap Events;
uint64 LadyDeathwisperElevatorGUID;
uint64 DeathbringerSaurfangGUID;
uint64 DeathbringerSaurfangDoorGUID;
@@ -1105,8 +1262,17 @@ class instance_icecrown_citadel : public InstanceMapScript
uint64 SpinestalkerGUID;
uint64 RimefangGUID;
uint64 TheLichKingGUID;
+ uint64 HighlordTirionFordringGUID;
+ uint64 TerenasMenethilGUID;
+ uint64 ArthasPlatformGUID;
+ uint64 ArthasPrecipiceGUID;
+ uint64 FrozenThroneEdgeGUID;
+ uint64 FrozenThroneWindGUID;
+ uint64 FrozenThroneWarningGUID;
+ uint64 FrozenBolvarGUID;
+ uint64 PillarsChainedGUID;
+ uint64 PillarsUnchainedGUID;
uint32 TeamInInstance;
- uint32 BloodQuickeningTimer;
uint32 ColdflameJetsState;
uint32 FrostwyrmCount;
uint32 SpinestalkerTrashCount;