diff --git a/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql b/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql new file mode 100644 index 000000000..4143a0738 --- /dev/null +++ b/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql @@ -0,0 +1,2 @@ +-- +UPDATE `creature_template` SET `spell_school_immune_mask` = `spell_school_immune_mask`|4 WHERE `entry` IN (19514, 19551); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 7264a07ae..e8e82b443 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -28,6 +28,7 @@ enum Spells SPELL_FLAME_QUILLS = 34229, SPELL_QUILL_MISSILE_1 = 34269, // 21 SPELL_QUILL_MISSILE_2 = 34314, // 3 + SPELL_CLEAR_ALL_DEBUFFS = 34098, SPELL_FLAME_BUFFET = 34121, SPELL_EMBER_BLAST = 34341, SPELL_REBIRTH_PHASE2 = 34342, @@ -38,15 +39,18 @@ enum Spells SPELL_DIVE_BOMB = 35181 }; -const Position alarPoints[7] = +// @todo: Alar doesnt seem to move to waypoints but instead to the triggers in p1 +const Position alarPoints[9] = { - {340.15f, 58.65f, 17.71f, 4.60f}, - {388.09f, 31.54f, 20.18f, 1.61f}, - {388.18f, -32.85f, 20.18f, 0.52f}, - {340.29f, -60.19f, 17.72f, 5.71f}, - {332.0f, 0.01f, 43.0f, 0.0f}, - {331.0f, 0.01f, -2.38f, 0.0f}, - {332.0f, 0.01f, 43.0f, 0.0f} + {335.638f, 59.4879f, 17.9319f, 4.60f}, //first platform + {388.751007f, 31.731199f, 20.263599f, 1.61f}, + {388.790985f, -33.105900f, 20.263599f, 0.52f}, + {332.722992f, -61.159f, 17.979099f, 5.71f}, + {258.959015f, -38.687099f, 20.262899f, 5.21f}, + {259.2277997, 35.879002f, 20.263f, 4.81f}, //sixth platform + {332.0f, 0.01f, 43.0f, 0.0f}, //quill + {331.0f, 0.01f, -2.38f, 0.0f}, //middle (p2) + {332.0f, 0.01f, 43.0f, 0.0f} // dive }; enum Misc @@ -56,26 +60,28 @@ enum Misc NPC_FLAME_PATCH = 20602, POINT_PLATFORM = 0, - POINT_QUILL = 4, - POINT_MIDDLE = 5, - POINT_DIVE = 6, + POINT_QUILL = 6, + POINT_MIDDLE = 7, + POINT_DIVE = 8, - EVENT_SWITCH_PLATFORM = 1, - EVENT_START_QUILLS = 2, - EVENT_RELOCATE_MIDDLE = 3, - EVENT_REBIRTH = 4, - EVENT_SPELL_MELT_ARMOR = 5, - EVENT_SPELL_FLAME_PATCH = 6, - EVENT_SPELL_CHARGE = 7, - EVENT_SPELL_DIVE_BOMB = 8, - EVENT_START_DIVE = 9, - EVENT_CAST_DIVE_BOMB = 10, - EVENT_SUMMON_DIVE_PHOENIX = 11, - EVENT_REBIRTH_DIVE = 12, - EVENT_SPELL_BERSERK = 13, + EVENT_RELOCATE_MIDDLE = 1, + EVENT_REBIRTH = 2, + EVENT_SPELL_BERSERK = 3, - EVENT_MOVE_TO_PHASE_2 = 20, - EVENT_FINISH_DIVE = 21 + EVENT_MOVE_TO_PHASE_2 = 4, + EVENT_FINISH_DIVE = 5 +}; + +enum PlatformMoveDirections +{ + DIRECTION_ANTI_CLOCKWISE = 0, + DIRECTION_CLOCKWISE = 1, + DIRECTION_ACROSS = 2 +}; + +enum GroupAlar +{ + GROUP_FLAME_BUFFET = 1 }; // Xinef: Ruse of the Ashtongue (10946) @@ -85,279 +91,284 @@ enum qruseoftheAshtongue QUEST_RUSE_OF_THE_ASHTONGUE = 10946, }; -class boss_alar : public CreatureScript +struct boss_alar : public BossAI { -public: - boss_alar() : CreatureScript("boss_alar") { } - struct boss_alarAI : public BossAI + boss_alar(Creature* creature) : BossAI(creature, DATA_ALAR) { - boss_alarAI(Creature* creature) : BossAI(creature, DATA_ALAR) - { - startPath = true; - SetCombatMovement(false); - } - - uint8 platform; - uint8 noQuillTimes; - bool startPath; - - void JustReachedHome() override - { - BossAI::JustReachedHome(); - startPath = true; - } - - void Reset() override - { - BossAI::Reset(); - platform = 0; - noQuillTimes = 0; - me->SetModelVisible(true); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true); - me->SetReactState(REACT_AGGRESSIVE); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 0); - } - - void JustDied(Unit* killer) override - { - me->SetModelVisible(true); - BossAI::JustDied(killer); - - // Xinef: Ruse of the Ashtongue (10946) - Map::PlayerList const& pl = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - { - Player* player = itr->GetSource(); - if (player->GetQuestStatus(QUEST_RUSE_OF_THE_ASHTONGUE) == QUEST_STATUS_INCOMPLETE) - if (player->HasAura(SPELL_ASHTONGUE_RUSE)) - player->AreaExploredOrEventHappens(QUEST_RUSE_OF_THE_ASHTONGUE); - } - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (summon->GetEntry() == NPC_EMBER_OF_ALAR) - summon->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true); - } - - void MoveInLineOfSight(Unit* /*who*/) override { } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth() && platform < POINT_MIDDLE) - { - damage = 0; - if (events.GetNextEventTime(EVENT_REBIRTH) == 0) - { - me->InterruptNonMeleeSpells(false); - me->SetHealth(me->GetMaxHealth()); - me->SetReactState(REACT_PASSIVE); - me->CastSpell(me, SPELL_EMBER_BLAST, true); - - me->setAttackTimer(BASE_ATTACK, 16000); - events.Reset(); - events.ScheduleEvent(EVENT_RELOCATE_MIDDLE, 8000); - events.ScheduleEvent(EVENT_MOVE_TO_PHASE_2, 12000); - events.ScheduleEvent(EVENT_REBIRTH, 16001); - } - } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - { - if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized() && !me->IsInCombat()) - startPath = true; - return; - } - - if (id == POINT_PLATFORM) - me->setAttackTimer(BASE_ATTACK, 1000); - else if (id == POINT_QUILL) - events.ScheduleEvent(EVENT_START_QUILLS, 1000); - else if (id == POINT_DIVE) - { - events.ScheduleEvent(EVENT_START_DIVE, 1000); - events.ScheduleEvent(EVENT_CAST_DIVE_BOMB, 5000); - } - } - - void UpdateAI(uint32 diff) override - { - if (startPath) - { - me->StopMoving(); - startPath = false; - if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath())) - { - Movement::PointsArray pathPoints; - pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - for (uint8 i = 0; i < i_path->size(); ++i) - { - WaypointData const* node = i_path->at(i); - pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); - } - me->GetMotionMaster()->MoveSplinePath(&pathPoints); - } - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SWITCH_PLATFORM: - if (roll_chance_i(20 * noQuillTimes)) - { - noQuillTimes = 0; - platform = RAND(0, 3); - me->GetMotionMaster()->MovePoint(POINT_QUILL, alarPoints[POINT_QUILL], false, true); - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 16000); - } - else - { - if (noQuillTimes++ > 0) - { - me->SetOrientation(alarPoints[platform].GetOrientation()); - me->SummonCreature(NPC_EMBER_OF_ALAR, *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - } - me->GetMotionMaster()->MovePoint(POINT_PLATFORM, alarPoints[platform], false, true); - platform = (platform + 1) % 4; - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 30000); - } - me->setAttackTimer(BASE_ATTACK, 20000); - break; - case EVENT_START_QUILLS: - me->CastSpell(me, SPELL_FLAME_QUILLS, false); - break; - case EVENT_RELOCATE_MIDDLE: - me->SetPosition(alarPoints[POINT_MIDDLE]); - break; - case EVENT_MOVE_TO_PHASE_2: - me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); - me->CastSpell(me, SPELL_REBIRTH_PHASE2, false); - break; - case EVENT_REBIRTH: - me->SetReactState(REACT_AGGRESSIVE); - platform = POINT_MIDDLE; - me->GetMotionMaster()->MoveChase(me->GetVictim()); - - events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 67000); - events.ScheduleEvent(EVENT_SPELL_CHARGE, 10000); - events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 20000); - events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000); - break; - case EVENT_SPELL_MELT_ARMOR: - me->CastSpell(me->GetVictim(), SPELL_MELT_ARMOR, false); - events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 60000); - break; - case EVENT_SPELL_CHARGE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) - me->CastSpell(target, SPELL_CHARGE, false); - events.ScheduleEvent(EVENT_SPELL_CHARGE, 30000); - break; - case EVENT_SPELL_FLAME_PATCH: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) - me->SummonCreature(NPC_FLAME_PATCH, *target, TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 30000); - break; - case EVENT_SPELL_DIVE_BOMB: - me->GetMotionMaster()->MovePoint(POINT_DIVE, alarPoints[POINT_DIVE], false, true); - events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000); - events.DelayEvents(15000); - me->setAttackTimer(BASE_ATTACK, 20000); - break; - case EVENT_START_DIVE: - me->CastSpell(me, SPELL_DIVE_BOMB_VISUAL, false); - break; - case EVENT_CAST_DIVE_BOMB: - events.ScheduleEvent(EVENT_SUMMON_DIVE_PHOENIX, 2000); - events.ScheduleEvent(EVENT_REBIRTH_DIVE, 6000); - events.ScheduleEvent(EVENT_FINISH_DIVE, 10000); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) - { - me->CastSpell(target, SPELL_DIVE_BOMB, false); - me->SetPosition(*target); - me->StopMovingOnCurrentPos(); - } - - me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); - break; - case EVENT_SUMMON_DIVE_PHOENIX: - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true); - me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - break; - } - case EVENT_REBIRTH_DIVE: - me->SetModelVisible(true); - me->CastSpell(me, SPELL_REBIRTH_DIVE, false); - break; - case EVENT_FINISH_DIVE: - me->GetMotionMaster()->MoveChase(me->GetVictim()); - break; - case EVENT_SPELL_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - break; - } - - if (me->isAttackReady()) - { - if (me->IsWithinMeleeRange(me->GetVictim())) - { - me->AttackerStateUpdate(me->GetVictim()); - me->resetAttackTimer(); - } - else - { - me->resetAttackTimer(); - ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) - if (me->IsWithinMeleeRange(unit)) - { - me->AttackerStateUpdate(unit); - return; - } - - me->CastSpell(me, SPELL_FLAME_BUFFET, false); - } - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTheEyeAI(creature); + SetCombatMovement(false); } + + void JustReachedHome() override + { + BossAI::JustReachedHome(); + if (me->IsEngaged()) + { + ConstructWaypointsAndMove(); + } + } + + void Reset() override + { + BossAI::Reset(); + _canAttackCooldown = true; + _baseAttackOverride = false; + _spawnPhoenixes = false; + _platform = 0; + _platformRoll = 0; + _noQuillTimes = 0; + _platformMoveRepeatTimer = 16s; + me->SetModelVisible(true); + me->SetReactState(REACT_AGGRESSIVE); + ConstructWaypointsAndMove(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + ScheduleTimedEvent(0s, [&] + { + if (roll_chance_i(20 * _noQuillTimes)) + { + _noQuillTimes = 0; + _platform = RAND(0, 5); + me->GetMotionMaster()->MovePoint(POINT_QUILL, alarPoints[POINT_QUILL], false, true); + _platformMoveRepeatTimer = 16s; + } + else + { + if (_noQuillTimes++ > 0) + { + me->SetOrientation(alarPoints[_platform].GetOrientation()); + if (_spawnPhoenixes) + { + SpawnPhoenixes(3, me); + } + } + me->GetMotionMaster()->MovePoint(POINT_PLATFORM, alarPoints[_platform], false, true); + _platformRoll = RAND(0, 2); + switch(_platformRoll) + { + case DIRECTION_ANTI_CLOCKWISE: + _platform = (_platform+5)%6; + _spawnPhoenixes = false; + break; + case DIRECTION_CLOCKWISE: + _platform = (_platform+1)%6; + _spawnPhoenixes = false; + break; + case DIRECTION_ACROSS: + _platform = (_platform+3)%6; + _spawnPhoenixes = true; + break; + } + _platformMoveRepeatTimer = 30s; + } + }, _platformMoveRepeatTimer); + ScheduleMainSpellAttack(0s); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + me->SetModelVisible(true); + + if (Map* map = me->GetMap()) + { + map->DoForAllPlayers([&](Player* player) + { + if (player->GetQuestStatus(QUEST_RUSE_OF_THE_ASHTONGUE) == QUEST_STATUS_INCOMPLETE) + { + if (player->HasAura(SPELL_ASHTONGUE_RUSE)) + { + player->AreaExploredOrEventHappens(QUEST_RUSE_OF_THE_ASHTONGUE); + } + } + }); + } + } + + void MoveInLineOfSight(Unit* /*who*/) override { } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + if (damage >= me->GetHealth() && _platform < POINT_MIDDLE) + { + damage = 0; + me->InterruptNonMeleeSpells(false); + me->SetHealth(me->GetMaxHealth()); + me->SetReactState(REACT_PASSIVE); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS); + DoCastSelf(SPELL_EMBER_BLAST, true); + scheduler.CancelAll(); + ScheduleUniqueTimedEvent(8s, [&]{ + me->SetPosition(alarPoints[POINT_MIDDLE]); + }, EVENT_RELOCATE_MIDDLE); + ScheduleUniqueTimedEvent(12s, [&] + { + me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); + DoCastSelf(SPELL_REBIRTH_PHASE2); + }, EVENT_MOVE_TO_PHASE_2); + ScheduleUniqueTimedEvent(16001ms, [&]{ + me->SetReactState(REACT_AGGRESSIVE); + _platform = POINT_MIDDLE; + me->GetMotionMaster()->MoveChase(me->GetVictim()); + ScheduleAbilities(); + }, EVENT_REBIRTH); + + } + } + + void ScheduleAbilities() + { + ScheduleTimedEvent(57s, [&] + { + DoCastVictim(SPELL_MELT_ARMOR); + }, 60s); + ScheduleTimedEvent(10s, [&] + { + DoCastRandomTarget(SPELL_CHARGE, 0, 50.0f); + }, 30s); + ScheduleTimedEvent(20s, [&] + { + // find spell from sniffs? + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) + { + me->SummonCreature(NPC_FLAME_PATCH, *target, TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); + } + }, 30s); + ScheduleTimedEvent(30s, [&] + { + me->GetMotionMaster()->MovePoint(POINT_DIVE, alarPoints[POINT_DIVE], false, true); + scheduler.DelayAll(15s); + }, 30s); + ScheduleUniqueTimedEvent(10min, [&] + { + DoCastSelf(SPELL_BERSERK); + }, EVENT_SPELL_BERSERK); + ScheduleMainSpellAttack(0s); + } + + void SpawnPhoenixes(uint8 count, Unit* targetToSpawnAt) + { + if (targetToSpawnAt) + { + for (uint8 i = 0; i < count; ++i) + { + me->SummonCreature(NPC_EMBER_OF_ALAR, *targetToSpawnAt, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); + } + } + } + + void DoDiveBomb() + { + scheduler.Schedule(2s, [this](TaskContext) + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true)) + { + SpawnPhoenixes(2, target); + } + }).Schedule(6s, [this](TaskContext) + { + me->SetModelVisible(true); + DoCastSelf(SPELL_REBIRTH_DIVE); + }).Schedule(10s, [this](TaskContext) + { + me->GetMotionMaster()->MoveChase(me->GetVictim()); + }); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) + { + DoCast(target, SPELL_DIVE_BOMB); + me->SetPosition(*target); + me->StopMovingOnCurrentPos(); + } + me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); + + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + { + if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized() && !me->IsInCombat()) + { + ConstructWaypointsAndMove(); + } + return; + } + + switch(id) + { + case POINT_QUILL: + scheduler.CancelGroup(GROUP_FLAME_BUFFET); + scheduler.Schedule(1s, [this](TaskContext) + { + DoCastSelf(SPELL_FLAME_QUILLS); + }); + ScheduleMainSpellAttack(13s); + break; + case POINT_DIVE: + scheduler.Schedule(1s, [this](TaskContext) + { + DoCastSelf(SPELL_DIVE_BOMB_VISUAL); + }).Schedule(5s, [this](TaskContext) + { + DoDiveBomb(); + }); + break; + default: + return; + } + } + + void ScheduleMainSpellAttack(std::chrono::seconds timer) + { + scheduler.Schedule(timer, GROUP_FLAME_BUFFET, [this](TaskContext context) + { + if (!me->IsWithinMeleeRange(me->GetVictim()) && !me->isMoving()) + { + DoCastVictim(SPELL_FLAME_BUFFET); + } + context.Repeat(2s); + }); + } + + void ConstructWaypointsAndMove() + { + me->StopMoving(); + if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath())) + { + Movement::PointsArray pathPoints; + pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); + for (uint8 i = 0; i < i_path->size(); ++i) + { + WaypointData const* node = i_path->at(i); + pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); + } + me->GetMotionMaster()->MoveSplinePath(&pathPoints); + } + } + +private: + bool _canAttackCooldown; + bool _baseAttackOverride; + bool _spawnPhoenixes; + uint8 _platform; + uint8 _platformRoll; + uint8 _noQuillTimes; + std::chrono::seconds _platformMoveRepeatTimer; }; class CastQuill : public BasicEvent { public: - CastQuill(Unit* caster, uint32 spellId) : _caster(caster), _spellId(spellId) - { - } + CastQuill(Unit* caster, uint32 spellId) : _caster(caster), _spellId(spellId){ } bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override { _caster->CastSpell(_caster, _spellId, true); return true; } - private: Unit* _caster; uint32 _spellId; @@ -496,7 +507,7 @@ public: void AddSC_boss_alar() { - new boss_alar(); + RegisterTheEyeAI(boss_alar); new spell_alar_flame_quills(); new spell_alar_ember_blast(); new spell_alar_ember_blast_death();