refactor(Scripts/OnyxiasLair): clean up boss_onyxia (#26829)

This commit is contained in:
Andrew
2026-07-27 08:27:30 -03:00
committed by GitHub
parent ff1f543a10
commit f23ac046ab
3 changed files with 124 additions and 92 deletions
@@ -84,14 +84,23 @@ enum Phases
PHASE_LANDED // Phase 3 - Landed after Airphase - 40% health PHASE_LANDED // Phase 3 - Landed after Airphase - 40% health
}; };
struct sOnyxMove // Ids 0-8 are reserved for the OnyxiaMoveData waypoints
enum Points
{ {
uint8 CurrId, DestId; POINT_GROUND_SOUTH = 10,
uint32 spellId; POINT_TAKEOFF = 11,
float x, y, z, o; POINT_PRE_LAND = 12,
POINT_LAND = 13
}; };
static sOnyxMove OnyxiaMoveData[] = struct OnyxiaMove
{
uint8 CurrId, DestId;
uint32 SpellId;
float X, Y, Z, O;
};
static OnyxiaMove const OnyxiaMoveData[] =
{ {
{0, 0, 0, -64.496f, -214.906f, -84.4f, 0.0f}, // south ground {0, 0, 0, -64.496f, -214.906f, -84.4f, 0.0f}, // south ground
{1, 5, SPELL_BREATH_S_TO_N, -64.496f, -214.906f, -60.0f, 0.0f}, // south {1, 5, SPELL_BREATH_S_TO_N, -64.496f, -214.906f, -60.0f, 0.0f}, // south
@@ -117,24 +126,25 @@ enum Yells
struct boss_onyxia : public BossAI struct boss_onyxia : public BossAI
{ {
public: public:
boss_onyxia(Creature* pCreature) : BossAI(pCreature, DATA_ONYXIA) boss_onyxia(Creature* creature) : BossAI(creature, DATA_ONYXIA)
{ {
Initialize(); Initialize();
} }
void Initialize() void Initialize()
{ {
CurrentWP = 0; _phase = PHASE_NONE;
whelpSpam = false; _currentWP = 0;
whelpCount = 0; _whelpSpam = false;
whelpSpamTimer = 0; _whelpCount = 0;
bManyWhelpsAvailable = false; _whelpSpamTimer = 0;
_manyWhelpsAvailable = false;
} }
void SetPhase(uint8 ph) void SetPhase(uint8 ph)
{ {
events.Reset(); events.Reset();
Phase = ph; _phase = ph;
switch (ph) switch (ph)
{ {
case PHASE_GROUNDED: case PHASE_GROUNDED:
@@ -149,6 +159,8 @@ public:
case PHASE_LANDED: case PHASE_LANDED:
events.ScheduleEvent(EVENT_START_PHASE_3, 5s); events.ScheduleEvent(EVENT_START_PHASE_3, 5s);
break; break;
default:
break;
} }
} }
@@ -168,12 +180,14 @@ public:
{ {
switch (param) switch (param)
{ {
case -1: case ACTION_WHELP_SUMMONED:
if (bManyWhelpsAvailable) if (_manyWhelpsAvailable)
{ {
instance->SetData(DATA_WHELP_SUMMONED, 1); instance->SetData(DATA_WHELP_SUMMONED, 1);
} }
break; break;
default:
break;
} }
} }
@@ -191,11 +205,11 @@ public:
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{ {
if (me->HealthBelowPctDamaged(65, damage) && Phase == PHASE_GROUNDED) if (me->HealthBelowPctDamaged(65, damage) && _phase == PHASE_GROUNDED)
{ {
SetPhase(PHASE_AIRPHASE); SetPhase(PHASE_AIRPHASE);
} }
else if (me->HealthBelowPctDamaged(40, damage) && Phase == PHASE_AIRPHASE) else if (me->HealthBelowPctDamaged(40, damage) && _phase == PHASE_AIRPHASE)
{ {
me->InterruptNonMeleeSpells(false); me->InterruptNonMeleeSpells(false);
SetPhase(PHASE_LANDED); SetPhase(PHASE_LANDED);
@@ -211,7 +225,7 @@ public:
return; return;
} }
if (summon->GetEntry() == NPC_ONYXIAN_LAIR_GUARD && Phase < PHASE_AIRPHASE) if (summon->GetEntry() == NPC_ONYXIAN_LAIR_GUARD && _phase < PHASE_AIRPHASE)
{ {
return; return;
} }
@@ -232,11 +246,11 @@ public:
if (id < 9) if (id < 9)
{ {
if (id > 0 && Phase == PHASE_AIRPHASE) if (id > 0 && _phase == PHASE_AIRPHASE)
{ {
me->SetFacingTo(OnyxiaMoveData[id].o); me->SetFacingTo(OnyxiaMoveData[id].O);
me->SetSpeed(MOVE_RUN, 1.6f, false); me->SetSpeed(MOVE_RUN, 1.6f, false);
CurrentWP = id; _currentWP = id;
events.ScheduleEvent(EVENT_SPELL_FIREBALL_FIRST, 1s); events.ScheduleEvent(EVENT_SPELL_FIREBALL_FIRST, 1s);
} }
} }
@@ -244,50 +258,58 @@ public:
{ {
switch (id) switch (id)
{ {
case 10: case POINT_GROUND_SOUTH:
me->SetFacingTo(OnyxiaMoveData[0].o); me->SetFacingTo(OnyxiaMoveData[0].O);
events.ScheduleEvent(EVENT_LIFTOFF, 0ms); events.ScheduleEvent(EVENT_LIFTOFF, 0ms);
break; break;
case 11: case POINT_TAKEOFF:
me->SetFacingTo(OnyxiaMoveData[1].o); me->SetFacingTo(OnyxiaMoveData[1].O);
events.ScheduleEvent(EVENT_FLY_S_TO_N, 0ms); events.ScheduleEvent(EVENT_FLY_S_TO_N, 0ms);
break; break;
case 12: case POINT_PRE_LAND:
me->SetFacingTo(OnyxiaMoveData[1].o); me->SetFacingTo(OnyxiaMoveData[1].O);
events.ScheduleEvent(EVENT_LAND, 0ms); events.ScheduleEvent(EVENT_LAND, 0ms);
break; break;
case 13: case POINT_LAND:
me->SetCanFly(false); me->SetCanFly(false);
me->SetDisableGravity(false); me->SetDisableGravity(false);
me->SetSpeed(MOVE_RUN, me->GetCreatureTemplate()->speed_run, false); me->SetSpeed(MOVE_RUN, me->GetCreatureTemplate()->speed_run, false);
events.ScheduleEvent(EVENT_PHASE_3_ATTACK, 0ms); events.ScheduleEvent(EVENT_PHASE_3_ATTACK, 0ms);
break; break;
default:
break;
} }
} }
} }
void HandleWhelpSpam(const uint32 diff) // Summons one whelp at each of the two side caves
void SummonWhelps()
{ {
if (whelpSpam) float angle = rand_norm() * 2 * M_PI;
float dist = rand_norm() * 4.0f;
me->CastSpell(-33.18f + std::cos(angle) * dist, -258.80f + std::sin(angle) * dist, -89.0f, SPELL_SUMMON_WHELP, true);
me->CastSpell(-32.535f + std::cos(angle) * dist, -170.190f + std::sin(angle) * dist, -89.0f, SPELL_SUMMON_WHELP, true);
}
void HandleWhelpSpam(uint32 diff)
{
if (_whelpSpam)
{ {
if (whelpCount < 40) if (_whelpCount < 40)
{ {
whelpSpamTimer -= diff; _whelpSpamTimer -= diff;
if (whelpSpamTimer <= 0) if (_whelpSpamTimer <= 0)
{ {
float angle = rand_norm() * 2 * M_PI; SummonWhelps();
float dist = rand_norm() * 4.0f; _whelpCount += 2;
me->CastSpell(-33.18f + cos(angle) * dist, -258.80f + std::sin(angle) * dist, -89.0f, 17646, true); _whelpSpamTimer += 600;
me->CastSpell(-32.535f + cos(angle) * dist, -170.190f + std::sin(angle) * dist, -89.0f, 17646, true);
whelpCount += 2;
whelpSpamTimer += 600;
} }
} }
else else
{ {
whelpSpam = false; _whelpSpam = false;
whelpCount = 0; _whelpCount = 0;
whelpSpamTimer = 0; _whelpSpamTimer = 0;
} }
} }
} }
@@ -319,8 +341,6 @@ public:
return; return;
} }
DoMeleeAttackIfReady();
switch (events.ExecuteEvent()) switch (events.ExecuteEvent())
{ {
case EVENT_SPELL_WINGBUFFET: case EVENT_SPELL_WINGBUFFET:
@@ -353,7 +373,7 @@ public:
me->SetReactState(REACT_PASSIVE); me->SetReactState(REACT_PASSIVE);
me->StopMoving(); me->StopMoving();
DoResetThreatList(); DoResetThreatList();
me->GetMotionMaster()->MovePoint(10, OnyxiaMoveData[0].x, OnyxiaMoveData[0].y, OnyxiaMoveData[0].z); me->GetMotionMaster()->MovePoint(POINT_GROUND_SOUTH, OnyxiaMoveData[0].X, OnyxiaMoveData[0].Y, OnyxiaMoveData[0].Z);
break; break;
} }
case EVENT_LIFTOFF: case EVENT_LIFTOFF:
@@ -364,23 +384,23 @@ public:
me->DisableSpline(); me->DisableSpline();
me->SetCanFly(true); me->SetCanFly(true);
me->SetDisableGravity(true); me->SetDisableGravity(true);
me->SetOrientation(OnyxiaMoveData[0].o); me->SetOrientation(OnyxiaMoveData[0].O);
me->SendMovementFlagUpdate(); me->SendMovementFlagUpdate();
me->GetMotionMaster()->MoveTakeoff(11, OnyxiaMoveData[1].x + 1.0f, OnyxiaMoveData[1].y, OnyxiaMoveData[1].z, 12.0f); me->GetMotionMaster()->MoveTakeoff(POINT_TAKEOFF, OnyxiaMoveData[1].X + 1.0f, OnyxiaMoveData[1].Y, OnyxiaMoveData[1].Z, 12.0f);
bManyWhelpsAvailable = true; _manyWhelpsAvailable = true;
events.RescheduleEvent(EVENT_END_MANY_WHELPS_TIME, 10s); events.RescheduleEvent(EVENT_END_MANY_WHELPS_TIME, 10s);
break; break;
} }
case EVENT_END_MANY_WHELPS_TIME: case EVENT_END_MANY_WHELPS_TIME:
bManyWhelpsAvailable = false; _manyWhelpsAvailable = false;
break; break;
case EVENT_FLY_S_TO_N: case EVENT_FLY_S_TO_N:
{ {
me->SetSpeed(MOVE_RUN, 2.95f, false); me->SetSpeed(MOVE_RUN, 2.95f, false);
me->GetMotionMaster()->MovePoint(5, OnyxiaMoveData[5].x, OnyxiaMoveData[5].y, OnyxiaMoveData[5].z); me->GetMotionMaster()->MovePoint(5, OnyxiaMoveData[5].X, OnyxiaMoveData[5].Y, OnyxiaMoveData[5].Z);
whelpSpam = true; _whelpSpam = true;
events.ScheduleEvent(EVENT_WHELP_SPAM, 90s); events.ScheduleEvent(EVENT_WHELP_SPAM, 90s);
events.ScheduleEvent(EVENT_SUMMON_LAIR_GUARD, 30s); events.ScheduleEvent(EVENT_SUMMON_LAIR_GUARD, 30s);
break; break;
@@ -393,7 +413,7 @@ public:
} }
case EVENT_WHELP_SPAM: case EVENT_WHELP_SPAM:
{ {
whelpSpam = true; _whelpSpam = true;
events.Repeat(90s); events.Repeat(90s);
break; break;
} }
@@ -401,16 +421,16 @@ public:
{ {
Talk(SAY_PHASE_3_TRANS); Talk(SAY_PHASE_3_TRANS);
me->SendMeleeAttackStop(me->GetVictim()); me->SendMeleeAttackStop(me->GetVictim());
me->GetMotionMaster()->MoveLand(13, OnyxiaMoveData[0].x + 1.0f, OnyxiaMoveData[0].y, OnyxiaMoveData[0].z, 12.0f); me->GetMotionMaster()->MoveLand(POINT_LAND, OnyxiaMoveData[0].X + 1.0f, OnyxiaMoveData[0].Y, OnyxiaMoveData[0].Z, 12.0f);
DoResetThreatList(); DoResetThreatList();
break; break;
} }
case EVENT_SPELL_FIREBALL_FIRST: case EVENT_SPELL_FIREBALL_FIRST:
{ {
if (Unit* v = SelectTarget(SelectTargetMethod::Random, 0, 200.0f, true)) if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200.0f, true))
{ {
me->SetFacingToObject(v); me->SetFacingToObject(target);
DoCast(v, SPELL_FIREBALL); DoCast(target, SPELL_FIREBALL);
} }
events.ScheduleEvent(EVENT_SPELL_FIREBALL_SECOND, 4s); events.ScheduleEvent(EVENT_SPELL_FIREBALL_SECOND, 4s);
@@ -418,66 +438,65 @@ public:
} }
case EVENT_SPELL_FIREBALL_SECOND: case EVENT_SPELL_FIREBALL_SECOND:
{ {
if (Unit* v = SelectTarget(SelectTargetMethod::Random, 0, 200.0f, true)) if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200.0f, true))
{ {
me->SetFacingToObject(v); me->SetFacingToObject(target);
DoCast(v, SPELL_FIREBALL); DoCast(target, SPELL_FIREBALL);
} }
uint8 rand = urand(0, 99); switch (urand(0, 2))
if (rand < 33)
{ {
events.ScheduleEvent(EVENT_PHASE_2_STEP_CW, 4s); case 0:
} events.ScheduleEvent(EVENT_PHASE_2_STEP_CW, 4s);
else if (rand < 66) break;
{ case 1:
events.ScheduleEvent(EVENT_PHASE_2_STEP_ACW, 4s); events.ScheduleEvent(EVENT_PHASE_2_STEP_ACW, 4s);
} break;
else default:
{ events.ScheduleEvent(EVENT_PHASE_2_STEP_ACROSS, 4s);
events.ScheduleEvent(EVENT_PHASE_2_STEP_ACROSS, 4s); break;
} }
break; break;
} }
case EVENT_PHASE_2_STEP_CW: case EVENT_PHASE_2_STEP_CW:
{ {
uint8 newWP = CurrentWP + 1; uint8 newWP = _currentWP + 1;
if (newWP > 8) if (newWP > 8)
{ {
newWP = 1; newWP = 1;
} }
me->GetMotionMaster()->MovePoint(newWP, OnyxiaMoveData[newWP].x, OnyxiaMoveData[newWP].y, OnyxiaMoveData[newWP].z); me->GetMotionMaster()->MovePoint(newWP, OnyxiaMoveData[newWP].X, OnyxiaMoveData[newWP].Y, OnyxiaMoveData[newWP].Z);
break; break;
} }
case EVENT_PHASE_2_STEP_ACW: case EVENT_PHASE_2_STEP_ACW:
{ {
uint8 newWP = CurrentWP - 1; uint8 newWP = _currentWP - 1;
if (newWP < 1) if (newWP < 1)
{ {
newWP = 8; newWP = 8;
} }
me->GetMotionMaster()->MovePoint(newWP, OnyxiaMoveData[newWP].x, OnyxiaMoveData[newWP].y, OnyxiaMoveData[newWP].z); me->GetMotionMaster()->MovePoint(newWP, OnyxiaMoveData[newWP].X, OnyxiaMoveData[newWP].Y, OnyxiaMoveData[newWP].Z);
break; break;
} }
case EVENT_PHASE_2_STEP_ACROSS: case EVENT_PHASE_2_STEP_ACROSS:
{ {
Talk(EMOTE_BREATH); Talk(EMOTE_BREATH);
me->SetFacingTo(OnyxiaMoveData[CurrentWP].o); me->SetFacingTo(OnyxiaMoveData[_currentWP].O);
DoCastAOE(OnyxiaMoveData[CurrentWP].spellId); DoCastAOE(OnyxiaMoveData[_currentWP].SpellId);
events.ScheduleEvent(EVENT_SPELL_BREATH, 8250ms); events.ScheduleEvent(EVENT_SPELL_BREATH, 8250ms);
break; break;
} }
case EVENT_SPELL_BREATH: case EVENT_SPELL_BREATH:
{ {
uint8 newWP = OnyxiaMoveData[CurrentWP].DestId; uint8 newWP = OnyxiaMoveData[_currentWP].DestId;
me->SetSpeed(MOVE_RUN, 2.95f, false); me->SetSpeed(MOVE_RUN, 2.95f, false);
me->GetMotionMaster()->MovePoint(newWP, OnyxiaMoveData[newWP].x, OnyxiaMoveData[newWP].y, OnyxiaMoveData[newWP].z); me->GetMotionMaster()->MovePoint(newWP, OnyxiaMoveData[newWP].X, OnyxiaMoveData[newWP].Y, OnyxiaMoveData[newWP].Z);
break; break;
} }
case EVENT_START_PHASE_3: case EVENT_START_PHASE_3:
{ {
me->SetSpeed(MOVE_RUN, 2.95f, false); me->SetSpeed(MOVE_RUN, 2.95f, false);
me->GetMotionMaster()->MovePoint(12, OnyxiaMoveData[1].x, OnyxiaMoveData[1].y, OnyxiaMoveData[1].z); me->GetMotionMaster()->MovePoint(POINT_PRE_LAND, OnyxiaMoveData[1].X, OnyxiaMoveData[1].Y, OnyxiaMoveData[1].Z);
break; break;
} }
case EVENT_PHASE_3_ATTACK: case EVENT_PHASE_3_ATTACK:
@@ -509,40 +528,45 @@ public:
} }
case EVENT_ERUPTION: case EVENT_ERUPTION:
{ {
if (Creature* trigger = me->SummonCreature(12758, *me, TEMPSUMMON_TIMED_DESPAWN, 1000)) if (Creature* trigger = me->SummonCreature(NPC_ONYXIA_TRIGGER, *me, TEMPSUMMON_TIMED_DESPAWN, 1000))
{ {
trigger->CastSpell(trigger, 17731, false); trigger->CastSpell(trigger, SPELL_ERUPTION, false);
} }
break; break;
} }
case EVENT_SUMMON_WHELP: case EVENT_SUMMON_WHELP:
{ {
float angle = rand_norm() * 2 * M_PI; SummonWhelps();
float dist = rand_norm() * 4.0f;
me->CastSpell(-33.18f + cos(angle) * dist, -258.80f + std::sin(angle) * dist, -89.0f, 17646, true);
me->CastSpell(-32.535f + cos(angle) * dist, -170.190f + std::sin(angle) * dist, -89.0f, 17646, true);
events.Repeat(30s); events.Repeat(30s);
break; break;
} }
default:
break;
} }
DoMeleeAttackIfReady();
} }
void SpellHitTarget(Unit* target, SpellInfo const* spell) override void SpellHitTarget(Unit* target, SpellInfo const* spell) override
{ {
if (target->IsPlayer() && spell->DurationEntry && spell->DurationEntry->ID == 328 && spell->Effects[EFFECT_1].TargetA.GetTarget() == 1 && (spell->Effects[EFFECT_1].Amplitude == 50 || spell->Effects[EFFECT_1].Amplitude == 215)) // Deep Breath // Deep Breath is a chain of dozens of triggered spells with no shared id,
// so identify a hit by the shape common to all of them
if (target->IsPlayer() && spell->DurationEntry && spell->DurationEntry->ID == 328
&& spell->Effects[EFFECT_1].TargetA.GetTarget() == TARGET_UNIT_CASTER
&& (spell->Effects[EFFECT_1].Amplitude == 50 || spell->Effects[EFFECT_1].Amplitude == 215))
{ {
instance->SetData(DATA_DEEP_BREATH_FAILED, 1); instance->SetData(DATA_DEEP_BREATH_FAILED, 1);
} }
} }
private: private:
uint8 Phase; uint8 _phase;
int8 CurrentWP; int8 _currentWP;
bool whelpSpam; bool _whelpSpam;
uint8 whelpCount; uint8 _whelpCount;
int32 whelpSpamTimer; int32 _whelpSpamTimer;
bool bManyWhelpsAvailable; bool _manyWhelpsAvailable;
}; };
struct npc_onyxian_lair_guard : public ScriptedAI struct npc_onyxian_lair_guard : public ScriptedAI
@@ -590,6 +614,8 @@ public:
events.Repeat(18s, 21s); events.Repeat(18s, 21s);
} }
break; break;
default:
break;
} }
if (!me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady()) if (!me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady())
@@ -60,7 +60,7 @@ public:
go->CastSpell((Unit*)nullptr, 17646); go->CastSpell((Unit*)nullptr, 17646);
if (Creature* onyxia = GetCreature(DATA_ONYXIA)) if (Creature* onyxia = GetCreature(DATA_ONYXIA))
{ {
onyxia->AI()->DoAction(-1); onyxia->AI()->DoAction(ACTION_WHELP_SUMMONED);
} }
break; break;
} }
@@ -36,9 +36,15 @@ enum eCreatures
{ {
NPC_ONYXIA = 10184, NPC_ONYXIA = 10184,
NPC_ONYXIAN_WHELP = 11262, NPC_ONYXIAN_WHELP = 11262,
NPC_ONYXIA_TRIGGER = 12758,
NPC_ONYXIAN_LAIR_GUARD = 36561, NPC_ONYXIAN_LAIR_GUARD = 36561,
}; };
enum eActions
{
ACTION_WHELP_SUMMONED = -1
};
enum eGameObjects enum eGameObjects
{ {
GO_WHELP_SPAWNER = 176510, GO_WHELP_SPAWNER = 176510,