diff options
4 files changed, 1080 insertions, 887 deletions
diff --git a/data/sql/updates/pending_db_world/rev_1604614793984918900.sql b/data/sql/updates/pending_db_world/rev_1604614793984918900.sql new file mode 100644 index 0000000000..4ff84af525 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1604614793984918900.sql @@ -0,0 +1,16 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1604614793984918900'); +-- Pyrobuffet (57557) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceGroup`=7 AND `SourceEntry`=57557 AND `SourceId`=0 AND `ElseGroup`=0 AND `ConditionTypeOrReference`=26 AND `ConditionTarget`=0 AND `ConditionValue1`=1 AND `ConditionValue2`=0 AND `ConditionValue3`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 7, 57557, 0, 0, 26, 0, 1, 0, 0, 0, 0, 0, '', 'Pyrobuffet - Target players which has phase mask 1'); + +-- Twilight Torment (57935) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceGroup`=7 AND `SourceEntry`=57935 AND `SourceId`=0 AND `ElseGroup`=0 AND `ConditionTypeOrReference`=26 AND `ConditionTarget`=0 AND `ConditionValue1`=1 AND `ConditionValue2`=0 AND `ConditionValue3`=0; +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=17 AND `SourceGroup`=0 AND `SourceEntry`=57935 AND `SourceId`=0 AND `ElseGroup`=0 AND `ConditionTypeOrReference`=26 AND `ConditionTarget`=1 AND `ConditionValue1`=1 AND `ConditionValue2`=0 AND `ConditionValue3`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17, 0, 57935, 0, 0, 26, 1, 1, 0, 0, 0, 0, 0, '', 'Twilight Torment - Target players which has phase mask 1'); + +-- Twilight Shift aura removes Twilight Torment +DELETE FROM `spell_linked_spell` WHERE `spell_trigger`=57620 AND `spell_effect`=-57935 AND `type`=0; +INSERT INTO `spell_linked_spell` (`spell_trigger`, `spell_effect`, `type`, `comment`) VALUES +(57620, -57935, 0, 'Twilight Shift aura removes Twilight Torment'); diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp index 15899a79ae..9c198bf38a 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp @@ -108,17 +108,28 @@ enum NPCs enum Misc { + // Actions ACTION_SWITCH_PHASE = 1, ACTION_CALL_DRAGON = 2, ACTION_DRAKE_DIED = 3, - DRAGON_TENEBRON = 0, - DRAGON_SHADRON = 1, - DRAGON_VESPERON = 2, - + // Movement points POINT_FINAL_TENEBRON = 8, POINT_FINAL_SHADRON = 4, POINT_FINAL_VESPERON = 4, + + // Lava directions. Its used to identify to which side lava was moving by last time + LAVA_LEFT_SIDE = 0, + LAVA_RIGHT_SIDE = 1, + + // Counters + MAX_LEFT_LAVA_TSUNAMIS = 3, + MAX_RIGHT_LAVA_TSUNAMIS = 2, + MAX_DRAGONS = 3, + MAX_AREA_TRIGGER_COUNT = 2, + MAX_CYCLONE_COUNT = 5, + MAX_TENEBORN_EGGS_SUMMONS = 6, + MAX_BOUNDARY_POSITIONS = 4, }; enum Events @@ -139,43 +150,76 @@ enum Events EVENT_SARTHARION_START_LAVA = 14, EVENT_SARTHARION_FINISH_LAVA = 15, EVENT_SARTHARION_LAVA_STRIKE = 16, - EVENT_SARTHARION_HEALTH_CHECK = 17, - EVENT_SARTHARION_BERSERK = 18, + EVENT_SARTHARION_BERSERK = 17, // Drake abilities called by sartharion EVENT_SARTHARION_CALL_TENEBRON = 30, EVENT_SARTHARION_CALL_SHADRON = 31, EVENT_SARTHARION_CALL_VESPERON = 32, - EVENT_SARTHARION_BOUNDARY = 33 + EVENT_SARTHARION_BOUNDARY = 33, + EVENT_MINIDRAKE_SPEECH = 34, }; const Position portalPos[4] = { - {3247.29f, 529.804f, 58.9595f, 0.0f}, - {3248.62f, 646.739f, 85.2939f, 0.0f}, - {3151.20f, 517.862f, 90.3389f, 0.0f}, - {3351.78f, 517.138f, 99.1620f, 0.0f}, + { 3247.29f, 529.804f, 58.9595f, 0.0f }, + { 3248.62f, 646.739f, 85.2939f, 0.0f }, + { 3151.20f, 517.862f, 90.3389f, 0.0f }, + { 3351.78f, 517.138f, 99.1620f, 0.0f }, }; -const Position EggsPos[12] = +// 0 = Tenebron normal +// 1 = Tenebron was called by Sartharion +const Position TenebronEggsPos[2][MAX_TENEBORN_EGGS_SUMMONS] = { - // Tenebron - {3253.09f, 657.439f, 86.9921f, 3.16334f}, - {3247.76f, 662.413f, 87.7281f, 4.12938f}, - {3246.01f, 656.606f, 86.8737f, 4.12938f}, - {3246.7f, 649.558f, 85.8179f, 4.12938f}, - {3238.72f, 650.386f, 85.9625f, 0.897469f}, - {3257.89f, 651.323f, 85.9177f, 0.897469f}, - // Sartharion - {3237.24f, 524.20f, 58.95f, 0.0f}, - {3238.95f, 513.96f, 58.662f, 0.7f}, - {3245.66f, 519.685f, 58.78f, 0.7f}, - {3254.64f, 524.6f, 58.811f, 1.966f}, - {3258.9f, 534.41f, 58.811f, 2.08f}, - {3248.23f, 541.93f, 58.718f, 3.29f} + // Teneborn normal + { + { 3253.09f, 657.439f, 86.9921f, 3.16334f }, + { 3247.76f, 662.413f, 87.7281f, 4.12938f }, + { 3246.01f, 656.606f, 86.8737f, 4.12938f }, + { 3246.7f, 649.558f, 85.8179f, 4.12938f }, + { 3238.72f, 650.386f, 85.9625f, 0.897469f }, + { 3257.89f, 651.323f, 85.9177f, 0.897469f }, + }, + // Tenebron eggs positions when he is called by Sartharion + { + { 3237.24f, 524.20f, 58.95f, 0.0f }, + { 3238.95f, 513.96f, 58.662f, 0.7f }, + { 3245.66f, 519.685f, 58.78f, 0.7f }, + { 3254.64f, 524.6f, 58.811f, 1.966f }, + { 3258.9f, 534.41f, 58.811f, 2.08f }, + { 3248.23f, 541.93f, 58.718f, 3.29f } + } +}; + +const Position CycloneSummonPos[MAX_CYCLONE_COUNT] = +{ + { 3235.28f, 591.180f, 57.0833f, 0.59037f }, + { 3200.97f, 480.929f, 57.0833f, 5.86197f }, + { 3281.57f, 507.984f, 57.0833f, 5.54346f }, + { 3210.11f, 531.957f, 57.0833f, 3.76777f }, + { 3286.42f, 585.010f, 57.0833f, 4.10307f }, +}; + +const Position AreaTriggerSummonPos[MAX_AREA_TRIGGER_COUNT] = +{ + { 3244.14f, 512.597f, 58.6534f, 0.0f }, + { 3242.84f, 553.979f, 58.8272f, 0.0f }, }; +const float SartharionBoundary[MAX_BOUNDARY_POSITIONS] = +{ + 3218.86f, // South X + 3275.69f, // North X + 484.68f, // East Y + 572.4f // West Y +}; + +const Position bigIslandMiddlePos = { 3242.822754f, 477.279816f, 57.430473f }; + +const uint32 dragons[MAX_DRAGONS] = { DATA_TENEBRON, DATA_VESPERON, DATA_SHADRON }; + ///////////////////////////// // SARTHARION ///////////////////////////// @@ -190,631 +234,950 @@ public: return new boss_sartharionAI (pCreature); } - struct boss_sartharionAI : public ScriptedAI + struct boss_sartharionAI : public BossAI { - boss_sartharionAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me) + boss_sartharionAI(Creature* pCreature) : BossAI(pCreature, DATA_SARTHARION), + dragonsCount(0), + lastLavaSide(LAVA_RIGHT_SIDE), + usedBerserk(false), + below11PctReached(false) { - pInstance = me->GetInstanceScript(); - dragons[0] = dragons[1] = dragons[2] = 0; } - InstanceScript* pInstance; - SummonList summons; - EventMap events; - uint64 dragons[3]; - uint8 dragonsCount; - bool usedBerserk; - std::list<uint32> volcanoBlows; + void Reset() override + { + _Reset(); + extraEvents.Reset(); + RespawnDragons(false); + SummonStartingTriggers(); + usedBerserk = false; + below11PctReached = false; + dragonsCount = 0; + volcanoBlows.clear(); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); + } - void HandleSartharionAbilities(); - void HandleDrakeAbilities(); + void DoAction(int32 param) override + { + if (param == ACTION_DRAKE_DIED) + { + DoCastSelf(SPELL_SARTHARION_TWILIGHT_REVENGE, true); + } + } - void SummonStartingTriggers() + void EnterCombat(Unit* pWho) override { - me->SummonCreature(NPC_FIRE_CYCLONE, 3235.28f, 591.180f, 57.0833f, 0.59037f); - me->SummonCreature(NPC_FIRE_CYCLONE, 3200.97f, 480.929f, 57.0833f, 5.86197f); - me->SummonCreature(NPC_FIRE_CYCLONE, 3281.57f, 507.984f, 57.0833f, 5.54346f); - me->SummonCreature(NPC_FIRE_CYCLONE, 3210.11f, 531.957f, 57.0833f, 3.76777f); - me->SummonCreature(NPC_FIRE_CYCLONE, 3286.42f, 585.010f, 57.0833f, 4.10307f); - - me->SummonCreature(NPC_SAFE_AREA_TRIGGER, 3244.14f, 512.597f, 58.6534f, 0.0f); - me->SummonCreature(NPC_SAFE_AREA_TRIGGER, 3242.84f, 553.979f, 58.8272f, 0.0f); + if (pWho && !IsTargetInBounds(pWho)) + { + EnterEvadeMode(); + return; + } + + _EnterCombat(); + DoCastSelf(SPELL_SARTHARION_PYROBUFFET, true); + Talk(SAY_SARTHARION_AGGRO); + + // Combat events + events.ScheduleEvent(EVENT_SARTHARION_CAST_CLEAVE, 7000); + events.ScheduleEvent(EVENT_SARTHARION_CAST_FLAME_BREATH, 15000); + events.ScheduleEvent(EVENT_SARTHARION_CAST_TAIL_LASH, 11000); + + // Extra events + extraEvents.ScheduleEvent(EVENT_SARTHARION_SUMMON_LAVA, 20000); + extraEvents.ScheduleEvent(EVENT_SARTHARION_LAVA_STRIKE, 5000); + extraEvents.ScheduleEvent(EVENT_SARTHARION_BERSERK, 900000); + extraEvents.ScheduleEvent(EVENT_SARTHARION_BOUNDARY, 250); + + // Store dragons + for (uint8 i = 0; i < MAX_DRAGONS; ++i) + { + Creature* dragon = ObjectAccessor::GetCreature(*me, instance->GetData64(dragons[i])); + if (!dragon || !dragon->IsAlive() || instance->GetBossState(dragons[i]) == DONE) + { + continue; + } + + dragon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + 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, 10000); + break; + } + case DATA_SHADRON: + { + dragon->CastSpell(dragon, SPELL_POWER_OF_SHADRON, true); + extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_SHADRON, 65000); + break; + } + case DATA_VESPERON: + { + dragon->CastSpell(dragon, SPELL_POWER_OF_VESPERON, true); + extraEvents.ScheduleEvent(EVENT_SARTHARION_CALL_VESPERON, 115000); + break; + } + } + } + + if (dragonsCount) + { + DoCastSelf(SPELL_WILL_OF_SARTHARION, true); + } + + me->CallForHelp(500.0f); } - void SummonLavaWaves() + void JustDied(Unit* /*pKiller*/) override { - summons.RemoveNotExisting(); - Talk(WHISPER_LAVA_CHURN); - events.ScheduleEvent(EVENT_SARTHARION_START_LAVA, 2000); - events.ScheduleEvent(EVENT_SARTHARION_FINISH_LAVA, 9000); + RespawnDragons(true); + _JustDied(); + Talk(SAY_SARTHARION_DEATH); + } - // Send wave from left - if (urand(0, 1)) + void SetData(uint32 type, uint32 data) override + { + if (type == DATA_VOLCANO_BLOWS && data) { - for (uint8 i = 0; i < 3; ++i) - me->SummonCreature(NPC_FLAME_TSUNAMI, 3208.44f, 580.0f - (i * 50.0f), 55.8f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 12000); + if (!volcanoBlows.empty() && std::find(volcanoBlows.begin(), volcanoBlows.end(), data) != volcanoBlows.end()) + { + return; + } + + volcanoBlows.push_back(data); } - // from right - else + } + + uint32 GetData(uint32 dataOrGuid) const override + { + // it means we want dragons count + if (dataOrGuid == DATA_ACHIEVEMENT_DRAGONS_COUNT) { - for (uint8 i = 0; i < 2; ++i) - me->SummonCreature(NPC_FLAME_TSUNAMI, 3283.44f, 555.0f - (i * 50.0f), 55.8f, 3.14f, TEMPSUMMON_TIMED_DESPAWN, 12000); + 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 SendLavaWaves(bool start) + void KilledUnit(Unit* pVictim) override { - Unit* cr = nullptr; - for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr) + if (!urand(0, 2) && pVictim->GetTypeId() == TYPEID_PLAYER) { - cr = ObjectAccessor::GetUnit(*me, *itr); - if (!cr || cr->GetEntry() != NPC_FLAME_TSUNAMI) - continue; + Talk(SAY_SARTHARION_SLAY); + } + } - if (start) - cr->GetMotionMaster()->MovePoint(0, ((cr->GetPositionX() < 3250.0f) ? 3283.44f : 3208.44f), cr->GetPositionY(), cr->GetPositionZ()); - else - cr->SetObjectScale(0.1f); + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) + { + case NPC_FLAME_TSUNAMI: + { + summon->SetSpeed(MOVE_FLIGHT, 1.5f); + break; + } + case NPC_FIRE_CYCLONE: + { + summon->GetMotionMaster()->MoveRandom(5.0f); + break; + } } + + summons.Summon(summon); } - void StoreDragons() + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override { - if (pInstance) + // Temporal hack, by some case some melee spells can bypass this aura damage reduction + if (me->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { - Unit* cr = nullptr; - for (uint8 i = 0; i < 3; ++i) - if ((cr = ObjectAccessor::GetUnit(*me, pInstance->GetData64(DATA_TENEBRON + i)))) - { - if (!cr->IsAlive()) - continue; + damage = 0; + return; + } - cr->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - dragons[i] = cr->GetGUID(); - dragonsCount++; - me->AddLootMode(1 << dragonsCount); + if (!usedBerserk && me->HealthBelowPctDamaged(30, damage)) + { + DoCastSelf(SPELL_SARTHARION_BERSERK, true); + usedBerserk = true; + return; + } - cr->SetHealth(cr->GetMaxHealth()); - switch(DATA_TENEBRON + i) + // Soft enrage + if (!below11PctReached && me->HealthBelowPctDamaged(10, damage)) + { + summons.RemoveNotExisting(); + if (!summons.empty()) + { + for (uint64 const summonGuid : summons) + { + Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid); + if (summon && summon->GetEntry() == NPC_FIRE_CYCLONE) { - case DATA_TENEBRON: - cr->CastSpell(cr, SPELL_POWER_OF_TENEBRON, true); - events.ScheduleEvent(EVENT_SARTHARION_CALL_TENEBRON, 10000); - break; - case DATA_SHADRON: - cr->CastSpell(cr, SPELL_POWER_OF_SHADRON, true); - events.ScheduleEvent(EVENT_SARTHARION_CALL_SHADRON, 65000); - break; - case DATA_VESPERON: - cr->CastSpell(cr, SPELL_POWER_OF_VESPERON, true); - events.ScheduleEvent(EVENT_SARTHARION_CALL_VESPERON, 115000); - break; + summon->CastSpell(summon, SPELL_CYCLONE_AURA_PERIODIC, true); } } - - if (dragonsCount) - me->CastSpell(me, SPELL_WILL_OF_SARTHARION, true); + } + Talk(SAY_SARTHARION_BERSERK); + below11PctReached = true; } } - void RespawnDragons(bool combat) + void UpdateAI(uint32 diff) override { - if (pInstance) + if (!UpdateVictim()) { - Creature* cr = nullptr; - for (uint8 i = 0; i < 3; ++i) - if (dragons[i]) - if ((cr = ObjectAccessor::GetCreature(*me, dragons[i]))) + return; + } + + extraEvents.Update(diff); + + // Special events which needs to be fired immidiately + while (uint32 const eventId = extraEvents.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SARTHARION_BOUNDARY: + { + if (!IsTargetInBounds(me->GetVictim())) + { + EnterEvadeMode(); + } + else + { + extraEvents.RepeatEvent(250); + } + break; + } + case EVENT_SARTHARION_SUMMON_LAVA: + { + if (!urand(0, 3)) { - if (combat && cr->IsInCombat()) - continue; + Talk(SAY_SARTHARION_SPECIAL); + } - cr->DespawnOrUnsummon(); - cr->SetRespawnTime(10); + SummonLavaWaves(); + extraEvents.RepeatEvent(25000); + 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->GetData64(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->GetData64(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->GetData64(DATA_VESPERON))) + { + vesperon->AI()->DoAction(ACTION_CALL_DRAGON); } - dragons[0] = dragons[1] = dragons[2] = 0; - dragonsCount = 0; + break; + } + } } - } - void Reset() - { - events.Reset(); - summons.DespawnAll(); - me->ResetLootMode(); - RespawnDragons(false); - SummonStartingTriggers(); - usedBerserk = false; - volcanoBlows.clear(); + // Handle Sartharion combat abbilities + events.Update(diff); - if (pInstance) + if (me->HasUnitState(UNIT_STATE_CASTING)) { - pInstance->SetData(BOSS_SARTHARION_EVENT, NOT_STARTED); - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); + return; } - } - void DoAction(int32 param) - { - if (param == ACTION_DRAKE_DIED) - me->CastSpell(me, SPELL_SARTHARION_TWILIGHT_REVENGE, true); - } + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SARTHARION_CAST_CLEAVE: + { + DoCastVictim(SPELL_SARTHARION_CLEAVE, false); + events.RepeatEvent(10000); + break; + } + case EVENT_SARTHARION_CAST_FLAME_BREATH: + { + DoCastVictim(SPELL_SARTHARION_FLAME_BREATH, false); + events.RepeatEvent(20000); + break; + } + case EVENT_SARTHARION_CAST_TAIL_LASH: + { + DoCastSelf(SPELL_SARTHARION_TAIL_LASH, false); + events.RepeatEvent(18000); + break; + } + case EVENT_SARTHARION_LAVA_STRIKE: + { + if (!urand(0, 2)) + { + Talk(SAY_SARTHARION_SPECIAL_4); + } - void EnterCombat(Unit* /*pWho*/) - { - me->CastSpell(me, SPELL_SARTHARION_PYROBUFFET, true); - me->SetInCombatWithZone(); - Talk(SAY_SARTHARION_AGGRO); - if (pInstance) - pInstance->SetData(BOSS_SARTHARION_EVENT, IN_PROGRESS); + summons.RemoveNotExisting(); + uint8 rand = urand(0, MAX_CYCLONE_COUNT - 1); // 5 - numer of cyclones + uint8 iter = 0; + if (!summons.empty()) + { + for (uint64 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.ScheduleEvent(EVENT_SARTHARION_CAST_CLEAVE, 7000); - events.ScheduleEvent(EVENT_SARTHARION_CAST_FLAME_BREATH, 15000); - events.ScheduleEvent(EVENT_SARTHARION_CAST_TAIL_LASH, 11000); - events.ScheduleEvent(EVENT_SARTHARION_SUMMON_LAVA, 20000); - events.ScheduleEvent(EVENT_SARTHARION_LAVA_STRIKE, 5000); - events.ScheduleEvent(EVENT_SARTHARION_HEALTH_CHECK, 10000); - events.ScheduleEvent(EVENT_SARTHARION_BERSERK, 900000); - events.ScheduleEvent(EVENT_SARTHARION_BOUNDARY, 1000); + events.RepeatEvent((below11PctReached ? urand(1400, 2000) : urand(5000, 20000))); + break; + } + case EVENT_SARTHARION_BERSERK: + { + summons.DespawnEntry(NPC_SAFE_AREA_TRIGGER); + break; + } + } - StoreDragons(); - me->CallForHelp(500.0f); + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; + } + } + + DoMeleeAttackIfReady(); } - void JustDied(Unit* /*pKiller*/) + private: + void SummonStartingTriggers() { - RespawnDragons(true); - summons.DespawnAll(); - Talk(SAY_SARTHARION_DEATH); + for (uint8 i = 0; i < MAX_CYCLONE_COUNT; ++i) + { + me->SummonCreature(NPC_FIRE_CYCLONE, CycloneSummonPos[i]); + } - if (pInstance) - pInstance->SetData(BOSS_SARTHARION_EVENT, DONE); + for (uint8 i = 0; i < MAX_AREA_TRIGGER_COUNT; ++i) + { + me->SummonCreature(NPC_SAFE_AREA_TRIGGER, AreaTriggerSummonPos[i]); + } } - void KilledUnit(Unit* pVictim) + void SummonLavaWaves() { - if (!urand(0, 2) && pVictim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SARTHARION_SLAY); - } + summons.RemoveNotExisting(); + Talk(WHISPER_LAVA_CHURN); + extraEvents.ScheduleEvent(EVENT_SARTHARION_START_LAVA, 2000); + extraEvents.ScheduleEvent(EVENT_SARTHARION_FINISH_LAVA, 9000); - void JustSummoned(Creature* cr) - { - if (cr->GetEntry() == NPC_FLAME_TSUNAMI) - cr->SetSpeed(MOVE_FLIGHT, 1.5f); - else if (cr->GetEntry() == NPC_FIRE_CYCLONE) - cr->GetMotionMaster()->MoveRandom(5.0f); + // Send wave from left + if (lastLavaSide == LAVA_RIGHT_SIDE) + { + for (uint8 i = 0; i < MAX_LEFT_LAVA_TSUNAMIS; ++i) + { + me->SummonCreature(NPC_FLAME_TSUNAMI, 3208.44f, 580.0f - (i * 50.0f), 55.8f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 12000); + } - summons.Summon(cr); + lastLavaSide = LAVA_LEFT_SIDE; + } + // from right + else + { + for (uint8 i = 0; i < MAX_RIGHT_LAVA_TSUNAMIS; ++i) + { + me->SummonCreature(NPC_FLAME_TSUNAMI, 3283.44f, 555.0f - (i * 50.0f), 55.8f, 3.14f, TEMPSUMMON_TIMED_DESPAWN, 12000); + } + + lastLavaSide = LAVA_RIGHT_SIDE; + } } - void SetData(uint32 type, uint32 data) + void SendLavaWaves(bool start) { - if (type != DATA_VOLCANO_BLOWS) + if (summons.empty()) + { return; + } - if (!volcanoBlows.empty()) - for (std::list<uint32>::const_iterator itr = volcanoBlows.begin(); itr != volcanoBlows.end(); ++itr) - if (data == (*itr)) - return; + for (uint64 const guid : summons) + { + Creature* tsunami = ObjectAccessor::GetCreature(*me, guid); + if (!tsunami || tsunami->GetEntry() != NPC_FLAME_TSUNAMI) + { + continue; + } - volcanoBlows.push_back(data); + if (start) + { + tsunami->GetMotionMaster()->MovePoint(0, ((tsunami->GetPositionX() < 3250.0f) ? 3283.44f : 3208.44f), tsunami->GetPositionY(), tsunami->GetPositionZ()); + } + else + { + tsunami->SetObjectScale(0.1f); + } + } } - uint32 GetData(uint32 dataOrGuid) const - { - // it means we want dragons count - if (dataOrGuid == DATA_ACHIEVEMENT_DRAGONS_COUNT) - return dragonsCount; + void RespawnDragons(bool checkCombat) + { + for (uint8 i = 0; i < MAX_DRAGONS; ++i) + { + if (instance->GetBossState(dragons[i]) == DONE) + { + continue; + } - // otherwise it is guid to check if player was hit by lava strike :) - if (!volcanoBlows.empty()) - for (std::list<uint32>::const_iterator itr = volcanoBlows.begin(); itr != volcanoBlows.end(); ++itr) - if (dataOrGuid == (*itr)) - return true; + if (Creature* dragon = ObjectAccessor::GetCreature(*me, instance->GetData64(dragons[i]))) + { + if (checkCombat && dragon->IsInCombat()) + { + continue; + } + + dragon->DespawnOrUnsummon(); + dragon->SetRespawnTime(5); + } + } - return false; + dragonsCount = 0; } - void UpdateAI(uint32 diff) + bool IsTargetInBounds(Unit const* victim) const { - if (!UpdateVictim()) - return; - - events.Update(diff); - - // Special events which needs to be fired immidiately - switch(events.ExecuteEvent()) + if (!victim || victim->GetPositionX() < SartharionBoundary[0] || victim->GetPositionX() > SartharionBoundary[1] || victim->GetPositionY() > SartharionBoundary[3]) { - case EVENT_SARTHARION_BOUNDARY: - if (me->GetPositionX() < 3218.86f || me->GetPositionX() > 3275.69f || me->GetPositionY() < 484.68f || me->GetPositionY() > 572.4f) // https://github.com/TrinityCore/TrinityCore/blob/3.3.5/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp#L31 - EnterEvadeMode(); - - events.RepeatEvent(1000); - break; - case EVENT_SARTHARION_SUMMON_LAVA: - if (!urand(0, 3)) - Talk(SAY_SARTHARION_SPECIAL); - - SummonLavaWaves(); - events.RepeatEvent(25000); - return; - case EVENT_SARTHARION_START_LAVA: - SendLavaWaves(true); - - return; - case EVENT_SARTHARION_FINISH_LAVA: - SendLavaWaves(false); - - return; + return false; } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - HandleSartharionAbilities(); - HandleDrakeAbilities(); + // Big island handling + if (victim->GetPositionY() < SartharionBoundary[2]) + { + return victim->GetDistance(bigIslandMiddlePos) <= 6.0f; + } - DoMeleeAttackIfReady(); + return true; } + + EventMap extraEvents; + std::list<uint32> volcanoBlows; + uint8 dragonsCount; + uint8 lastLavaSide; // 0 = left, 1 = right + bool usedBerserk; + bool below11PctReached; }; }; -void boss_sartharion::boss_sartharionAI::HandleSartharionAbilities() +struct boss_sartharion_dragonAI : public BossAI { - // Handling of Sartharion Events - switch (events.ExecuteEvent()) + boss_sartharion_dragonAI(Creature* pCreature, uint32 bossId) : BossAI(pCreature, bossId), + portalGUID(0), + isCalledBySartharion(false) { - case EVENT_SARTHARION_CAST_CLEAVE: - me->CastSpell(me->GetVictim(), SPELL_SARTHARION_CLEAVE, false); - events.RepeatEvent(10000); - break; - case EVENT_SARTHARION_CAST_FLAME_BREATH: - me->CastSpell(me->GetVictim(), SPELL_SARTHARION_FLAME_BREATH, false); - events.RepeatEvent(20000); - break; - case EVENT_SARTHARION_CAST_TAIL_LASH: - me->CastSpell(me, SPELL_SARTHARION_TAIL_LASH, false); - events.RepeatEvent(18000); - break; - case EVENT_SARTHARION_LAVA_STRIKE: - { - if (!urand(0, 2)) - Talk(SAY_SARTHARION_SPECIAL_4); + } - Creature* cr = nullptr; - summons.RemoveNotExisting(); - uint8 rand = urand(0, 4); // 5 - numer of cyclones - uint8 iter = 0; - for (SummonList::iterator i = summons.begin(); i != summons.end(); ++i) + void Reset() override + { + _Reset(); + events.Reset(); + ClearInstance(); + + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + me->SetSpeed(MOVE_FLIGHT, 1.0f); + me->SetCanFly(false); + me->ResetLootMode(); + portalGUID = 0; + isCalledBySartharion = false; + instance->DoAction(ACTION_CLEAR_PORTAL); + } + + void DoAction(int32 param) final + { + if (param == ACTION_CALL_DRAGON && !isCalledBySartharion) + { + isCalledBySartharion = true; + extraEvents.RescheduleEvent(EVENT_MINIDRAKE_SPEECH, 4000); + } + } + + void MoveInLineOfSight(Unit* who) final + { + if (isCalledBySartharion) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void MovementInform(uint32 type, uint32 pointId) final + { + if (type != WAYPOINT_MOTION_TYPE) + { + return; + } + + switch (me->GetEntry()) + { + case NPC_TENEBRON: + { + if (pointId != POINT_FINAL_TENEBRON) { - if ((cr = ObjectAccessor::GetCreature(*me, *i))) - if (cr->GetEntry() == NPC_FIRE_CYCLONE) - { - if (iter == rand) - { - cr->CastSpell(cr, SPELL_CYCLONE_AURA_PERIODIC, true); - break; - } - ++iter; - } + return; } - - events.RepeatEvent(20000); break; } - case EVENT_SARTHARION_HEALTH_CHECK: - if (dragonsCount && !usedBerserk && me->HealthBelowPct(36)) + case NPC_SHADRON: { - me->CastSpell(me, SPELL_SARTHARION_BERSERK, true); - usedBerserk = true; - events.RepeatEvent(2000); + if (pointId != POINT_FINAL_SHADRON) + { + return; + } break; } - - if (me->HealthBelowPct(11)) + case NPC_VESPERON: { - Creature* cr = nullptr; - summons.RemoveNotExisting(); - for (SummonList::iterator i = summons.begin(); i != summons.end(); ++i) + if (pointId != POINT_FINAL_VESPERON) { - if ((cr = ObjectAccessor::GetCreature(*me, *i))) - if (cr->GetEntry() == NPC_FIRE_CYCLONE) - cr->CastSpell(cr, SPELL_CYCLONE_AURA_PERIODIC, true); + return; } - Talk(SAY_SARTHARION_BERSERK); - break; } - events.RepeatEvent(2000); - break; - case EVENT_SARTHARION_BERSERK: - summons.DespawnEntry(NPC_SAFE_AREA_TRIGGER); - - break; - } -} + } -void boss_sartharion::boss_sartharionAI::HandleDrakeAbilities() -{ - // Handling of Drakes Events - switch (events.ExecuteEvent()) - { - // Dragon Calls - case EVENT_SARTHARION_CALL_TENEBRON: - Talk(SAY_SARTHARION_CALL_TENEBRON); - if (Creature* tenebron = ObjectAccessor::GetCreature(*me, dragons[DRAGON_TENEBRON])) - tenebron->AI()->DoAction(ACTION_CALL_DRAGON); - - break; - case EVENT_SARTHARION_CALL_SHADRON: - Talk(SAY_SARTHARION_CALL_SHADRON); - if (Creature* shadron = ObjectAccessor::GetCreature(*me, dragons[DRAGON_SHADRON])) - shadron->AI()->DoAction(ACTION_CALL_DRAGON); - - break; - case EVENT_SARTHARION_CALL_VESPERON: - Talk(SAY_SARTHARION_CALL_VESPERON); - if (Creature* vesperon = ObjectAccessor::GetCreature(*me, dragons[DRAGON_VESPERON])) - vesperon->AI()->DoAction(ACTION_CALL_DRAGON); - - break; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + me->SetInCombatWithZone(); } -} - -///////////////////////////// -// TENEBRON -///////////////////////////// - -class boss_sartharion_tenebron : public CreatureScript -{ -public: - boss_sartharion_tenebron() : CreatureScript("boss_sartharion_tenebron") { } - CreatureAI* GetAI(Creature* pCreature) const + void JustSummoned(Creature* summon) override { - return new boss_sartharion_tenebronAI (pCreature); + // Transfer summons to Sartharion + if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + { + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) + { + sartharion->AI()->JustSummoned(summon); + } + } + + BossAI::JustSummoned(summon); } - struct boss_sartharion_tenebronAI : public ScriptedAI + void EnterCombat(Unit* /*who*/) final { - boss_sartharion_tenebronAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me), summons2(me) + me->setActive(true); + DoZoneInCombat(); + Talk(SAY_TENEBRON_AGGRO); + + if (me->IsFlying()) { - portalGUID = 0; - pInstance = me->GetInstanceScript(); + me->SetSpeed(MOVE_FLIGHT, 1.0f); + me->SetCanFly(false); } - EventMap events; - SummonList summons; - SummonList summons2; - uint64 portalGUID; - InstanceScript* pInstance; - bool isSartharion; - uint32 timer; - - void ClearInstance() + if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) { - events.Reset(); - summons.DespawnAll(); - // Remove phase shift - if (pInstance) - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); - - RemoveTwilightPortal(); + switch (me->GetEntry()) + { + case NPC_TENEBRON: + { + instance->SetBossState(DATA_TENEBRON, IN_PROGRESS); + break; + } + case NPC_SHADRON: + { + instance->SetBossState(DATA_SHADRON, IN_PROGRESS); + break; + } + case NPC_VESPERON: + { + instance->SetBossState(DATA_VESPERON, IN_PROGRESS); + break; + } + } } - void RemoveTwilightPortal() + if (isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) == DONE) { - if (portalGUID) - if (GameObject* gobj = me->GetMap()->GetGameObject(portalGUID)) - gobj->Delete(); - - portalGUID = 0; + me->SetLootMode(0); } - void DoAction(int32 param) + if (isCalledBySartharion) { - if (param == ACTION_CALL_DRAGON) + if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 1, 500, true)) { - isSartharion = true; - timer++; + AttackStart(target); } } + } - void MoveInLineOfSight(Unit* who) - { - if (isSartharion) - return; + void JustDied(Unit* /*killer*/) override + { + //_JustDied(); + events.Reset(); - ScriptedAI::MoveInLineOfSight(who); + // Despawn minions only if not called by Sartharion + if (!isCalledBySartharion) + { + summons.DespawnAll(); } - void Reset() + switch (me->GetEntry()) { - summons2.DespawnAll(); - ClearInstance(); + case NPC_TENEBRON: + { + Talk(SAY_TENEBRON_DEATH); + if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + { + instance->SetBossState(DATA_SHADRON, DONE); + } + break; + } + case NPC_SHADRON: + { + Talk(SAY_SHADRON_DEATH); + if (isCalledBySartharion) + { + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) + { + sartharion->RemoveAura(SPELL_GIFT_OF_TWILIGHT_FIRE); + } + } - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - me->SetDisableGravity(false); - me->SetSpeed(MOVE_FLIGHT, 1.0f); - me->SetCanFly(false); - me->ResetLootMode(); - isSartharion = false; - timer = 0; + if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + { + instance->SetBossState(DATA_SHADRON, DONE); + } + break; + } + case NPC_VESPERON: + { + Talk(SAY_VESPERON_DEATH); + instance->DoAction(ACTION_CLEAR_PORTAL); + if (!isCalledBySartharion || instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + { + instance->SetBossState(DATA_VESPERON, DONE); + } + break; + } + } - if (pInstance) + if (!isCalledBySartharion) + { + ClearInstance(); + } + else + { + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) { - pInstance->SetData(BOSS_TENEBRON_EVENT, NOT_STARTED); - pInstance->SetData(DATA_CLEAR_PORTAL, 0); + sartharion->AI()->DoAction(ACTION_DRAKE_DIED); } } + } - void EnterCombat(Unit* ) + void KilledUnit(Unit* victim) final + { + if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2)) { - Talk(SAY_TENEBRON_AGGRO); - me->SetInCombatWithZone(); + return; + } - if (me->IsFlying()) + switch (me->GetEntry()) + { + case NPC_TENEBRON: + { + Talk(SAY_TENEBRON_SLAY); + break; + } + case NPC_SHADRON: + { + Talk(SAY_SHADRON_SLAY); + break; + } + case NPC_VESPERON: { - me->SetSpeed(MOVE_FLIGHT, 1.0f); - me->SetCanFly(false); + Talk(SAY_VESPERON_SLAY); + break; } + } + } - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20000); - events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10000); - events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 15000); + void UpdateAI(uint32 diff) final + { + extraEvents.Update(diff); + while (uint32 const eventId = extraEvents.ExecuteEvent()) + { + HandleExtraEvent(eventId); + } - if (pInstance && !isSartharion) - pInstance->SetData(BOSS_TENEBRON_EVENT, IN_PROGRESS); + if (!UpdateVictim()) + { + return; + } - if (isSartharion || (pInstance && pInstance->GetData(BOSS_SARTHARION_EVENT) == DONE)) - me->SetLootMode(0); + events.Update(diff); - if (isSartharion) - if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 1, 500, true)) - me->AI()->AttackStart(target); + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; } - void JustDied(Unit* ) + while (uint32 const eventId = events.ExecuteEvent()) { - Talk(SAY_TENEBRON_DEATH); + ExecuteEvent(eventId); - if (!isSartharion) + if (me->HasUnitState(UNIT_STATE_CASTING)) { - ClearInstance(); - if (pInstance) - pInstance->SetData(BOSS_TENEBRON_EVENT, DONE); + return; } - else if (pInstance) + } + + DoMeleeAttackIfReady(); + } + + virtual void ClearInstance() + { + events.Reset(); + summons.DespawnAll(); + // Remove phase shift + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); + RemoveTwilightPortal(); + } + + virtual void HandleExtraEvent(uint32 const /*eventId*/) { } +protected: + void RemoveTwilightPortal() + { + if (portalGUID) + { + if (GameObject* gobj = me->GetMap()->GetGameObject(portalGUID)) { - pInstance->SetData(DATA_CLEAR_PORTAL, 0); - if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetData64(DATA_SARTHARION))) - cr->AI()->DoAction(ACTION_DRAKE_DIED); + gobj->Delete(); } + + portalGUID = 0; + } + } + + EventMap extraEvents; + uint64 portalGUID; + bool isCalledBySartharion; +}; + +///////////////////////////// +// TENEBRON +///////////////////////////// + +class boss_sartharion_tenebron : public CreatureScript +{ +public: + boss_sartharion_tenebron() : CreatureScript("boss_sartharion_tenebron") { } + + CreatureAI* GetAI(Creature* pCreature) const + { + return new boss_sartharion_tenebronAI (pCreature); + } + + struct boss_sartharion_tenebronAI : public boss_sartharion_dragonAI + { + boss_sartharion_tenebronAI(Creature* pCreature) : boss_sartharion_dragonAI(pCreature, DATA_TENEBRON), summons2(pCreature) + { } - void JustKilled(Unit* victim) + void Reset() override { - if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2)) - return; + boss_sartharion_dragonAI::Reset(); + if (!isCalledBySartharion) + { + summons2.DespawnAll(); + } - Talk(SAY_TENEBRON_SLAY); + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20000); + events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10000); + events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 15000); } - void MovementInform(uint32 type, uint32 pointId) + void JustSummoned(Creature* summon) override { - if (type == WAYPOINT_MOTION_TYPE && pointId == POINT_FINAL_TENEBRON) + if (summon->GetEntry() != NPC_TWILIGHT_EGG) { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - me->SetInCombatWithZone(); + summons.Summon(summon); } + // Summons to Sartharion are linked manually } - void UpdateAI(uint32 diff) + void JustDied(Unit* killer) override { - // Call speach - if (timer) + if (!isCalledBySartharion) { - timer += diff; - if (timer >= 4000) - { - Talk(SAY_TENEBRON_RESPOND); - me->SetCanFly(true); - me->SetSpeed(MOVE_FLIGHT, 3.0f); - me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false); - timer = 0; - } + summons2.DespawnAll(); } - if (!UpdateVictim()) - return; + boss_sartharion_dragonAI::JustDied(killer); + } - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + 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()->MovePath(me->GetEntry() * 10, false); + } + } - switch (events.ExecuteEvent()) + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { case EVENT_MINIBOSS_SHADOW_BREATH: + { if (!urand(0, 10)) + { Talk(SAY_TENEBRON_BREATH); - me->CastSpell(me->GetVictim(), SPELL_SHADOW_BREATH, false); + } + DoCastVictim(SPELL_SHADOW_BREATH, false); events.RepeatEvent(17500); break; + } case EVENT_MINIBOSS_SHADOW_FISSURE: + { if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) - me->CastSpell(target, SPELL_SHADOW_FISSURE, false); + { + DoCast(target, SPELL_SHADOW_FISSURE, false); + } events.RepeatEvent(22500); break; + } case EVENT_MINIBOSS_OPEN_PORTAL: + { Talk(WHISPER_OPEN_PORTAL); Talk(SAY_TENEBRON_SPECIAL); - if (!isSartharion) + if (!isCalledBySartharion) { - if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[BOSS_TENEBRON_EVENT].GetPositionX(), portalPos[BOSS_TENEBRON_EVENT].GetPositionY(), portalPos[BOSS_TENEBRON_EVENT].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + 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); } - else if (pInstance) - pInstance->SetData(DATA_ADD_PORTAL, 0); - events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2000); events.RepeatEvent(60000); break; + } case EVENT_MINIBOSS_SPAWN_HELPERS: + { + Talk(WHISPER_HATCH_EGGS); + for (uint8 i = 0; i < MAX_TENEBORN_EGGS_SUMMONS; ++i) { - Talk(WHISPER_HATCH_EGGS); - Creature* cr = nullptr; - for (uint8 i = 0; i < 6; ++i) + if (Creature* egg = me->SummonCreature(NPC_TWILIGHT_EGG, TenebronEggsPos[isCalledBySartharion ? 1 : 0][i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000)) { - if ((cr = me->SummonCreature(NPC_TWILIGHT_EGG, EggsPos[isSartharion ? i + 6 : i].GetPositionX(), EggsPos[isSartharion ? i + 6 : i].GetPositionY(), EggsPos[isSartharion ? i + 6 : i].GetPositionZ(), EggsPos[isSartharion ? i + 6 : i].GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000))) + summons.Summon(egg); + if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) { - summons.Summon(cr); - cr->SetPhaseMask(16, true); + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) + { + sartharion->AI()->JustSummoned(egg); + } } + egg->SetPhaseMask(16, true); } - - events.ScheduleEvent(EVENT_MINIBOSS_HATCH_EGGS, 25000); - break; } + + events.ScheduleEvent(EVENT_MINIBOSS_HATCH_EGGS, 25000); + break; + } case EVENT_MINIBOSS_HATCH_EGGS: + { + summons.RemoveNotExisting(); + summons.DespawnEntry(NPC_TWILIGHT_WHELP); + for (uint64 const summonGuid : summons) { - Creature* cr = nullptr; - summons.RemoveNotExisting(); - summons.DespawnEntry(NPC_TWILIGHT_WHELP); - for (SummonList::iterator i = summons.begin(); i != summons.end(); ++i) + Creature const* summon = ObjectAccessor::GetCreature(*me, summonGuid); + if (!summon || !summon->IsAlive() || summon->GetEntry() != NPC_TWILIGHT_EGG) { - if ((cr = ObjectAccessor::GetCreature(*me, *i))) - { - if (!cr->IsAlive()) - continue; - - if (cr->GetEntry() == NPC_TWILIGHT_EGG) - if ((cr = me->SummonCreature(NPC_TWILIGHT_WHELP, cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ(), cr->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000))) - summons2.Summon(cr); - } + continue; } - if (!isSartharion) + if (Creature* whelp = me->SummonCreature(NPC_TWILIGHT_WHELP, summon->GetPosition(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000)) { - // Remove phase shift - if (InstanceScript* instance = me->GetInstanceScript()) - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); - - RemoveTwilightPortal(); + summons2.Summon(whelp); + if (isCalledBySartharion && instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + { + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) + { + sartharion->AI()->JustSummoned(whelp); + } + } } - else if (pInstance) - pInstance->SetData(DATA_CLEAR_PORTAL, 0); + } - EntryCheckPredicate pred(NPC_TWILIGHT_EGG); - summons.DoAction(ACTION_SWITCH_PHASE, pred); - break; + 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; + } } + } - DoMeleeAttackIfReady(); + void ClearInstance() override + { + boss_sartharion_dragonAI::ClearInstance(); + summons2.DespawnAll(); } + + private: + SummonList summons2; }; }; @@ -832,135 +1195,34 @@ public: return new boss_sartharion_shadronAI (pCreature); } - struct boss_sartharion_shadronAI : public ScriptedAI + struct boss_sartharion_shadronAI : public boss_sartharion_dragonAI { - boss_sartharion_shadronAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me) - { - portalGUID = 0; - pInstance = me->GetInstanceScript(); - } - - EventMap events; - SummonList summons; - uint64 portalGUID; - InstanceScript* pInstance; - bool isSartharion; - uint32 timer; - - void ClearInstance() + boss_sartharion_shadronAI(Creature* pCreature) : boss_sartharion_dragonAI(pCreature, DATA_SHADRON) { - summons.DespawnAll(); - // Remove phase shift - if (pInstance) - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); - - RemoveTwilightPortal(); } - void RemoveTwilightPortal() + void Reset() override { - if (portalGUID) - if (GameObject* gobj = me->GetMap()->GetGameObject(portalGUID)) - gobj->Delete(); - - portalGUID = 0; - } - - void DoAction(int32 param) - { - if (param == ACTION_CALL_DRAGON) - { - isSartharion = true; - timer++; - } - } - - void MoveInLineOfSight(Unit* who) - { - if (isSartharion) - return; - - ScriptedAI::MoveInLineOfSight(who); - } - - void Reset() - { - events.Reset(); - ClearInstance(); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - me->SetSpeed(MOVE_FLIGHT, 1.0f); - me->SetCanFly(false); - me->ResetLootMode(); - isSartharion = false; - timer = 0; - - if (pInstance) - { - pInstance->SetData(BOSS_SHADRON_EVENT, NOT_STARTED); - pInstance->SetData(DATA_CLEAR_PORTAL, 0); - } - } - - void EnterCombat(Unit* ) - { - Talk(SAY_SHADRON_AGGRO); - me->SetInCombatWithZone(); - me->SetDisableGravity(false); - if (me->IsFlying()) - { - me->SetSpeed(MOVE_FLIGHT, 1.0f); - me->SetCanFly(false); - } - + boss_sartharion_dragonAI::Reset(); events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20000); events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10000); events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 15000); - - if (pInstance && !isSartharion) - pInstance->SetData(BOSS_SHADRON_EVENT, IN_PROGRESS); - - if (isSartharion || (pInstance && pInstance->GetData(BOSS_SARTHARION_EVENT) == DONE)) - me->SetLootMode(0); - - if (isSartharion) - if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 1, 500, true)) - me->AI()->AttackStart(target); - } - - void JustDied(Unit* ) - { - Talk(SAY_SHADRON_DEATH); - - if (!isSartharion) - { - ClearInstance(); - if (pInstance) - pInstance->SetData(BOSS_SHADRON_EVENT, DONE); - } - else if (pInstance) - { - if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetData64(DATA_SARTHARION))) - cr->AI()->DoAction(ACTION_DRAKE_DIED); - } - } - - void JustKilled(Unit* victim) - { - if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2)) - return; - - Talk(SAY_SHADRON_SLAY); } - void SummonedCreatureDies(Creature* /*summon*/, Unit*) + void SummonedCreatureDies(Creature* /*summon*/, Unit* /*summon*/) override { - if (isSartharion && pInstance) + if (isCalledBySartharion) { - if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetData64(DATA_SARTHARION))) - cr->RemoveAura(SPELL_GIFT_OF_TWILIGHT_FIRE); + // Acolytes are dead + if (!summons.HasEntry(NPC_ACOLYTE_OF_SHADRON)) + { + instance->DoAction(ACTION_CLEAR_PORTAL); + } - pInstance->SetData(DATA_CLEAR_PORTAL, 0); + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) + { + sartharion->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGHT_FIRE); + } } else { @@ -971,79 +1233,75 @@ public: events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30000); } - void MovementInform(uint32 type, uint32 pointId) + void HandleExtraEvent(uint32 const eventId) override { - if (type == WAYPOINT_MOTION_TYPE && pointId == POINT_FINAL_SHADRON) + if (eventId == EVENT_MINIDRAKE_SPEECH) { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - me->SetInCombatWithZone(); + Talk(SAY_SHADRON_RESPOND); + me->SetCanFly(true); + me->SetSpeed(MOVE_FLIGHT, 3.0f); + me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false); } } - void UpdateAI(uint32 diff) + void ExecuteEvent(uint32 eventId) override { - // Call speach - if (timer) - { - timer += diff; - if (timer >= 4000) - { - Talk(SAY_SHADRON_RESPOND); - me->SetCanFly(true); - me->SetSpeed(MOVE_FLIGHT, 3.0f); - me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false); - timer = 0; - } - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) + switch (eventId) { case EVENT_MINIBOSS_SHADOW_BREATH: + { if (!urand(0, 10)) + { Talk(SAY_SHADRON_BREATH); - me->CastSpell(me->GetVictim(), SPELL_SHADOW_BREATH, false); + } + + DoCastVictim(SPELL_SHADOW_BREATH, false); events.RepeatEvent(17500); break; + } case EVENT_MINIBOSS_SHADOW_FISSURE: + { if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) - me->CastSpell(target, SPELL_SHADOW_FISSURE, false); + { + DoCast(target, SPELL_SHADOW_FISSURE, false); + } events.RepeatEvent(22500); break; + } case EVENT_MINIBOSS_OPEN_PORTAL: + { Talk(WHISPER_OPEN_PORTAL); Talk(SAY_SHADRON_SPECIAL); - if (!isSartharion) + if (!isCalledBySartharion) { - if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[BOSS_SHADRON_EVENT].GetPositionX(), portalPos[BOSS_SHADRON_EVENT].GetPositionY(), portalPos[BOSS_SHADRON_EVENT].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + 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); } - else if (pInstance) - pInstance->SetData(DATA_ADD_PORTAL, 0); events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2000); - break; + } case EVENT_MINIBOSS_SPAWN_HELPERS: + { Talk(WHISPER_SUMMON_DICIPLE); - me->CastSpell(me, (isSartharion ? SPELL_GIFT_OF_TWILIGHT_FIRE : SPELL_GIFT_OF_TWILIGHT_SHADOW), true); - if (Creature* cr = me->SummonCreature((isSartharion ? NPC_ACOLYTE_OF_SHADRON : NPC_DISCIPLE_OF_SHADRON), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation())) + DoCastAOE(static_cast<uint32>((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())) { - summons.Summon(cr); - cr->SetPhaseMask(16, true); + // TODO: inpect JustSummoned + summons.Summon(acolyte); + acolyte->SetPhaseMask(16, true); } - break; + } } - - DoMeleeAttackIfReady(); } }; }; @@ -1062,218 +1320,113 @@ public: return new boss_sartharion_vesperonAI (pCreature); } - struct boss_sartharion_vesperonAI : public ScriptedAI + struct boss_sartharion_vesperonAI : public boss_sartharion_dragonAI { - boss_sartharion_vesperonAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me) - { - portalGUID = 0; - pInstance = me->GetInstanceScript(); - } - - EventMap events; - SummonList summons; - uint64 portalGUID; - InstanceScript* pInstance; - bool isSartharion; - uint32 timer; - - void ClearInstance() + boss_sartharion_vesperonAI(Creature* pCreature) : boss_sartharion_dragonAI(pCreature, DATA_VESPERON) { - // Remove phase shift - if (pInstance) - { - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESPERON); - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION); - } - - summons.DespawnAll(); - RemoveTwilightPortal(); } - void RemoveTwilightPortal() + void Reset() override { - if (portalGUID) - if (GameObject* gobj = me->GetMap()->GetGameObject(portalGUID)) - gobj->Delete(); - - portalGUID = 0; - } - - void DoAction(int32 param) - { - if (param == ACTION_CALL_DRAGON) - { - isSartharion = true; - timer++; - } - } - - void MoveInLineOfSight(Unit* who) - { - if (isSartharion) - return; - - ScriptedAI::MoveInLineOfSight(who); - } - - void Reset() - { - events.Reset(); - ClearInstance(); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - me->SetSpeed(MOVE_FLIGHT, 1.0f); - me->SetCanFly(false); - me->ResetLootMode(); - isSartharion = false; - timer = 0; - - if (pInstance) - { - pInstance->SetData(BOSS_VESPERON_EVENT, NOT_STARTED); - pInstance->SetData(DATA_CLEAR_PORTAL, 1); - } - } - - void EnterCombat(Unit* ) - { - Talk(SAY_VESPERON_AGGRO); - me->SetInCombatWithZone(); - - if (me->IsFlying()) - { - me->SetSpeed(MOVE_FLIGHT, 1.0f); - me->SetCanFly(false); - } - + boss_sartharion_dragonAI::Reset(); events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_FISSURE, 20000); events.ScheduleEvent(EVENT_MINIBOSS_SHADOW_BREATH, 10000); events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30000); - - if (pInstance && !isSartharion) - pInstance->SetData(BOSS_VESPERON_EVENT, IN_PROGRESS); - - if (isSartharion || (pInstance && pInstance->GetData(BOSS_SARTHARION_EVENT) == DONE)) - me->SetLootMode(0); - - if (isSartharion) - if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 1, 500, true)) - me->AI()->AttackStart(target); } - void JustDied(Unit* ) + void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override { - Talk(SAY_VESPERON_DEATH); - - if (!isSartharion) + if (!isCalledBySartharion) { ClearInstance(); - if (pInstance) - pInstance->SetData(BOSS_VESPERON_EVENT, DONE); - } - else if (pInstance) - { - if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetData64(DATA_SARTHARION))) - cr->AI()->DoAction(ACTION_DRAKE_DIED); } - } - - void JustKilled(Unit* victim) - { - if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2)) - return; - - Talk(SAY_VESPERON_SLAY); - } - - void SummonedCreatureDies(Creature* /*summon*/, Unit*) - { - if (!isSartharion) - ClearInstance(); - else if (pInstance) + else { - pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION); - pInstance->SetData(DATA_CLEAR_PORTAL, 1); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION); + instance->DoAction(ACTION_CLEAR_PORTAL); } events.ScheduleEvent(EVENT_MINIBOSS_OPEN_PORTAL, 30000); } - void MovementInform(uint32 type, uint32 pointId) + void HandleExtraEvent(uint32 const eventId) override { - if (type == WAYPOINT_MOTION_TYPE && pointId == POINT_FINAL_VESPERON) + if (eventId == EVENT_MINIDRAKE_SPEECH) { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); - me->SetInCombatWithZone(); + Talk(SAY_SHADRON_RESPOND); + me->SetCanFly(true); + me->SetSpeed(MOVE_FLIGHT, 3.0f); + me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false); } } - void UpdateAI(uint32 diff) + void ExecuteEvent(uint32 eventId) override { - // Call speach - if (timer) - { - timer += diff; - if (timer >= 4000) - { - Talk(SAY_VESPERON_RESPOND); - me->SetCanFly(true); - me->SetSpeed(MOVE_FLIGHT, 3.0f); - me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false); - timer = 0; - } - return; - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) + switch (eventId) { case EVENT_MINIBOSS_SHADOW_BREATH: + { if (!urand(0, 10)) + { Talk(SAY_SHADRON_BREATH); - me->CastSpell(me->GetVictim(), SPELL_SHADOW_BREATH, false); + } + + DoCastVictim(SPELL_SHADOW_BREATH, false); events.RepeatEvent(17500); break; + } case EVENT_MINIBOSS_SHADOW_FISSURE: + { if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) - me->CastSpell(target, SPELL_SHADOW_FISSURE, false); + { + DoCast(target, SPELL_SHADOW_FISSURE, false); + } + events.RepeatEvent(22500); break; + } case EVENT_MINIBOSS_OPEN_PORTAL: + { Talk(WHISPER_OPEN_PORTAL); Talk(SAY_VESPERON_SPECIAL); - if (!isSartharion) + if (!isCalledBySartharion) { - if (GameObject* Portal = me->GetVictim()->SummonGameObject(GO_TWILIGHT_PORTAL, portalPos[BOSS_VESPERON_EVENT].GetPositionX(), portalPos[BOSS_VESPERON_EVENT].GetPositionY(), portalPos[BOSS_VESPERON_EVENT].GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + 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); } - else if (pInstance) - pInstance->SetData(DATA_ADD_PORTAL, 0); events.ScheduleEvent(EVENT_MINIBOSS_SPAWN_HELPERS, 2000); - break; + } case EVENT_MINIBOSS_SPAWN_HELPERS: + { Talk(WHISPER_SUMMON_DICIPLE); - me->CastSpell(me, (isSartharion ? (uint32)SPELL_TWILIGHT_TORMENT_SARTHARION : (uint32)SPELL_TWILIGHT_TORMENT_VESPERON), true); - if (Creature* cr = me->SummonCreature((isSartharion ? NPC_ACOLYTE_OF_VESPERON : NPC_DISCIPLE_OF_VESPERON), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation())) + DoCastSelf(isCalledBySartharion ? static_cast<uint32>(SPELL_TWILIGHT_TORMENT_SARTHARION) : static_cast<uint32>(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(cr); - cr->SetPhaseMask(16, true); + summons.Summon(acolyte); + acolyte->SetPhaseMask(16, true); } - break; + } } + } - DoMeleeAttackIfReady(); + private: + void ClearInstance() override + { + boss_sartharion_dragonAI::ClearInstance(); + + // Remove phase shift + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESPERON); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_SARTHARION); } }; }; @@ -1291,21 +1444,25 @@ public: struct npc_twilight_summonAI : public ScriptedAI { - npc_twilight_summonAI(Creature* pCreature) : ScriptedAI(pCreature) + npc_twilight_summonAI(Creature* pCreature) : ScriptedAI(pCreature), + fadeArmorTimer(urand(0, 15000)) { } - uint32 timer; - void Reset() + void Reset() override { - timer = urand(0, 15000); + fadeArmorTimer = urand(0, 15000); if (me->GetEntry() == NPC_TWILIGHT_EGG) + { me->SetControlled(true, UNIT_STATE_STUNNED); + } else + { me->SetInCombatWithZone(); + } } - void DoAction(int32 param) + void DoAction(int32 param) override { if (param == ACTION_SWITCH_PHASE) { @@ -1313,24 +1470,33 @@ public: } } - void UpdateAI(uint32 diff) + void UpdateAI(uint32 diff) override { if (!UpdateVictim() || me->GetEntry() == NPC_TWILIGHT_EGG) + { return; + } - timer += diff; - if (timer >= 15000) + fadeArmorTimer += diff; + if (fadeArmorTimer >= 15000) { if (Aura* aur = me->GetVictim()->GetAura(SPELL_FADE_ARMOR)) + { aur->ModStackAmount(1); + } else - me->CastSpell(me->GetVictim(), SPELL_FADE_ARMOR, false); + { + DoCastVictim(SPELL_FADE_ARMOR, false); + } - timer = 0; + fadeArmorTimer = 0; } DoMeleeAttackIfReady(); } + + private: + uint32 fadeArmorTimer; }; }; @@ -1345,7 +1511,11 @@ public: bool spawned; - bool Load() { spawned = false; return true; } + bool Load() override + { + spawned = false; + return true; + } void HandleDummy(SpellEffIndex /*effIndex*/) { @@ -1358,23 +1528,31 @@ public: void HandleSchoolDamage(SpellEffIndex /*effIndex*/) { if (!GetCaster() || !GetHitUnit() || spawned) + { return; + } if (InstanceScript* pInstance = GetCaster()->GetInstanceScript()) + { if (Creature* sarth = ObjectAccessor::GetCreature(*GetHitUnit(), pInstance->GetData64(DATA_SARTHARION))) { sarth->AI()->SetData(DATA_VOLCANO_BLOWS, GetHitUnit()->GetGUIDLow()); sarth->CastSpell(GetHitUnit(), SPELL_LAVA_STRIKE_SUMMON, true); spawned = true; } + } } - void Register() + void Register() override { if (m_scriptSpellId == 57578) // Dummy lava strike + { OnEffectHitTarget += SpellEffectFn(spell_sartharion_lava_strike_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } else + { OnEffectHitTarget += SpellEffectFn(spell_sartharion_lava_strike_SpellScript::HandleSchoolDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } } }; diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp index 97fdf3ada7..66bde45863 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp @@ -19,36 +19,29 @@ public: struct instance_obsidian_sanctum_InstanceMapScript : public InstanceScript { - instance_obsidian_sanctum_InstanceMapScript(Map* pMap) : InstanceScript(pMap) {} - - uint64 m_uiSartharionGUID; - uint64 m_uiTenebronGUID; - uint64 m_uiShadronGUID; - uint64 m_uiVesperonGUID; - uint32 Encounters[MAX_ENCOUNTERS]; - uint64 m_uiPortalGUID; - uint8 portalCount; - - void Initialize() + instance_obsidian_sanctum_InstanceMapScript(Map* pMap) : InstanceScript(pMap), + m_uiSartharionGUID(0), + m_uiTenebronGUID(0), + m_uiShadronGUID(0), + m_uiVesperonGUID(0), + m_uiPortalGUID(0), + portalCount(0) { - m_uiSartharionGUID = 0; - m_uiTenebronGUID = 0; - m_uiShadronGUID = 0; - m_uiVesperonGUID = 0; - m_uiPortalGUID = 0; - portalCount = 0; - memset(&Encounters, 0, sizeof(Encounters)); - }; - - bool IsEncounterInProgress() const + SetBossNumber(MAX_ENCOUNTERS); + } + + bool IsEncounterInProgress() const override { - if (Encounters[BOSS_SARTHARION_EVENT] == IN_PROGRESS) - return true; + for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) + { + if (GetBossState(i) == IN_PROGRESS) + return true; + } return false; } - void OnCreatureCreate(Creature* pCreature) + void OnCreatureCreate(Creature* pCreature) override { switch(pCreature->GetEntry()) { @@ -67,21 +60,7 @@ public: } } - uint32 GetData(uint32 id) const - { - switch (id) - { - case BOSS_SARTHARION_EVENT: - case BOSS_TENEBRON_EVENT: - case BOSS_SHADRON_EVENT: - case BOSS_VESPERON_EVENT: - return Encounters[id]; - } - - return 0; - } - - uint64 GetData64(uint32 uiData) const + uint64 GetData64(uint32 uiData) const override { switch(uiData) { @@ -97,7 +76,7 @@ public: return 0; } - bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* source, Unit const* /*target*/, uint32 /*miscvalue1*/) + bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* source, Unit const* /*target*/, uint32 /*miscvalue1*/) override { switch(criteria_id) { @@ -105,109 +84,122 @@ public: case 7326: // Gonna Go When the Volcano Blows (25 player) (2048) case 7327: - if (Creature* cr = instance->GetCreature(m_uiSartharionGUID)) - if (!cr->AI()->GetData(source->GetGUIDLow())) - return true; - break; + { + Creature const* sartharion = instance->GetCreature(m_uiSartharionGUID); + return sartharion && !sartharion->AI()->GetData(source->GetGUIDLow()); + } // Less Is More (10 player) (624) case 7189: case 7190: case 7191: case 522: - if (instance->GetPlayersCountExceptGMs() < 9) - return true; - break; + { + return instance->GetPlayersCountExceptGMs() < 9; + } // Less Is More (25 player) (1877) case 7185: case 7186: case 7187: case 7188: - if (instance->GetPlayersCountExceptGMs() < 21) - return true; - break; + { + return instance->GetPlayersCountExceptGMs() < 21; + } // Twilight Assist (10 player) (2049) case 7328: // Twilight Assist (25 player) (2052) case 7331: - if (Creature* cr = instance->GetCreature(m_uiSartharionGUID)) - if (cr->AI()->GetData(DATA_ACHIEVEMENT_DRAGONS_COUNT) >= 1) - return true; - break; + { + Creature const* sartharion = instance->GetCreature(m_uiSartharionGUID); + return sartharion && sartharion->AI()->GetData(DATA_ACHIEVEMENT_DRAGONS_COUNT) >= 1; + } // Twilight Duo (10 player) (2050) case 7329: // Twilight Duo (25 player) (2053) case 7332: - if (Creature* cr = instance->GetCreature(m_uiSartharionGUID)) - if (cr->AI()->GetData(DATA_ACHIEVEMENT_DRAGONS_COUNT) >= 2) - return true; - break; + { + Creature const* sartharion = instance->GetCreature(m_uiSartharionGUID); + return sartharion && sartharion->AI()->GetData(DATA_ACHIEVEMENT_DRAGONS_COUNT) >= 2; + } // Twilight Zone (10 player) (2051) case 7330: // Twilight Zone (25 player) (2054) case 7333: - if (Creature* cr = instance->GetCreature(m_uiSartharionGUID)) - if (cr->AI()->GetData(DATA_ACHIEVEMENT_DRAGONS_COUNT) >= 3) - return true; - break; - + { + Creature const* sartharion = instance->GetCreature(m_uiSartharionGUID); + return sartharion && sartharion->AI()->GetData(DATA_ACHIEVEMENT_DRAGONS_COUNT) >= 3; + } } + return false; } - void SetData(uint32 type, uint32 data) + bool SetBossState(uint32 type, EncounterState state) override { - switch(type) + if (InstanceScript::SetBossState(type, state)) { - case BOSS_SARTHARION_EVENT: - case BOSS_TENEBRON_EVENT: - case BOSS_SHADRON_EVENT: - case BOSS_VESPERON_EVENT: - Encounters[type] = data; - break; - case DATA_ADD_PORTAL: + return false; + } + + if (state == DONE) + { + SaveToDB(); + } + return true; + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_ADD_PORTAL: + { if (!m_uiPortalGUID) { - if (Creature* cr = instance->GetCreature(m_uiSartharionGUID)) - if (GameObject* go = cr->SummonGameObject(GO_TWILIGHT_PORTAL, 3247.29f, 529.804f, 58.9595f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + if (Creature* sartharion = instance->GetCreature(m_uiSartharionGUID)) + { + if (GameObject* portal = sartharion->SummonGameObject(GO_TWILIGHT_PORTAL, 3247.29f, 529.804f, 58.9595f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) { - cr->RemoveGameObject(go, false); - m_uiPortalGUID = go->GetGUID(); + sartharion->RemoveGameObject(portal, false); + m_uiPortalGUID = portal->GetGUID(); } + } portalCount = 0; } - portalCount++; + ++portalCount; break; - case DATA_CLEAR_PORTAL: - portalCount--; - if (!portalCount || data == 2) + } + case ACTION_CLEAR_PORTAL: + { + --portalCount; + if (!portalCount) { if (GameObject* go = instance->GetGameObject(m_uiPortalGUID)) + { go->Delete(); + } DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_SHIFT); m_uiPortalGUID = 0; } break; + } } - - if (data == DONE) - SaveToDB(); } - std::string GetSaveData() + std::string GetSaveData() override { OUT_SAVE_INST_DATA; std::ostringstream saveStream; - saveStream << "O S " << Encounters[0] << ' ' << Encounters[1] << ' ' << Encounters[2] << ' ' << Encounters[3]; + saveStream << "O S " << GetBossSaveData(); OUT_SAVE_INST_DATA_COMPLETE; return saveStream.str(); } - void Load(const char* strIn) + void Load(const char* strIn) override { if (!strIn) { @@ -226,14 +218,25 @@ public: { for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) { - loadStream >> Encounters[i]; - if (Encounters[i] == IN_PROGRESS) - Encounters[i] = NOT_STARTED; + uint32 temp; + loadStream >> temp; + if (temp == IN_PROGRESS) + temp = NOT_STARTED; + + SetBossState(i, static_cast<EncounterState>(temp)); } } OUT_LOAD_INST_DATA_COMPLETE; } + + private: + uint64 m_uiSartharionGUID; + uint64 m_uiTenebronGUID; + uint64 m_uiShadronGUID; + uint64 m_uiVesperonGUID; + uint64 m_uiPortalGUID; + uint8 portalCount; }; }; diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h index 56c8268d3f..8c0dc705ba 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h @@ -5,25 +5,15 @@ #ifndef DEF_OBSIDIAN_SANCTUM_H #define DEF_OBSIDIAN_SANCTUM_H -enum Data +enum Data : uint32 { - // Encounter - BOSS_SARTHARION_EVENT = 0, - BOSS_TENEBRON_EVENT = 1, - BOSS_VESPERON_EVENT = 2, - BOSS_SHADRON_EVENT = 3, + // Encounters + DATA_SARTHARION = 0, + DATA_TENEBRON = 1, + DATA_VESPERON = 2, + DATA_SHADRON = 3, MAX_ENCOUNTERS = 4, - // GUIDs - DATA_SARTHARION = 10, - DATA_TENEBRON = 11, - DATA_SHADRON = 12, - DATA_VESPERON = 13, - - // Portal - DATA_CLEAR_PORTAL = 20, - DATA_ADD_PORTAL = 21, - // Achievements DATA_ACHIEVEMENT_DRAGONS_COUNT = 30, DATA_VOLCANO_BLOWS = 31, @@ -44,4 +34,10 @@ enum Data SPELL_TWILIGHT_TORMENT_SARTHARION = 58835, }; +enum OSActions +{ + // Portal + ACTION_CLEAR_PORTAL = -1, + ACTION_ADD_PORTAL = -2, +}; #endif |