aboutsummaryrefslogtreecommitdiff
path: root/src/server/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/scripts')
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp8
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp16
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp294
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp6
-rw-r--r--src/server/scripts/Northrend/CMakeLists.txt1
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp4
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp1740
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp138
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h53
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp45
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp9
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp83
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp299
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/oculus.h13
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp21
-rw-r--r--src/server/scripts/Northrend/wintergrasp.cpp39
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp22
-rw-r--r--src/server/scripts/World/go_scripts.cpp168
19 files changed, 2627 insertions, 340 deletions
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index dd40a590647..7c7adafdfeb 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -476,12 +476,12 @@ public:
}
else if (map->IsDungeon())
{
- Map* map = target->GetMap();
+ Map* destMap = target->GetMap();
- if (map->Instanceable() && map->GetInstanceId() != map->GetInstanceId())
+ if (destMap->Instanceable() && destMap->GetInstanceId() != map->GetInstanceId())
target->UnbindInstance(map->GetInstanceId(), target->GetDungeonDifficulty(), true);
- // we are in instance, and can summon only player in our group with us as lead
+ // we are in an instance, and can only summon players in our group with us as leader
if (!handler->GetSession()->GetPlayer()->GetGroup() || !target->GetGroup() ||
(target->GetGroup()->GetLeaderGUID() != handler->GetSession()->GetPlayer()->GetGUID()) ||
(handler->GetSession()->GetPlayer()->GetGroup()->GetLeaderGUID() != handler->GetSession()->GetPlayer()->GetGUID()))
@@ -2567,7 +2567,7 @@ public:
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
// not let dismiss dead pet
- if (pet && pet->isAlive())
+ if (pet->isAlive())
player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
}
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
index 61df92c18e2..8cf964808d3 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
@@ -27,6 +27,7 @@ EndScriptData */
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "scarlet_monastery.h"
+#include "LFGMgr.h"
//this texts are already used by 3975 and 3976
enum Says
@@ -562,6 +563,13 @@ public:
CAST_AI(mob_wisp_invis::mob_wisp_invisAI, wisp->AI())->SetType(4);
if (instance)
instance->SetData(DATA_HORSEMAN_EVENT, DONE);
+
+ Map::PlayerList const& players = me->GetMap()->GetPlayers();
+ if (!players.isEmpty())
+ for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
+ if (Player* player = i->getSource())
+ if (player->IsAtGroupRewardDistance(me))
+ sLFGMgr->RewardDungeonDoneFor(285, player);
}
void SpellHit(Unit* caster, const SpellInfo* spell)
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
index ebab2cb99a9..b34a990d39d 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
@@ -44,7 +44,7 @@ enum event
{
EVENT_SPAWN = 1,
EVENT_MINI,
- EVENT_ROOT,
+ EVENT_ROOT,
EVENT_BASH,
EVENT_BOLT,
EVENT_AURA
@@ -58,13 +58,13 @@ public:
struct boss_amanitarAI : public BossAI
{
boss_amanitarAI(Creature* creature) : BossAI(creature, DATA_AMANITAR) { }
-
+
void Reset()
{
_Reset();
-
+
me->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE);
- me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true);
+ me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true);
summons.DespawnAll();
if (instance)
@@ -119,7 +119,7 @@ public:
{
trigger->DisappearAndDie();
}
- else
+ else
{
u = 1 - u;
trigger->DisappearAndDie();
@@ -143,7 +143,7 @@ public:
{
switch (eventId)
{
- case EVENT_SPAWN:
+ case EVENT_SPAWN:
SpawnAdds();
events.ScheduleEvent(EVENT_SPAWN, 20*IN_MILLISECONDS);
break;
@@ -151,7 +151,7 @@ public:
DoCast(SPELL_MINI);
events.ScheduleEvent(EVENT_MINI, urand(25,30)*IN_MILLISECONDS);
break;
- case EVENT_ROOT:
+ case EVENT_ROOT:
DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_ENTANGLING_ROOTS,true);
events.ScheduleEvent(EVENT_ROOT, urand(10,15)*IN_MILLISECONDS);
break;
@@ -192,7 +192,7 @@ public:
{
events.Reset();
events.ScheduleEvent(EVENT_AURA, 1*IN_MILLISECONDS);
-
+
me->SetDisplayId(me->GetCreatureTemplate()->Modelid2);
DoCast(SPELL_PUTRID_MUSHROOM);
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
index 9274a016e2f..fe86f54ae15 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
@@ -17,6 +17,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellScript.h"
#include "ahnkahet.h"
enum Yells
@@ -30,22 +31,31 @@ enum Yells
enum Spells
{
- SPELL_BROOD_PLAGUE = 56130,
- H_SPELL_BROOD_PLAGUE = 59467,
- H_SPELL_BROOD_RAGE = 59465,
- SPELL_ENRAGE = 26662, // Enraged if too far away from home
- SPELL_SUMMON_SWARMERS = 56119, //2x 30178 -- 2x every 10secs
- SPELL_SUMMON_SWARM_GUARD = 56120, //1x 30176 -- every 25secs
+ SPELL_BROOD_PLAGUE = 56130,
+ H_SPELL_BROOD_RAGE = 59465,
+ SPELL_ENRAGE = 26662, // Enraged if too far away from home
+ SPELL_SUMMON_SWARMERS = 56119, // 2x 30178 -- 2x every 10secs
+ SPELL_SUMMON_SWARM_GUARD = 56120, // 1x 30176 -- every 25%
+ // Spells Adds
+ SPELL_SPRINT = 56354,
+ SPELL_GUARDIAN_AURA = 56151
};
enum Creatures
{
- MOB_AHNKAHAR_SWARMER = 30178,
- MOB_AHNKAHAR_GUARDIAN_ENTRY = 30176
+ NPC_AHNKAHAR_SWARMER = 30178,
+ NPC_AHNKAHAR_GUARDIAN = 30176
};
-#define ACTION_AHNKAHAR_GUARDIAN_DEAD 1
-#define DATA_RESPECT_YOUR_ELDERS 2
+enum Events
+{
+ EVENT_PLAGUE = 1,
+ EVENT_RAGE,
+ EVENT_SUMMON_SWARMER,
+ EVENT_CHECK_ENRAGE,
+ EVENT_SPRINT,
+ DATA_RESPECT_YOUR_ELDERS
+};
class boss_elder_nadox : public CreatureScript
{
@@ -54,32 +64,24 @@ class boss_elder_nadox : public CreatureScript
struct boss_elder_nadoxAI : public ScriptedAI
{
- boss_elder_nadoxAI(Creature* creature) : ScriptedAI(creature)
+ boss_elder_nadoxAI(Creature* creature) : ScriptedAI(creature), summons(me)
{
instance = creature->GetInstanceScript();
}
- uint32 uiPlagueTimer;
- uint32 uiRagueTimer;
-
- uint32 uiSwarmerSpawnTimer;
- uint32 uiGuardSpawnTimer;
- uint32 uiEnrageTimer;
-
- bool bGuardSpawned;
- bool respectYourElders;
-
+ bool GuardianDied;
+ uint8 AmountHealthModifier;
InstanceScript* instance;
+ SummonList summons;
+ EventMap events;
void Reset()
{
- uiPlagueTimer = 13000;
- uiRagueTimer = 20000;
- uiSwarmerSpawnTimer = 10000;
- uiGuardSpawnTimer = 25000;
- uiEnrageTimer = 5000;
- bGuardSpawned = false;
- respectYourElders = true;
+ events.Reset();
+ summons.DespawnAll();
+
+ AmountHealthModifier = 1;
+ GuardianDied = false;
if (instance)
instance->SetData(DATA_ELDER_NADOX_EVENT, NOT_STARTED);
@@ -91,98 +93,95 @@ class boss_elder_nadox : public CreatureScript
if (instance)
instance->SetData(DATA_ELDER_NADOX_EVENT, IN_PROGRESS);
- }
- void KilledUnit(Unit* /*who*/)
- {
- Talk(SAY_SLAY);
+ events.ScheduleEvent(EVENT_PLAGUE, 13*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10*IN_MILLISECONDS);
+
+ if (IsHeroic())
+ {
+ events.ScheduleEvent(EVENT_RAGE, 12*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5*IN_MILLISECONDS);
+ }
}
- void JustDied(Unit* /*killer*/)
+ void JustSummoned(Creature* summon)
{
- Talk(SAY_DEATH);
-
- if (instance)
- instance->SetData(DATA_ELDER_NADOX_EVENT, DONE);
+ summons.Summon(summon);
+ summon->AI()->DoZoneInCombat();
}
- void DoAction(int32 const action)
+ void SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
{
- if (action == ACTION_AHNKAHAR_GUARDIAN_DEAD)
- respectYourElders = false;
+ if (summon->GetEntry() == NPC_AHNKAHAR_GUARDIAN)
+ GuardianDied = true;
}
uint32 GetData(uint32 type)
{
if (type == DATA_RESPECT_YOUR_ELDERS)
- return respectYourElders ? 1 : 0;
+ return !GuardianDied ? 1 : 0;
return 0;
}
+ void KilledUnit(Unit* /*victim*/)
+ {
+ Talk(SAY_SLAY);
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ Talk(SAY_DEATH);
+
+ summons.DespawnAll();
+
+ if (instance)
+ instance->SetData(DATA_ELDER_NADOX_EVENT, DONE);
+ }
+
void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
- if (uiPlagueTimer <= diff)
- {
- DoCastVictim(SPELL_BROOD_PLAGUE);
- uiPlagueTimer = 15000;
- }
- else
- uiPlagueTimer -= diff;
+ events.Update(diff);
- if (IsHeroic())
+ while (uint32 eventId = events.ExecuteEvent())
{
- if (uiRagueTimer <= diff)
+ switch (eventId)
{
- if (Creature* Swarmer = me->FindNearestCreature(MOB_AHNKAHAR_SWARMER, 35.0f))
- {
- DoCast(Swarmer, H_SPELL_BROOD_RAGE, true);
- uiRagueTimer = 15000;
- }
+ case EVENT_PLAGUE:
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_BROOD_PLAGUE,true);
+ events.ScheduleEvent(EVENT_PLAGUE, 15*IN_MILLISECONDS);
+ break;
+ case EVENT_RAGE:
+ DoCast(H_SPELL_BROOD_RAGE);
+ events.ScheduleEvent(EVENT_RAGE, urand(10*IN_MILLISECONDS, 50*IN_MILLISECONDS));
+ break;
+ case EVENT_SUMMON_SWARMER:
+ DoCast(me, SPELL_SUMMON_SWARMERS);
+ if (urand(1, 3) == 3) // 33% chance of dialog
+ Talk(SAY_EGG_SAC);
+ events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10*IN_MILLISECONDS);
+ break;
+ case EVENT_CHECK_ENRAGE:
+ if (me->HasAura(SPELL_ENRAGE))
+ return;
+ if (me->GetPositionZ() < 24.0f)
+ DoCast(me, SPELL_ENRAGE, true);
+ events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5*IN_MILLISECONDS);
+ break;
+ default:
+ break;
}
- else
- uiRagueTimer -= diff;
}
- if (uiSwarmerSpawnTimer <= diff)
- {
- DoCast(me, SPELL_SUMMON_SWARMERS, true);
- DoCast(me, SPELL_SUMMON_SWARMERS);
- if (urand(1, 3) == 3) // 33% chance of dialog
- Talk(SAY_EGG_SAC);
-
- uiSwarmerSpawnTimer = 10000;
- }
- else
- uiSwarmerSpawnTimer -= diff;
-
- if (!bGuardSpawned && uiGuardSpawnTimer <= diff)
+ if (me->HealthBelowPct(100 - AmountHealthModifier * 25))
{
Talk(EMOTE_HATCHES, me->GetGUID());
DoCast(me, SPELL_SUMMON_SWARM_GUARD);
- bGuardSpawned = true;
+ ++AmountHealthModifier;
}
- else
- uiGuardSpawnTimer -= diff;
-
- if (uiEnrageTimer <= diff)
- {
- if (me->HasAura(SPELL_ENRAGE, 0))
- return;
-
- float x, y, z, o;
- me->GetHomePosition(x, y, z, o);
- if (z < 24)
- if (!me->IsNonMeleeSpellCasted(false))
- DoCast(me, SPELL_ENRAGE, true);
-
- uiEnrageTimer = 5000;
- }
- else
- uiEnrageTimer -= diff;
DoMeleeAttackIfReady();
}
@@ -194,12 +193,6 @@ class boss_elder_nadox : public CreatureScript
}
};
-enum AddSpells
-{
- SPELL_SPRINT = 56354,
- SPELL_GUARDIAN_AURA = 56151
-};
-
class mob_ahnkahar_nerubian : public CreatureScript
{
public:
@@ -207,50 +200,44 @@ class mob_ahnkahar_nerubian : public CreatureScript
struct mob_ahnkahar_nerubianAI : public ScriptedAI
{
- mob_ahnkahar_nerubianAI(Creature* creature) : ScriptedAI(creature)
- {
- instance = creature->GetInstanceScript();
- }
+ mob_ahnkahar_nerubianAI(Creature* creature) : ScriptedAI(creature) { }
- InstanceScript* instance;
- uint32 uiSprintTimer;
+ EventMap events;
void Reset()
{
- if (me->GetEntry() == MOB_AHNKAHAR_GUARDIAN_ENTRY)
+ if (me->GetEntry() == NPC_AHNKAHAR_GUARDIAN)
DoCast(me, SPELL_GUARDIAN_AURA, true);
- uiSprintTimer = 10000;
+
+ events.ScheduleEvent(EVENT_SPRINT, 13*IN_MILLISECONDS);
}
void JustDied(Unit* /*killer*/)
{
- if (me->GetEntry() == MOB_AHNKAHAR_GUARDIAN_ENTRY)
- if (Creature* Nadox = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ELDER_NADOX)))
- Nadox->AI()->DoAction(ACTION_AHNKAHAR_GUARDIAN_DEAD);
+ if (me->GetEntry() == NPC_AHNKAHAR_GUARDIAN)
+ me->RemoveAurasDueToSpell(SPELL_GUARDIAN_AURA);
}
- void EnterCombat(Unit* /*who*/) {}
-
- void UpdateAI(uint32 const diff)
+ void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
- if (me->GetEntry() == MOB_AHNKAHAR_GUARDIAN_ENTRY)
- me->RemoveAurasDueToSpell(SPELL_GUARDIAN_AURA);
+ events.Update(diff);
- if (instance)
- if (instance->GetData(DATA_ELDER_NADOX_EVENT) != IN_PROGRESS)
- me->DespawnOrUnsummon();
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- if (uiSprintTimer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
{
- DoCast(me, SPELL_SPRINT);
- uiSprintTimer = 25000;
+ switch (eventId)
+ {
+ case EVENT_SPRINT:
+ DoCast(me, SPELL_SPRINT);
+ events.ScheduleEvent(EVENT_SPRINT, 20*IN_MILLISECONDS);
+ break;
+ }
}
- else
- uiSprintTimer -= diff;
-
DoMeleeAttackIfReady();
}
};
@@ -274,6 +261,7 @@ public:
creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
creature->UpdateAllStats();
}
+
void Reset() {}
void EnterCombat(Unit* /*who*/) {}
void AttackStart(Unit* /*victim*/) {}
@@ -287,28 +275,68 @@ public:
}
};
-class achievement_respect_your_elders : public AchievementCriteriaScript
+class GuardianCheck
{
- public:
- achievement_respect_your_elders() : AchievementCriteriaScript("achievement_respect_your_elders") {}
+public:
+ bool operator()(const WorldObject* target) const
+ {
+ if (target->GetEntry() == NPC_AHNKAHAR_GUARDIAN)
+ return true;
+
+ return false;
+ }
+};
- bool OnCheck(Player* /*player*/, Unit* target)
+class spell_elder_nadox_guardian : public SpellScriptLoader
+{
+public:
+ spell_elder_nadox_guardian() : SpellScriptLoader("spell_elder_nadox_guardian") { }
+
+ class spell_elder_nadox_guardian_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_elder_nadox_guardian_SpellScript);
+
+ void FilterTargets(std::list<WorldObject*>& targets)
{
- if (!target)
- return false;
+ targets.remove_if(GuardianCheck());
+ }
+
+ void Register()
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_elder_nadox_guardian_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_elder_nadox_guardian_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ALLY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_elder_nadox_guardian_SpellScript();
+ }
+};
- if (Creature* Nadox = target->ToCreature())
- if (Nadox->AI()->GetData(DATA_RESPECT_YOUR_ELDERS))
- return true;
+class achievement_respect_your_elders : public AchievementCriteriaScript
+{
+public:
+ achievement_respect_your_elders() : AchievementCriteriaScript("achievement_respect_your_elders") {}
+ bool OnCheck(Player* /*player*/, Unit* target)
+ {
+ if (!target)
return false;
- }
+
+ if (Creature* Nadox = target->ToCreature())
+ if (Nadox->AI()->GetData(DATA_RESPECT_YOUR_ELDERS))
+ return true;
+
+ return false;
+ }
};
void AddSC_boss_elder_nadox()
{
- new boss_elder_nadox;
- new mob_ahnkahar_nerubian;
- new mob_nadox_eggs;
+ new boss_elder_nadox();
+ new mob_ahnkahar_nerubian();
+ new mob_nadox_eggs();
+ new spell_elder_nadox_guardian();
new achievement_respect_your_elders();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
index dcf1338bc74..3bc91855d36 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
@@ -25,10 +25,8 @@ enum Spells
SPELL_SUMMON_CARRION_BEETLES = 53521,
SPELL_LEECHING_SWARM = 53467,
SPELL_POUND = 53472,
- SPELL_POUND_H = 59433,
SPELL_SUBMERGE = 53421,
SPELL_IMPALE_DMG = 53454,
- SPELL_IMPALE_DMG_H = 59446,
SPELL_IMPALE_SHAKEGROUND = 53455,
SPELL_IMPALE_SPIKE = 53539, //this is not the correct visual effect
//SPELL_IMPALE_TARGET = 53458,
@@ -211,7 +209,7 @@ public:
break;
case IMPALE_PHASE_DMG:
if (Creature* impaleTarget = Unit::GetCreature(*me, ImpaleTarget))
- me->CastSpell(impaleTarget, DUNGEON_MODE(SPELL_IMPALE_DMG, SPELL_IMPALE_DMG_H), true);
+ me->CastSpell(impaleTarget, SPELL_IMPALE_DMG, true);
ImpalePhase = IMPALE_PHASE_TARGET;
ImpaleTimer = 9*IN_MILLISECONDS;
break;
@@ -328,7 +326,7 @@ public:
if (Unit* target = me->getVictim())
{
if (Creature* pImpaleTarget = DoSummonImpaleTarget(target))
- me->CastSpell(pImpaleTarget, DUNGEON_MODE(SPELL_POUND, SPELL_POUND_H), false);
+ me->CastSpell(pImpaleTarget, SPELL_POUND, false);
}
PoundTimer = 16500;
} else PoundTimer -= diff;
diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt
index dd8dd17c947..22d0f37a37f 100644
--- a/src/server/scripts/Northrend/CMakeLists.txt
+++ b/src/server/scripts/Northrend/CMakeLists.txt
@@ -51,6 +51,7 @@ set(scripts_STAT_SRCS
Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp
Northrend/ChamberOfAspects/RubySanctum/boss_saviana_ragefire.cpp
Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
+ Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h
Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp
Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
index 41eb31d815d..531e5d9268e 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
@@ -158,9 +158,9 @@ class boss_general_zarithrian : public CreatureScript
{
case EVENT_SUMMON_ADDS:
{
- if (Creature* stalker1 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHIAN_SPAWN_STALKER_1)))
+ if (Creature* stalker1 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHRIAN_SPAWN_STALKER_1)))
stalker1->AI()->DoCast(stalker1, SPELL_SUMMON_FLAMECALLER);
- if (Creature* stalker2 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHIAN_SPAWN_STALKER_2)))
+ if (Creature* stalker2 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHRIAN_SPAWN_STALKER_2)))
stalker2->AI()->DoCast(stalker2, SPELL_SUMMON_FLAMECALLER);
Talk(SAY_ADDS);
events.ScheduleEvent(EVENT_SUMMON_ADDS, 42000);
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
new file mode 100644
index 00000000000..64210e97122
--- /dev/null
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -0,0 +1,1740 @@
+/*
+ * Copyright (C) 2008-2012 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 "ScriptMgr.h"
+#include "SpellScript.h"
+#include "SpellAuraEffects.h"
+#include "Spell.h"
+#include "Vehicle.h"
+#include "MapManager.h"
+#include "GameObjectAI.h"
+#include "ScriptedCreature.h"
+#include "ruby_sanctum.h"
+
+/* ScriptData
+SDName: ruby_sanctum
+SDAuthors: Kaelima, Warpten
+SD%Complete: 90%
+SDComment: Based on Kaelima's initial work (nearly half of it). Corporeality handling is a pure guess, we lack info.
+SDCategory: Chamber of Aspects
+EndScriptData */
+
+enum Texts
+{
+ // Shared
+ SAY_REGENERATE = 0, // Without pressure in both realms, %s begins to regenerate.
+
+ // Halion
+ SAY_INTRO = 1, // Meddlesome insects! You are too late. The Ruby Sanctum is lost!
+ SAY_AGGRO = 2, // Your world teeters on the brink of annihilation. You will ALL bear witness to the coming of a new age of DESTRUCTION!
+ SAY_METEOR_STRIKE = 3, // The heavens burn!
+ SAY_PHASE_TWO = 4, // You will find only suffering within the realm of twilight! Enter if you dare!
+ SAY_DEATH = 5, // Relish this victory, mortals, for it will be your last! This world will burn with the master's return!
+ SAY_KILL = 6, // Another "hero" falls.
+ SAY_BERSERK = 7, // Not good enough.
+ EMOTE_CORPOREALITY_POT = 8, // Your efforts force %s further out of the physical realm!
+ EMOTE_CORPOREALITY_PIP = 9, // Your companions' efforts force %s further into the physical realm!
+
+ // Twilight Halion
+ SAY_SPHERE_PULSE = 1, // Beware the shadow!
+ SAY_PHASE_THREE = 2, // I am the light and the darkness! Cower, mortals, before the herald of Deathwing!
+ EMOTE_CORPOREALITY_TIT = 3, // Your companions' efforts force %s further into the twilight realm!
+ EMOTE_CORPOREALITY_TOT = 4, // Your efforts force %s further out of the twilight realm!
+
+ EMOTE_WARN_LASER = 0, // The orbiting spheres pulse with dark energy!
+};
+
+enum Spells
+{
+ // Halion
+ SPELL_FLAME_BREATH = 74525,
+ SPELL_CLEAVE = 74524,
+ SPELL_METEOR_STRIKE = 74637,
+ SPELL_TAIL_LASH = 74531,
+
+ SPELL_FIERY_COMBUSTION = 74562,
+ SPELL_MARK_OF_COMBUSTION = 74567,
+ SPELL_FIERY_COMBUSTION_EXPLOSION = 74607,
+ SPELL_FIERY_COMBUSTION_SUMMON = 74610,
+
+ // Combustion & Consumption
+ SPELL_SCALE_AURA = 70507, // Aura created in spell_dbc.
+ SPELL_COMBUSTION_DAMAGE_AURA = 74629,
+ SPELL_CONSUMPTION_DAMAGE_AURA = 74803,
+
+ // Twilight Halion
+ SPELL_DARK_BREATH = 74806,
+
+ SPELL_MARK_OF_CONSUMPTION = 74795,
+ SPELL_SOUL_CONSUMPTION = 74792,
+ SPELL_SOUL_CONSUMPTION_EXPLOSION = 74799,
+ SPELL_SOUL_CONSUMPTION_SUMMON = 74800,
+
+ // Living Inferno
+ SPELL_BLAZING_AURA = 75885,
+
+ // Halion Controller
+ SPELL_COSMETIC_FIRE_PILLAR = 76006,
+ SPELL_FIERY_EXPLOSION = 76010,
+ SPELL_CLEAR_DEBUFFS = 75396,
+
+ // Meteor Strike
+ SPELL_METEOR_STRIKE_COUNTDOWN = 74641,
+ SPELL_METEOR_STRIKE_AOE_DAMAGE = 74648,
+ SPELL_METEOR_STRIKE_FIRE_AURA_1 = 74713,
+ SPELL_METEOR_STRIKE_FIRE_AURA_2 = 74718,
+ SPELL_BIRTH_NO_VISUAL = 40031,
+
+ // Shadow Orb
+ SPELL_TWILIGHT_CUTTER = 74768, // Unknown dummy effect (EFFECT_0)
+ SPELL_TWILIGHT_CUTTER_TRIGGERED = 74769,
+ SPELL_TWILIGHT_PULSE_PERIODIC = 78861,
+ SPELL_TRACK_ROTATION = 74758,
+
+ // Misc
+ SPELL_TWILIGHT_DIVISION = 75063, // Phase spell from phase 2 to phase 3
+ SPELL_LEAVE_TWILIGHT_REALM = 74812,
+ SPELL_TWILIGHT_PHASING = 74808, // Phase spell from phase 1 to phase 2
+ SPELL_SUMMON_TWILIGHT_PORTAL = 74809, // Summons go 202794
+ SPELL_SUMMON_EXIT_PORTALS = 74805, // Custom spell created in spell_dbc.
+ SPELL_TWILIGHT_MENDING = 75509,
+ SPELL_TWILIGHT_REALM = 74807,
+ SPELL_COPY_DAMAGE = 74810 // Aura not found in DBCs.
+};
+
+enum Events
+{
+ // Halion
+ EVENT_ACTIVATE_FIREWALL = 1,
+ EVENT_CLEAVE = 2,
+ EVENT_FLAME_BREATH = 3,
+ EVENT_METEOR_STRIKE = 4,
+ EVENT_FIERY_COMBUSTION = 5,
+ EVENT_TAIL_LASH = 6,
+
+ // Twilight Halion
+ EVENT_DARK_BREATH = 7,
+ EVENT_SOUL_CONSUMPTION = 8,
+
+ // Meteor Strike
+ EVENT_SPAWN_METEOR_FLAME = 9,
+
+ // Halion Controller
+ EVENT_START_INTRO = 10,
+ EVENT_INTRO_PROGRESS_1 = 11,
+ EVENT_INTRO_PROGRESS_2 = 12,
+ EVENT_INTRO_PROGRESS_3 = 13,
+ EVENT_CHECK_CORPOREALITY = 14,
+ EVENT_SHADOW_PULSARS_SHOOT = 15,
+ EVENT_TRIGGER_BERSERK = 16,
+ EVENT_TWILIGHT_MENDING = 17
+};
+
+enum Actions
+{
+ // Meteor Strike
+ ACTION_METEOR_STRIKE_BURN = 1,
+ ACTION_METEOR_STRIKE_AOE = 2,
+
+ // Halion Controller
+ ACTION_PHASE_TWO = 3,
+ ACTION_PHASE_THREE = 4,
+ ACTION_CLEANUP = 5,
+
+ // Orb Carrier
+ ACTION_SHOOT = 6
+};
+
+enum Phases
+{
+ PHASE_ALL = 0,
+ PHASE_INTRO = 1,
+ PHASE_ONE = 2,
+ PHASE_TWO = 3,
+ PHASE_THREE = 4,
+
+ PHASE_INTRO_MASK = 1 << PHASE_INTRO,
+ PHASE_ONE_MASK = 1 << PHASE_ONE,
+ PHASE_TWO_MASK = 1 << PHASE_TWO,
+ PHASE_THREE_MASK = 1 << PHASE_THREE
+};
+
+enum Misc
+{
+ DATA_TWILIGHT_DAMAGE_TAKEN = 1,
+ DATA_MATERIAL_DAMAGE_TAKEN = 2,
+ DATA_STACKS_DISPELLED = 3,
+ DATA_FIGHT_PHASE = 4,
+ DATA_EVADE_METHOD = 5
+};
+
+enum OrbCarrierSeats
+{
+ SEAT_NORTH = 0,
+ SEAT_SOUTH = 1,
+ SEAT_EAST = 2,
+ SEAT_WEST = 3
+};
+
+enum CorporealityEvent
+{
+ CORPOREALITY_NONE = 0,
+ CORPOREALITY_TWILIGHT_MENDING = 1,
+ CORPOREALITY_INCREASE = 2,
+ CORPOREALITY_DECREASE = 3
+};
+
+Position const HalionSpawnPos = {3156.67f, 533.8108f, 72.98822f, 3.159046f};
+
+uint8 const MAX_CORPOREALITY_STATE = 11;
+
+struct CorporealityEntry
+{
+ uint32 materialRealmSpell;
+ uint32 twilightRealmSpell;
+};
+
+CorporealityEntry const _corporealityReference[MAX_CORPOREALITY_STATE] = {
+ {74836, 74831},
+ {74835, 74830},
+ {74834, 74829},
+ {74833, 74828},
+ {74832, 74827},
+ {74826, 74826},
+ {74827, 74832},
+ {74828, 74833},
+ {74829, 74834},
+ {74830, 74835},
+ {74831, 74836}
+};
+
+struct generic_halionAI : public BossAI
+{
+ generic_halionAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), _canEvade(false) { }
+
+ void EnterCombat(Unit* /*who*/)
+ {
+ Talk(SAY_AGGRO);
+ _EnterCombat();
+ _canEvade = false;
+ events.Reset();
+ events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000));
+ }
+
+ void EnterEvadeMode()
+ {
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ _EnterEvadeMode();
+ }
+
+ void ExecuteEvent(uint32 const eventId)
+ {
+ switch (eventId)
+ {
+ case EVENT_CLEAVE:
+ DoCastVictim(SPELL_CLEAVE);
+ events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000));
+ break;
+ }
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ ExecuteEvent(eventId);
+
+ DoMeleeAttackIfReady();
+ }
+
+ void SetData(uint32 index, uint32 dataValue)
+ {
+ switch (index)
+ {
+ case DATA_EVADE_METHOD:
+ _canEvade = (dataValue == 1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo)
+ {
+ if (spellInfo->Id == SPELL_TWILIGHT_MENDING)
+ Talk(SAY_REGENERATE);
+ }
+
+protected:
+ bool _canEvade;
+};
+
+class boss_halion : public CreatureScript
+{
+ public:
+ boss_halion() : CreatureScript("boss_halion") { }
+
+ struct boss_halionAI : public generic_halionAI
+ {
+ boss_halionAI(Creature* creature) : generic_halionAI(creature, DATA_HALION) { }
+
+ void Reset()
+ {
+ generic_halionAI::Reset();
+ me->SetReactState(REACT_DEFENSIVE);
+
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+
+ me->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ }
+
+ void EnterEvadeMode()
+ {
+ // Phase 1: We always can evade. Phase 2 & 3: We can evade if and only if the controller tells us to.
+ // Controller has absolute priority over the phasemask.
+ if ((events.GetPhaseMask() & PHASE_ONE_MASK) || _canEvade)
+ generic_halionAI::EnterEvadeMode();
+ }
+
+ void EnterCombat(Unit* who)
+ {
+ generic_halionAI::EnterCombat(who);
+
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1);
+ instance->SetBossState(DATA_HALION, IN_PROGRESS);
+
+ events.SetPhase(PHASE_ONE);
+ events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, 10000);
+ events.ScheduleEvent(EVENT_FLAME_BREATH, urand(10000, 12000));
+ events.ScheduleEvent(EVENT_METEOR_STRIKE, urand(20000, 25000));
+ events.ScheduleEvent(EVENT_FIERY_COMBUSTION, urand(15000, 18000));
+ events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
+
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_ONE);
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
+
+ Talk(SAY_DEATH);
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
+ me->Kill(controller);
+ }
+
+ Position const* GetMeteorStrikePosition() const { return &_meteorStrikePos; }
+
+ void DamageTaken(Unit* attacker, uint32& damage)
+ {
+ if (me->HealthBelowPctDamaged(75, damage) && (events.GetPhaseMask() & PHASE_ONE_MASK))
+ {
+ events.SetPhase(PHASE_TWO);
+ Talk(SAY_PHASE_TWO);
+
+ me->CastStop();
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ DoCast(me, SPELL_TWILIGHT_PHASING);
+
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_TWO);
+ return;
+ }
+
+ if (events.GetPhaseMask() & PHASE_THREE_MASK)
+ {
+ // Don't consider copied damage.
+ if (!me->InSamePhase(attacker))
+ return;
+
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->SetData(DATA_MATERIAL_DAMAGE_TAKEN, damage);
+ }
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (events.GetPhaseMask() & PHASE_TWO_MASK)
+ return;
+
+ generic_halionAI::UpdateAI(diff);
+ }
+
+ void ExecuteEvent(uint32 const eventId)
+ {
+ switch (eventId)
+ {
+ case EVENT_ACTIVATE_FIREWALL:
+ {
+ // Flame ring is activated 10 seconds after starting encounter, DOOR_TYPE_ROOM is only instant.
+ for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i)
+ if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetData64(i)))
+ instance->HandleGameObject(instance->GetData64(DATA_FLAME_RING), false, flameRing);
+ break;
+ }
+ case EVENT_FLAME_BREATH:
+ DoCast(me, SPELL_FLAME_BREATH);
+ events.ScheduleEvent(EVENT_FLAME_BREATH, 25000);
+ break;
+ case EVENT_TAIL_LASH:
+ DoCastAOE(SPELL_TAIL_LASH);
+ events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
+ break;
+ case EVENT_METEOR_STRIKE:
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM))
+ {
+ target->GetPosition(&_meteorStrikePos);
+ me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, NULL, NULL, me->GetGUID());
+ Talk(SAY_METEOR_STRIKE);
+ }
+ events.ScheduleEvent(EVENT_METEOR_STRIKE, 40000);
+ break;
+ }
+ case EVENT_FIERY_COMBUSTION:
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM))
+ DoCast(target, SPELL_FIERY_COMBUSTION);
+ events.ScheduleEvent(EVENT_FIERY_COMBUSTION, 25000);
+ break;
+ }
+ default:
+ generic_halionAI::ExecuteEvent(eventId);
+ break;
+ }
+ }
+
+ void SetData(uint32 index, uint32 value)
+ {
+ switch (index)
+ {
+ case DATA_FIGHT_PHASE:
+ events.SetPhase(value);
+ break;
+ default:
+ generic_halionAI::SetData(index, value);
+ }
+ }
+
+ private:
+ Position _meteorStrikePos;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<boss_halionAI>(creature);
+ }
+};
+
+typedef boss_halion::boss_halionAI HalionAI;
+
+class boss_twilight_halion : public CreatureScript
+{
+ public:
+ boss_twilight_halion() : CreatureScript("boss_twilight_halion") { }
+
+ struct boss_twilight_halionAI : public generic_halionAI
+ {
+ boss_twilight_halionAI(Creature* creature) : generic_halionAI(creature, DATA_TWILIGHT_HALION)
+ {
+ Creature* halion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION));
+ if (!halion)
+ return;
+
+ // We use explicit targeting here to avoid conditions + SPELL_ATTR6_CANT_TARGET_SELF.
+ // Using AddAura because no spell cast packet in sniffs.
+ halion->AddAura(SPELL_COPY_DAMAGE, me);
+ me->AddAura(SPELL_COPY_DAMAGE, halion);
+
+ me->SetHealth(halion->GetHealth());
+ me->SetPhaseMask(0x20, true);
+ me->SetReactState(REACT_AGGRESSIVE);
+
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 2);
+
+ events.Reset();
+ events.SetPhase(PHASE_TWO);
+ events.ScheduleEvent(EVENT_DARK_BREATH, urand(10000, 15000));
+ events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000);
+ events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
+ }
+
+ void EnterEvadeMode()
+ {
+ // We don't care about evading, we will be despawned.
+ }
+
+ void KilledUnit(Unit* victim)
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_KILL);
+
+ // Victims should not be in the Twilight Realm
+ me->CastSpell(victim, SPELL_LEAVE_TWILIGHT_REALM, true);
+ }
+
+ void JustDied(Unit* killer)
+ {
+ if (Creature* halion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION)))
+ {
+ // Ensure looting
+ if (me->IsDamageEnoughForLootingAndReward())
+ halion->LowerPlayerDamageReq(halion->GetMaxHealth());
+
+ if (halion->isAlive())
+ killer->Kill(halion);
+ }
+
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->Kill(controller);
+
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ }
+
+ void DamageTaken(Unit* attacker, uint32& damage)
+ {
+ if (me->HealthBelowPctDamaged(50, damage) && (events.GetPhaseMask() & PHASE_TWO_MASK))
+ {
+ events.SetPhase(PHASE_THREE);
+ me->CastStop();
+ DoCast(me, SPELL_TWILIGHT_DIVISION);
+ Talk(SAY_PHASE_THREE);
+ return;
+ }
+
+ if (events.GetPhaseMask() & PHASE_THREE_MASK)
+ {
+ // Don't consider copied damage.
+ if (!me->InSamePhase(attacker))
+ return;
+
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->SetData(DATA_TWILIGHT_DAMAGE_TAKEN, damage);
+ }
+ }
+
+ void SpellHit(Unit* who, SpellInfo const* spell)
+ {
+ switch (spell->Id)
+ {
+ case SPELL_TWILIGHT_DIVISION:
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_THREE);
+ break;
+ default:
+ generic_halionAI::SpellHit(who, spell);
+ break;
+ }
+ }
+
+ void ExecuteEvent(uint32 const eventId)
+ {
+ switch (eventId)
+ {
+ case EVENT_DARK_BREATH:
+ DoCast(me, SPELL_DARK_BREATH);
+ events.ScheduleEvent(EVENT_DARK_BREATH, urand(10000, 15000));
+ break;
+ case EVENT_SOUL_CONSUMPTION:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM))
+ DoCast(target, SPELL_SOUL_CONSUMPTION);
+ events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000);
+ break;
+ case EVENT_TAIL_LASH:
+ DoCastAOE(SPELL_TAIL_LASH);
+ events.ScheduleEvent(EVENT_TAIL_LASH, 10000);
+ break;
+ default:
+ generic_halionAI::ExecuteEvent(eventId);
+ break;
+ }
+ }
+
+ private:
+ EventMap events;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<boss_twilight_halionAI>(creature);
+ }
+};
+
+class npc_halion_controller : public CreatureScript
+{
+ public:
+ npc_halion_controller() : CreatureScript("npc_halion_controller") { }
+
+ struct npc_halion_controllerAI : public ScriptedAI
+ {
+ npc_halion_controllerAI(Creature* creature) : ScriptedAI(creature),
+ _instance(creature->GetInstanceScript()), _summons(me)
+ {
+ me->SetPhaseMask(me->GetPhaseMask() | 0x20, true);
+ _events.SetPhase(PHASE_INTRO);
+ }
+
+ void Reset()
+ {
+ _summons.DespawnAll();
+ _events.Reset();
+ _materialCorporealityValue = 5;
+
+ DoCast(me, SPELL_CLEAR_DEBUFFS);
+ }
+
+ void JustSummoned(Creature* who)
+ {
+ _summons.Summon(who);
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _events.Reset();
+ _summons.DespawnAll();
+
+ DoCast(me, SPELL_CLEAR_DEBUFFS);
+ }
+
+ void EnterCombat(Unit* /*who*/)
+ {
+ _twilightDamageTaken = 0;
+ _materialDamageTaken = 0;
+
+ _events.ScheduleEvent(EVENT_TRIGGER_BERSERK, 8 * MINUTE * IN_MILLISECONDS);
+ }
+
+ void JustReachedHome()
+ {
+ if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TWILIGHT_HALION)))
+ twilightHalion->DespawnOrUnsummon();
+
+ if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION)))
+ {
+ halion->AI()->SetData(DATA_EVADE_METHOD, 1);
+ halion->AI()->EnterEvadeMode();
+ }
+
+ _instance->SetBossState(DATA_HALION, FAIL);
+ }
+
+ void DoAction(int32 const action)
+ {
+ switch (action)
+ {
+ case ACTION_INTRO_HALION:
+ _events.Reset();
+ _events.SetPhase(PHASE_INTRO);
+ _events.ScheduleEvent(EVENT_START_INTRO, 2000);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ // The isInCombat() check is needed because that check should be false when Halion is
+ // not engaged, while it would return true without as UpdateVictim() checks for
+ // combat state.
+ if (!(_events.GetPhaseMask() & PHASE_INTRO_MASK) && me->isInCombat() && !UpdateVictim())
+ {
+ EnterEvadeMode();
+ return;
+ }
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_START_INTRO:
+ DoCast(me, SPELL_COSMETIC_FIRE_PILLAR, true);
+ _events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, 4000);
+ break;
+ case EVENT_INTRO_PROGRESS_1:
+ for (uint8 i = DATA_BURNING_TREE_3; i <= DATA_BURNING_TREE_4; ++i)
+ if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetData64(i)))
+ _instance->HandleGameObject(_instance->GetData64(i), true, tree);
+ _events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, 4000);
+ break;
+ case EVENT_INTRO_PROGRESS_2:
+ for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_2; ++i)
+ if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetData64(i)))
+ _instance->HandleGameObject(_instance->GetData64(i), true, tree);
+ _events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, 4000);
+ break;
+ case EVENT_INTRO_PROGRESS_3:
+ DoCast(me, SPELL_FIERY_EXPLOSION);
+ if (Creature* halion = me->GetMap()->SummonCreature(NPC_HALION, HalionSpawnPos))
+ halion->AI()->Talk(SAY_INTRO);
+ break;
+ case EVENT_TWILIGHT_MENDING:
+ if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION)))
+ if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TWILIGHT_HALION)))
+ twilightHalion->CastSpell((Unit*)NULL, SPELL_TWILIGHT_MENDING, true);
+ break;
+ case EVENT_TRIGGER_BERSERK:
+ for (uint8 i = DATA_HALION; i <= DATA_TWILIGHT_HALION; i++)
+ if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(i)))
+ halion->CastSpell(halion, SPELL_BERSERK, true);
+ break;
+ case EVENT_SHADOW_PULSARS_SHOOT:
+ if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TWILIGHT_HALION)))
+ twilightHalion->AI()->Talk(SAY_SPHERE_PULSE);
+
+ if (Creature* orbCarrier = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_ORB_CARRIER)))
+ orbCarrier->AI()->DoAction(ACTION_SHOOT);
+
+ _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000);
+ break;
+ case EVENT_CHECK_CORPOREALITY:
+ UpdateCorporeality();
+ _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 5000);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void SetData(uint32 id, uint32 value)
+ {
+ switch (id)
+ {
+ case DATA_MATERIAL_DAMAGE_TAKEN:
+ _materialDamageTaken += value;
+ break;
+ case DATA_TWILIGHT_DAMAGE_TAKEN:
+ _twilightDamageTaken += value;
+ break;
+ case DATA_FIGHT_PHASE:
+ _events.SetPhase(value);
+ switch (value)
+ {
+ case PHASE_ONE:
+ DoZoneInCombat();
+ break;
+ case PHASE_TWO:
+ // Timer taken from a 4.3.4 solo video and confirmed by TankSpot's 3.3.5 guide. http://www.tankspot.com/showthread.php?67195-Halion-Encounter-Guide-Live
+ _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000);
+ break;
+ case PHASE_THREE:
+ _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 5000);
+ // Load up corporeality data.
+ for (uint8 itr = DATA_HALION; itr <= DATA_TWILIGHT_HALION; itr++)
+ {
+ Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(itr));
+ if (!halion)
+ continue;
+
+ halion->CastSpell(halion, GetSpell(_materialCorporealityValue, itr == DATA_TWILIGHT_HALION), false);
+ halion->AI()->SetData(DATA_FIGHT_PHASE, PHASE_THREE);
+
+ if (itr == DATA_TWILIGHT_HALION)
+ continue;
+
+ halion->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING);
+ halion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ }
+
+ // Summon Twilight portals
+ DoCast(me, SPELL_SUMMON_EXIT_PORTALS);
+
+ _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TOGGLE, 1);
+ // Hardcoding doesn't really matter here.
+ _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, 50);
+ _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 50);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private:
+ /// TODO: Find out a better scaling, if any.
+ // [0 , 0.98[: Corporeality goes down
+ // [0.98, 0.99]: Do nothing
+ // ]0.99, 1.01[: Twilight Mending
+ // [1.01, 1.02]: Do nothing
+ // ]1.02, +oo [: Corporeality goes up
+ void UpdateCorporeality()
+ {
+ uint8 oldValue = _materialCorporealityValue;
+ if (_twilightDamageTaken == 0 || _materialDamageTaken == 0)
+ {
+ _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100);
+ _twilightDamageTaken = 0;
+ _materialDamageTaken = 0;
+ return;
+ }
+
+ float damageRatio = float(_materialDamageTaken) / float(_twilightDamageTaken);
+
+ CorporealityEvent action = CORPOREALITY_NONE;
+ if (damageRatio < 0.98f) // [0 , 0.98[: Corporeality goes down
+ action = CORPOREALITY_DECREASE;
+ else if (0.99f < damageRatio && damageRatio < 1.0f) // ]0.99, 1.01[: Twilight Mending
+ action = CORPOREALITY_TWILIGHT_MENDING;
+ else if (1.02f < damageRatio) // ]1.02, +oo [: Corporeality goes up
+ action = CORPOREALITY_INCREASE;
+
+ switch (action)
+ {
+ case CORPOREALITY_NONE:
+ return;
+ case CORPOREALITY_INCREASE:
+ {
+ if (_materialCorporealityValue >= (MAX_CORPOREALITY_STATE - 1))
+ return;
+ ++_materialCorporealityValue;
+ break;
+ }
+ case CORPOREALITY_DECREASE:
+ {
+ if (_materialCorporealityValue <= 0)
+ return;
+ --_materialCorporealityValue;
+ break;
+ }
+ case CORPOREALITY_TWILIGHT_MENDING:
+ {
+ _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100);
+ _materialDamageTaken = 0;
+ _twilightDamageTaken = 0;
+ return;
+ }
+ default:
+ break;
+ }
+
+ _materialDamageTaken = 0;
+ _twilightDamageTaken = 0;
+
+ _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, _materialCorporealityValue * 10);
+ _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 100 - _materialCorporealityValue * 10);
+
+ for (uint8 itr = DATA_HALION; itr <= DATA_TWILIGHT_HALION; itr++)
+ {
+ if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(itr)))
+ {
+ RemoveCorporeality(halion, itr == DATA_TWILIGHT_HALION);
+ halion->CastSpell(halion, GetSpell(_materialCorporealityValue, itr == DATA_TWILIGHT_HALION), true);
+
+ if (itr == DATA_TWILIGHT_HALION)
+ halion->AI()->Talk(oldValue < _materialCorporealityValue ? EMOTE_CORPOREALITY_TOT : EMOTE_CORPOREALITY_TIT, halion->GetGUID());
+ else // if (itr == DATA_HALION)
+ halion->AI()->Talk(oldValue > _materialCorporealityValue ? EMOTE_CORPOREALITY_POT : EMOTE_CORPOREALITY_PIP, halion->GetGUID());
+ }
+ }
+ }
+
+ void RemoveCorporeality(Creature* who, bool isTwilight = false)
+ {
+ for (uint8 i = 0; i < MAX_CORPOREALITY_STATE; i++)
+ {
+ uint32 spellID = (isTwilight ? _corporealityReference[i].twilightRealmSpell : _corporealityReference[i].materialRealmSpell);
+ if (who->HasAura(spellID))
+ {
+ who->RemoveAurasDueToSpell(spellID);
+ break;
+ }
+ }
+ }
+
+ uint32 GetSpell(uint8 pctValue, bool isTwilight = false) const
+ {
+ CorporealityEntry entry = _corporealityReference[pctValue];
+ return isTwilight ? entry.twilightRealmSpell : entry.materialRealmSpell;
+ }
+
+ EventMap _events;
+ InstanceScript* _instance;
+ SummonList _summons;
+
+ bool _corporealityCheck;
+
+ uint32 _twilightDamageTaken;
+ uint32 _materialDamageTaken;
+ uint8 _materialCorporealityValue;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<npc_halion_controllerAI>(creature);
+ }
+};
+
+typedef npc_halion_controller::npc_halion_controllerAI controllerAI;
+
+class npc_orb_carrier : public CreatureScript
+{
+ public:
+ npc_orb_carrier() : CreatureScript("npc_orb_carrier") { }
+
+ struct npc_orb_carrierAI : public ScriptedAI
+ {
+ npc_orb_carrierAI(Creature* creature) : ScriptedAI(creature),
+ instance(creature->GetInstanceScript())
+ {
+ ASSERT(creature->GetVehicleKit());
+ }
+
+ void UpdateAI(uint32 const /*diff*/)
+ {
+ /// According to sniffs this spell is cast every 1 or 2 seconds.
+ /// However, refreshing it looks bad, so just cast the spell if
+ /// we are not channeling it.
+ if (!me->HasUnitState(UNIT_STATE_CASTING))
+ me->CastSpell((Unit*)NULL, SPELL_TRACK_ROTATION, false);
+
+ /// Workaround: This is here because even though the above spell has SPELL_ATTR1_CHANNEL_TRACK_TARGET,
+ /// we are having two creatures involded here. This attribute is handled clientside, meaning the client
+ /// sends orientation update itself. Here, no packet is sent, and the creature does not rotate. By
+ /// forcing the carrier to always be facing the rotation focus, we ensure everything works as it should.
+ if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ORB_ROTATION_FOCUS)))
+ me->SetFacingToObject(rotationFocus); // setInFront
+ }
+
+ void DoAction(int32 const action)
+ {
+ if (action == ACTION_SHOOT)
+ {
+ Vehicle* vehicle = me->GetVehicleKit();
+ Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH);
+ Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH);
+ if (southOrb && northOrb)
+ {
+ if (northOrb->GetTypeId() == TYPEID_UNIT)
+ northOrb->ToCreature()->AI()->Talk(EMOTE_WARN_LASER);
+ TriggerCutter(northOrb, southOrb);
+ }
+
+ if (!IsHeroic())
+ return;
+
+ Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST);
+ Unit* westOrb = vehicle->GetPassenger(SEAT_WEST);
+ if (eastOrb && westOrb)
+ TriggerCutter(eastOrb, westOrb);
+ }
+ }
+ private:
+ InstanceScript* instance;
+
+ void TriggerCutter(Unit* caster, Unit* target)
+ {
+ caster->CastSpell(caster, SPELL_TWILIGHT_PULSE_PERIODIC, true);
+ target->CastSpell(target, SPELL_TWILIGHT_PULSE_PERIODIC, true);
+ caster->CastSpell(target, SPELL_TWILIGHT_CUTTER, false);
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<npc_orb_carrierAI>(creature);
+ }
+};
+
+class npc_meteor_strike_initial : public CreatureScript
+{
+ public:
+ npc_meteor_strike_initial() : CreatureScript("npc_meteor_strike_initial") { }
+
+ struct npc_meteor_strike_initialAI : public Scripted_NoMovementAI
+ {
+ npc_meteor_strike_initialAI(Creature* creature) : Scripted_NoMovementAI(creature),
+ _instance(creature->GetInstanceScript())
+ { }
+
+ void DoAction(int32 const action)
+ {
+ switch (action)
+ {
+ case ACTION_METEOR_STRIKE_AOE:
+ DoCast(me, SPELL_METEOR_STRIKE_AOE_DAMAGE, true);
+ DoCast(me, SPELL_METEOR_STRIKE_FIRE_AURA_1, true);
+ for (std::list<Creature*>::iterator itr = _meteorList.begin(); itr != _meteorList.end(); ++itr)
+ (*itr)->AI()->DoAction(ACTION_METEOR_STRIKE_BURN);
+ break;
+ }
+ }
+
+ void IsSummonedBy(Unit* summoner)
+ {
+ Creature* owner = summoner->ToCreature();
+ if (!owner)
+ return;
+
+ // Let Halion Controller count as summoner
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->JustSummoned(me);
+
+ DoCast(me, SPELL_METEOR_STRIKE_COUNTDOWN);
+ DoCast(me, SPELL_BIRTH_NO_VISUAL); // Unknown purpose
+
+ if (HalionAI* halionAI = CAST_AI(HalionAI, owner->AI()))
+ {
+ Position const* ownerPos = halionAI->GetMeteorStrikePosition();
+ Position newPos;
+ float angle[4];
+ angle[0] = me->GetAngle(ownerPos);
+ angle[1] = me->GetAngle(ownerPos) - static_cast<float>(M_PI/2);
+ angle[2] = me->GetAngle(ownerPos) - static_cast<float>(-M_PI/2);
+ angle[3] = me->GetAngle(ownerPos) - static_cast<float>(M_PI);
+
+ _meteorList.clear();
+ for (uint8 i = 0; i < 4; i++)
+ {
+ angle[i] = MapManager::NormalizeOrientation(angle[i]);
+ me->SetOrientation(angle[i]);
+ me->GetNearPosition(newPos, 10.0f, 0.0f); // Exact distance
+ if (Creature* meteor = me->SummonCreature(NPC_METEOR_STRIKE_NORTH + i, newPos, TEMPSUMMON_TIMED_DESPAWN, 30000))
+ _meteorList.push_back(meteor);
+ }
+ }
+ }
+
+ void UpdateAI(uint32 const /*diff*/) { }
+ void EnterEvadeMode() { }
+ private:
+ InstanceScript* _instance;
+ std::list<Creature*> _meteorList;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<npc_meteor_strike_initialAI>(creature);
+ }
+};
+
+class npc_meteor_strike : public CreatureScript
+{
+ public:
+ npc_meteor_strike() : CreatureScript("npc_meteor_strike") { }
+
+ struct npc_meteor_strikeAI : public Scripted_NoMovementAI
+ {
+ npc_meteor_strikeAI(Creature* creature) : Scripted_NoMovementAI(creature),
+ _instance(creature->GetInstanceScript())
+ {
+ _range = 5.0f;
+ _spawnCount = 0;
+ }
+
+ void DoAction(int32 const action)
+ {
+ if (action == ACTION_METEOR_STRIKE_BURN)
+ {
+ DoCast(me, SPELL_METEOR_STRIKE_FIRE_AURA_2, true);
+ me->setActive(true);
+ _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 500);
+ }
+ }
+
+ void IsSummonedBy(Unit* /*summoner*/)
+ {
+ // Let Halion Controller count as summoner.
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->JustSummoned(me);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (_spawnCount > 5)
+ return;
+
+ _events.Update(diff);
+
+ if (_events.ExecuteEvent() == EVENT_SPAWN_METEOR_FLAME)
+ {
+ Position pos;
+ me->GetNearPosition(pos, _range, 0.0f);
+
+ if (Creature* flame = me->SummonCreature(NPC_METEOR_STRIKE_FLAME, pos, TEMPSUMMON_TIMED_DESPAWN, 25000))
+ {
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->JustSummoned(flame);
+
+ flame->CastSpell(flame, SPELL_METEOR_STRIKE_FIRE_AURA_2, true);
+ ++_spawnCount;
+ }
+ _range += 5.0f;
+ _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 800);
+ }
+ }
+
+ private:
+ InstanceScript* _instance;
+ EventMap _events;
+ float _range;
+ uint8 _spawnCount;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<npc_meteor_strikeAI>(creature);
+ }
+};
+
+class npc_combustion_consumption : public CreatureScript
+{
+ public:
+ npc_combustion_consumption() : CreatureScript("npc_combustion_consumption") { }
+
+ struct npc_combustion_consumptionAI : public Scripted_NoMovementAI
+ {
+ npc_combustion_consumptionAI(Creature* creature) : Scripted_NoMovementAI(creature),
+ _summonerGuid(0), _instance(creature->GetInstanceScript())
+ {
+ switch (me->GetEntry())
+ {
+ case NPC_COMBUSTION:
+ _explosionSpell = SPELL_FIERY_COMBUSTION_EXPLOSION;
+ _damageSpell = SPELL_COMBUSTION_DAMAGE_AURA;
+ me->SetPhaseMask(0x01, true);
+ break;
+ case NPC_CONSUMPTION:
+ _explosionSpell = SPELL_SOUL_CONSUMPTION_EXPLOSION;
+ _damageSpell = SPELL_CONSUMPTION_DAMAGE_AURA;
+ me->SetPhaseMask(0x20, true);
+ break;
+ default: // Should never happen
+ _explosionSpell = 0;
+ _damageSpell = 0;
+ break;
+ }
+
+ if (IsHeroic())
+ me->SetPhaseMask(0x01 | 0x20, true);
+ }
+
+ void IsSummonedBy(Unit* summoner)
+ {
+ // Let Halion Controller count as summoner
+ if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER)))
+ controller->AI()->JustSummoned(me);
+
+ _summonerGuid = summoner->GetGUID();
+ }
+
+ void SetData(uint32 type, uint32 stackAmount)
+ {
+ Unit* summoner = ObjectAccessor::GetUnit(*me, _summonerGuid);
+
+ if (type != DATA_STACKS_DISPELLED || !_damageSpell || !_explosionSpell || !summoner)
+ return;
+
+ me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount, me);
+ DoCast(me, _damageSpell);
+
+ int32 damage = 1200 + (stackAmount * 1290); // Needs more researches.
+ summoner->CastCustomSpell(_explosionSpell, SPELLVALUE_BASE_POINT0, damage, summoner);
+ }
+
+ void UpdateAI(uint32 const /*diff*/) { }
+
+ private:
+ InstanceScript* _instance;
+ uint32 _explosionSpell;
+ uint32 _damageSpell;
+ uint64 _summonerGuid;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<npc_combustion_consumptionAI>(creature);
+ }
+};
+
+class npc_living_inferno : public CreatureScript
+{
+ public:
+ npc_living_inferno() : CreatureScript("npc_living_inferno") { }
+
+ struct npc_living_infernoAI : public ScriptedAI
+ {
+ npc_living_infernoAI(Creature* creature) : ScriptedAI(creature) { }
+
+ void JustSummoned(Creature* /*summoner*/)
+ {
+ me->SetInCombatWithZone();
+ DoCast(me, SPELL_BLAZING_AURA);
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<npc_living_infernoAI>(creature);
+ }
+};
+
+//! Need sniff data
+class npc_living_ember : public CreatureScript
+{
+ public:
+ npc_living_ember() : CreatureScript("npc_living_ember") { }
+
+ struct npc_living_emberAI : public ScriptedAI
+ {
+ npc_living_emberAI(Creature* creature) : ScriptedAI(creature) { }
+
+ void Reset()
+ {
+ _hasEnraged = false;
+ }
+
+ void EnterCombat(Unit* /*who*/)
+ {
+ _enrageTimer = 20000;
+ _hasEnraged = false;
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ if (!_hasEnraged && _enrageTimer <= diff)
+ {
+ _hasEnraged = true;
+ DoCast(me, SPELL_BERSERK);
+ }
+ else _enrageTimer -= diff;
+
+ DoMeleeAttackIfReady();
+ }
+
+ private:
+ uint32 _enrageTimer;
+ bool _hasEnraged;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetRubySanctumAI<npc_living_emberAI>(creature);
+ }
+};
+
+class go_twilight_portal : public GameObjectScript
+{
+ public:
+ go_twilight_portal() : GameObjectScript("go_twilight_portal") { }
+
+ struct go_twilight_portalAI : public GameObjectAI
+ {
+ go_twilight_portalAI(GameObject* gameobject) : GameObjectAI(gameobject),
+ _instance(gameobject->GetInstanceScript()), _deleted(false)
+ {
+ switch (gameobject->GetEntry())
+ {
+ case GO_HALION_PORTAL_EXIT:
+ gameobject->SetPhaseMask(0x20, true);
+ _spellId = gameobject->GetGOInfo()->goober.spellId;
+ break;
+ case GO_HALION_PORTAL_1:
+ case GO_HALION_PORTAL_2: // Not used, not seen in sniffs. Just in case.
+ gameobject->SetPhaseMask(0x1, true);
+ /// Because WDB template has non-existent spell ID, not seen in sniffs either, meh
+ _spellId = SPELL_TWILIGHT_REALM;
+ break;
+ default:
+ _spellId = 0;
+ break;
+ }
+ }
+
+ bool GossipHello(Player* player)
+ {
+ if (_spellId != 0)
+ player->CastSpell(player, _spellId, true);
+ return true;
+ }
+
+ void UpdateAI(uint32 /*diff*/)
+ {
+ if (_instance->GetBossState(DATA_HALION) == IN_PROGRESS)
+ return;
+
+ if (!_deleted)
+ {
+ _deleted = true;
+ go->Delete();
+ }
+ }
+
+ private:
+ InstanceScript* _instance;
+ uint32 _spellId;
+ bool _deleted;
+ };
+
+ GameObjectAI* GetAI(GameObject* gameobject) const
+ {
+ return GetRubySanctumAI<go_twilight_portalAI>(gameobject);
+ }
+};
+
+class spell_halion_meteor_strike_marker : public SpellScriptLoader
+{
+ public:
+ spell_halion_meteor_strike_marker() : SpellScriptLoader("spell_halion_meteor_strike_marker") { }
+
+ class spell_halion_meteor_strike_marker_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_halion_meteor_strike_marker_AuraScript);
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (!GetCaster())
+ return;
+
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
+ if (Creature* creCaster = GetCaster()->ToCreature())
+ creCaster->AI()->DoAction(ACTION_METEOR_STRIKE_AOE);
+ }
+
+ void Register()
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_halion_meteor_strike_marker_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_halion_meteor_strike_marker_AuraScript();
+ }
+};
+
+class spell_halion_combustion_consumption : public SpellScriptLoader
+{
+ public:
+ spell_halion_combustion_consumption(char const* scriptName, uint32 spell) : SpellScriptLoader(scriptName), _spellID(spell) { }
+
+ class spell_halion_combustion_consumption_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_halion_combustion_consumption_AuraScript);
+
+ public:
+ spell_halion_combustion_consumption_AuraScript(uint32 spellID) : AuraScript(), _markSpell(spellID) { }
+
+ bool Validate(SpellEntry const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(_markSpell))
+ return false;
+ return true;
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH)
+ return;
+
+ if (GetTarget()->HasAura(_markSpell))
+ GetTarget()->RemoveAurasDueToSpell(_markSpell, 0, 0, AURA_REMOVE_BY_EXPIRE);
+ }
+
+ void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ GetTarget()->CastSpell(GetTarget(), _markSpell, true);
+ }
+
+ void AddMarkStack(AuraEffect const* /*aurEff*/)
+ {
+ GetTarget()->CastSpell(GetTarget(), _markSpell, true);
+ }
+
+ void Register()
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_halion_combustion_consumption_AuraScript::AddMarkStack, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ AfterEffectApply += AuraEffectApplyFn(spell_halion_combustion_consumption_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_halion_combustion_consumption_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
+ }
+
+ uint32 _markSpell;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_halion_combustion_consumption_AuraScript(_spellID);
+ }
+
+ private:
+ uint32 _spellID;
+};
+
+class spell_halion_marks : public SpellScriptLoader
+{
+ public:
+ spell_halion_marks(char const* scriptName, uint32 summonSpell, uint32 removeSpell) : SpellScriptLoader(scriptName),
+ _summonSpell(summonSpell), _removeSpell(removeSpell) { }
+
+ class spell_halion_marks_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_halion_marks_AuraScript);
+
+ public:
+ spell_halion_marks_AuraScript(uint32 summonSpell, uint32 removeSpell) : AuraScript(),
+ _summonSpellId(summonSpell), _removeSpellId(removeSpell) { }
+
+ bool Validate(SpellEntry const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(_summonSpellId))
+ return false;
+ return true;
+ }
+
+ /// We were purged. Force removed stacks to zero and trigger the appropriated remove handler.
+ void BeforeDispel(DispelInfo* dispelData)
+ {
+ // Prevent any stack from being removed at this point.
+ dispelData->SetRemovedCharges(0);
+
+ if (Unit* dispelledUnit = GetUnitOwner())
+ if (dispelledUnit->HasAura(_removeSpellId))
+ dispelledUnit->RemoveAurasDueToSpell(_removeSpellId, 0, 0, AURA_REMOVE_BY_EXPIRE);
+ }
+
+ void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE)
+ return;
+
+ // Stacks marker
+ GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, NULL, NULL, GetCasterGUID());
+ }
+
+ void Register()
+ {
+ OnDispel += AuraDispelFn(spell_halion_marks_AuraScript::BeforeDispel);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_halion_marks_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+
+ uint32 _summonSpellId;
+ uint32 _removeSpellId;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_halion_marks_AuraScript(_summonSpell, _removeSpell);
+ }
+
+ private:
+ uint32 _summonSpell;
+ uint32 _removeSpell;
+};
+
+class spell_halion_damage_aoe_summon : public SpellScriptLoader
+{
+ public:
+ spell_halion_damage_aoe_summon() : SpellScriptLoader("spell_halion_damage_aoe_summon") { }
+
+ class spell_halion_damage_aoe_summon_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_halion_damage_aoe_summon_SpellScript);
+
+ void HandleSummon(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ Unit* caster = GetCaster();
+ uint32 entry = uint32(GetSpellInfo()->Effects[effIndex].MiscValue);
+ SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(uint32(GetSpellInfo()->Effects[effIndex].MiscValueB));
+ uint32 duration = uint32(GetSpellInfo()->GetDuration());
+
+ Position pos;
+ caster->GetPosition(&pos);
+ if (Creature* summon = caster->GetMap()->SummonCreature(entry, pos, properties, duration, caster, GetSpellInfo()->Id))
+ if (summon->IsAIEnabled)
+ summon->AI()->SetData(DATA_STACKS_DISPELLED, GetSpellValue()->EffectBasePoints[EFFECT_1]);
+ }
+
+ void Register()
+ {
+ OnEffectHit += SpellEffectFn(spell_halion_damage_aoe_summon_SpellScript::HandleSummon, EFFECT_0, SPELL_EFFECT_SUMMON);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_halion_damage_aoe_summon_SpellScript();
+ }
+};
+
+class spell_halion_twilight_realm_handlers : public SpellScriptLoader
+{
+ public:
+ spell_halion_twilight_realm_handlers(const char* scriptName, uint32 beforeHitSpell, bool isApplyHandler) : SpellScriptLoader(scriptName),
+ _beforeHitSpell(beforeHitSpell), _isApplyHandler(isApplyHandler)
+ { }
+
+ class spell_halion_twilight_realm_handlers_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_halion_twilight_realm_handlers_AuraScript);
+
+ public:
+ spell_halion_twilight_realm_handlers_AuraScript(uint32 beforeHitSpell, bool isApplyHandler) : AuraScript(),
+ _isApply(isApplyHandler), _beforeHitSpellId(beforeHitSpell)
+ { }
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(_beforeHitSpellId))
+ return false;
+ return true;
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*handle*/)
+ {
+ GetTarget()->RemoveAurasDueToSpell(SPELL_TWILIGHT_REALM);
+ if (InstanceScript* instance = GetTarget()->GetInstanceScript())
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_UNK7);
+ }
+
+ void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*handle*/)
+ {
+ Unit* target = GetTarget();
+ if (!target)
+ return;
+
+ target->RemoveAurasDueToSpell(_beforeHitSpellId, 0, 0, AURA_REMOVE_BY_ENEMY_SPELL);
+ if (InstanceScript* instance = target->GetInstanceScript())
+ instance->SendEncounterUnit(ENCOUNTER_FRAME_UNK7);
+ }
+
+ void Register()
+ {
+ if (!_isApply)
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_halion_twilight_realm_handlers_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_halion_twilight_realm_handlers_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ else
+ AfterEffectApply += AuraEffectApplyFn(spell_halion_twilight_realm_handlers_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PHASE, AURA_EFFECT_HANDLE_REAL);
+ }
+
+ bool _isApply;
+ uint32 _beforeHitSpellId;
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_halion_twilight_realm_handlers_AuraScript(_beforeHitSpell, _isApplyHandler);
+ }
+
+ private:
+ uint32 _beforeHitSpell;
+ bool _isApplyHandler;
+};
+
+class spell_halion_clear_debuffs : public SpellScriptLoader
+{
+ public:
+ spell_halion_clear_debuffs() : SpellScriptLoader("spell_halion_clear_debuffs") { }
+
+ class spell_halion_clear_debuffs_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_halion_clear_debuffs_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_CLEAR_DEBUFFS))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_TWILIGHT_REALM))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ if (GetHitUnit()->HasAura(GetSpellInfo()->Effects[effIndex].CalcValue()))
+ GetHitUnit()->RemoveAurasDueToSpell(GetSpellInfo()->Effects[effIndex].CalcValue());
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_halion_clear_debuffs_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_halion_clear_debuffs_SpellScript();
+ }
+};
+
+class TwilightCutterSelector
+{
+ public:
+ TwilightCutterSelector(Unit* caster, Unit* cutterCaster) : _caster(caster), _cutterCaster(cutterCaster) {}
+
+ bool operator()(WorldObject* unit)
+ {
+ return !unit->IsInBetween(_caster, _cutterCaster, 4.0f);
+ }
+
+ private:
+ Unit* _caster;
+ Unit* _cutterCaster;
+};
+
+class spell_halion_twilight_cutter : public SpellScriptLoader
+{
+ public:
+ spell_halion_twilight_cutter() : SpellScriptLoader("spell_halion_twilight_cutter") { }
+
+ class spell_halion_twilight_cutter_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_halion_twilight_cutter_SpellScript);
+
+ void RemoveNotBetween(std::list<WorldObject*>& unitList)
+ {
+ if (unitList.empty())
+ return;
+
+ Unit* caster = GetCaster();
+ if (Aura* cutter = caster->GetAura(SPELL_TWILIGHT_CUTTER))
+ {
+ if (Unit* cutterCaster = cutter->GetCaster())
+ {
+ unitList.remove_if(TwilightCutterSelector(caster, cutterCaster));
+ return;
+ }
+ }
+
+ // In case cutter caster werent found for some reason
+ unitList.clear();
+ }
+
+ void Register()
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_halion_twilight_cutter_SpellScript::RemoveNotBetween, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_halion_twilight_cutter_SpellScript();
+ }
+};
+
+class spell_halion_twilight_phasing : public SpellScriptLoader
+{
+ public:
+ spell_halion_twilight_phasing() : SpellScriptLoader("spell_halion_twilight_phasing") { }
+
+ class spell_halion_twilight_phasing_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_halion_twilight_phasing_SpellScript);
+
+ void Phase()
+ {
+ Unit* caster = GetCaster();
+ caster->CastSpell(caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ(), SPELL_SUMMON_TWILIGHT_PORTAL, true);
+ caster->GetMap()->SummonCreature(NPC_TWILIGHT_HALION, HalionSpawnPos);
+ }
+
+ void Register()
+ {
+ OnHit += SpellHitFn(spell_halion_twilight_phasing_SpellScript::Phase);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_halion_twilight_phasing_SpellScript();
+ }
+};
+
+class spell_halion_summon_exit_portals : public SpellScriptLoader
+{
+ public:
+ spell_halion_summon_exit_portals() : SpellScriptLoader("spell_halion_summon_exit_portals") { }
+
+ class spell_halion_summon_exit_portals_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_halion_summon_exit_portals_SpellScript);
+
+ void OnSummon(SpellEffIndex effIndex)
+ {
+ WorldLocation summonPos = *GetExplTargetDest();
+ Position offset = {0.0f, 20.0f, 0.0f, 0.0f};
+ if (effIndex == EFFECT_1)
+ offset.m_positionY = -20.0f;
+
+ summonPos.RelocateOffset(offset);
+
+ SetExplTargetDest(summonPos);
+ GetHitDest()->RelocateOffset(offset);
+ }
+
+ void Register()
+ {
+ OnEffectLaunch += SpellEffectFn(spell_halion_summon_exit_portals_SpellScript::OnSummon, EFFECT_0, SPELL_EFFECT_SUMMON_OBJECT_WILD);
+ OnEffectLaunch += SpellEffectFn(spell_halion_summon_exit_portals_SpellScript::OnSummon, EFFECT_1, SPELL_EFFECT_SUMMON_OBJECT_WILD);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_halion_summon_exit_portals_SpellScript();
+ }
+};
+
+void AddSC_boss_halion()
+{
+ new boss_halion();
+ new boss_twilight_halion();
+
+ new npc_halion_controller();
+ new npc_meteor_strike_initial();
+ new npc_meteor_strike();
+ new npc_combustion_consumption();
+ new npc_orb_carrier();
+ new npc_living_inferno();
+ new npc_living_ember();
+
+ new go_twilight_portal();
+
+ new spell_halion_meteor_strike_marker();
+ new spell_halion_combustion_consumption("spell_halion_soul_consumption", SPELL_MARK_OF_CONSUMPTION);
+ new spell_halion_combustion_consumption("spell_halion_fiery_combustion", SPELL_MARK_OF_COMBUSTION);
+ new spell_halion_marks("spell_halion_mark_of_combustion", SPELL_FIERY_COMBUSTION_SUMMON, SPELL_FIERY_COMBUSTION);
+ new spell_halion_marks("spell_halion_mark_of_consumption", SPELL_SOUL_CONSUMPTION_SUMMON, SPELL_SOUL_CONSUMPTION);
+ new spell_halion_damage_aoe_summon();
+ new spell_halion_twilight_realm_handlers("spell_halion_leave_twilight_realm", SPELL_SOUL_CONSUMPTION, false);
+ new spell_halion_twilight_realm_handlers("spell_halion_enter_twilight_realm", SPELL_FIERY_COMBUSTION, true);
+ new spell_halion_summon_exit_portals();
+ new spell_halion_twilight_phasing();
+ new spell_halion_twilight_cutter();
+ new spell_halion_clear_debuffs();
+}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
index 5678bbbeb83..a6b50467538 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
@@ -37,18 +37,23 @@ class instance_ruby_sanctum : public InstanceMapScript
{
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
- BaltharusTheWarbornGUID = 0;
- GeneralZarithrianGUID = 0;
- SavianaRagefireGUID = 0;
- HalionGUID = 0;
- HalionControllerGUID = 0;
+ BaltharusTheWarbornGUID = 0;
+ GeneralZarithrianGUID = 0;
+ SavianaRagefireGUID = 0;
+ HalionGUID = 0;
+ TwilightHalionGUID = 0;
+ OrbCarrierGUID = 0;
+ OrbRotationFocusGUID = 0;
+ HalionControllerGUID = 0;
+ CombatStalkerGUID = 0;
CrystalChannelTargetGUID = 0;
- XerestraszaGUID = 0;
- BaltharusSharedHealth = 0;
- FlameWallsGUID = 0;
- FlameRingGUID = 0;
- memset(ZarithianSpawnStalkerGUID, 0, 2*sizeof(uint64));
- memset(BurningTreeGUID, 0, 4*sizeof(uint64));
+ XerestraszaGUID = 0;
+ BaltharusSharedHealth = 0;
+ FlameWallsGUID = 0;
+ FlameRingGUID = 0;
+
+ memset(ZarithrianSpawnStalkerGUID, 0, 2 * sizeof(uint64));
+ memset(BurningTreeGUID, 0, 4 * sizeof(uint64));
}
void OnCreatureCreate(Creature* creature)
@@ -67,19 +72,32 @@ class instance_ruby_sanctum : public InstanceMapScript
case NPC_HALION:
HalionGUID = creature->GetGUID();
break;
+ case NPC_TWILIGHT_HALION:
+ TwilightHalionGUID = creature->GetGUID();
+ break;
case NPC_HALION_CONTROLLER:
HalionControllerGUID = creature->GetGUID();
+ break;
+ case NPC_ORB_CARRIER:
+ OrbCarrierGUID = creature->GetGUID();
+ break;
+ case NPC_ORB_ROTATION_FOCUS:
+ OrbRotationFocusGUID = creature->GetGUID();
+ break;
+ case NPC_COMBAT_STALKER:
+ CombatStalkerGUID = creature->GetGUID();
+ break;
case NPC_BALTHARUS_TARGET:
CrystalChannelTargetGUID = creature->GetGUID();
break;
case NPC_XERESTRASZA:
XerestraszaGUID = creature->GetGUID();
break;
- case NPC_ZARITHIAN_SPAWN_STALKER:
- if (!ZarithianSpawnStalkerGUID[0])
- ZarithianSpawnStalkerGUID[0] = creature->GetGUID();
+ case NPC_ZARITHRIAN_SPAWN_STALKER:
+ if (!ZarithrianSpawnStalkerGUID[0])
+ ZarithrianSpawnStalkerGUID[0] = creature->GetGUID();
else
- ZarithianSpawnStalkerGUID[1] = creature->GetGUID();
+ ZarithrianSpawnStalkerGUID[1] = creature->GetGUID();
break;
default:
break;
@@ -101,6 +119,9 @@ class instance_ruby_sanctum : public InstanceMapScript
case GO_FLAME_RING:
FlameRingGUID = go->GetGUID();
break;
+ case GO_TWILIGHT_FLAME_RING:
+ TwilightFlameRingGUID = go->GetGUID();
+ break;
case GO_BURNING_TREE_1:
BurningTreeGUID[0] = go->GetGUID();
if (GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE)
@@ -152,24 +173,30 @@ class instance_ruby_sanctum : public InstanceMapScript
return SavianaRagefireGUID;
case DATA_GENERAL_ZARITHRIAN:
return GeneralZarithrianGUID;
- case DATA_ZARITHIAN_SPAWN_STALKER_1:
- return ZarithianSpawnStalkerGUID[0];
- case DATA_ZARITHIAN_SPAWN_STALKER_2:
- return ZarithianSpawnStalkerGUID[1];
+ case DATA_ZARITHRIAN_SPAWN_STALKER_1:
+ case DATA_ZARITHRIAN_SPAWN_STALKER_2:
+ return ZarithrianSpawnStalkerGUID[type - DATA_ZARITHRIAN_SPAWN_STALKER_1];
case DATA_HALION:
return HalionGUID;
+ case DATA_TWILIGHT_HALION:
+ return TwilightHalionGUID;
+ case DATA_ORB_CARRIER:
+ return OrbCarrierGUID;
+ case DATA_ORB_ROTATION_FOCUS:
+ return OrbRotationFocusGUID;
case DATA_HALION_CONTROLLER:
return HalionControllerGUID;
case DATA_BURNING_TREE_1:
- return BurningTreeGUID[0];
case DATA_BURNING_TREE_2:
- return BurningTreeGUID[1];
case DATA_BURNING_TREE_3:
- return BurningTreeGUID[2];
case DATA_BURNING_TREE_4:
- return BurningTreeGUID[3];
+ return BurningTreeGUID[type - DATA_BURNING_TREE_1];
case DATA_FLAME_RING:
return FlameRingGUID;
+ case DATA_TWILIGHT_FLAME_RING:
+ return TwilightFlameRingGUID;
+ case DATA_COMBAT_STALKER:
+ return CombatStalkerGUID;
default:
break;
}
@@ -180,7 +207,14 @@ class instance_ruby_sanctum : public InstanceMapScript
bool SetBossState(uint32 type, EncounterState state)
{
if (!InstanceScript::SetBossState(type, state))
+ {
+ // Summon Halion on instance loading if conditions are met. Without those lines,
+ // InstanceScript::SetBossState returns false, thus preventing the switch from being called.
+ if (type == DATA_HALION && state != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE && !GetData64(DATA_HALION_CONTROLLER))
+ if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
+ halionController->AI()->DoAction(ACTION_INTRO_HALION);
return false;
+ }
switch (type)
{
@@ -205,20 +239,30 @@ class instance_ruby_sanctum : public InstanceMapScript
break;
}
case DATA_GENERAL_ZARITHRIAN:
+ {
if (GetBossState(DATA_SAVIANA_RAGEFIRE) == DONE && GetBossState(DATA_BALTHARUS_THE_WARBORN) == DONE)
HandleGameObject(FlameWallsGUID, state != IN_PROGRESS);
- /*
- if (state == DONE)
+
+ // Not called at instance loading, no big deal.
+ if (state == DONE && GetBossState(DATA_HALION) != DONE)
if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
halionController->AI()->DoAction(ACTION_INTRO_HALION);
- */
break;
+ }
case DATA_HALION:
- /*
- if (state != IN_PROGRESS)
+ {
+ DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TOGGLE, 0);
+ DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 0);
+ DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, 0);
+
+ // Reopen rings on wipe or success
+ if (state == DONE || state == FAIL)
+ {
HandleGameObject(FlameRingGUID, true);
- */
+ HandleGameObject(TwilightFlameRingGUID, true);
+ }
break;
+ }
default:
break;
}
@@ -228,25 +272,18 @@ class instance_ruby_sanctum : public InstanceMapScript
void SetData(uint32 type, uint32 data)
{
- switch (type)
- {
- case DATA_BALTHARUS_SHARED_HEALTH:
- BaltharusSharedHealth = data;
- break;
- }
+ if (type != DATA_BALTHARUS_SHARED_HEALTH)
+ return;
+
+ BaltharusSharedHealth = data;
}
uint32 GetData(uint32 type)
{
- switch (type)
- {
- case DATA_BALTHARUS_SHARED_HEALTH:
- return BaltharusSharedHealth;
- default:
- break;
- }
+ if (type != DATA_BALTHARUS_SHARED_HEALTH)
+ return 0;
- return 0;
+ return BaltharusSharedHealth;
}
std::string GetSaveData()
@@ -260,6 +297,13 @@ class instance_ruby_sanctum : public InstanceMapScript
return saveStream.str();
}
+ void FillInitialWorldStates(WorldPacket& data)
+ {
+ data << uint32(WORLDSTATE_CORPOREALITY_MATERIAL) << uint32(50);
+ data << uint32(WORLDSTATE_CORPOREALITY_TWILIGHT) << uint32(50);
+ data << uint32(WORLDSTATE_CORPOREALITY_TOGGLE) << uint32(0);
+ }
+
void Load(char const* str)
{
if (!str)
@@ -298,13 +342,19 @@ class instance_ruby_sanctum : public InstanceMapScript
uint64 GeneralZarithrianGUID;
uint64 SavianaRagefireGUID;
uint64 HalionGUID;
+ uint64 TwilightHalionGUID;
uint64 HalionControllerGUID;
+ uint64 OrbCarrierGUID;
+ uint64 OrbRotationFocusGUID;
uint64 CrystalChannelTargetGUID;
uint64 XerestraszaGUID;
uint64 FlameWallsGUID;
- uint64 ZarithianSpawnStalkerGUID[2];
+ uint64 ZarithrianSpawnStalkerGUID[2];
uint64 BurningTreeGUID[4];
uint64 FlameRingGUID;
+ uint64 TwilightFlameRingGUID;
+ uint64 CombatStalkerGUID;
+
uint32 BaltharusSharedHealth;
};
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h
index 02ade2ff3e7..7eb1b73721c 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h
@@ -21,6 +21,7 @@
#include "SpellScript.h"
#include "Map.h"
#include "Creature.h"
+#include "GameObjectAI.h"
#define RSScriptName "instance_ruby_sanctum"
uint32 const EncounterCount = 4;
@@ -36,17 +37,22 @@ enum DataTypes
DATA_HALION = 3,
// Etc
- DATA_XERESTRASZA = 4,
- DATA_CRYSTAL_CHANNEL_TARGET = 5,
- DATA_BALTHARUS_SHARED_HEALTH = 6,
- DATA_ZARITHIAN_SPAWN_STALKER_1 = 7,
- DATA_ZARITHIAN_SPAWN_STALKER_2 = 8,
- DATA_HALION_CONTROLLER = 9,
- DATA_BURNING_TREE_1 = 10,
- DATA_BURNING_TREE_2 = 11,
- DATA_BURNING_TREE_3 = 12,
- DATA_BURNING_TREE_4 = 13,
- DATA_FLAME_RING = 14,
+ DATA_TWILIGHT_HALION = 4,
+ DATA_XERESTRASZA = 5,
+ DATA_CRYSTAL_CHANNEL_TARGET = 6,
+ DATA_BALTHARUS_SHARED_HEALTH = 7,
+ DATA_ZARITHRIAN_SPAWN_STALKER_1 = 8,
+ DATA_ZARITHRIAN_SPAWN_STALKER_2 = 9,
+ DATA_HALION_CONTROLLER = 10,
+ DATA_ORB_CARRIER = 11,
+ DATA_ORB_ROTATION_FOCUS = 12,
+ DATA_BURNING_TREE_1 = 13,
+ DATA_BURNING_TREE_2 = 14,
+ DATA_BURNING_TREE_3 = 15,
+ DATA_BURNING_TREE_4 = 16,
+ DATA_FLAME_RING = 17,
+ DATA_TWILIGHT_FLAME_RING = 18,
+ DATA_COMBAT_STALKER = 19,
};
enum SharedActions
@@ -66,14 +72,14 @@ enum CreaturesIds
// General Zarithrian
NPC_GENERAL_ZARITHRIAN = 39746,
NPC_ONYX_FLAMECALLER = 39814,
- NPC_ZARITHIAN_SPAWN_STALKER = 39794,
+ NPC_ZARITHRIAN_SPAWN_STALKER = 39794,
// Saviana Ragefire
NPC_SAVIANA_RAGEFIRE = 39747,
// Halion
NPC_HALION = 39863,
- NPC_HALION_TWILIGHT = 40142,
+ NPC_TWILIGHT_HALION = 40142,
NPC_HALION_CONTROLLER = 40146,
NPC_LIVING_INFERNO = 40681,
NPC_LIVING_EMBER = 40683,
@@ -81,6 +87,8 @@ enum CreaturesIds
NPC_ORB_ROTATION_FOCUS = 40091,
NPC_SHADOW_ORB_N = 40083,
NPC_SHADOW_ORB_S = 40100,
+ NPC_SHADOW_ORB_E = 40468,
+ NPC_SHADOW_ORB_W = 40469,
NPC_METEOR_STRIKE_MARK = 40029,
NPC_METEOR_STRIKE_NORTH = 40041,
NPC_METEOR_STRIKE_EAST = 40042,
@@ -88,6 +96,8 @@ enum CreaturesIds
NPC_METEOR_STRIKE_SOUTH = 40044,
NPC_METEOR_STRIKE_FLAME = 40055,
NPC_COMBUSTION = 40001,
+ NPC_CONSUMPTION = 40135,
+ NPC_COMBAT_STALKER = 40151,
// Xerestrasza
NPC_XERESTRASZA = 40429,
@@ -101,6 +111,7 @@ enum GameObjectsIds
GO_FIRE_FIELD = 203005,
GO_FLAME_WALLS = 203006,
GO_FLAME_RING = 203007,
+ GO_TWILIGHT_FLAME_RING = 203624,
GO_BURNING_TREE_1 = 203034,
GO_BURNING_TREE_2 = 203035,
GO_BURNING_TREE_3 = 203036,
@@ -114,6 +125,11 @@ enum WorldStatesRS
WORLDSTATE_CORPOREALITY_TOGGLE = 5051,
};
+enum InstanceSpell
+{
+ SPELL_BERSERK = 26662,
+};
+
template<class AI>
CreatureAI* GetRubySanctumAI(Creature* creature)
{
@@ -124,4 +140,15 @@ CreatureAI* GetRubySanctumAI(Creature* creature)
return NULL;
}
+template<class AI>
+GameObjectAI* GetRubySanctumAI(GameObject* go)
+{
+ if (InstanceMap* instance = go->GetMap()->ToInstanceMap())
+ if (instance->GetInstanceScript())
+ if (instance->GetScriptId() == sObjectMgr->GetScriptId(RSScriptName))
+ return new AI(go);
+
+ return NULL;
+}
+
#endif // RUBY_SANCTUM_H_
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp
index 270e31f4350..c691db4230c 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp
+++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_eregos.cpp
@@ -68,51 +68,6 @@ enum Actions
ACTION_SET_NORMAL_EVENTS = 1
};
-/*Ruby Drake,
-(npc 27756) (item 37860)
-(summoned by spell Ruby Essence = 37860 ---> Call Amber Drake == 49462 ---> Summon 27756)
-*/
-enum RubyDrake
-{
- NPC_RUBY_DRAKE_VEHICLE = 27756,
- SPELL_RIDE_RUBY_DRAKE_QUE = 49463, //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49464
- SPELL_RUBY_DRAKE_SADDLE = 49464, //Allows you to ride on the back of an Amber Drake. ---> Dummy
- SPELL_RUBY_SEARING_WRATH = 50232, //(60 yds) - Instant - Breathes a stream of fire at an enemy dragon, dealing 6800 to 9200 Fire damage and then jumping to additional dragons within 30 yards. Each jump increases the damage by 50%. Affects up to 5 total targets
- SPELL_RUBY_EVASIVE_AURA = 50248, //Instant - Allows the Ruby Drake to generate Evasive Charges when hit by hostile attacks and spells.
- SPELL_RUBY_EVASIVE_MANEUVERS = 50240, //Instant - 5 sec. cooldown - Allows your drake to dodge all incoming attacks and spells. Requires Evasive Charges to use. Each attack or spell dodged while this ability is active burns one Evasive Charge. Lasts 30 sec. or until all charges are exhausted.
- //you do not have acces to until you kill Mage-Lord Urom
- SPELL_RUBY_MARTYR = 50253 //Instant - 10 sec. cooldown - Redirect all harmful spells cast at friendly drakes to yourself for 10 sec.
-};
-/*Amber Drake,
-(npc 27755) (item 37859)
-(summoned by spell Amber Essence = 37859 ---> Call Amber Drake == 49461 ---> Summon 27755)
-*/
-enum AmberDrake
-{
- NPC_AMBER_DRAKE_VEHICLE = 27755,
- SPELL_RIDE_AMBER_DRAKE_QUE = 49459, //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49460
- SPELL_AMBER_DRAKE_SADDLE = 49460, //Allows you to ride on the back of an Amber Drake. ---> Dummy
- SPELL_AMBER_SHOCK_LANCE = 49840, //(60 yds) - Instant - Deals 4822 to 5602 Arcane damage and detonates all Shock Charges on an enemy dragon. Damage is increased by 6525 for each detonated.
-// SPELL_AMBER_STOP_TIME //Instant - 1 min cooldown - Halts the passage of time, freezing all enemy dragons in place for 10 sec. This attack applies 5 Shock Charges to each affected target.
- //you do not have access to until you kill the Mage-Lord Urom.
- SPELL_AMBER_TEMPORAL_RIFT = 49592 //(60 yds) - Channeled - Channels a temporal rift on an enemy dragon for 10 sec. While trapped in the rift, all damage done to the target is increased by 100%. In addition, for every 15, 000 damage done to a target affected by Temporal Rift, 1 Shock Charge is generated.
-};
-
-/*Emerald Drake,
-(npc 27692) (item 37815),
- (summoned by spell Emerald Essence = 37815 ---> Call Emerald Drake == 49345 ---> Summon 27692)
-*/
-enum EmeraldDrake
-{
- NPC_EMERALD_DRAKE_VEHICLE = 27692,
- SPELL_RIDE_EMERALD_DRAKE_QUE = 49427, //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49346
- SPELL_EMERALD_DRAKE_SADDLE = 49346, //Allows you to ride on the back of an Amber Drake. ---> Dummy
- SPELL_EMERALD_LEECHING_POISON = 50328, //(60 yds) - Instant - Poisons the enemy dragon, leeching 1300 to the caster every 2 sec. for 12 sec. Stacks up to 3 times.
- SPELL_EMERALD_TOUCH_THE_NIGHTMARE = 50341, //(60 yds) - Instant - Consumes 30% of the caster's max health to inflict 25, 000 nature damage to an enemy dragon and reduce the damage it deals by 25% for 30 sec.
- // you do not have access to until you kill the Mage-Lord Urom
- SPELL_EMERALD_DREAM_FUNNEL = 50344 //(60 yds) - Channeled - Transfers 5% of the caster's max health to a friendly drake every second for 10 seconds as long as the caster channels.
-};
-
enum EregosData
{
DATA_RUBY_VOID = 0, // http://www.wowhead.com/achievement=2044
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp
index 791bc0180e5..179dedb290b 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp
+++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp
@@ -38,7 +38,8 @@ enum Spells
SPELL_SUMMON_MENAGERIE_3 = 50496,
SPELL_TELEPORT = 51112, //Teleports to the center of Oculus
SPELL_TIME_BOMB = 51121, //Deals arcane damage to a random player, and after 6 seconds, deals zone damage to nearby equal to the health missing of the target afflicted by the debuff.
- SPELL_TIME_BOMB_2 = 59376
+ SPELL_TIME_BOMB_2 = 59376,
+ SPELL_EVOCATE = 51602 // He always cast it on reset or after teleportation
};
enum Yells
@@ -103,8 +104,7 @@ public:
void Reset()
{
- if (instance->GetBossState(DATA_VAROS_EVENT) != DONE)
- DoCast(SPELL_ARCANE_SHIELD);
+ me->CastSpell(me, SPELL_EVOCATE);
_Reset();
@@ -307,14 +307,17 @@ public:
case SPELL_SUMMON_MENAGERIE:
me->SetHomePosition(968.66f, 1042.53f, 527.32f, 0.077f);
LeaveCombat();
+ me->CastSpell(me, SPELL_EVOCATE);
break;
case SPELL_SUMMON_MENAGERIE_2:
me->SetHomePosition(1164.02f, 1170.85f, 527.321f, 3.66f);
LeaveCombat();
+ me->CastSpell(me, SPELL_EVOCATE);
break;
case SPELL_SUMMON_MENAGERIE_3:
me->SetHomePosition(1118.31f, 1080.377f, 508.361f, 4.25f);
LeaveCombat();
+ me->CastSpell(me, SPELL_EVOCATE);
break;
case SPELL_TELEPORT:
//! Unconfirmed, previous below
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp
index ca4ae883747..a0b5aded315 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp
+++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp
@@ -56,9 +56,13 @@ public:
eregosCacheGUID = 0;
- azureDragonsList.clear();
+ gwhelpList.clear();
gameObjectList.clear();
- }
+
+ belgaristraszGUID = 0;
+ eternosGUID = 0;
+ verdisaGUID = 0;
+}
void OnUnitDeath(Unit* unit)
{
@@ -112,17 +116,49 @@ public:
break;
case NPC_VAROS:
varosGUID = creature->GetGUID();
+ if (GetBossState(DATA_DRAKOS_EVENT) == DONE)
+ creature->SetPhaseMask(1, true);
break;
case NPC_UROM:
uromGUID = creature->GetGUID();
+ if (GetBossState(DATA_VAROS_EVENT) == DONE)
+ creature->SetPhaseMask(1, true);
break;
case NPC_EREGOS:
eregosGUID = creature->GetGUID();
+ if (GetBossState(DATA_UROM_EVENT) == DONE)
+ creature->SetPhaseMask(1, true);
break;
case NPC_CENTRIFUGE_CONSTRUCT:
if (creature->isAlive())
DoUpdateWorldState(WORLD_STATE_CENTRIFUGE_CONSTRUCT_AMOUNT, ++centrifugueConstructCounter);
break;
+ case NPC_BELGARISTRASZ:
+ belgaristraszGUID = creature->GetGUID();
+ if (GetBossState(DATA_DRAKOS_EVENT) == DONE)
+ creature->SetWalk(true),
+ creature->GetMotionMaster()->MovePoint(0, 941.453f, 1044.1f, 359.967f),
+ creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ break;
+ case NPC_ETERNOS:
+ eternosGUID = creature->GetGUID();
+ if (GetBossState(DATA_DRAKOS_EVENT) == DONE)
+ creature->SetWalk(true),
+ creature->GetMotionMaster()->MovePoint(0, 943.202f, 1059.35f, 359.967f),
+ creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ break;
+ case NPC_VERDISA:
+ verdisaGUID = creature->GetGUID();
+ if (GetBossState(DATA_DRAKOS_EVENT) == DONE)
+ creature->SetWalk(true),
+ creature->GetMotionMaster()->MovePoint(0, 949.188f, 1032.91f, 359.967f),
+ creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ break;
+ case NPC_GREATER_WHELP:
+ if (GetBossState(DATA_UROM_EVENT) == DONE)
+ creature->SetPhaseMask(1, true);
+ gwhelpList.push_back(creature->GetGUID());
+ break;
}
}
@@ -159,11 +195,22 @@ public:
DoUpdateWorldState(WORLD_STATE_CENTRIFUGE_CONSTRUCT_SHOW, 1);
DoUpdateWorldState(WORLD_STATE_CENTRIFUGE_CONSTRUCT_AMOUNT, centrifugueConstructCounter);
OpenCageDoors();
+ FreeDragons();
+ if (Creature* varos = instance->GetCreature(varosGUID))
+ varos->SetPhaseMask(1, true);
}
break;
case DATA_VAROS_EVENT:
if (state == DONE)
DoUpdateWorldState(WORLD_STATE_CENTRIFUGE_CONSTRUCT_SHOW, 0);
+ if (Creature* urom = instance->GetCreature(uromGUID))
+ urom->SetPhaseMask(1, true);
+ break;
+ case DATA_UROM_EVENT:
+ if (state == DONE)
+ if (Creature* eregos = instance->GetCreature(eregosGUID))
+ eregos->SetPhaseMask(1, true);
+ GreaterWhelps();
break;
case DATA_EREGOS_EVENT:
if (state == DONE)
@@ -221,6 +268,31 @@ public:
}
}
+ void FreeDragons()
+ {
+ if (Creature* belgaristrasz = instance->GetCreature(belgaristraszGUID))
+ belgaristrasz->SetWalk(true),
+ belgaristrasz->GetMotionMaster()->MovePoint(0, 941.453f, 1044.1f, 359.967f);
+ if (Creature* eternos = instance->GetCreature(eternosGUID))
+ eternos->SetWalk(true),
+ eternos->GetMotionMaster()->MovePoint(0, 943.202f, 1059.35f, 359.967f);
+ if (Creature* verdisa = instance->GetCreature(verdisaGUID))
+ verdisa->SetWalk(true),
+ verdisa->GetMotionMaster()->MovePoint(0, 949.188f, 1032.91f, 359.967f);
+ }
+
+ void GreaterWhelps()
+ {
+ if (gwhelpList.empty())
+ return;
+
+ for (std::list<uint64>::const_iterator itr = gwhelpList.begin(); itr != gwhelpList.end(); ++itr)
+ {
+ if (Creature* gwhelp = instance->GetCreature(*itr))
+ gwhelp->SetPhaseMask(1, true);
+ }
+ }
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
@@ -269,6 +341,10 @@ public:
uint64 uromGUID;
uint64 eregosGUID;
+ uint64 belgaristraszGUID;
+ uint64 eternosGUID;
+ uint64 verdisaGUID;
+
uint8 platformUrom;
uint8 centrifugueConstructCounter;
@@ -277,9 +353,8 @@ public:
std::string str_data;
std::list<uint64> gameObjectList;
- std::list<uint64> azureDragonsList;
+ std::list<uint64> gwhelpList;
};
-
};
void AddSC_instance_oculus()
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
index 1d8b5b986f6..39fff139b52 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
+++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
@@ -20,6 +20,8 @@
#include "ScriptedGossip.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
+#include "Vehicle.h"
+#include "CombatAI.h"
#include "oculus.h"
#define GOSSIP_ITEM_DRAKES "So where do we go from here?"
@@ -32,7 +34,7 @@
#define HAS_ESSENCE(a) ((a)->HasItemCount(ITEM_EMERALD_ESSENCE, 1) || (a)->HasItemCount(ITEM_AMBER_ESSENCE, 1) || (a)->HasItemCount(ITEM_RUBY_ESSENCE, 1))
-enum Drakes
+enum GossipNPCs
{
GOSSIP_TEXTID_DRAKES = 13267,
GOSSIP_TEXTID_BELGARISTRASZ1 = 12916,
@@ -49,23 +51,65 @@ enum Drakes
ITEM_AMBER_ESSENCE = 37859,
ITEM_RUBY_ESSENCE = 37860,
- NPC_VERDISA = 27657,
- NPC_BELGARISTRASZ = 27658,
- NPC_ETERNOS = 27659,
+ SPELL_SHOCK_CHARGE = 49836
+};
+
+enum Drakes
+{
+/*Ruby Drake,
+(npc 27756) (item 37860)
+(summoned by spell Ruby Essence = 37860 ---> Call Amber Drake == 49462 ---> Summon 27756)
+*/
+ SPELL_RIDE_RUBY_DRAKE_QUE = 49463, //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49464
+ SPELL_RUBY_DRAKE_SADDLE = 49464, //Allows you to ride on the back of an Amber Drake. ---> Dummy
+ SPELL_RUBY_SEARING_WRATH = 50232, //(60 yds) - Instant - Breathes a stream of fire at an enemy dragon, dealing 6800 to 9200 Fire damage and then jumping to additional dragons within 30 yards. Each jump increases the damage by 50%. Affects up to 5 total targets
+ SPELL_RUBY_EVASIVE_AURA = 50248, //Instant - Allows the Ruby Drake to generate Evasive Charges when hit by hostile attacks and spells.
+ SPELL_RUBY_EVASIVE_MANEUVERS = 50240, //Instant - 5 sec. cooldown - Allows your drake to dodge all incoming attacks and spells. Requires Evasive Charges to use. Each attack or spell dodged while this ability is active burns one Evasive Charge. Lasts 30 sec. or until all charges are exhausted.
+ //you do not have acces to until you kill Mage-Lord Urom
+ SPELL_RUBY_MARTYR = 50253, //Instant - 10 sec. cooldown - Redirect all harmful spells cast at friendly drakes to yourself for 10 sec.
+
+/*Amber Drake,
+(npc 27755) (item 37859)
+(summoned by spell Amber Essence = 37859 ---> Call Amber Drake == 49461 ---> Summon 27755)
+*/
- SPELL_SHOCK_CHARGE = 49836,
+ SPELL_RIDE_AMBER_DRAKE_QUE = 49459, //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49460
+ SPELL_AMBER_DRAKE_SADDLE = 49460, //Allows you to ride on the back of an Amber Drake. ---> Dummy
+ SPELL_AMBER_SHOCK_LANCE = 49840, //(60 yds) - Instant - Deals 4822 to 5602 Arcane damage and detonates all Shock Charges on an enemy dragon. Damage is increased by 6525 for each detonated.
+ // SPELL_AMBER_STOP_TIME //Instant - 1 min cooldown - Halts the passage of time, freezing all enemy dragons in place for 10 sec. This attack applies 5 Shock Charges to each affected target.
+ //you do not have access to until you kill the Mage-Lord Urom.
+ SPELL_AMBER_TEMPORAL_RIFT = 49592, //(60 yds) - Channeled - Channels a temporal rift on an enemy dragon for 10 sec. While trapped in the rift, all damage done to the target is increased by 100%. In addition, for every 15, 000 damage done to a target affected by Temporal Rift, 1 Shock Charge is generated.
+
+/*Emerald Drake,
+(npc 27692) (item 37815),
+ (summoned by spell Emerald Essence = 37815 ---> Call Emerald Drake == 49345 ---> Summon 27692)
+*/
+ SPELL_RIDE_EMERALD_DRAKE_QUE = 49427, //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49346
+ SPELL_EMERALD_DRAKE_SADDLE = 49346, //Allows you to ride on the back of an Amber Drake. ---> Dummy
+ SPELL_EMERALD_LEECHING_POISON = 50328, //(60 yds) - Instant - Poisons the enemy dragon, leeching 1300 to the caster every 2 sec. for 12 sec. Stacks up to 3 times.
+ SPELL_EMERALD_TOUCH_THE_NIGHTMARE = 50341, //(60 yds) - Instant - Consumes 30% of the caster's max health to inflict 25, 000 nature damage to an enemy dragon and reduce the damage it deals by 25% for 30 sec.
+ // you do not have access to until you kill the Mage-Lord Urom
+ SPELL_EMERALD_DREAM_FUNNEL = 50344, //(60 yds) - Channeled - Transfers 5% of the caster's max health to a friendly drake every second for 10 seconds as long as the caster channels.
};
enum Says
{
- SAY_VAROS = 0,
- SAY_UROM = 1
+ SAY_VAROS = 0,
+ SAY_UROM = 1,
+ SAY_BELGARISTRASZ = 0,
+ SAY_DRAKES_TAKEOFF = 0,
+ WHISPER_DRAKES_WELCOME = 1,
+ WHISPER_DRAKES_ABILITIES = 2,
+ WHISPER_DRAKES_SPECIAL = 3,
+ WHISPER_DRAKES_LOWHEALTH = 4
};
-class npc_oculus_drake : public CreatureScript
+class npc_verdisa_beglaristrasz_eternos : public CreatureScript
{
public:
- npc_oculus_drake() : CreatureScript("npc_oculus_drake") { }
+ npc_verdisa_beglaristrasz_eternos() : CreatureScript("npc_verdisa_beglaristrasz_eternos") { }
+
+ InstanceScript* instance;
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action)
{
@@ -184,6 +228,28 @@ public:
return true;
}
+ struct npc_verdisa_beglaristrasz_eternosAI : public ScriptedAI
+ {
+ npc_verdisa_beglaristrasz_eternosAI(Creature* creature) : ScriptedAI(creature) { }
+
+ void MovementInform(uint32 type, uint32 id)
+ {
+ // When Belgaristraz finish his moving say grateful text
+ if (me->GetEntry() == NPC_BELGARISTRASZ)
+ if (id == 0)
+ {
+ Talk(SAY_BELGARISTRASZ);
+ }
+ // The gossip flag should activate when Drakos die and not from DB
+ if (id == 0)
+ me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new npc_verdisa_beglaristrasz_eternosAI(creature);
+ }
};
class npc_image_belgaristrasz : public CreatureScript
@@ -193,7 +259,7 @@ public:
struct npc_image_belgaristraszAI : public ScriptedAI
{
- npc_image_belgaristraszAI(Creature* creature) : ScriptedAI(creature) {}
+ npc_image_belgaristraszAI(Creature* creature) : ScriptedAI(creature) { }
void IsSummonedBy(Unit* summoner)
{
@@ -216,6 +282,176 @@ public:
}
};
+class npc_ruby_emerald_amber_drake : public CreatureScript
+{
+public:
+ npc_ruby_emerald_amber_drake() : CreatureScript("npc_ruby_emerald_amber_drake") { }
+
+ struct npc_ruby_emerald_amber_drakeAI : public VehicleAI
+ {
+ npc_ruby_emerald_amber_drakeAI(Creature* creature) : VehicleAI(creature)
+ {
+ instance = creature->GetInstanceScript();
+ }
+
+ InstanceScript* instance;
+
+ uint64 summonerGUID;
+ uint32 WelcomeTimer;
+ uint32 WelcomeSequelTimer;
+ uint32 SpecialTimer;
+ uint32 WarningTimer;
+ uint32 TakeOffTimer;
+
+ bool WelcomeOff;
+ bool WelcomeSequelOff;
+ bool SpecialOff;
+ bool HealthWarningOff;
+ bool DisableTakeOff;
+
+ void Reset()
+ {
+ summonerGUID = 0;
+ WelcomeTimer = 4500;
+ WelcomeSequelTimer = 4500;
+ SpecialTimer = 10000;
+ WarningTimer = 25000;
+ TakeOffTimer = 3500;
+
+ WelcomeOff = false;
+ WelcomeSequelOff = false;
+ SpecialOff = false;
+ HealthWarningOff = false;
+ DisableTakeOff = false;
+ }
+
+ void IsSummonedBy(Unit* summoner)
+ {
+ if (instance->GetBossState(DATA_EREGOS_EVENT) == IN_PROGRESS)
+ if (Creature* eregos = me->FindNearestCreature(NPC_EREGOS, 450.0f, true))
+ {
+ eregos->DespawnOrUnsummon(); // On retail this kills abusive call of drake during engaged Eregos
+ }
+ summonerGUID = summoner->GetGUID();
+ me->SetFacingToObject(summoner);
+ // TO DO: Drake Ques should be casted from vehicle to player, however the way core handle triggered spells from auras break it no matter the conditions. So this change the caster and give the same result until someone fix triggered spells from auras that involve implicit targets or make exception for this case.
+ if (me->GetEntry() == NPC_RUBY_DRAKE_VEHICLE)
+ summoner->CastSpell(summoner, SPELL_RIDE_RUBY_DRAKE_QUE);
+ if (me->GetEntry() == NPC_EMERALD_DRAKE_VEHICLE)
+ summoner->CastSpell(summoner, SPELL_RIDE_EMERALD_DRAKE_QUE);
+ if (me->GetEntry() == NPC_AMBER_DRAKE_VEHICLE)
+ summoner->CastSpell(summoner, SPELL_RIDE_AMBER_DRAKE_QUE);
+ Position pos;
+ summoner->GetPosition(&pos);
+ me->GetMotionMaster()->MovePoint(0, pos);
+ }
+
+ void MovementInform(uint32 type, uint32 id)
+ {
+ if (type == POINT_MOTION_TYPE && id == 0)
+ {
+ me->SetDisableGravity(false); // Needed this for proper animation after spawn, the summon in air fall to ground bug leave no other option for now, if this isn't used the drake will only walk on move.
+ }
+ }
+
+ void UpdateAI(const uint32 diff)
+ {
+ if (!(instance->GetBossState(DATA_VAROS_EVENT) == DONE))
+ {
+ if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE))
+ {
+ if (!(WelcomeOff))
+ {
+ if (WelcomeTimer <= diff)
+ {
+ Talk(WHISPER_DRAKES_WELCOME, me->GetCreatorGUID());
+ WelcomeOff = true;
+ WelcomeSequelOff = true;
+ }
+ else WelcomeTimer -= diff;
+ }
+ }
+ }
+ if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE))
+ {
+ if (WelcomeSequelOff)
+ {
+ if (WelcomeSequelTimer <= diff)
+ {
+ Talk(WHISPER_DRAKES_ABILITIES, me->GetCreatorGUID());
+ WelcomeSequelOff = false;
+ }
+ else WelcomeSequelTimer -= diff;
+ }
+ }
+ if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE))
+ {
+ if (instance->GetBossState(DATA_UROM_EVENT) == DONE)
+ {
+ if (!(SpecialOff))
+ {
+ if (SpecialTimer <= diff)
+ {
+ Talk(WHISPER_DRAKES_SPECIAL, me->GetCreatorGUID());
+ SpecialOff = true;
+ }
+ else SpecialTimer -= diff;
+ }
+ }
+ }
+ if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE))
+ {
+ if (!(HealthWarningOff))
+ {
+ if (me->GetHealthPct() <= 40.0f)
+ {
+ Talk(WHISPER_DRAKES_LOWHEALTH, me->GetCreatorGUID());
+ HealthWarningOff = true;
+ }
+ }
+ }
+ if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE))
+ {
+ if (HealthWarningOff)
+ {
+ if (WarningTimer <= diff)
+ {
+ HealthWarningOff = false;
+ WarningTimer = 25000;
+ }
+ else WarningTimer -= diff;
+ }
+ }
+ if (!(me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE)))
+ {
+ if (!(DisableTakeOff))
+ {
+ if (TakeOffTimer <= diff)
+ {
+ me->DespawnOrUnsummon(2050);
+ me->SetOrientation(2.5f);
+ me->SetSpeed(MOVE_FLIGHT, 1.0f, true);
+ Talk(SAY_DRAKES_TAKEOFF);
+ Position pos;
+ me->GetPosition(&pos);
+ pos.m_positionX += 10.0f;
+ pos.m_positionY += 10.0f;
+ pos.m_positionZ += 12.0f;
+ me->GetMotionMaster()->MovePoint(1, pos);
+ DisableTakeOff = true;
+ }
+ else TakeOffTimer -= diff;
+ }
+ }
+ };
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new npc_ruby_emerald_amber_drakeAI(creature);
+ }
+};
+
class spell_gen_stop_time : public SpellScriptLoader
{
public:
@@ -247,9 +483,50 @@ public:
}
};
+class spell_call_ruby_emerald_amber_drake : public SpellScriptLoader
+{
+public:
+ spell_call_ruby_emerald_amber_drake() : SpellScriptLoader("spell_call_ruby_emerald_amber_drake") { }
+
+ class spell_call_ruby_emerald_amber_drake_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_call_ruby_emerald_amber_drake_SpellScript);
+
+ void ChangeSummonPos(SpellEffIndex /*effIndex*/)
+ {
+ // Adjust effect summon position
+ WorldLocation summonPos = *GetExplTargetDest();
+ Position offset = {0.0f, 0.0f, 12.0f, 0.0f};
+ summonPos.RelocateOffset(offset);
+ SetExplTargetDest(summonPos);
+ GetHitDest()->RelocateOffset(offset);
+ }
+
+ void ModDestHeight(SpellEffIndex /*effIndex*/)
+ {
+ // Used to cast visual effect at proper position
+ Position offset = {0.0f, 0.0f, 12.0f, 0.0f};
+ const_cast<WorldLocation*>(GetExplTargetDest())->RelocateOffset(offset);
+ }
+
+ void Register()
+ {
+ OnEffectHit += SpellEffectFn(spell_call_ruby_emerald_amber_drake_SpellScript::ChangeSummonPos, EFFECT_0, SPELL_EFFECT_SUMMON);
+ OnEffectLaunch += SpellEffectFn(spell_call_ruby_emerald_amber_drake_SpellScript::ModDestHeight, EFFECT_0, SPELL_EFFECT_SUMMON);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_call_ruby_emerald_amber_drake_SpellScript();
+ }
+};
+
void AddSC_oculus()
{
- new npc_oculus_drake();
+ new npc_verdisa_beglaristrasz_eternos();
new npc_image_belgaristrasz();
+ new npc_ruby_emerald_amber_drake();
new spell_gen_stop_time();
+ new spell_call_ruby_emerald_amber_drake();
}
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h
index 81d1e9f9ea8..c536b43f905 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h
+++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h
@@ -35,15 +35,22 @@ enum Data64
DATA_EREGOS
};
-enum Bosses
+enum Bosses_NPCs
{
NPC_DRAKOS = 27654,
NPC_VAROS = 27447,
NPC_UROM = 27655,
NPC_EREGOS = 27656,
- NPC_AZURE_RING_GUARDIAN = 28236,
- NPC_CENTRIFUGE_CONSTRUCT = 27641,
+ NPC_AZURE_RING_GUARDIAN = 28236,
+ NPC_CENTRIFUGE_CONSTRUCT = 27641,
+ NPC_RUBY_DRAKE_VEHICLE = 27756,
+ NPC_EMERALD_DRAKE_VEHICLE = 27692,
+ NPC_AMBER_DRAKE_VEHICLE = 27755,
+ NPC_VERDISA = 27657,
+ NPC_BELGARISTRASZ = 27658,
+ NPC_ETERNOS = 27659,
+ NPC_GREATER_WHELP = 28276
};
enum GameObjects
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp
index 0967c38c2e7..92e56d4dd9a 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp
@@ -255,7 +255,12 @@ class boss_steelbreaker : public CreatureScript
{
DoScriptText(RAND(SAY_STEELBREAKER_DEATH_1, SAY_STEELBREAKER_DEATH_2), me);
if (IsEncounterComplete(instance, me))
- instance->SetData(BOSS_ASSEMBLY_OF_IRON, DONE);
+ {
+ instance->SetBossState(BOSS_ASSEMBLY_OF_IRON, DONE);
+ instance->SetBossState(BOSS_STEELBREAKER, DONE);
+ instance->SetBossState(BOSS_MOLGEIM, DONE);
+ instance->SetBossState(BOSS_BRUNDIR, DONE);
+ }
else
me->SetLootRecipient(NULL);
@@ -379,7 +384,12 @@ class boss_runemaster_molgeim : public CreatureScript
{
DoScriptText(RAND(SAY_MOLGEIM_DEATH_1, SAY_MOLGEIM_DEATH_2), me);
if (IsEncounterComplete(instance, me))
- instance->SetData(BOSS_ASSEMBLY_OF_IRON, DONE);
+ {
+ instance->SetBossState(BOSS_ASSEMBLY_OF_IRON, DONE);
+ instance->SetBossState(BOSS_STEELBREAKER, DONE);
+ instance->SetBossState(BOSS_MOLGEIM, DONE);
+ instance->SetBossState(BOSS_BRUNDIR, DONE);
+ }
else
me->SetLootRecipient(NULL);
@@ -620,7 +630,12 @@ class boss_stormcaller_brundir : public CreatureScript
{
DoScriptText(RAND(SAY_BRUNDIR_DEATH_1, SAY_BRUNDIR_DEATH_2), me);
if (IsEncounterComplete(instance, me))
- instance->SetData(BOSS_ASSEMBLY_OF_IRON, DONE);
+ {
+ instance->SetBossState(BOSS_ASSEMBLY_OF_IRON, DONE);
+ instance->SetBossState(BOSS_STEELBREAKER, DONE);
+ instance->SetBossState(BOSS_MOLGEIM, DONE);
+ instance->SetBossState(BOSS_BRUNDIR, DONE);
+ }
else
me->SetLootRecipient(NULL);
diff --git a/src/server/scripts/Northrend/wintergrasp.cpp b/src/server/scripts/Northrend/wintergrasp.cpp
index 59e9a31c4cf..2aed813550d 100644
--- a/src/server/scripts/Northrend/wintergrasp.cpp
+++ b/src/server/scripts/Northrend/wintergrasp.cpp
@@ -541,6 +541,11 @@ public:
}
};
+enum WgTeleport
+{
+ SPELL_WINTERGRASP_TELEPORT_TRIGGER = 54643,
+};
+
class spell_wintergrasp_defender_teleport : public SpellScriptLoader
{
public:
@@ -554,7 +559,7 @@ public:
{
if (Battlefield* wg = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG))
if (Player* target = GetExplTargetUnit()->ToPlayer())
- if (target->GetTeamId() != wg->GetDefenderTeam())
+ if (target->GetTeamId() != wg->GetDefenderTeam() || target->HasAura(SPELL_WINTERGRASP_TELEPORT_TRIGGER))
return SPELL_FAILED_BAD_TARGETS;
return SPELL_CAST_OK;
}
@@ -571,6 +576,37 @@ public:
}
};
+class spell_wintergrasp_defender_teleport_trigger : public SpellScriptLoader
+{
+public:
+ spell_wintergrasp_defender_teleport_trigger() : SpellScriptLoader("spell_wintergrasp_defender_teleport_trigger") { }
+
+ class spell_wintergrasp_defender_teleport_trigger_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_wintergrasp_defender_teleport_trigger_SpellScript);
+
+ void HandleDummy(SpellEffIndex /*effindex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ {
+ WorldLocation loc;
+ target->GetPosition(&loc);
+ SetExplTargetDest(loc);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_defender_teleport_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_wintergrasp_defender_teleport_trigger_SpellScript();
+ }
+};
+
void AddSC_wintergrasp()
{
new npc_wg_queue();
@@ -582,4 +618,5 @@ void AddSC_wintergrasp()
new spell_wintergrasp_grab_passenger();
new achievement_wg_didnt_stand_a_chance();
new spell_wintergrasp_defender_teleport();
+ new spell_wintergrasp_defender_teleport_trigger();
}
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index 31aafe8dd38..9c922f2c6fb 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -277,27 +277,31 @@ class spell_hun_masters_call : public SpellScriptLoader
return true;
}
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* ally = GetHitUnit())
+ if (Player* caster = GetCaster()->ToPlayer())
+ if (Pet* target = caster->GetPet())
+ {
+ TriggerCastFlags castMask = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_CASTER_AURASTATE);
+ target->CastSpell(ally, GetEffectValue(), castMask);
+ target->CastSpell(ally, GetSpellInfo()->Effects[EFFECT_0].CalcValue(), castMask);
+ }
+ }
+
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Unit* target = GetHitUnit())
{
// Cannot be processed while pet is dead
TriggerCastFlags castMask = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_CASTER_AURASTATE);
- target->CastSpell(target, GetEffectValue(), castMask);
target->CastSpell(target, HUNTER_SPELL_MASTERS_CALL_TRIGGERED, castMask);
- // there is a possibility that this effect should access effect 0 (dummy) target, but i dubt that
- // it's more likely that on on retail it's possible to call target selector based on dbc values
- // anyways, we're using GetExplTargetUnit() here and it's ok
- if (Unit* ally = GetExplTargetUnit())
- {
- target->CastSpell(ally, GetEffectValue(), castMask);
- target->CastSpell(ally, GetSpellInfo()->Effects[EFFECT_0].CalcValue(), castMask);
- }
}
}
void Register()
{
+ OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp
index 3ce136b9737..2a5d58122ed 100644
--- a/src/server/scripts/World/go_scripts.cpp
+++ b/src/server/scripts/World/go_scripts.cpp
@@ -53,12 +53,14 @@ EndContentData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
+#include "GameObjectAI.h"
+#include "Spell.h"
/*######
## go_cat_figurine
######*/
-enum eCatFigurine
+enum CatFigurine
{
SPELL_SUMMON_GHOST_SABER = 5968,
};
@@ -178,7 +180,7 @@ public:
## go_gilded_brazier (Paladin First Trail quest (9678))
######*/
-enum eGildedBrazier
+enum GildedBrazier
{
NPC_STILLBLADE = 17716,
};
@@ -282,7 +284,7 @@ public:
## go_ethereum_prison
######*/
-enum eEthereumPrison
+enum EthereumPrison
{
SPELL_REP_LC = 39456,
SPELL_REP_SHAT = 39457,
@@ -367,7 +369,7 @@ public:
## go_resonite_cask
######*/
-enum eResoniteCask
+enum ResoniteCask
{
NPC_GOGGEROC = 11920
};
@@ -410,7 +412,7 @@ public:
## go_shrine_of_the_birds
######*/
-enum eShrineOfTheBirds
+enum ShrineOfTheBirds
{
NPC_HAWK_GUARD = 22992,
NPC_EAGLE_GUARD = 22993,
@@ -456,7 +458,7 @@ public:
## go_southfury_moonstone
######*/
-enum eSouthfury
+enum Southfury
{
NPC_RIZZLE = 23002,
SPELL_BLACKJACK = 39865, //stuns player
@@ -484,7 +486,7 @@ public:
## go_tele_to_dalaran_crystal
######*/
-enum eDalaranCrystal
+enum DalaranCrystal
{
QUEST_LEARN_LEAVE_RETURN = 12790,
QUEST_TELE_CRYSTAL_FLAG = 12845
@@ -536,7 +538,7 @@ public:
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_5 "Purchase 5 Unstable Flask of the Beast for the cost of 50 Apexis Shards"
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_RETURN "Use the fel crystalforge to make another purchase."
-enum eFelCrystalforge
+enum FelCrystalforge
{
SPELL_CREATE_1_FLASK_OF_BEAST = 40964,
SPELL_CREATE_5_FLASK_OF_BEAST = 40965,
@@ -595,7 +597,7 @@ public:
#define GOSSIP_BASHIR_CRYSTALFORGE_ITEM_5 "Purchase 5 Unstable Flask of the Sorcerer for the cost of 50 Apexis Shards"
#define GOSSIP_BASHIR_CRYSTALFORGE_ITEM_RETURN "Use the bashir crystalforge to make another purchase."
-enum eBashirCrystalforge
+enum BashirCrystalforge
{
SPELL_CREATE_1_FLASK_OF_SORCERER = 40968,
SPELL_CREATE_5_FLASK_OF_SORCERER = 40970,
@@ -648,7 +650,7 @@ public:
## matrix_punchograph
######*/
-enum eMatrixPunchograph
+enum MatrixPunchograph
{
ITEM_WHITE_PUNCH_CARD = 9279,
ITEM_YELLOW_PUNCH_CARD = 9280,
@@ -713,7 +715,7 @@ public:
## go_scourge_cage
######*/
-enum eScourgeCage
+enum ScourgeCage
{
NPC_SCOURGE_PRISONER = 25610
};
@@ -740,7 +742,7 @@ public:
## go_arcane_prison
######*/
-enum eArcanePrison
+enum ArcanePrison
{
QUEST_PRISON_BREAK = 11587,
SPELL_ARCANE_PRISONER_KILL_CREDIT = 45456
@@ -787,7 +789,7 @@ public:
## go_jotunheim_cage
######*/
-enum eJotunheimCage
+enum JotunheimCage
{
NPC_EBON_BLADE_PRISONER_HUMAN = 30186,
NPC_EBON_BLADE_PRISONER_NE = 30194,
@@ -842,7 +844,7 @@ public:
}
};
-enum eTableTheka
+enum TableTheka
{
GOSSIP_TABLE_THEKA = 1653,
@@ -869,7 +871,7 @@ public:
## go_inconspicuous_landmark
######*/
-enum eInconspicuousLandmark
+enum InconspicuousLandmark
{
SPELL_SUMMON_PIRATES_TREASURE_AND_TRIGGER_MOB = 11462,
ITEM_CUERGOS_KEY = 9275,
@@ -895,7 +897,7 @@ public:
## go_ethereal_teleport_pad
######*/
-enum eEtherealTeleportPad
+enum EtherealTeleportPad
{
NPC_IMAGE_WIND_TRADER = 20518,
ITEM_TELEPORTER_POWER_PACK = 28969,
@@ -921,45 +923,105 @@ public:
## go_soulwell
######*/
-class go_soulwell : public GameObjectScript
+enum SoulWellData
{
-public:
- go_soulwell() : GameObjectScript("go_soulwell") { }
+ GO_SOUL_WELL_R1 = 181621,
+ GO_SOUL_WELL_R2 = 193169,
- bool OnGossipHello(Player* player, GameObject* go)
- {
- Unit* caster = go->GetOwner();
- if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
- return true;
+ SPELL_IMPROVED_HEALTH_STONE_R1 = 18692,
+ SPELL_IMPROVED_HEALTH_STONE_R2 = 18693,
- if (!player->IsInSameRaidWith(static_cast<Player*>(caster)))
- return true;
+ SPELL_CREATE_MASTER_HEALTH_STONE_R0 = 34130,
+ SPELL_CREATE_MASTER_HEALTH_STONE_R1 = 34149,
+ SPELL_CREATE_MASTER_HEALTH_STONE_R2 = 34150,
+
+ SPELL_CREATE_FEL_HEALTH_STONE_R0 = 58890,
+ SPELL_CREATE_FEL_HEALTH_STONE_R1 = 58896,
+ SPELL_CREATE_FEL_HEALTH_STONE_R2 = 58898,
+};
- // Repeating this at every use is ugly and inefficient. But as long as we don't have proper
- // GO scripting with at least On Create and On Update events, the other options are no less
- // ugly and hacky.
- uint32 newSpell = 0;
- if (go->GetEntry() == 193169) // Soulwell for rank 2
+class go_soulwell : public GameObjectScript
+{
+ public:
+ go_soulwell() : GameObjectScript("go_soulwell") {}
+
+ struct go_soulwellAI : public GameObjectAI
{
- if (caster->HasAura(18693)) // Improved Healthstone rank 2
- newSpell = 58898;
- else if (caster->HasAura(18692)) // Improved Healthstone rank 1
- newSpell = 58896;
- else newSpell = 58890;
- }
- else if (go->GetEntry() == 181621) // Soulwell for rank 1
+ go_soulwellAI(GameObject* go) : GameObjectAI(go)
+ {
+ _stoneSpell = 0;
+ _stoneId = 0;
+ switch (go->GetEntry())
+ {
+ case GO_SOUL_WELL_R1:
+ _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R0;
+ if (Unit* owner = go->GetOwner())
+ {
+ if (owner->HasAura(SPELL_IMPROVED_HEALTH_STONE_R1))
+ _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R1;
+ else if (owner->HasAura(SPELL_CREATE_MASTER_HEALTH_STONE_R2))
+ _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R2;
+ }
+ break;
+ case GO_SOUL_WELL_R2:
+ _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R0;
+ if (Unit* owner = go->GetOwner())
+ {
+ if (owner->HasAura(SPELL_IMPROVED_HEALTH_STONE_R1))
+ _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R1;
+ else if (owner->HasAura(SPELL_CREATE_MASTER_HEALTH_STONE_R2))
+ _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R2;
+ }
+ break;
+ }
+ if (_stoneSpell == 0) // Should never happen
+ return;
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_stoneSpell);
+ if (!spellInfo)
+ return;
+
+ _stoneId = spellInfo->Effects[EFFECT_0].ItemType;
+ }
+
+ /// Due to the fact that this GameObject triggers CMSG_GAMEOBJECT_USE
+ /// _and_ CMSG_GAMEOBJECT_REPORT_USE, this GossipHello hook is called
+ /// twice. The script's handling is fine as it won't remove two charges
+ /// on the well. We have to find how to segregate REPORT_USE and USE.
+ bool GossipHello(Player* player)
+ {
+ Unit* owner = go->GetOwner();
+ if (_stoneSpell == 0 || _stoneId == 0)
+ return true;
+
+ if (!owner || owner->GetTypeId() != TYPEID_PLAYER || !player->IsInSameRaidWith(owner->ToPlayer()))
+ return true;
+
+ // Don't try to add a stone if we already have one.
+ if (player->HasItemCount(_stoneId, 1))
+ {
+ if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(_stoneSpell))
+ Spell::SendCastResult(player, spell, 0, SPELL_FAILED_TOO_MANY_OF_ITEM);
+ return true;
+ }
+
+ owner->CastSpell(player, _stoneSpell, true);
+ // Item has to actually be created to remove a charge on the well.
+ if (player->HasItemCount(_stoneId, 1))
+ go->AddUse();
+
+ return false;
+ }
+
+ private:
+ uint32 _stoneSpell;
+ uint32 _stoneId;
+ };
+
+ GameObjectAI* GetAI(GameObject* go) const
{
- if (caster->HasAura(18693)) // Improved Healthstone rank 2
- newSpell = 34150;
- else if (caster->HasAura(18692)) // Improved Healthstone rank 1
- newSpell = 34149;
- else newSpell = 34130;
+ return new go_soulwellAI(go);
}
-
- go->AddUse();
- player->CastSpell(player, newSpell, true);
- return true;
- }
};
/*######
@@ -967,7 +1029,7 @@ public:
## go_dragonflayer_cage
######*/
-enum ePrisonersOfWyrmskull
+enum PrisonersOfWyrmskull
{
QUEST_PRISONERS_OF_WYRMSKULL = 11255,
NPC_PRISONER_PRIEST = 24086,
@@ -1017,7 +1079,7 @@ public:
## go_tadpole_cage
######*/
-enum eTadpoles
+enum Tadpoles
{
QUEST_OH_NOES_THE_TADPOLES = 11560,
NPC_WINTERFIN_TADPOLE = 25201
@@ -1052,7 +1114,7 @@ public:
#define GOSSIP_USE_OUTHOUSE "Use the outhouse."
#define GO_ANDERHOLS_SLIDER_CIDER_NOT_FOUND "Quest item Anderhol's Slider Cider not found."
-enum eAmberpineOuthouse
+enum AmberpineOuthouse
{
ITEM_ANDERHOLS_SLIDER_CIDER = 37247,
NPC_OUTHOUSE_BUNNY = 27326,
@@ -1114,7 +1176,7 @@ public:
## go_hive_pod
######*/
-enum eHives
+enum Hives
{
QUEST_HIVE_IN_THE_TOWER = 9544,
NPC_HIVE_AMBUSHER = 13301
@@ -1281,7 +1343,7 @@ public:
## go_midsummer_bonfire
######*/
-enum eMidsummerBonfire
+enum MidsummerBonfire
{
STAMP_OUT_BONFIRE_QUEST_COMPLETE = 45458,
};