From bc25ade49825501aaee80b9da79ce14b1e7839cc Mon Sep 17 00:00:00 2001 From: Dan <83884799+elthehablo@users.noreply.github.com> Date: Thu, 8 Feb 2024 03:43:19 +0100 Subject: [PATCH] fix(Scripts/TheEye): refactor Al'ar (#18264) * initial * restore quill event * with ismoving and debug * final before timers * clear all debuffs * remove justreachedhome bossai * huh? * some fixes * more fixes * add 2 extra platforms for p1 * fix coords * finalise * change melt armor timer so it is not delayed indefinitely - this is because we have a dive after 30 secs that delays all timers. So at 30 secs we will have melt armor at 33 seconds, which then gets an extra 15 seconds from the delay all and another dive after 30 so it gets pushed back even more before being able to be cast --- .../spell_immunity_alar_and_adds.sql | 2 + .../Outland/TempestKeep/Eye/boss_alar.cpp | 581 +++++++++--------- 2 files changed, 298 insertions(+), 285 deletions(-) create mode 100644 data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql 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();