diff options
Diffstat (limited to 'src')
9 files changed, 1947 insertions, 99 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c41436e1947..0ebb0c54a24 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9113,6 +9113,9 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) case 4100: // The Culling of Stratholme NumberOfFields = 13; break; + case 4987: // The Ruby Sanctum + NumberOfFields = 3; + break; case 4273: // Ulduar NumberOfFields = 10; break; @@ -9639,6 +9642,17 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) data << uint32(4345) << uint32(1); // 24 unknown } break; + // The Ruby Sanctum + case 4987: + if (instance && mapid == 724) + instance->FillInitialWorldStates(data); + else + { + data << uint32(5049) << uint32(50); // 9 WORLDSTATE_CORPOREALITY_MATERIAL + data << uint32(5050) << uint32(50); // 10 WORLDSTATE_CORPOREALITY_TWILIGHT + data << uint32(5051) << uint32(0); // 11 WORLDSTATE_CORPOREALITY_TOGGLE + } + break; // Icecrown Citadel case 4812: if (instance && mapid == 631) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 7f279a8b97e..d3a87a1c44e 100755 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -794,21 +794,21 @@ enum MeleeHitOutcome class DispelInfo { -private: - Unit* const m_dispeller; - uint32 const m_dispellerSpellId; - uint8 m_chargesRemoved; public: - explicit DispelInfo(Unit* _dispeller, uint32 _dispellerSpellId, uint8 _chargesRemoved) : - m_dispeller(_dispeller), m_dispellerSpellId(_dispellerSpellId), m_chargesRemoved(_chargesRemoved) {} + explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : + _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) {} - Unit* GetDispeller() { return m_dispeller; } - uint32 GetDispellerSpellId() const { return m_dispellerSpellId; } - uint8 GetRemovedCharges() const { return m_chargesRemoved; } + Unit* GetDispeller() const { return _dispellerUnit; } + uint32 GetDispellerSpellId() const { return _dispellerSpell; } + uint8 GetRemovedCharges() const { return _chargesRemoved; } void SetRemovedCharges(uint8 amount) { - m_chargesRemoved = amount; + _chargesRemoved = amount; } +private: + Unit* _dispellerUnit; + uint32 _dispellerSpell; + uint8 _chargesRemoved; }; struct CleanDamage diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index fc56106ab6f..54a74a021c1 100755 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -502,6 +502,7 @@ void AddSC_ruby_sanctum(); void AddSC_boss_baltharus_the_warborn(); void AddSC_boss_saviana_ragefire(); void AddSC_boss_general_zarithrian(); +void AddSC_boss_halion(); void AddSC_dalaran(); void AddSC_borean_tundra(); @@ -1222,6 +1223,7 @@ void AddNorthrendScripts() AddSC_boss_baltharus_the_warborn(); AddSC_boss_saviana_ragefire(); AddSC_boss_general_zarithrian(); + AddSC_boss_halion(); AddSC_dalaran(); AddSC_borean_tundra(); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index c89442855e1..edce89a1a5b 100755 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3402,7 +3402,7 @@ void SpellMgr::LoadDbcDataCorrections() break; case 69055: // Saber Lash (Lord Marrowgar) case 70814: // Saber Lash (Lord Marrowgar) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_5_YARDS; // 5yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_5_YARDS; // 5yd break; case 69075: // Bone Storm (Lord Marrowgar) case 70834: // Bone Storm (Lord Marrowgar) @@ -3412,7 +3412,7 @@ void SpellMgr::LoadDbcDataCorrections() case 71160: // Plague Stench (Stinky) case 71161: // Plague Stench (Stinky) case 71123: // Decimate (Stinky & Precious) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_100_YARDS; // 100yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_100_YARDS; // 100yd break; case 72378: // Blood Nova (Deathbringer Saurfang) case 73058: // Blood Nova (Deathbringer Saurfang) @@ -3430,7 +3430,7 @@ void SpellMgr::LoadDbcDataCorrections() spellInfo->Effect[2] = 0; break; case 70460: // Coldflame Jets (Traps after Saurfang) - spellInfo->DurationIndex = 1; // 10 seconds + spellInfo->DurationIndex = 1; // 10 seconds break; case 71412: // Green Ooze Summon (Professor Putricide) case 71415: // Orange Ooze Summon (Professor Putricide) @@ -3453,7 +3453,7 @@ void SpellMgr::LoadDbcDataCorrections() case 72464: // Mutated Plague (Professor Putricide) case 72506: // Mutated Plague (Professor Putricide) case 72507: // Mutated Plague (Professor Putricide) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 70911: // Unbound Plague (Professor Putricide) (needs target selection script) case 72854: // Unbound Plague (Professor Putricide) (needs target selection script) @@ -3464,7 +3464,7 @@ void SpellMgr::LoadDbcDataCorrections() case 71518: // Unholy Infusion Quest Credit (Professor Putricide) case 72934: // Blood Infusion Quest Credit (Blood-Queen Lana'thel) case 72289: // Frost Infusion Quest Credit (Sindragosa) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // another missing radius + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // another missing radius break; case 71708: // Empowered Flare (Blood Prince Council) case 72785: // Empowered Flare (Blood Prince Council) @@ -3513,62 +3513,62 @@ void SpellMgr::LoadDbcDataCorrections() case 73708: // Defile case 73709: // Defile case 73710: // Defile - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 69030: // Val'kyr Target Search - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 69198: // Raging Spirit Visual - spellInfo->rangeIndex = 13; // 50000yd + spellInfo->rangeIndex = 13; // 50000yd break; case 73654: // Harvest Souls case 74295: // Harvest Souls case 74296: // Harvest Souls case 74297: // Harvest Souls - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd - spellInfo->EffectRadiusIndex[2] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[2] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 73655: // Harvest Soul spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS; break; case 73540: // Summon Shadow Trap - spellInfo->DurationIndex = 23; // 90 seconds + spellInfo->DurationIndex = 23; // 90 seconds break; case 73530: // Shadow Trap (visual) - spellInfo->DurationIndex = 28; // 5 seconds + spellInfo->DurationIndex = 28; // 5 seconds break; case 73529: // Shadow Trap - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_10_YARDS; // 10yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_10_YARDS; // 10yd break; case 74282: // Shadow Trap (searcher) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_3_YARDS; // 3yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_3_YARDS; // 3yd break; case 72595: // Restore Soul case 73650: // Restore Soul - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 74086: // Destroy Soul - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 74302: // Summon Spirit Bomb case 74342: // Summon Spirit Bomb - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd spellInfo->MaxAffectedTargets = 1; break; case 74341: // Summon Spirit Bomb case 74343: // Summon Spirit Bomb - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd spellInfo->MaxAffectedTargets = 3; break; case 73579: // Summon Spirit Bomb - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd break; case 72350: // Fury of Frostmourne - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 75127: // Kill Frostmourne Players case 72351: // Fury of Frostmourne @@ -3576,19 +3576,35 @@ void SpellMgr::LoadDbcDataCorrections() case 72429: // Mass Resurrection case 73159: // Play Movie case 73582: // Trigger Vile Spirit (Inside, Heroic) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 72376: // Raise Dead spellInfo->MaxAffectedTargets = 3; - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 71809: // Jump - spellInfo->rangeIndex = 3; // 20yd - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd + spellInfo->rangeIndex = 3; // 20yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd break; case 72405: // Broken Frostmourne - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + break; + // ENDOF ICECROWN CITADEL SPELLS + // + // RUBY SANCTUM SPELLS + // + case 74769: // Twilight Cutter + case 77844: // Twilight Cutter + case 77845: // Twilight Cutter + case 77846: // Twilight Cutter + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_100_YARDS; // 100yd break; + case 75509: // Twilight Mending + spellInfo->AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE; + spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + break; + // ENDOF RUBY SANCTUM SPELLS + // case 40055: // Introspection case 40165: // Introspection case 40166: // Introspection @@ -3630,5 +3646,5 @@ void SpellMgr::LoadDbcDataCorrections() properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647)); // 52893 properties->Type = SUMMON_TYPE_TOTEM; - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loading spell dbc data corrections in %u ms", GetMSTimeDiffToNow(oldMSTime)); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded spell dbc data corrections in %u ms", GetMSTimeDiffToNow(oldMSTime)); } 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..c6c57a34922 --- /dev/null +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -0,0 +1,1738 @@ +/* + * 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 "SpellScript.h" +#include "SpellAuraEffects.h" +#include "Spell.h" +#include "Vehicle.h" +#include "MapManager.h" +#include "GameObjectAI.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..215a1f6185b 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, // Not sure which entry is east and west. + 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_ |