diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp index a45be95fd..bec4a8d4d 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp @@ -253,493 +253,438 @@ const uint32 dragons[MAX_DRAGONS] = { DATA_TENEBRON, DATA_VESPERON, DATA_SHADRON // SARTHARION ///////////////////////////// -class boss_sartharion : public CreatureScript +struct boss_sartharion : public BossAI { -public: - boss_sartharion() : CreatureScript("boss_sartharion") { } - - CreatureAI* GetAI(Creature* pCreature) const override + boss_sartharion(Creature* creature) : BossAI(creature, DATA_SARTHARION), + dragonsCount(0), + lastLavaSide(LAVA_RIGHT_SIDE), + usedBerserk(false), + below11PctReached(false) { - return GetObsidianSanctumAI (pCreature); } - struct boss_sartharionAI : public BossAI + void Reset() override { - boss_sartharionAI(Creature* pCreature) : BossAI(pCreature, DATA_SARTHARION), - dragonsCount(0), - lastLavaSide(LAVA_RIGHT_SIDE), - usedBerserk(false), - below11PctReached(false) + _Reset(); + extraEvents.Reset(); + RespawnDragons(false); + SummonStartingTriggers(); + usedBerserk = false; + below11PctReached = false; + dragonsCount = 0; + volcanoBlows.clear(); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); + } + + void DoAction(int32 param) override + { + if (param == ACTION_DRAKE_DIED) + DoCastSelf(SPELL_SARTHARION_TWILIGHT_REVENGE, true); + } + + void JustEngagedWith(Unit* who) override + { + if (who && !IsTargetInBounds(who)) { + EnterEvadeMode(); + return; } - void Reset() override + _JustEngagedWith(); + DoCastSelf(SPELL_SARTHARION_PYROBUFFET, true); + Talk(SAY_SARTHARION_AGGRO); + + // Combat events + events.ScheduleEvent(EVENT_SARTHARION_CAST_CLEAVE, 7s); + events.ScheduleEvent(EVENT_SARTHARION_CAST_FLAME_BREATH, 15s); + events.ScheduleEvent(EVENT_SARTHARION_CAST_TAIL_LASH, 11s); + + // Extra events + extraEvents.ScheduleEvent(EVENT_SARTHARION_SUMMON_LAVA, 20s); + extraEvents.ScheduleEvent(EVENT_SARTHARION_LAVA_STRIKE, 5s); + extraEvents.ScheduleEvent(EVENT_SARTHARION_BERSERK, 15min); + extraEvents.ScheduleEvent(EVENT_SARTHARION_BOUNDARY, 250ms); + + // Store dragons + for (uint8 i = 0; i < MAX_DRAGONS; ++i) { - _Reset(); - extraEvents.Reset(); - RespawnDragons(false); - SummonStartingTriggers(); - usedBerserk = false; - below11PctReached = false; - dragonsCount = 0; - volcanoBlows.clear(); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); - } + Creature* dragon = ObjectAccessor::GetCreature(*me, instance->GetGuidData(dragons[i])); + if (!dragon || !dragon->IsAlive() || instance->GetBossState(dragons[i]) == DONE) + continue; - void DoAction(int32 param) override - { - if (param == ACTION_DRAKE_DIED) + dragon->SetImmuneToNPC(true); + dragon->SetFullHealth(); + + ++dragonsCount; + me->AddLootMode(1 << dragonsCount); + + switch (dragons[i]) { - DoCastSelf(SPELL_SARTHARION_TWILIGHT_REVENGE, true); - } - } - - void JustEngagedWith(Unit* pWho) override - { - if (pWho && !IsTargetInBounds(pWho)) - { - EnterEvadeMode(); - return; - } - - _JustEngagedWith(); - DoCastSelf(SPELL_SARTHARION_PYROBUFFET, true); - Talk(SAY_SARTHARION_AGGRO); - - // Combat events - events.ScheduleEvent(EVENT_SARTHARION_CAST_CLEAVE, 7s); - events.ScheduleEvent(EVENT_SARTHARION_CAST_FLAME_BREATH, 15s); - events.ScheduleEvent(EVENT_SARTHARION_CAST_TAIL_LASH, 11s); - - // Extra events - extraEvents.ScheduleEvent(EVENT_SARTHARION_SUMMON_LAVA, 20s); - extraEvents.ScheduleEvent(EVENT_SARTHARION_LAVA_STRIKE, 5s); - extraEvents.ScheduleEvent(EVENT_SARTHARION_BERSERK, 15min); - extraEvents.ScheduleEvent(EVENT_SARTHARION_BOUNDARY, 250ms); - - // Store dragons - for (uint8 i = 0; i < MAX_DRAGONS; ++i) - { - Creature* dragon = ObjectAccessor::GetCreature(*me, instance->GetGuidData(dragons[i])); - if (!dragon || !dragon->IsAlive() || instance->GetBossState(dragons[i]) == DONE) + case DATA_TENEBRON: { - continue; - } - - dragon->SetImmuneToNPC(true); - dragon->SetFullHealth(); - - ++dragonsCount; - me->AddLootMode(1 << dragonsCount); - - switch (dragons[i]) - { - case DATA_TENEBRON: - { - dragon->CastSpell(dragon, SPELL_POWER_OF_TENEBRON, true); - extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_TENEBRON, 10s); - break; - } - case DATA_SHADRON: - { - dragon->CastSpell(dragon, SPELL_POWER_OF_SHADRON, true); - extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_SHADRON, 65s); - break; - } - case DATA_VESPERON: - { - dragon->CastSpell(dragon, SPELL_POWER_OF_VESPERON, true); - extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_VESPERON, 115s); - break; - } - } - } - - if (dragonsCount) - { - DoCastSelf(SPELL_WILL_OF_SARTHARION, true); - } - - me->CallForHelp(500.0f); - } - - void JustDied(Unit* /*killer*/) override - { - RespawnDragons(true); - _JustDied(); - Talk(SAY_SARTHARION_DEATH); - } - - void SetData(uint32 type, uint32 data) override - { - if (type == DATA_VOLCANO_BLOWS && data) - { - if (!volcanoBlows.empty() && std::find(volcanoBlows.begin(), volcanoBlows.end(), data) != volcanoBlows.end()) - { - return; - } - - volcanoBlows.push_back(data); - } - } - - uint32 GetData(uint32 dataOrGuid) const override - { - // it means we want dragons count - if (dataOrGuid == DATA_ACHIEVEMENT_DRAGONS_COUNT) - { - return dragonsCount; - } - - // otherwise it is guid to check if player was hit by lava strike :) - if (!volcanoBlows.empty() && std::find(volcanoBlows.begin(), volcanoBlows.end(), dataOrGuid) != volcanoBlows.end()) - { - return 1; - } - - return 0; - } - - void KilledUnit(Unit* pVictim) override - { - if (!urand(0, 2) && pVictim->IsPlayer()) - { - Talk(SAY_SARTHARION_SLAY); - } - } - - void JustSummoned(Creature* summon) override - { - switch (summon->GetEntry()) - { - case NPC_FLAME_TSUNAMI: - { - summon->SetSpeed(MOVE_FLIGHT, 1.5f); + dragon->CastSpell(dragon, SPELL_POWER_OF_TENEBRON, true); + extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_TENEBRON, 10s); break; } - case NPC_FIRE_CYCLONE: + case DATA_SHADRON: { - summon->GetMotionMaster()->MoveRandom(5.0f); + dragon->CastSpell(dragon, SPELL_POWER_OF_SHADRON, true); + extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_SHADRON, 65s); + break; + } + case DATA_VESPERON: + { + dragon->CastSpell(dragon, SPELL_POWER_OF_VESPERON, true); + extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_VESPERON, 115s); break; } } - - summons.Summon(summon); } - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override + if (dragonsCount) + DoCastSelf(SPELL_WILL_OF_SARTHARION, true); + + me->CallForHelp(500.0f); + } + + void JustDied(Unit* /*killer*/) override + { + RespawnDragons(true); + _JustDied(); + Talk(SAY_SARTHARION_DEATH); + } + + void SetData(uint32 type, uint32 data) override + { + if (type == DATA_VOLCANO_BLOWS && data) { - // Temporal hack, by some case some melee spells can bypass this aura damage reduction - if (me->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) - { - damage = 0; + if (!volcanoBlows.empty() && std::find(volcanoBlows.begin(), volcanoBlows.end(), data) != volcanoBlows.end()) return; - } - if (!usedBerserk && me->HealthBelowPctDamaged(30, damage)) - { - DoCastSelf(SPELL_SARTHARION_BERSERK, true); - usedBerserk = true; - return; - } - - // Soft enrage - if (!below11PctReached && me->HealthBelowPctDamaged(10, damage)) - { - summons.RemoveNotExisting(); - if (!summons.empty()) - { - for (ObjectGuid const& summonGuid : summons) - { - Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid); - if (summon && summon->GetEntry() == NPC_FIRE_CYCLONE) - { - summon->CastSpell(summon, SPELL_CYCLONE_AURA_PERIODIC, true); - } - } - } - Talk(SAY_SARTHARION_BERSERK); - below11PctReached = true; - } + volcanoBlows.push_back(data); } + } - void UpdateAI(uint32 diff) override + uint32 GetData(uint32 dataOrGuid) const override + { + // it means we want dragons count + if (dataOrGuid == DATA_ACHIEVEMENT_DRAGONS_COUNT) + return dragonsCount; + + // otherwise it is guid to check if player was hit by lava strike :) + if (!volcanoBlows.empty() && std::find(volcanoBlows.begin(), volcanoBlows.end(), dataOrGuid) != volcanoBlows.end()) + return 1; + + return 0; + } + + void KilledUnit(Unit* victim) override + { + if (!urand(0, 2) && victim->IsPlayer()) + Talk(SAY_SARTHARION_SLAY); + } + + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) { - if (!UpdateVictim()) + case NPC_FLAME_TSUNAMI: { - return; + summon->SetSpeed(MOVE_FLIGHT, 1.5f); + break; } - - extraEvents.Update(diff); - - // Special events which needs to be fired immidiately - while (uint32 const eventId = extraEvents.ExecuteEvent()) + case NPC_FIRE_CYCLONE: { - switch (eventId) - { - case EVENT_SARTHARION_BOUNDARY: - { - if (!IsTargetInBounds(me->GetVictim())) - { - EnterEvadeMode(); - } - else - { - extraEvents.Repeat(250ms); - } - break; - } - case EVENT_SARTHARION_SUMMON_LAVA: - { - if (!urand(0, 3)) - { - Talk(SAY_SARTHARION_SPECIAL); - } - - SummonLavaWaves(); - extraEvents.Repeat(25s); - return; - } - case EVENT_SARTHARION_START_LAVA: - { - SendLavaWaves(true); - return; - } - case EVENT_SARTHARION_FINISH_LAVA: - { - SendLavaWaves(false); - return; - } - // Handling of Drakes Events - // Dragon Calls - case EVENT_SARTHARION_CALL_TENEBRON: - { - Talk(SAY_SARTHARION_CALL_TENEBRON); - if (Creature* tenebron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_TENEBRON))) - { - tenebron->AI()->DoAction(ACTION_CALL_DRAGON); - } - break; - } - case EVENT_SARTHARION_CALL_SHADRON: - { - Talk(SAY_SARTHARION_CALL_SHADRON); - if (Creature* shadron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADRON))) - { - shadron->AI()->DoAction(ACTION_CALL_DRAGON); - } - break; - } - case EVENT_SARTHARION_CALL_VESPERON: - { - Talk(SAY_SARTHARION_CALL_VESPERON); - if (Creature* vesperon = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VESPERON))) - { - vesperon->AI()->DoAction(ACTION_CALL_DRAGON); - } - - break; - } - } + summon->GetMotionMaster()->MoveRandom(5.0f); + break; } - - // Handle Sartharion combat abbilities - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SARTHARION_CAST_CLEAVE: - { - DoCastVictim(SPELL_SARTHARION_CLEAVE, false); - events.Repeat(10s); - break; - } - case EVENT_SARTHARION_CAST_FLAME_BREATH: - { - DoCastVictim(SPELL_SARTHARION_FLAME_BREATH, false); - events.Repeat(20s); - break; - } - case EVENT_SARTHARION_CAST_TAIL_LASH: - { - DoCastSelf(SPELL_SARTHARION_TAIL_LASH, false); - events.Repeat(18s); - break; - } - case EVENT_SARTHARION_LAVA_STRIKE: - { - if (!urand(0, 2)) - { - Talk(SAY_SARTHARION_SPECIAL_4); - } - - summons.RemoveNotExisting(); - uint8 rand = urand(0, MAX_CYCLONE_COUNT - 1); // 5 - numer of cyclones - uint8 iter = 0; - if (!summons.empty()) - { - for (ObjectGuid const& summonGuid : summons) - { - Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid); - if (summon && summon->GetEntry() == NPC_FIRE_CYCLONE && iter == rand) - { - summon->CastSpell(summon, SPELL_CYCLONE_AURA_PERIODIC, true); - ++iter; - } - } - } - - events.Repeat((below11PctReached ? randtime(1400ms, 2s) : randtime(5s, 20s))); - break; - } - case EVENT_SARTHARION_BERSERK: - { - summons.DespawnEntry(NPC_SAFE_AREA_TRIGGER); - break; - } - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - } - - DoMeleeAttackIfReady(); } - private: - void SummonStartingTriggers() + summons.Summon(summon); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override + { + // Temporal hack, by some case some melee spells can bypass this aura damage reduction + if (me->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { - for (uint8 i = 0; i < MAX_CYCLONE_COUNT; ++i) - { - me->SummonCreature(NPC_FIRE_CYCLONE, CycloneSummonPos[i]); - } - - for (uint8 i = 0; i < MAX_AREA_TRIGGER_COUNT; ++i) - { - me->SummonCreature(NPC_SAFE_AREA_TRIGGER, AreaTriggerSummonPos[i]); - } + damage = 0; + return; } - void SummonLavaWaves() + if (!usedBerserk && me->HealthBelowPctDamaged(30, damage)) + { + DoCastSelf(SPELL_SARTHARION_BERSERK, true); + usedBerserk = true; + return; + } + + // Soft enrage + if (!below11PctReached && me->HealthBelowPctDamaged(10, damage)) { summons.RemoveNotExisting(); - Talk(WHISPER_LAVA_CHURN); - extraEvents.ScheduleEvent(EVENT_SARTHARION_START_LAVA, 3600ms); - extraEvents.ScheduleEvent(EVENT_SARTHARION_FINISH_LAVA, 11s); - - // Send wave from left - if (lastLavaSide == LAVA_RIGHT_SIDE) + if (!summons.empty()) { - for (uint8 i = 0; i < MAX_LEFT_LAVA_TSUNAMIS; ++i) + for (ObjectGuid const& summonGuid : summons) { - Creature* tsunami = me->SummonCreature(NPC_FLAME_TSUNAMI, 3211.0f, FlameTsunamiLeftOffsets[i], 57.083332f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 13500); - - if (((i - 2) % 3 == 0) && tsunami) // If center of wave - tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_VISUAL, true); + Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid); + if (summon && summon->GetEntry() == NPC_FIRE_CYCLONE) + summon->CastSpell(summon, SPELL_CYCLONE_AURA_PERIODIC, true); } - - lastLavaSide = LAVA_LEFT_SIDE; - } - // from right - else - { - for (uint8 i = 0; i < MAX_RIGHT_LAVA_TSUNAMIS; ++i) - { - Creature* tsunami = me->SummonCreature(NPC_FLAME_TSUNAMI, 3286.0f, FlameTsunamiRightOffsets[i], 57.083332f, 3.14f, TEMPSUMMON_TIMED_DESPAWN, 13500); - - if (((i - 2) % 3 == 0) && tsunami) // If center of wave - tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_VISUAL, true); - } - - lastLavaSide = LAVA_RIGHT_SIDE; } + Talk(SAY_SARTHARION_BERSERK); + below11PctReached = true; } + } - void SendLavaWaves(bool start) + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + extraEvents.Update(diff); + + // Special events which needs to be fired immediately + while (uint32 const eventId = extraEvents.ExecuteEvent()) { - if (summons.empty()) - return; - - for (ObjectGuid const& guid : summons) + switch (eventId) { - Creature* tsunami = ObjectAccessor::GetCreature(*me, guid); - if (!tsunami || tsunami->GetEntry() != NPC_FLAME_TSUNAMI) - continue; - - if (start) // Movement possibly simplified from official, ideally reevaluate in the future. + case EVENT_SARTHARION_BOUNDARY: { - tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_DAMAGE_AURA, true); - tsunami->GetMotionMaster()->MovePoint(0, ((tsunami->GetPositionX() < 3250.0f) ? 3286.0f : 3211.0f), tsunami->GetPositionY(), tsunami->GetPositionZ()); + if (!IsTargetInBounds(me->GetVictim())) + EnterEvadeMode(); + else + extraEvents.Repeat(250ms); + break; } - else + case EVENT_SARTHARION_SUMMON_LAVA: { - tsunami->RemoveAura(SPELL_FLAME_TSUNAMI_DAMAGE_AURA); - tsunami->SetObjectScale(0.1f); + if (!urand(0, 3)) + Talk(SAY_SARTHARION_SPECIAL); + + SummonLavaWaves(); + extraEvents.Repeat(25s); + return; + } + case EVENT_SARTHARION_START_LAVA: + { + SendLavaWaves(true); + return; + } + case EVENT_SARTHARION_FINISH_LAVA: + { + SendLavaWaves(false); + return; + } + // Handling of Drakes Events + // Dragon Calls + case EVENT_SARTHARION_CALL_TENEBRON: + { + Talk(SAY_SARTHARION_CALL_TENEBRON); + if (Creature* tenebron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_TENEBRON))) + tenebron->AI()->DoAction(ACTION_CALL_DRAGON); + break; + } + case EVENT_SARTHARION_CALL_SHADRON: + { + Talk(SAY_SARTHARION_CALL_SHADRON); + if (Creature* shadron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADRON))) + shadron->AI()->DoAction(ACTION_CALL_DRAGON); + break; + } + case EVENT_SARTHARION_CALL_VESPERON: + { + Talk(SAY_SARTHARION_CALL_VESPERON); + if (Creature* vesperon = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VESPERON))) + vesperon->AI()->DoAction(ACTION_CALL_DRAGON); + + break; } } } - void RespawnDragons(bool checkCombat) - { - for (uint8 i = 0; i < MAX_DRAGONS; ++i) - { - if (instance->GetBossState(dragons[i]) == DONE) - { - continue; - } + // Handle Sartharion combat abilities + events.Update(diff); - if (Creature* dragon = ObjectAccessor::GetCreature(*me, instance->GetGuidData(dragons[i]))) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SARTHARION_CAST_CLEAVE: { - if (checkCombat && dragon->IsInCombat()) + DoCastVictim(SPELL_SARTHARION_CLEAVE, false); + events.Repeat(10s); + break; + } + case EVENT_SARTHARION_CAST_FLAME_BREATH: + { + DoCastVictim(SPELL_SARTHARION_FLAME_BREATH, false); + events.Repeat(20s); + break; + } + case EVENT_SARTHARION_CAST_TAIL_LASH: + { + DoCastSelf(SPELL_SARTHARION_TAIL_LASH, false); + events.Repeat(18s); + break; + } + case EVENT_SARTHARION_LAVA_STRIKE: + { + if (!urand(0, 2)) + Talk(SAY_SARTHARION_SPECIAL_4); + + summons.RemoveNotExisting(); + uint8 rand = urand(0, MAX_CYCLONE_COUNT - 1); // 5 - number of cyclones + uint8 iter = 0; + if (!summons.empty()) { - continue; + for (ObjectGuid const& summonGuid : summons) + { + Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid); + if (summon && summon->GetEntry() == NPC_FIRE_CYCLONE && iter == rand) + { + summon->CastSpell(summon, SPELL_CYCLONE_AURA_PERIODIC, true); + ++iter; + } + } } - dragon->DespawnOrUnsummon(); - dragon->SetRespawnTime(5); + events.Repeat((below11PctReached ? randtime(1400ms, 2s) : randtime(5s, 20s))); + break; + } + case EVENT_SARTHARION_BERSERK: + { + summons.DespawnEntry(NPC_SAFE_AREA_TRIGGER); + break; } } - dragonsCount = 0; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - bool IsTargetInBounds(Unit const* victim) const + DoMeleeAttackIfReady(); + } + +private: + void SummonStartingTriggers() + { + for (uint8 i = 0; i < MAX_CYCLONE_COUNT; ++i) { - if (!victim || victim->GetPositionX() < SartharionBoundary[0] || victim->GetPositionX() > SartharionBoundary[1] || victim->GetPositionY() > SartharionBoundary[3]) - { - return false; - } - - // Big island handling - if (victim->GetPositionY() < SartharionBoundary[2]) - { - return victim->GetDistance(bigIslandMiddlePos) <= 6.0f; - } - - return true; + me->SummonCreature(NPC_FIRE_CYCLONE, CycloneSummonPos[i]); } - EventMap extraEvents; - std::list volcanoBlows; - uint8 dragonsCount; - uint8 lastLavaSide; // 0 = left, 1 = right - bool usedBerserk; - bool below11PctReached; - }; + for (uint8 i = 0; i < MAX_AREA_TRIGGER_COUNT; ++i) + { + me->SummonCreature(NPC_SAFE_AREA_TRIGGER, AreaTriggerSummonPos[i]); + } + } + + void SummonLavaWaves() + { + summons.RemoveNotExisting(); + Talk(WHISPER_LAVA_CHURN); + extraEvents.ScheduleEvent(EVENT_SARTHARION_START_LAVA, 3600ms); + extraEvents.ScheduleEvent(EVENT_SARTHARION_FINISH_LAVA, 11s); + + // Send wave from left + if (lastLavaSide == LAVA_RIGHT_SIDE) + { + for (uint8 i = 0; i < MAX_LEFT_LAVA_TSUNAMIS; ++i) + { + Creature* tsunami = me->SummonCreature(NPC_FLAME_TSUNAMI, 3211.0f, FlameTsunamiLeftOffsets[i], 57.083332f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 13500); + + if (((i - 2) % 3 == 0) && tsunami) // If center of wave + tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_VISUAL, true); + } + + lastLavaSide = LAVA_LEFT_SIDE; + } + // from right + else + { + for (uint8 i = 0; i < MAX_RIGHT_LAVA_TSUNAMIS; ++i) + { + Creature* tsunami = me->SummonCreature(NPC_FLAME_TSUNAMI, 3286.0f, FlameTsunamiRightOffsets[i], 57.083332f, 3.14f, TEMPSUMMON_TIMED_DESPAWN, 13500); + + if (((i - 2) % 3 == 0) && tsunami) // If center of wave + tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_VISUAL, true); + } + + lastLavaSide = LAVA_RIGHT_SIDE; + } + } + + void SendLavaWaves(bool start) + { + if (summons.empty()) + return; + + for (ObjectGuid const& guid : summons) + { + Creature* tsunami = ObjectAccessor::GetCreature(*me, guid); + if (!tsunami || tsunami->GetEntry() != NPC_FLAME_TSUNAMI) + continue; + + if (start) // Movement possibly simplified from official, ideally reevaluate in the future. + { + tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_DAMAGE_AURA, true); + tsunami->GetMotionMaster()->MovePoint(0, ((tsunami->GetPositionX() < 3250.0f) ? 3286.0f : 3211.0f), tsunami->GetPositionY(), tsunami->GetPositionZ()); + } + else + { + tsunami->RemoveAura(SPELL_FLAME_TSUNAMI_DAMAGE_AURA); + tsunami->SetObjectScale(0.1f); + } + } + } + + void RespawnDragons(bool checkCombat) + { + for (uint8 i = 0; i < MAX_DRAGONS; ++i) + { + if (instance->GetBossState(dragons[i]) == DONE) + continue; + + if (Creature* dragon = ObjectAccessor::GetCreature(*me, instance->GetGuidData(dragons[i]))) + { + if (checkCombat && dragon->IsInCombat()) + continue; + + dragon->DespawnOrUnsummon(); + dragon->SetRespawnTime(5); + } + } + + dragonsCount = 0; + } + + bool IsTargetInBounds(Unit const* victim) const + { + if (!victim || victim->GetPositionX() < SartharionBoundary[0] || victim->GetPositionX() > SartharionBoundary[1] || victim->GetPositionY() > SartharionBoundary[3]) + return false; + + // Big island handling + if (victim->GetPositionY() < SartharionBoundary[2]) + return victim->GetDistance(bigIslandMiddlePos) <= 6.0f; + + return true; + } + + EventMap extraEvents; + std::list volcanoBlows; + uint8 dragonsCount; + uint8 lastLavaSide; // 0 = left, 1 = right + bool usedBerserk; + bool below11PctReached; }; struct boss_sartharion_dragonAI : public BossAI { - boss_sartharion_dragonAI(Creature* pCreature, uint32 bossId) : BossAI(pCreature, bossId), isCalledBySartharion(false) + boss_sartharion_dragonAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), isCalledBySartharion(false) { } @@ -778,34 +723,26 @@ struct boss_sartharion_dragonAI : public BossAI void MovementInform(uint32 type, uint32 pointId) final { if (type != WAYPOINT_MOTION_TYPE) - { return; - } switch (me->GetEntry()) { case NPC_TENEBRON: { if (pointId != POINT_FINAL_TENEBRON) - { return; - } break; } case NPC_SHADRON: { if (pointId != POINT_FINAL_SHADRON) - { return; - } break; } case NPC_VESPERON: { if (pointId != POINT_FINAL_VESPERON) - { return; - } break; } } @@ -820,9 +757,7 @@ struct boss_sartharion_dragonAI : public BossAI if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) { if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) - { sartharion->AI()->JustSummoned(summon); - } } BossAI::JustSummoned(summon); @@ -863,16 +798,12 @@ struct boss_sartharion_dragonAI : public BossAI } if (isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) == DONE) - { me->SetLootMode(0); - } if (isCalledBySartharion) { if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 0, 500, true, false)) - { AttackStart(target); - } } } @@ -883,9 +814,7 @@ struct boss_sartharion_dragonAI : public BossAI // Despawn minions only if not called by Sartharion if (!isCalledBySartharion) - { summons.DespawnAll(); - } switch (me->GetEntry()) { @@ -893,9 +822,7 @@ struct boss_sartharion_dragonAI : public BossAI { Talk(SAY_TENEBRON_DEATH); if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) - { instance->SetBossState(DATA_TENEBRON, DONE); - } break; } case NPC_SHADRON: @@ -904,15 +831,11 @@ struct boss_sartharion_dragonAI : public BossAI if (isCalledBySartharion) { if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) - { sartharion->RemoveAura(SPELL_GIFT_OF_TWILIGHT_FIRE); - } } if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) - { instance->SetBossState(DATA_SHADRON, DONE); - } break; } case NPC_VESPERON: @@ -920,32 +843,24 @@ struct boss_sartharion_dragonAI : public BossAI Talk(SAY_VESPERON_DEATH); instance->DoAction(ACTION_CLEAR_PORTAL); if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) - { instance->SetBossState(DATA_VESPERON, DONE); - } break; } } if (!isCalledBySartharion) - { ClearInstance(); - } else { if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) - { sartharion->AI()->DoAction(ACTION_DRAKE_DIED); - } } } void KilledUnit(Unit* victim) final { if (!victim->IsPlayer() || urand(0, 2)) - { return; - } switch (me->GetEntry()) { @@ -976,25 +891,19 @@ struct boss_sartharion_dragonAI : public BossAI } if (!UpdateVictim()) - { return; - } events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } while (uint32 const eventId = events.ExecuteEvent()) { ExecuteEvent(eventId); if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } } DoMeleeAttackIfReady(); @@ -1016,9 +925,7 @@ protected: if (portalGUID) { if (GameObject* gobj = me->GetMap()->GetGameObject(portalGUID)) - { gobj->Delete(); - } portalGUID.Clear(); } @@ -1033,502 +940,402 @@ protected: // TENEBRON ///////////////////////////// -class boss_sartharion_tenebron : public CreatureScript +struct boss_sartharion_tenebron : public boss_sartharion_dragonAI { -public: - boss_sartharion_tenebron() : CreatureScript("boss_sartharion_tenebron") { } - - CreatureAI* GetAI(Creature* pCreature) const override + boss_sartharion_tenebron(Creature* creature) : boss_sartharion_dragonAI(creature, DATA_TENEBRON), summons2(creature) { - return GetObsidianSanctumAI(pCreature); } - struct boss_sartharion_tenebronAI : public boss_sartharion_dragonAI + void Reset() override { - boss_sartharion_tenebronAI(Creature* pCreature) : boss_sartharion_dragonAI(pCreature, DATA_TENEBRON), summons2(pCreature) - { - } - - void Reset() override - { - boss_sartharion_dragonAI::Reset(); - if (!isCalledBySartharion) - { - summons2.DespawnAll(); - } - - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20s); - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10s); - events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 15s); - } - - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() != NPC_TWILIGHT_EGG) - { - summons.Summon(summon); - } - // Summons to Sartharion are linked manually - } - - void JustDied(Unit* killer) override - { - if (!isCalledBySartharion) - { - summons2.DespawnAll(); - } - - boss_sartharion_dragonAI::JustDied(killer); - } - - void HandleExtraEvent(uint32 const eventId) override - { - if (eventId == EVENT_MINIDRAKE_SPEECH) - { - Talk(SAY_TENEBRON_RESPOND); - me->SetCanFly(true); - me->SetSpeed(MOVE_FLIGHT, 3.0f); - me->GetMotionMaster()->MoveWaypoint(me->GetEntry() * 10, false); - } - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_MINIBOSS_SHADOW_BREATH: - { - if (!urand(0, 10)) - { - Talk(SAY_TENEBRON_BREATH); - } - DoCastVictim(SPELL_SHADOW_BREATH, false); - events.Repeat(17500ms); - break; - } - case EVENT_MINIBOSS_SHADOW_FISSURE: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random)) - { - DoCast(target, SPELL_SHADOW_FISSURE, false); - } - events.Repeat(22500ms); - break; - } - case EVENT_MINIBOSS_OPEN_PORTAL: - { - Talk(WHISPER_OPEN_PORTAL); - Talk(SAY_TENEBRON_SPECIAL); - - if (!isCalledBySartharion) - { - if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[DATA_TENEBRON].GetPositionX(), portalPos[DATA_TENEBRON].GetPositionY(), portalPos[DATA_TENEBRON].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) - { - portalGUID = Portal->GetGUID(); - } - } - else - { - instance->DoAction(ACTION_ADD_PORTAL); - } - - events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2s); - events.Repeat(60s); - break; - } - case EVENT_MINIBOSS_SPAWN_HELPERS: - { - Talk(WHISPER_HATCH_EGGS); - for (uint8 i = 0; i < MAX_TENEBORN_EGGS_SUMMONS; ++i) - { - if (Creature* egg = me->SummonCreature(NPC_TWILIGHT_EGG, TenebronEggsPos[isCalledBySartharion ? 1 : 0][i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000)) - { - summons.Summon(egg); - if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) - { - if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) - { - sartharion->AI()->JustSummoned(egg); - } - } - egg->SetPhaseMask(16, true); - } - } - - events.ScheduleEvent(EVENT_MINIBOSS_HATCH_EGGS, 25s); - break; - } - case EVENT_MINIBOSS_HATCH_EGGS: - { - summons.RemoveNotExisting(); - summons.DespawnEntry(NPC_TWILIGHT_WHELP); - for (ObjectGuid const& summonGuid : summons) - { - Creature const* summon = ObjectAccessor::GetCreature(*me, summonGuid); - if (!summon || !summon->IsAlive() || summon->GetEntry() != NPC_TWILIGHT_EGG) - { - continue; - } - - if (Creature* whelp = me->SummonCreature(NPC_TWILIGHT_WHELP, summon->GetPosition(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000)) - { - summons2.Summon(whelp); - if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) - { - if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) - { - sartharion->AI()->JustSummoned(whelp); - } - } - } - } - - if (!isCalledBySartharion) - { - // Remove phase shift - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); - RemoveTwilightPortal(); - } - else - { - instance->DoAction(ACTION_CLEAR_PORTAL); - } - - EntryCheckPredicate pred(NPC_TWILIGHT_EGG); - summons.DoAction(ACTION_SWITCH_PHASE, pred); - break; - } - } - } - - void ClearInstance() override - { - boss_sartharion_dragonAI::ClearInstance(); + boss_sartharion_dragonAI::Reset(); + if (!isCalledBySartharion) summons2.DespawnAll(); - } - private: - SummonList summons2; - }; + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20s); + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10s); + events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 15s); + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() != NPC_TWILIGHT_EGG) + summons.Summon(summon); + // Summons to Sartharion are linked manually + } + + void JustDied(Unit* killer) override + { + if (!isCalledBySartharion) + summons2.DespawnAll(); + + boss_sartharion_dragonAI::JustDied(killer); + } + + void HandleExtraEvent(uint32 const eventId) override + { + if (eventId == EVENT_MINIDRAKE_SPEECH) + { + Talk(SAY_TENEBRON_RESPOND); + me->SetCanFly(true); + me->SetSpeed(MOVE_FLIGHT, 3.0f); + me->GetMotionMaster()->MoveWaypoint(me->GetEntry() * 10, false); + } + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_MINIBOSS_SHADOW_BREATH: + { + if (!urand(0, 10)) + Talk(SAY_TENEBRON_BREATH); + DoCastVictim(SPELL_SHADOW_BREATH, false); + events.Repeat(17500ms); + break; + } + case EVENT_MINIBOSS_SHADOW_FISSURE: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random)) + DoCast(target, SPELL_SHADOW_FISSURE, false); + events.Repeat(22500ms); + break; + } + case EVENT_MINIBOSS_OPEN_PORTAL: + { + Talk(WHISPER_OPEN_PORTAL); + Talk(SAY_TENEBRON_SPECIAL); + + if (!isCalledBySartharion) + { + if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[DATA_TENEBRON].GetPositionX(), portalPos[DATA_TENEBRON].GetPositionY(), portalPos[DATA_TENEBRON].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + portalGUID = Portal->GetGUID(); + } + else + instance->DoAction(ACTION_ADD_PORTAL); + + events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2s); + events.Repeat(60s); + break; + } + case EVENT_MINIBOSS_SPAWN_HELPERS: + { + Talk(WHISPER_HATCH_EGGS); + for (uint8 i = 0; i < MAX_TENEBORN_EGGS_SUMMONS; ++i) + { + if (Creature* egg = me->SummonCreature(NPC_TWILIGHT_EGG, TenebronEggsPos[isCalledBySartharion ? 1 : 0][i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000)) + { + summons.Summon(egg); + if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + { + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) + sartharion->AI()->JustSummoned(egg); + } + egg->SetPhaseMask(16, true); + } + } + + events.ScheduleEvent(EVENT_MINIBOSS_HATCH_EGGS, 25s); + break; + } + case EVENT_MINIBOSS_HATCH_EGGS: + { + summons.RemoveNotExisting(); + summons.DespawnEntry(NPC_TWILIGHT_WHELP); + for (ObjectGuid const& summonGuid : summons) + { + Creature const* summon = ObjectAccessor::GetCreature(*me, summonGuid); + if (!summon || !summon->IsAlive() || summon->GetEntry() != NPC_TWILIGHT_EGG) + continue; + + if (Creature* whelp = me->SummonCreature(NPC_TWILIGHT_WHELP, summon->GetPosition(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000)) + { + summons2.Summon(whelp); + if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + { + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) + sartharion->AI()->JustSummoned(whelp); + } + } + } + + if (!isCalledBySartharion) + { + // Remove phase shift + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); + RemoveTwilightPortal(); + } + else + instance->DoAction(ACTION_CLEAR_PORTAL); + + EntryCheckPredicate pred(NPC_TWILIGHT_EGG); + summons.DoAction(ACTION_SWITCH_PHASE, pred); + break; + } + } + } + + void ClearInstance() override + { + boss_sartharion_dragonAI::ClearInstance(); + summons2.DespawnAll(); + } + +private: + SummonList summons2; }; ///////////////////////////// // SHADRON ///////////////////////////// -class boss_sartharion_shadron : public CreatureScript +struct boss_sartharion_shadron : public boss_sartharion_dragonAI { -public: - boss_sartharion_shadron() : CreatureScript("boss_sartharion_shadron") { } - - CreatureAI* GetAI(Creature* pCreature) const override + boss_sartharion_shadron(Creature* creature) : boss_sartharion_dragonAI(creature, DATA_SHADRON) { - return GetObsidianSanctumAI(pCreature); } - struct boss_sartharion_shadronAI : public boss_sartharion_dragonAI + void Reset() override { - boss_sartharion_shadronAI(Creature* pCreature) : boss_sartharion_dragonAI(pCreature, DATA_SHADRON) + boss_sartharion_dragonAI::Reset(); + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20s); + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10s); + events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 15s); + } + + void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override + { + if (isCalledBySartharion) { + // Acolytes are dead + if (!summons.HasEntry(NPC_ACOLYTE_OF_SHADRON)) + instance->DoAction(ACTION_CLEAR_PORTAL); + + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) + sartharion->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGHT_FIRE); + } + else + { + ClearInstance(); + me->RemoveAura(SPELL_GIFT_OF_TWILIGHT_SHADOW); } - void Reset() override - { - boss_sartharion_dragonAI::Reset(); - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20s); - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10s); - events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 15s); - } + events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30s); + } - void SummonedCreatureDies(Creature* /*summon*/, Unit* /*summon*/) override + void HandleExtraEvent(uint32 const eventId) override + { + if (eventId == EVENT_MINIDRAKE_SPEECH) { - if (isCalledBySartharion) + Talk(SAY_SHADRON_RESPOND); + me->SetCanFly(true); + me->SetSpeed(MOVE_FLIGHT, 3.0f); + me->GetMotionMaster()->MoveWaypoint(me->GetEntry() * 10, false); + } + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_MINIBOSS_SHADOW_BREATH: { - // Acolytes are dead - if (!summons.HasEntry(NPC_ACOLYTE_OF_SHADRON)) + if (!urand(0, 10)) + Talk(SAY_SHADRON_BREATH); + + DoCastVictim(SPELL_SHADOW_BREATH, false); + events.Repeat(17500ms); + break; + } + case EVENT_MINIBOSS_SHADOW_FISSURE: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random)) + DoCast(target, SPELL_SHADOW_FISSURE, false); + events.Repeat(22500ms); + break; + } + case EVENT_MINIBOSS_OPEN_PORTAL: + { + Talk(WHISPER_OPEN_PORTAL); + Talk(SAY_SHADRON_SPECIAL); + if (!isCalledBySartharion) { - instance->DoAction(ACTION_CLEAR_PORTAL); + if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[DATA_SHADRON].GetPositionX(), portalPos[DATA_SHADRON].GetPositionY(), portalPos[DATA_SHADRON].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + portalGUID = Portal->GetGUID(); + } + else + instance->DoAction(ACTION_ADD_PORTAL); + + events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2s); + break; + } + case EVENT_MINIBOSS_SPAWN_HELPERS: + { + Talk(WHISPER_SUMMON_DICIPLE); + DoCastAOE(static_cast((isCalledBySartharion ? SPELL_GIFT_OF_TWILIGHT_FIRE : SPELL_GIFT_OF_TWILIGHT_SHADOW)), true); + + if (Creature* acolyte = me->SummonCreature((isCalledBySartharion ? NPC_ACOLYTE_OF_SHADRON : NPC_DISCIPLE_OF_SHADRON), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation())) + { + /// @todo: inspect JustSummoned + summons.Summon(acolyte); + acolyte->SetPhaseMask(16, true); } - if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SARTHARION))) - { - sartharion->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGHT_FIRE); - } - } - else - { - ClearInstance(); - me->RemoveAura(SPELL_GIFT_OF_TWILIGHT_SHADOW); - } - - events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30s); - } - - void HandleExtraEvent(uint32 const eventId) override - { - if (eventId == EVENT_MINIDRAKE_SPEECH) - { - Talk(SAY_SHADRON_RESPOND); - me->SetCanFly(true); - me->SetSpeed(MOVE_FLIGHT, 3.0f); - me->GetMotionMaster()->MoveWaypoint(me->GetEntry() * 10, false); + break; } } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_MINIBOSS_SHADOW_BREATH: - { - if (!urand(0, 10)) - { - Talk(SAY_SHADRON_BREATH); - } - - DoCastVictim(SPELL_SHADOW_BREATH, false); - events.Repeat(17500ms); - break; - } - case EVENT_MINIBOSS_SHADOW_FISSURE: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random)) - { - DoCast(target, SPELL_SHADOW_FISSURE, false); - } - events.Repeat(22500ms); - break; - } - case EVENT_MINIBOSS_OPEN_PORTAL: - { - Talk(WHISPER_OPEN_PORTAL); - Talk(SAY_SHADRON_SPECIAL); - if (!isCalledBySartharion) - { - if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[DATA_SHADRON].GetPositionX(), portalPos[DATA_SHADRON].GetPositionY(), portalPos[DATA_SHADRON].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) - { - portalGUID = Portal->GetGUID(); - } - } - else - { - instance->DoAction(ACTION_ADD_PORTAL); - } - - events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2s); - break; - } - case EVENT_MINIBOSS_SPAWN_HELPERS: - { - Talk(WHISPER_SUMMON_DICIPLE); - DoCastAOE(static_cast((isCalledBySartharion ? SPELL_GIFT_OF_TWILIGHT_FIRE : SPELL_GIFT_OF_TWILIGHT_SHADOW)), true); - - if (Creature* acolyte = me->SummonCreature((isCalledBySartharion ? NPC_ACOLYTE_OF_SHADRON : NPC_DISCIPLE_OF_SHADRON), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation())) - { - /// @todo: inpect JustSummoned - summons.Summon(acolyte); - acolyte->SetPhaseMask(16, true); - } - - break; - } - } - } - }; + } }; ///////////////////////////// // VESPERON ///////////////////////////// -class boss_sartharion_vesperon : public CreatureScript +struct boss_sartharion_vesperon : public boss_sartharion_dragonAI { -public: - boss_sartharion_vesperon() : CreatureScript("boss_sartharion_vesperon") { } - - CreatureAI* GetAI(Creature* pCreature) const override + boss_sartharion_vesperon(Creature* creature) : boss_sartharion_dragonAI(creature, DATA_VESPERON) { - return GetObsidianSanctumAI(pCreature); } - struct boss_sartharion_vesperonAI : public boss_sartharion_dragonAI + void Reset() override { - boss_sartharion_vesperonAI(Creature* pCreature) : boss_sartharion_dragonAI(pCreature, DATA_VESPERON) + boss_sartharion_dragonAI::Reset(); + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20s); + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10s); + events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30s); + } + + void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override + { + if (!isCalledBySartharion) + ClearInstance(); + else { - } - - void Reset() override - { - boss_sartharion_dragonAI::Reset(); - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20s); - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10s); - events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30s); - } - - void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override - { - if (!isCalledBySartharion) - { - ClearInstance(); - } - else - { - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION); - instance->DoAction(ACTION_CLEAR_PORTAL); - } - - events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30s); - } - - void HandleExtraEvent(uint32 const eventId) override - { - if (eventId == EVENT_MINIDRAKE_SPEECH) - { - Talk(SAY_SHADRON_RESPOND); - me->SetCanFly(true); - me->SetSpeed(MOVE_FLIGHT, 3.0f); - me->GetMotionMaster()->MoveWaypoint(me->GetEntry() * 10, false); - } - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_MINIBOSS_SHADOW_BREATH: - { - if (!urand(0, 10)) - { - Talk(SAY_SHADRON_BREATH); - } - - DoCastVictim(SPELL_SHADOW_BREATH, false); - events.Repeat(17s + 500ms); - break; - } - case EVENT_MINIBOSS_SHADOW_FISSURE: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random)) - { - DoCast(target, SPELL_SHADOW_FISSURE, false); - } - - events.Repeat(22s + 500ms); - break; - } - case EVENT_MINIBOSS_OPEN_PORTAL: - { - Talk(WHISPER_OPEN_PORTAL); - Talk(SAY_VESPERON_SPECIAL); - if (!isCalledBySartharion) - { - if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[DATA_VESPERON].GetPositionX(), portalPos[DATA_VESPERON].GetPositionY(), portalPos[DATA_VESPERON].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) - { - portalGUID = Portal->GetGUID(); - } - } - else - { - instance->DoAction(ACTION_ADD_PORTAL); - } - - events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2s); - break; - } - case EVENT_MINIBOSS_SPAWN_HELPERS: - { - Talk(WHISPER_SUMMON_DICIPLE); - DoCastSelf(isCalledBySartharion ? static_cast(SPELL_TWILIGHT_TORMENT_SARTHARION) : static_cast(SPELL_TWILIGHT_TORMENT_VESPERON), true); - if (Creature* acolyte = me->SummonCreature((isCalledBySartharion ? NPC_ACOLYTE_OF_VESPERON : NPC_DISCIPLE_OF_VESPERON), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation())) - { - summons.Summon(acolyte); - acolyte->SetPhaseMask(16, true); - } - - break; - } - } - } - - private: - void ClearInstance() override - { - boss_sartharion_dragonAI::ClearInstance(); - - // Remove phase shift - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESPERON); instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION); + instance->DoAction(ACTION_CLEAR_PORTAL); } - }; + + events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30s); + } + + void HandleExtraEvent(uint32 const eventId) override + { + if (eventId == EVENT_MINIDRAKE_SPEECH) + { + Talk(SAY_SHADRON_RESPOND); + me->SetCanFly(true); + me->SetSpeed(MOVE_FLIGHT, 3.0f); + me->GetMotionMaster()->MoveWaypoint(me->GetEntry() * 10, false); + } + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_MINIBOSS_SHADOW_BREATH: + { + if (!urand(0, 10)) + Talk(SAY_SHADRON_BREATH); + + DoCastVictim(SPELL_SHADOW_BREATH, false); + events.Repeat(17s + 500ms); + break; + } + case EVENT_MINIBOSS_SHADOW_FISSURE: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random)) + DoCast(target, SPELL_SHADOW_FISSURE, false); + + events.Repeat(22s + 500ms); + break; + } + case EVENT_MINIBOSS_OPEN_PORTAL: + { + Talk(WHISPER_OPEN_PORTAL); + Talk(SAY_VESPERON_SPECIAL); + if (!isCalledBySartharion) + { + if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[DATA_VESPERON].GetPositionX(), portalPos[DATA_VESPERON].GetPositionY(), portalPos[DATA_VESPERON].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + portalGUID = Portal->GetGUID(); + } + else + instance->DoAction(ACTION_ADD_PORTAL); + + events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2s); + break; + } + case EVENT_MINIBOSS_SPAWN_HELPERS: + { + Talk(WHISPER_SUMMON_DICIPLE); + DoCastSelf(isCalledBySartharion ? static_cast(SPELL_TWILIGHT_TORMENT_SARTHARION) : static_cast(SPELL_TWILIGHT_TORMENT_VESPERON), true); + if (Creature* acolyte = me->SummonCreature((isCalledBySartharion ? NPC_ACOLYTE_OF_VESPERON : NPC_DISCIPLE_OF_VESPERON), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation())) + { + summons.Summon(acolyte); + acolyte->SetPhaseMask(16, true); + } + + break; + } + } + } + +private: + void ClearInstance() override + { + boss_sartharion_dragonAI::ClearInstance(); + + // Remove phase shift + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESPERON); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION); + } }; // other -class npc_twilight_summon : public CreatureScript +struct npc_twilight_summon : public ScriptedAI { -public: - npc_twilight_summon() : CreatureScript("npc_twilight_summon") { } - - CreatureAI* GetAI(Creature* pCreature) const override + npc_twilight_summon(Creature* creature) : ScriptedAI(creature), + fadeArmorTimer(urand(0, 15000)) { - return GetObsidianSanctumAI(pCreature); } - struct npc_twilight_summonAI : public ScriptedAI + void Reset() override { - npc_twilight_summonAI(Creature* pCreature) : ScriptedAI(pCreature), - fadeArmorTimer(urand(0, 15000)) - { - } + fadeArmorTimer = urand(0, 15000); + if (me->GetEntry() == NPC_TWILIGHT_EGG) + me->SetControlled(true, UNIT_STATE_STUNNED); + else + me->SetInCombatWithZone(); + } - void Reset() override + void DoAction(int32 param) override + { + if (param == ACTION_SWITCH_PHASE) + me->DespawnOrUnsummon(1ms); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || me->GetEntry() == NPC_TWILIGHT_EGG) + return; + + fadeArmorTimer += diff; + if (fadeArmorTimer >= 15000) { - fadeArmorTimer = urand(0, 15000); - if (me->GetEntry() == NPC_TWILIGHT_EGG) - { - me->SetControlled(true, UNIT_STATE_STUNNED); - } + if (Aura* aur = me->GetVictim()->GetAura(SPELL_FADE_ARMOR)) + aur->ModStackAmount(1); else - { - me->SetInCombatWithZone(); - } + DoCastVictim(SPELL_FADE_ARMOR, false); + + fadeArmorTimer = 0; } - void DoAction(int32 param) override - { - if (param == ACTION_SWITCH_PHASE) - { - me->DespawnOrUnsummon(1ms); - } - } + DoMeleeAttackIfReady(); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() || me->GetEntry() == NPC_TWILIGHT_EGG) - { - return; - } - - fadeArmorTimer += diff; - if (fadeArmorTimer >= 15000) - { - if (Aura* aur = me->GetVictim()->GetAura(SPELL_FADE_ARMOR)) - { - aur->ModStackAmount(1); - } - else - { - DoCastVictim(SPELL_FADE_ARMOR, false); - } - - fadeArmorTimer = 0; - } - - DoMeleeAttackIfReady(); - } - - private: - uint32 fadeArmorTimer; - }; +private: + uint32 fadeArmorTimer; }; class spell_sartharion_lava_strike : public SpellScript @@ -1549,9 +1356,7 @@ class spell_sartharion_lava_strike : public SpellScript void HandleDummy(SpellEffIndex /*effIndex*/) { if (!GetCaster() || !GetHitUnit()) - { return; - } GetCaster()->CastSpell(GetHitUnit()->GetPositionX(), GetHitUnit()->GetPositionY(), GetHitUnit()->GetPositionZ(), SPELL_LAVA_STRIKE_DUMMY_TRIGGER, true); } @@ -1559,9 +1364,7 @@ class spell_sartharion_lava_strike : public SpellScript void HandleSchoolDamage(SpellEffIndex /*effIndex*/) { if (!GetCaster() || !GetHitUnit() || _spawned) - { return; - } if (InstanceScript* instance = GetCaster()->GetInstanceScript()) { @@ -1577,13 +1380,9 @@ class spell_sartharion_lava_strike : public SpellScript void Register() override { if (m_scriptSpellId == SPELL_LAVA_STRIKE_DUMMY) - { OnEffectHitTarget += SpellEffectFn(spell_sartharion_lava_strike::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } else - { OnEffectHitTarget += SpellEffectFn(spell_sartharion_lava_strike::HandleSchoolDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - } } private: @@ -1637,11 +1436,11 @@ class spell_obsidian_sanctum_flame_tsunami_leap : public SpellScript void AddSC_boss_sartharion() { - new boss_sartharion(); - new boss_sartharion_shadron(); - new boss_sartharion_tenebron(); - new boss_sartharion_vesperon(); - new npc_twilight_summon(); + RegisterObsidianSanctumCreatureAI(boss_sartharion); + RegisterObsidianSanctumCreatureAI(boss_sartharion_shadron); + RegisterObsidianSanctumCreatureAI(boss_sartharion_tenebron); + RegisterObsidianSanctumCreatureAI(boss_sartharion_vesperon); + RegisterObsidianSanctumCreatureAI(npc_twilight_summon); RegisterSpellScript(spell_sartharion_lava_strike); RegisterSpellScript(spell_obsidian_sanctum_flame_tsunami); RegisterSpellScript(spell_obsidian_sanctum_flame_tsunami_leap); diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h index c92d28754..02c6b3983 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h @@ -66,4 +66,6 @@ inline AI* GetObsidianSanctumAI(T* obj) return GetInstanceAI(obj, ObsidianSanctumScriptName); } +#define RegisterObsidianSanctumCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetObsidianSanctumAI) + #endif