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
This commit is contained in:
Dan
2024-02-08 03:43:19 +01:00
committed by GitHub
parent b8118fa06b
commit bc25ade498
2 changed files with 298 additions and 285 deletions

View File

@@ -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<boss_alarAI>(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();