diff options
Diffstat (limited to 'src/server/scripts')
16 files changed, 2263 insertions, 291 deletions
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 2c9623b23a6..f7280cf906e 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())) @@ -2568,7 +2568,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 028cf640d21..ca91fb920b1 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/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_urom.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp index 8e7863259ad..179dedb290b 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp @@ -105,8 +105,8 @@ public: void Reset() { me->CastSpell(me, SPELL_EVOCATE); - - _Reset(); + + _Reset(); if (instance->GetData(DATA_UROM_PLATAFORM) == 0) { diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp index 1f4a3d1b229..a0b5aded315 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp @@ -278,7 +278,7 @@ public: 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); + verdisa->GetMotionMaster()->MovePoint(0, 949.188f, 1032.91f, 359.967f); } void GreaterWhelps() diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index f2fa3158603..39fff139b52 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -108,10 +108,10 @@ class npc_verdisa_beglaristrasz_eternos : public CreatureScript { public: npc_verdisa_beglaristrasz_eternos() : CreatureScript("npc_verdisa_beglaristrasz_eternos") { } - - InstanceScript* instance; - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) + + InstanceScript* instance; + + bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) { player->PlayerTalkClass->ClearMenus(); switch (creature->GetEntry()) @@ -245,7 +245,7 @@ public: me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } }; - + CreatureAI* GetAI(Creature* creature) const { return new npc_verdisa_beglaristrasz_eternosAI(creature); @@ -293,7 +293,7 @@ public: { instance = creature->GetInstanceScript(); } - + InstanceScript* instance; uint64 summonerGUID; @@ -324,9 +324,9 @@ public: 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)) { @@ -357,7 +357,7 @@ public: void UpdateAI(const uint32 diff) { if (!(instance->GetBossState(DATA_VAROS_EVENT) == DONE)) - { + { if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE)) { if (!(WelcomeOff)) @@ -380,7 +380,7 @@ public: { Talk(WHISPER_DRAKES_ABILITIES, me->GetCreatorGUID()); WelcomeSequelOff = false; - } + } else WelcomeSequelTimer -= diff; } } @@ -391,10 +391,10 @@ public: if (!(SpecialOff)) { if (SpecialTimer <= diff) - { + { Talk(WHISPER_DRAKES_SPECIAL, me->GetCreatorGUID()); SpecialOff = true; - } + } else SpecialTimer -= diff; } } @@ -402,25 +402,25 @@ public: 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))) { @@ -433,7 +433,7 @@ public: me->SetSpeed(MOVE_FLIGHT, 1.0f, true); Talk(SAY_DRAKES_TAKEOFF); Position pos; - me->GetPosition(&pos); + me->GetPosition(&pos); pos.m_positionX += 10.0f; pos.m_positionY += 10.0f; pos.m_positionZ += 12.0f; 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, }; |
