aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/CreatureAIImpl.h2
-rw-r--r--src/server/game/Scripting/ScriptLoader.cpp2
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp8
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp798
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp1
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h19
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp54
7 files changed, 871 insertions, 13 deletions
diff --git a/src/server/game/AI/CreatureAIImpl.h b/src/server/game/AI/CreatureAIImpl.h
index 0f05d233891..57c2cfe9c82 100644
--- a/src/server/game/AI/CreatureAIImpl.h
+++ b/src/server/game/AI/CreatureAIImpl.h
@@ -325,6 +325,8 @@ class EventMap : private std::map<uint32, uint32>
void Update(uint32 time) { m_time += time; }
+ uint32 GetPhaseMask() const { return (m_phase >> 24) & 0xFF; }
+
void SetPhase(uint32 phase)
{
if (phase && phase < 9)
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 6cc04d14f3a..92b5840bdb7 100644
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -418,6 +418,7 @@ void AddSC_halls_of_reflection();
void AddSC_boss_falric();
void AddSC_boss_marwyn();
void AddSC_boss_lord_marrowgar(); // Icecrown Citadel
+void AddSC_boss_lady_deathwhisper();
void AddSC_instance_icecrown_citadel();
void AddSC_dalaran();
@@ -1082,6 +1083,7 @@ void AddNorthrendScripts()
AddSC_boss_falric();
AddSC_boss_marwyn();
AddSC_boss_lord_marrowgar(); // Icecrown Citadel
+ AddSC_boss_lady_deathwhisper();
AddSC_instance_icecrown_citadel();
AddSC_dalaran();
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index f4affd83f46..47121fd2ac7 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -2224,6 +2224,14 @@ void AuraEffect::TriggerSpell(Unit * target, Unit * caster) const
case 39857: triggerSpellId = 39856; break;
// Personalized Weather
case 46736: triggerSpellId = 46737; break;
+ // Mana Barrier - Lady Deathwhisper
+ case 70842:
+ {
+ int32 missingHealth = caster->GetMaxHealth() - caster->GetHealth();
+ caster->ModifyHealth(missingHealth);
+ caster->ModifyPower(POWER_MANA, -missingHealth);
+ return;
+ }
}
break;
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
new file mode 100644
index 00000000000..c882763e945
--- /dev/null
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
@@ -0,0 +1,798 @@
+/*
+ * Copyright (C) 2008-2010 Trinity <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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ScriptPCH.h"
+#include "icecrown_citadel.h"
+
+enum eScriptTexts
+{
+ SAY_INTRO_1 = -1631011,
+ SAY_INTRO_2 = -1631012,
+ SAY_INTRO_3 = -1631013,
+ SAY_INTRO_4 = -1631014,
+ SAY_INTRO_5 = -1631015,
+ SAY_INTRO_6 = -1631016,
+ SAY_INTRO_7 = -1631017,
+ SAY_AGGRO = -1631018,
+ SAY_PHASE_2 = -1631019,
+ SAY_PHASE_2_EMOTE = -1631020,
+ SAY_DOMINATE_MIND = -1631021,
+ SAY_DARK_EMPOWERMENT = -1631022,
+ SAY_DARK_TRANSFORMATION = -1631023,
+ SAY_ANIMATE_DEAD = -1631024,
+ SAY_KILL_1 = -1631025,
+ SAY_KILL_2 = -1631026,
+ SAY_BERSERK = -1631027,
+ SAY_DEATH = -1631028
+};
+
+enum eSpells
+{
+ // Lady Deathwhisper
+ SPELL_MANA_BARRIER = 70842,
+ SPELL_SHADOW_BOLT = 71254,
+ SPELL_DEATH_AND_DECAY = 71001,
+ SPELL_DOMINATE_MIND_H = 71289,
+ SPELL_FROSTBOLT = 71420,
+ SPELL_FROSTBOLT_VOLLEY = 72905,
+ SPELL_TOUCH_OF_INSIGNIFICANCE = 71204,
+ SPELL_SUMMON_SHADE = 71363,
+ SPELL_SHADOW_CHANNELING = 43897, // Prefight, during intro
+ SPELL_DARK_TRANSFORMATION_T = 70895,
+ SPELL_DARK_EMPOWERMENT_T = 70896,
+ SPELL_DARK_MARTYRDOM_T = 70897,
+
+ // Achievement
+ SPELL_FULL_HOUSE = 72827, // does not exist in dbc but still can be used for criteria check
+
+ // Both Adds
+ SPELL_TELEPORT_VISUAL = 41236,
+
+ // Fanatics
+ SPELL_DARK_TRANSFORMATION = 70900,
+ SPELL_NECROTIC_STRIKE = 70659,
+ SPELL_SHADOW_CLEAVE = 70670,
+ SPELL_VAMPIRIC_MIGHT = 70674,
+ SPELL_FANATIC_S_DETERMINATION = 71235,
+ SPELL_DARK_MARTYRDOM_FANATIC = 71236,
+
+ // Adherents
+ SPELL_DARK_EMPOWERMENT = 70901,
+ SPELL_FROST_FEVER = 67767,
+ SPELL_DEATHCHILL_BOLT = 70594,
+ SPELL_DEATHCHILL_BLAST = 70906,
+ SPELL_CURSE_OF_TORPOR = 71237,
+ SPELL_SHORUD_OF_THE_OCCULT = 70768,
+ SPELL_ADHERENT_S_DETERMINATION = 71234,
+ SPELL_DARK_MARTYRDOM_ADHERENT = 70903,
+
+ // Vengeful Shade
+ SPELL_VENGEFUL_BLAST = 71544,
+ SPELL_VENGEFUL_BLAST_PASSIVE = 71494
+};
+
+enum eEvents
+{
+ // Lady Deathwhisper
+ EVENT_INTRO_2 = 1,
+ EVENT_INTRO_3 = 2,
+ EVENT_INTRO_4 = 3,
+ EVENT_INTRO_5 = 4,
+ EVENT_INTRO_6 = 5,
+ EVENT_INTRO_7 = 6,
+ EVENT_INTRO_FINISH = 7,
+ EVENT_BERSERK = 8,
+ EVENT_DEATH_AND_DECAY = 9,
+ EVENT_DOMINATE_MIND_H = 10,
+
+ // Phase 1 only
+ EVENT_P1_SUMMON_WAVE = 11,
+ EVENT_P1_SHADOW_BOLT = 12,
+ EVENT_P1_EMPOWER_CULTIST = 13,
+ EVENT_P1_REANIMATE_CULTIST = 14,
+
+ // Phase 2 only
+ EVENT_P2_SUMMON_WAVE = 15,
+ EVENT_P2_FROSTBOLT = 16,
+ EVENT_P2_FROSTBOLT_VOLLEY = 17,
+ EVENT_P2_TOUCH_OF_INSIGNIFICANCE = 18,
+ EVENT_P2_SUMMON_SHADE = 19,
+
+ // Shared adds events
+ EVENT_CULTIST_DARK_MARTYRDOM = 20,
+
+ // Cult Fanatic
+ EVENT_FANATIC_NECROTIC_STRIKE = 21,
+ EVENT_FANATIC_SHADOW_CLEAVE = 22,
+ EVENT_FANATIC_VAMPIRIC_MIGHT = 23,
+
+ // Cult Adherent
+ EVENT_ADHERENT_FROST_FEVER = 24,
+ EVENT_ADHERENT_DEATHCHILL = 25,
+ EVENT_ADHERENT_CURSE_OF_TORPOR = 26,
+ EVENT_ADHERENT_SHORUD_OF_THE_OCCULT = 27
+};
+
+enum ePhases
+{
+ PHASE_ALL = 0,
+ PHASE_INTRO = 1,
+ PHASE_ONE = 2,
+ PHASE_TWO = 3,
+
+ PHASE_INTRO_MASK = 1 << PHASE_INTRO,
+ PHASE_ONE_MASK = 1 << PHASE_ONE,
+};
+
+static const uint32 uiAddEntries[2] = {NPC_CULT_FANATIC, NPC_CULT_ADHERENT};
+
+static const Position addSpawnPos[7] =
+{
+ {-578.7066f, 2154.167f, 51.01529f, 1.692969f}, // 1 Left Door 1 (Cult Fanatic)
+ {-598.9028f, 2155.005f, 51.01530f, 1.692969f}, // 2 Left Door 2 (Cult Adherent)
+ {-619.2864f, 2154.460f, 51.01530f, 1.692969f}, // 3 Left Door 3 (Cult Fanatic)
+ {-578.6996f, 2269.856f, 51.01529f, 4.590216f}, // 4 Right Door 1 (Cult Adherent)
+ {-598.9688f, 2269.264f, 51.01529f, 4.590216f}, // 5 Right Door 2 (Cult Fanatic)
+ {-619.4323f, 2268.523f, 51.01530f, 4.590216f}, // 6 Right Door 3 (Cult Adherent)
+ {-524.2480f, 2211.920f, 62.90960f, 3.141592f} // 7 Upper (Random Cultist)
+};
+
+class boss_lady_deathwhisper : public CreatureScript
+{
+ public:
+ boss_lady_deathwhisper() : CreatureScript("boss_lady_deathwhisper") { }
+
+ struct boss_lady_deathwhisperAI : public ScriptedAI
+ {
+ boss_lady_deathwhisperAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me)
+ {
+ pInstance = pCreature->GetInstanceScript();
+ bIntroDone = false;
+ uiDominateMindCount = RAID_MODE(0,1,1,3);
+ }
+
+ void Reset()
+ {
+ me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA));
+ me->SetLastManaUse(0xFFFFFFFF); // hacky, but no other way atm to prevent mana regen
+ events.Reset();
+ events.SetPhase(PHASE_ONE);
+ uiAddWaveCounter = 0;
+ uiNextVengefulShadeTarget = 0;
+ DoCast(me, SPELL_SHADOW_CHANNELING);
+ me->RemoveAurasDueToSpell(SPELL_BERSERK);
+ me->RemoveAurasDueToSpell(SPELL_MANA_BARRIER);
+ me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
+ me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false);
+ if (pInstance)
+ pInstance->SetData(DATA_LADY_DEATHWHISPER, NOT_STARTED);
+ }
+
+ void MoveInLineOfSight(Unit* who)
+ {
+ if (!bIntroDone && me->IsWithinDistInMap(who, 100.0f))
+ {
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ bIntroDone = true;
+ DoScriptText(SAY_INTRO_1, me);
+ events.SetPhase(PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_2, 11000, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_3, 21000, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_4, 31500, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_5, 39500, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_6, 48500, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_7, 58000, 0, PHASE_INTRO);
+ events.ScheduleEvent(EVENT_INTRO_FINISH, 76000, 0, PHASE_INTRO);
+ }
+ }
+
+ void AttackStart(Unit* victim)
+ {
+ if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
+ return;
+
+ if (victim && me->Attack(victim, true) && !(events.GetPhaseMask() & PHASE_ONE_MASK))
+ me->GetMotionMaster()->MoveChase(victim);
+ }
+
+ void EnterCombat(Unit* who)
+ {
+ events.Reset();
+ events.SetPhase(PHASE_ONE);
+ // phase-independent events
+ events.ScheduleEvent(EVENT_BERSERK, 600000);
+ events.ScheduleEvent(EVENT_DEATH_AND_DECAY, 10000);
+ // phase one only
+ events.ScheduleEvent(EVENT_P1_SUMMON_WAVE, 5000, 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_P1_SHADOW_BOLT, urand(5500, 6000), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_P1_EMPOWER_CULTIST, urand(20000, 30000), 0, PHASE_ONE);
+ if (getDifficulty() != RAID_DIFFICULTY_10MAN_NORMAL)
+ events.ScheduleEvent(EVENT_DOMINATE_MIND_H, 27000);
+
+ DoScriptText(SAY_AGGRO, me);
+ DoStartNoMovement(who);
+ me->RemoveAurasDueToSpell(SPELL_SHADOW_CHANNELING);
+ DoCast(me, SPELL_MANA_BARRIER, true);
+
+ if (pInstance)
+ pInstance->SetData(DATA_LADY_DEATHWHISPER, IN_PROGRESS);
+ }
+
+ void JustDied(Unit* killer)
+ {
+ DoScriptText(SAY_DEATH, me);
+
+ if (pInstance)
+ pInstance->SetData(DATA_LADY_DEATHWHISPER, DONE);
+
+ std::set<uint32> livingAddEntries;
+ // Full House achievement
+ for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
+ if (Unit* unit = ObjectAccessor::GetUnit(*me, *itr))
+ if (unit->isAlive() && unit->GetEntry() != NPC_VENGEFUL_SHADE)
+ livingAddEntries.insert(unit->GetEntry());
+
+ if (livingAddEntries.size() >= 5)
+ {
+ if (Player *player = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
+ {
+ if (Group* group = player->GetGroup())
+ {
+ for (GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* member = itr->getSource();
+ if (!member || !member->IsAtGroupRewardDistance(me))
+ continue;
+
+ if (member->isAlive()|| !member->GetCorpse())
+ member->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, SPELL_FULL_HOUSE, 0, me);
+ }
+ }
+ else
+ player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, SPELL_FULL_HOUSE, 0, me);
+ }
+ }
+
+ summons.DespawnAll();
+ }
+
+ void JustReachedHome()
+ {
+ if(pInstance)
+ pInstance->SetData(DATA_LADY_DEATHWHISPER, FAIL);
+
+ summons.DespawnAll();
+ }
+
+ void KilledUnit(Unit *victim)
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ DoScriptText(RAND(SAY_KILL_1, SAY_KILL_2), me);
+ }
+
+ void DamageTaken(Unit* /*damageDealer*/, uint32& uiDamage)
+ {
+ // phase transition
+ if (events.GetPhaseMask() & PHASE_ONE_MASK && uiDamage > me->GetPower(POWER_MANA))
+ {
+ DoScriptText(SAY_PHASE_2, me);
+ DoScriptText(SAY_PHASE_2_EMOTE, me);
+ DoStartMovement(me->getVictim());
+ me->SetPower(POWER_MANA, 0);
+ uiDamage -= me->GetPower(POWER_MANA);
+ me->RemoveAurasDueToSpell(SPELL_MANA_BARRIER);
+ events.SetPhase(PHASE_TWO);
+ events.ScheduleEvent(EVENT_P2_FROSTBOLT, urand(10000, 12000), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_P2_FROSTBOLT_VOLLEY, urand(19000, 21000), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_P2_TOUCH_OF_INSIGNIFICANCE, urand(6000, 9000), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_P2_SUMMON_SHADE, urand(12000, 15000), 0, PHASE_TWO);
+ // on heroic mode Lady Deathwhisper is immune to taunt effects in phase 2 and continues summoning adds
+ if (IsHeroic())
+ {
+ me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
+ me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
+ events.ScheduleEvent(EVENT_P2_SUMMON_WAVE, urand(30000, 60000), 0, PHASE_TWO);
+ }
+ }
+ }
+
+ void JustSummoned(Creature* summon)
+ {
+ summons.push_back(summon->GetGUID());
+ Unit* target = NULL;
+ if (summon->GetEntry() == NPC_VENGEFUL_SHADE)
+ {
+ target = ObjectAccessor::GetUnit(*me, uiNextVengefulShadeTarget); // Vengeful Shade
+ uiNextVengefulShadeTarget = 0;
+ }
+ else
+ target = SelectTarget(SELECT_TARGET_RANDOM); // Wave adds
+
+ summon->AI()->AttackStart(target); // CAN be NULL
+ if (summon->GetEntry() == NPC_REANIMATED_FANATIC)
+ summon->AI()->DoCast(summon, SPELL_FANATIC_S_DETERMINATION);
+ else if (summon->GetEntry() == NPC_REANIMATED_ADHERENT)
+ summon->AI()->DoCast(summon, SPELL_ADHERENT_S_DETERMINATION);
+ }
+
+ void SummonedCreatureDespawn(Creature* summon)
+ {
+ summons.remove(summon->GetGUID());
+ }
+
+ void UpdateAI(const uint32 diff)
+ {
+ if (!UpdateVictim() && !(events.GetPhaseMask() & PHASE_INTRO_MASK))
+ return;
+
+ events.Update(diff);
+
+ if (me->hasUnitState(UNIT_STAT_CASTING) && !(events.GetPhaseMask() & PHASE_INTRO_MASK))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_INTRO_2:
+ DoScriptText(SAY_INTRO_2, me);
+ break;
+ case EVENT_INTRO_3:
+ DoScriptText(SAY_INTRO_3, me);
+ break;
+ case EVENT_INTRO_4:
+ DoScriptText(SAY_INTRO_4, me);
+ break;
+ case EVENT_INTRO_5:
+ DoScriptText(SAY_INTRO_5, me);
+ break;
+ case EVENT_INTRO_6:
+ DoScriptText(SAY_INTRO_6, me);
+ break;
+ case EVENT_INTRO_7:
+ DoScriptText(SAY_INTRO_7, me);
+ break;
+ case EVENT_INTRO_FINISH:
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ break;
+ case EVENT_DEATH_AND_DECAY:
+ if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
+ DoCast(pTarget, SPELL_DEATH_AND_DECAY);
+ events.ScheduleEvent(EVENT_DEATH_AND_DECAY, urand(10000, 12000));
+ break;
+ case EVENT_DOMINATE_MIND_H:
+ DoScriptText(SAY_DOMINATE_MIND, me);
+ for (uint8 i = 0; i < uiDominateMindCount; i++)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_DOMINATE_MIND_H))
+ DoCast(target, SPELL_DOMINATE_MIND_H);
+ events.ScheduleEvent(EVENT_DOMINATE_MIND_H, urand(18000, 22000));
+ break;
+ case EVENT_P1_SUMMON_WAVE:
+ SummonWaveP1();
+ events.ScheduleEvent(EVENT_P1_SUMMON_WAVE, 60000, 0, PHASE_ONE);
+ break;
+ case EVENT_P1_SHADOW_BOLT:
+ if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM))
+ DoCast(pTarget, SPELL_SHADOW_BOLT);
+ events.ScheduleEvent(EVENT_P1_SHADOW_BOLT, urand(5000, 8000), 0, PHASE_ONE);
+ break;
+ case EVENT_P1_REANIMATE_CULTIST:
+ ReanimateCultist();
+ break;
+ case EVENT_P1_EMPOWER_CULTIST:
+ EmpowerCultist();
+ events.ScheduleEvent(EVENT_P1_EMPOWER_CULTIST, urand(18000, 25000));
+ break;
+ case EVENT_P2_FROSTBOLT:
+ DoCastVictim(SPELL_FROSTBOLT);
+ events.ScheduleEvent(EVENT_P2_FROSTBOLT, urand(10000, 11000), 0, PHASE_TWO);
+ break;
+ case EVENT_P2_FROSTBOLT_VOLLEY:
+ DoCastAOE(SPELL_FROSTBOLT_VOLLEY);
+ events.ScheduleEvent(EVENT_P2_FROSTBOLT_VOLLEY, urand(13000, 15000), 0, PHASE_TWO);
+ break;
+ case EVENT_P2_TOUCH_OF_INSIGNIFICANCE:
+ DoCastVictim(SPELL_TOUCH_OF_INSIGNIFICANCE);
+ events.ScheduleEvent(EVENT_P2_TOUCH_OF_INSIGNIFICANCE, urand(9000, 13000), 0, PHASE_TWO);
+ break;
+ case EVENT_P2_SUMMON_SHADE:
+ if (Unit* pShadeTarget = SelectUnit(SELECT_TARGET_RANDOM, 1))
+ {
+ uiNextVengefulShadeTarget = pShadeTarget->GetGUID();
+ DoCast(pShadeTarget, SPELL_SUMMON_SHADE);
+ }
+ events.ScheduleEvent(EVENT_P2_SUMMON_SHADE, urand(18000, 23000), 0, PHASE_TWO);
+ break;
+ case EVENT_P2_SUMMON_WAVE:
+ SummonWaveP2();
+ events.ScheduleEvent(EVENT_P2_SUMMON_WAVE, 60000, 0, PHASE_TWO);
+ break;
+ case EVENT_BERSERK:
+ DoCast(me, SPELL_BERSERK);
+ DoScriptText(SAY_BERSERK, me);
+ break;
+ }
+ }
+
+ // We should not melee attack when barrier is up
+ if (me->HasAura(SPELL_MANA_BARRIER))
+ return;
+
+ DoMeleeAttackIfReady();
+ }
+
+ // summoning function for first phase
+ void SummonWaveP1()
+ {
+ uint8 addIndex = uiAddWaveCounter & 1;
+ uint8 addIndexOther = addIndex ^ 1;
+ _SummonAdd(uiAddEntries[addIndex], addSpawnPos[addIndex*3]);
+ _SummonAdd(uiAddEntries[addIndexOther], addSpawnPos[addIndex*3+1]);
+ _SummonAdd(uiAddEntries[addIndex], addSpawnPos[addIndex*3+2]);
+ if (getDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL || getDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC)
+ {
+ _SummonAdd(uiAddEntries[addIndexOther], addSpawnPos[addIndexOther*3]);
+ _SummonAdd(uiAddEntries[addIndex], addSpawnPos[addIndexOther*3+1]);
+ _SummonAdd(uiAddEntries[addIndexOther], addSpawnPos[addIndexOther*3+2]);
+ _SummonAdd(uiAddEntries[urand(0,1)], addSpawnPos[6]);
+ }
+ ++uiAddWaveCounter;
+ }
+
+ // summoning function for second phase
+ void SummonWaveP2()
+ {
+ if (getDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC)
+ {
+ uint8 addIndex = uiAddWaveCounter & 1;
+ _SummonAdd(uiAddEntries[addIndex], addSpawnPos[addIndex*3]);
+ _SummonAdd(uiAddEntries[addIndex ^ 1], addSpawnPos[addIndex*3+1]);
+ _SummonAdd(uiAddEntries[addIndex], addSpawnPos[addIndex*3+2]);
+ }
+ else
+ _SummonAdd(uiAddEntries[urand(0,1)], addSpawnPos[6]);
+ ++uiAddWaveCounter;
+ }
+
+ // helper for summoning wave mobs
+ void _SummonAdd(uint32 entry, const Position& pos)
+ {
+ if (TempSummon* summon = me->SummonCreature(entry, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000))
+ summon->AI()->DoCast(summon, SPELL_TELEPORT_VISUAL);
+ }
+
+ void AddToReanimationQueue(Unit* summon)
+ {
+ reanimationQueue.push_back(summon->GetGUID());
+ events.ScheduleEvent(EVENT_P1_REANIMATE_CULTIST, 3000, 0, PHASE_ONE);
+ }
+
+ void ReanimateCultist()
+ {
+ if (reanimationQueue.empty())
+ return;
+
+ uint64 cultistGUID = reanimationQueue.front();
+ Creature* cultist = ObjectAccessor::GetCreature(*me, cultistGUID);
+ reanimationQueue.pop_front();
+ if (!cultist)
+ return;
+
+ DoScriptText(SAY_ANIMATE_DEAD, me);
+ DoCast(cultist, SPELL_DARK_MARTYRDOM_T);
+ }
+
+ void SpellHitTarget(Unit * target, const SpellEntry * spell)
+ {
+ if (spell->Id == SPELL_DARK_MARTYRDOM_T)
+ {
+ Position pos;
+ target->GetPosition(&pos);
+ if (target->GetEntry() == NPC_CULT_FANATIC)
+ me->SummonCreature(NPC_REANIMATED_FANATIC, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
+ else
+ me->SummonCreature(NPC_REANIMATED_ADHERENT, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
+
+ if (TempSummon* summon = target->ToTempSummon())
+ summon->UnSummon();
+ }
+ }
+
+ void EmpowerCultist()
+ {
+ if (summons.empty())
+ return;
+
+ std::list<Creature*> tmpList;
+ for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
+ if (Creature* cre = ObjectAccessor::GetCreature(*me, *itr))
+ if (cre->isAlive() && (cre->GetEntry() == NPC_CULT_FANATIC || cre->GetEntry() == NPC_CULT_ADHERENT))
+ tmpList.push_back(cre);
+
+ // noone to empower
+ if (tmpList.empty())
+ return;
+
+ // select random cultist
+ std::list<Creature*>::iterator cultistItr = tmpList.begin();
+ std::advance(cultistItr, urand(0, tmpList.size()-1));
+
+ Creature* cultist = *cultistItr;
+ DoCast(cultist, cultist->GetEntry() == NPC_CULT_FANATIC ? SPELL_DARK_TRANSFORMATION_T : SPELL_DARK_EMPOWERMENT_T, true);
+ DoScriptText(cultist->GetEntry() == NPC_CULT_FANATIC ? SAY_DARK_TRANSFORMATION : SAY_DARK_EMPOWERMENT, me);
+ }
+
+ private:
+ EventMap events;
+ InstanceScript* pInstance;
+ bool bIntroDone;
+ uint32 uiAddWaveCounter;
+ uint64 uiNextVengefulShadeTarget;
+ SummonList summons;
+ std::deque<uint64> reanimationQueue;
+ uint8 uiDominateMindCount;
+ };
+
+ CreatureAI* GetAI(Creature* pCreature) const
+ {
+ return new boss_lady_deathwhisperAI(pCreature);
+ }
+};
+
+class npc_cult_fanatic : public CreatureScript
+{
+ public:
+ npc_cult_fanatic() : CreatureScript("npc_cult_fanatic") { }
+
+ struct npc_cult_fanaticAI : public ScriptedAI
+ {
+ npc_cult_fanaticAI(Creature* pCreature) : ScriptedAI(pCreature) {}
+
+ void Reset()
+ {
+ events.Reset();
+ events.ScheduleEvent(EVENT_FANATIC_NECROTIC_STRIKE, urand(10000, 12000));
+ events.ScheduleEvent(EVENT_FANATIC_SHADOW_CLEAVE, urand(14000, 16000));
+ events.ScheduleEvent(EVENT_FANATIC_VAMPIRIC_MIGHT, urand(20000, 27000));
+ if (me->GetEntry() == NPC_CULT_FANATIC)
+ events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(18000, 32000));
+ }
+
+ void SpellHit(Unit* /*caster*/, const SpellEntry * spell)
+ {
+ if (spell->Id == SPELL_DARK_TRANSFORMATION)
+ me->UpdateEntry(NPC_DEFORMED_FANATIC);
+ else if (spell->Id == SPELL_DARK_TRANSFORMATION_T)
+ {
+ events.CancelEvent(EVENT_CULTIST_DARK_MARTYRDOM);
+ me->InterruptNonMeleeSpells(true);
+ DoCast(me, SPELL_DARK_TRANSFORMATION);
+ }
+ }
+
+ void UpdateAI(const uint32 diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ if (me->hasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_FANATIC_NECROTIC_STRIKE:
+ DoCastVictim(SPELL_NECROTIC_STRIKE);
+ events.ScheduleEvent(SPELL_NECROTIC_STRIKE, urand(11000, 13000));
+ break;
+ case EVENT_FANATIC_SHADOW_CLEAVE:
+ DoCastVictim(SPELL_SHADOW_CLEAVE);
+ events.ScheduleEvent(EVENT_FANATIC_SHADOW_CLEAVE, urand(9500, 11000));
+ break;
+ case EVENT_FANATIC_VAMPIRIC_MIGHT:
+ DoCast(me, SPELL_VAMPIRIC_MIGHT);
+ events.ScheduleEvent(EVENT_FANATIC_VAMPIRIC_MIGHT, urand(20000, 27000));
+ break;
+ case EVENT_CULTIST_DARK_MARTYRDOM:
+ DoCast(me, SPELL_DARK_MARTYRDOM_FANATIC);
+ events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(16000, 21000));
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap events;
+ };
+
+ CreatureAI* GetAI(Creature* pCreature) const
+ {
+ return new npc_cult_fanaticAI(pCreature);
+ }
+};
+
+class npc_cult_adherent : public CreatureScript
+{
+ public:
+ npc_cult_adherent() : CreatureScript("npc_cult_adherent") { }
+
+ struct npc_cult_adherentAI : public ScriptedAI
+ {
+ npc_cult_adherentAI(Creature* pCreature) : ScriptedAI(pCreature) {}
+
+ void Reset()
+ {
+ events.Reset();
+ events.ScheduleEvent(EVENT_ADHERENT_FROST_FEVER, urand(10000, 12000));
+ events.ScheduleEvent(EVENT_ADHERENT_DEATHCHILL, urand(14000, 16000));
+ events.ScheduleEvent(EVENT_ADHERENT_CURSE_OF_TORPOR, urand(14000, 16000));
+ events.ScheduleEvent(EVENT_ADHERENT_SHORUD_OF_THE_OCCULT, urand(32000, 39000));
+ if (me->GetEntry() == NPC_CULT_ADHERENT)
+ events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(18000, 32000));
+ }
+
+ void SpellHit(Unit* /*caster*/, const SpellEntry * spell)
+ {
+ if (spell->Id == SPELL_DARK_EMPOWERMENT)
+ me->UpdateEntry(NPC_EMPOWERED_ADHERENT);
+ else if (spell->Id == SPELL_DARK_EMPOWERMENT_T)
+ {
+ events.CancelEvent(EVENT_CULTIST_DARK_MARTYRDOM);
+ me->InterruptNonMeleeSpells(true);
+ DoCast(me, SPELL_DARK_EMPOWERMENT);
+ }
+ }
+
+ void UpdateAI(const uint32 diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ if (me->hasUnitState(UNIT_STAT_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_ADHERENT_FROST_FEVER:
+ DoCastVictim(SPELL_FROST_FEVER);
+ events.ScheduleEvent(EVENT_ADHERENT_FROST_FEVER, urand(9000, 13000));
+ break;
+ case EVENT_ADHERENT_DEATHCHILL:
+ if (me->GetEntry() == NPC_EMPOWERED_ADHERENT)
+ DoCastVictim(SPELL_DEATHCHILL_BLAST);
+ else
+ DoCastVictim(SPELL_DEATHCHILL_BOLT);
+ events.ScheduleEvent(EVENT_ADHERENT_DEATHCHILL, urand(9000, 13000));
+ break;
+ case EVENT_ADHERENT_CURSE_OF_TORPOR:
+ if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 1))
+ DoCast(pTarget, SPELL_CURSE_OF_TORPOR);
+ events.ScheduleEvent(EVENT_ADHERENT_CURSE_OF_TORPOR, urand(9000, 13000));
+ break;
+ case EVENT_ADHERENT_SHORUD_OF_THE_OCCULT:
+ DoCast(me, SPELL_SHORUD_OF_THE_OCCULT);
+ events.ScheduleEvent(EVENT_ADHERENT_SHORUD_OF_THE_OCCULT, urand(27000, 32000));
+ break;
+ case EVENT_CULTIST_DARK_MARTYRDOM:
+ DoCast(me, SPELL_DARK_MARTYRDOM_ADHERENT);
+ events.ScheduleEvent(EVENT_CULTIST_DARK_MARTYRDOM, urand(16000, 21000));
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ EventMap events;
+ };
+
+ CreatureAI* GetAI(Creature* pCreature) const
+ {
+ return new npc_cult_adherentAI(pCreature);
+ }
+};
+
+class npc_vengeful_shade : public CreatureScript
+{
+ public:
+ npc_vengeful_shade() : CreatureScript("npc_vengeful_shade") { }
+
+ struct npc_vengeful_shadeAI : public ScriptedAI
+ {
+ npc_vengeful_shadeAI(Creature* pCreature) : ScriptedAI(pCreature)
+ {
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ }
+
+ void Reset()
+ {
+ me->AddAura(SPELL_VENGEFUL_BLAST_PASSIVE, me);
+ }
+
+ void SpellHitTarget(Unit * /*pTarget*/, const SpellEntry * spell)
+ {
+ if (spell->Id == SPELL_VENGEFUL_BLAST)
+ me->Kill(me);
+ }
+ };
+
+ CreatureAI* GetAI(Creature* pCreature) const
+ {
+ return new npc_vengeful_shadeAI(pCreature);
+ }
+};
+
+class spell_cultist_dark_martyrdom : public SpellHandlerScript
+{
+ public:
+ spell_cultist_dark_martyrdom() : SpellHandlerScript("spell_cultist_dark_martyrdom") { }
+
+ class spell_cultist_dark_martyrdom_SpellScript : public SpellScript
+ {
+ bool Validate(SpellEntry const* spellEntry)
+ {
+ if (uint32 scriptId = sObjectMgr.GetScriptId("boss_lady_deathwhisper"))
+ if (CreatureInfo const* creInfo = ObjectMgr::GetCreatureTemplate(NPC_LADY_DEATHWHISPER))
+ if (creInfo->ScriptID == scriptId)
+ return true;
+
+ return false;
+ }
+
+ void HandleEffect(SpellEffIndex effIndex)
+ {
+ if (GetCaster()->isSummon())
+ if (Unit* owner = GetCaster()->ToTempSummon()->GetSummoner())
+ if (owner->GetEntry() == NPC_LADY_DEATHWHISPER)
+ CAST_AI(boss_lady_deathwhisper::boss_lady_deathwhisperAI, owner->ToCreature()->AI())->AddToReanimationQueue(GetCaster());
+
+ GetCaster()->Kill(GetCaster());
+ GetCaster()->SetDisplayId(GetCaster()->GetEntry() == NPC_CULT_FANATIC ? 38009 : 38010);
+ }
+
+ void Register()
+ {
+ EffectHandlers += EffectHandlerFn(spell_cultist_dark_martyrdom_SpellScript::HandleEffect, EFFECT_2, SPELL_EFFECT_FORCE_DESELECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_cultist_dark_martyrdom_SpellScript();
+ }
+};
+
+void AddSC_boss_lady_deathwhisper()
+{
+ new boss_lady_deathwhisper();
+ new npc_cult_fanatic();
+ new npc_cult_adherent();
+ new npc_vengeful_shade();
+ new spell_cultist_dark_martyrdom();
+}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
index 23043630a2b..50a8214a35e 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
@@ -91,6 +91,7 @@ class boss_lord_marrowgar : public CreatureScript
{
me->SetSpeed(MOVE_RUN, fBaseSpeed, true);
me->RemoveAurasDueToSpell(SPELL_BONE_STORM);
+ me->RemoveAurasDueToSpell(SPELL_BERSERK);
events.Reset();
events.ScheduleEvent(EVENT_ENABLE_BONE_SLICE, 10000);
events.ScheduleEvent(EVENT_BONE_SPIKE_GRAVEYARD, urand(20000, 30000));
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h
index a85a6fcd3ac..3ef6729ace3 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h
+++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h
@@ -48,14 +48,25 @@ enum eCreatures
{
NPC_LORD_MARROWGAR = 36612,
NPC_COLDFLAME = 36672,
- NPC_BONE_SPIKE = 36619
+ NPC_BONE_SPIKE = 36619,
+
+ NPC_LADY_DEATHWHISPER = 36855,
+ NPC_CULT_FANATIC = 37890,
+ NPC_DEFORMED_FANATIC = 38135,
+ NPC_REANIMATED_FANATIC = 38009,
+ NPC_CULT_ADHERENT = 37949,
+ NPC_EMPOWERED_ADHERENT = 38136,
+ NPC_REANIMATED_ADHERENT = 38010,
+ NPC_VENGEFUL_SHADE = 38222,
};
enum eGameobjects
{
- DOODAD_ICECROWN_ICEWALL02 = 201910,
- ICEWALL = 201911,
- LORD_MARROWGAR_S_ENTRANCE = 201857
+ DOODAD_ICECROWN_ICEWALL02 = 201910,
+ ICEWALL = 201911,
+ LORD_MARROWGAR_S_ENTRANCE = 201857,
+ ORATORY_OF_THE_DAMNED_ENTRANCE = 201563,
+ LADY_DEATHWHISPER_ELEVATOR = 202220,
};
enum eAchievementCriteria
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index b25e56c1125..8ab824442b2 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -26,11 +26,13 @@ class instance_icecrown_citadel : public InstanceMapScript
struct instance_icecrown_citadel_InstanceMapScript : public InstanceScript
{
- instance_icecrown_citadel_InstanceMapScript(Map* pMap) : InstanceScript(pMap)
+ instance_icecrown_citadel_InstanceMapScript(InstanceMap* pMap) : InstanceScript(pMap)
{
memset(uiEncounterState, 0, MAX_ENCOUNTER*sizeof(uint32));
memset(uiIcewall, 0, 2*sizeof(uint32));
uiMarrowgarEntrance = 0;
+ uiLadyDeathwisperElevator = 0;
+ uiOratoryDoor = 0;
isBonedEligible = true;
}
@@ -49,19 +51,32 @@ class instance_icecrown_citadel : public InstanceMapScript
{
case DOODAD_ICECROWN_ICEWALL02:
uiIcewall[0] = pGo->GetGUID();
- if (GetData(DATA_LORD_MARROWGAR) == NOT_STARTED)
- HandleGameObject(uiIcewall[0], false, pGo);
+ if (GetData(DATA_LORD_MARROWGAR) == DONE)
+ HandleGameObject(uiIcewall[0], true, pGo);
break;
case ICEWALL:
uiIcewall[1] = pGo->GetGUID();
- if (GetData(DATA_LORD_MARROWGAR) == NOT_STARTED)
- HandleGameObject(uiIcewall[1], false, pGo);
+ if (GetData(DATA_LORD_MARROWGAR) == DONE)
+ HandleGameObject(uiIcewall[1], true, pGo);
break;
case LORD_MARROWGAR_S_ENTRANCE:
uiMarrowgarEntrance = pGo->GetGUID();
if (GetData(DATA_LORD_MARROWGAR) == DONE)
HandleGameObject(uiMarrowgarEntrance, true, pGo);
break;
+ case ORATORY_OF_THE_DAMNED_ENTRANCE:
+ uiOratoryDoor = pGo->GetGUID();
+ if (GetData(DATA_LADY_DEATHWHISPER) == DONE)
+ HandleGameObject(uiOratoryDoor, true, pGo);
+ break;
+ case LADY_DEATHWHISPER_ELEVATOR:
+ uiLadyDeathwisperElevator = pGo->GetGUID();
+ if (GetData(DATA_LADY_DEATHWHISPER) == DONE)
+ {
+ pGo->SetUInt32Value(GAMEOBJECT_LEVEL, 0);
+ pGo->SetGoState(GO_STATE_READY);
+ }
+ break;
}
}
@@ -111,6 +126,25 @@ class instance_icecrown_citadel : public InstanceMapScript
}
break;
case DATA_LADY_DEATHWHISPER:
+ uiEncounterState[type] = data;
+ switch(data)
+ {
+ case DONE:
+ HandleGameObject(uiOratoryDoor, true);
+ if (GameObject* elevator = instance->GetGameObject(uiLadyDeathwisperElevator))
+ {
+ elevator->SetUInt32Value(GAMEOBJECT_LEVEL, 0);
+ elevator->SetGoState(GO_STATE_READY);
+ }
+ break;
+ case IN_PROGRESS:
+ HandleGameObject(uiOratoryDoor, false);
+ break;
+ case NOT_STARTED:
+ HandleGameObject(uiOratoryDoor, true);
+ break;
+ }
+ break;
case DATA_GUNSHIP_EVENT:
case DATA_DEATHBRINGER_SAURFANG:
case DATA_FESTERGUT:
@@ -126,7 +160,11 @@ class instance_icecrown_citadel : public InstanceMapScript
case COMMAND_FAIL_BONED:
isBonedEligible = data ? true : false;
+ break;
}
+
+ if (data == DONE && type < MAX_ENCOUNTER)
+ SaveToDB();
}
bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/)
@@ -152,8 +190,6 @@ class instance_icecrown_citadel : public InstanceMapScript
for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
saveStream << uiEncounterState[i] << " ";
- saveStream << isBonedEligible;
-
OUT_SAVE_INST_DATA_COMPLETE;
return saveStream.str();
}
@@ -183,8 +219,6 @@ class instance_icecrown_citadel : public InstanceMapScript
tmpState = NOT_STARTED;
uiEncounterState[i] = tmpState;
}
-
- loadStream >> isBonedEligible;
} else OUT_LOAD_INST_DATA_FAIL;
OUT_LOAD_INST_DATA_COMPLETE;
@@ -194,6 +228,8 @@ class instance_icecrown_citadel : public InstanceMapScript
uint32 uiEncounterState[MAX_ENCOUNTER];
uint64 uiIcewall[2];
uint64 uiMarrowgarEntrance;
+ uint64 uiLadyDeathwisperElevator;
+ uint64 uiOratoryDoor;
bool isBonedEligible;
};