fix(Scripts/MoltenCore): Ragnaros rewrite (#8935)

This commit is contained in:
Andrius Peleckas
2021-11-22 17:17:32 +02:00
committed by GitHub
parent dc9c3a5fe4
commit edf7b54315
3 changed files with 211 additions and 111 deletions
@@ -0,0 +1,12 @@
INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1635972083556511200');
-- Majordomo texts
DELETE FROM `creature_text` WHERE `CreatureID`=12018 AND `GroupID`=11 AND `ID`=0;
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(12018, 11, 0, 'My flame... Please, don\'t take away my flame...', 14, 0, 100, 0, 0, 8042, 0, 0, 'majordomo SAY_DEATH');
-- Ragnaros auras
UPDATE `creature_template_addon` SET `auras`='20563 21387' WHERE `entry`=11502;
-- Ragnaros Texts
UPDATE `creature_text` SET `comment`='ragnaros SAY_KNOCKBACK' WHERE `CreatureID`=11502 AND `GroupID`=7 AND `ID`=0;
@@ -40,6 +40,7 @@ enum Texts
// Majordomo // Majordomo
SAY_RAG_SUM_1 = 9, SAY_RAG_SUM_1 = 9,
SAY_RAG_SUM_2 = 10, SAY_RAG_SUM_2 = 10,
SAY_DEATH = 11,
// Ragnaros // Ragnaros
SAY_ARRIVAL1_RAG = 1, SAY_ARRIVAL1_RAG = 1,
@@ -130,8 +131,11 @@ public:
{ {
boss_majordomoAI(Creature* creature) : BossAI(creature, DATA_MAJORDOMO_EXECUTUS), spawnInTextTimer(0) {} boss_majordomoAI(Creature* creature) : BossAI(creature, DATA_MAJORDOMO_EXECUTUS), spawnInTextTimer(0) {}
// Disabled events void JustDied(Unit* /*killer*/) override
void JustDied(Unit* /*killer*/) override {} {
Talk(SAY_DEATH);
me->DespawnOrUnsummon(10s, 0s);
}
void JustSummoned(Creature* summon) override void JustSummoned(Creature* summon) override
{ {
@@ -28,52 +28,64 @@ enum Texts
SAY_ARRIVAL5_RAG = 4, SAY_ARRIVAL5_RAG = 4,
SAY_REINFORCEMENTS1 = 5, SAY_REINFORCEMENTS1 = 5,
SAY_REINFORCEMENTS2 = 6, SAY_REINFORCEMENTS2 = 6,
SAY_HAND = 7, SAY_KNOCKBACK = 7, // Text is used for SPELL_HAND_OF_RAGNAROS & SPELL_MIGHT_OF_RAGNAROS. "By fire be purged!"
SAY_WRATH = 8, SAY_WRATH = 8,
SAY_KILL = 9, SAY_KILL = 9,
SAY_MAGMABURST = 10 SAY_MAGMABURST = 10,
}; };
enum Spells enum Spells
{ {
SPELL_HAND_OF_RAGNAROS = 19780, SPELL_HAND_OF_RAGNAROS = 19780,
SPELL_WRATH_OF_RAGNAROS = 20566, SPELL_WRATH_OF_RAGNAROS = 20566,
SPELL_LAVA_BURST = 21158, SPELL_LAVA_BURST = 21908, // Randomly trigger one of server side spells (21886, 21900 - 21907) which summons Go 178088 (TODO)
SPELL_MAGMA_BLAST = 20565, // Ranged attack SPELL_MAGMA_BLAST = 20565, // Ranged attack
SPELL_SONS_OF_FLAME_DUMMY = 21108, // Server side effect SPELL_SONS_OF_FLAME_DUMMY = 21108, // Server side effect
SPELL_RAGSUBMERGE = 21107, // Stealth aura SPELL_RAGSUBMERGE = 21107, // Stealth aura
SPELL_RAGNA_SUBMERGE_VISUAL = 20567, // Visual for submerging into lava SPELL_RAGNA_SUBMERGE_VISUAL = 20567, // Visual for submerging into lava
SPELL_RAGEMERGE = 20568, SPELL_RAGEMERGE = 20568,
SPELL_MELT_WEAPON = 21387, SPELL_RAGNAROS_SUBMERGE_EFFECT = 21859, // Applies pacify state and applies all schools immunity (server side)
SPELL_ELEMENTAL_FIRE = 20563, SPELL_ELEMENTAL_FIRE_KILL = 19773, // Spell is used only on Majordomo
SPELL_ERRUPTION = 17731, SPELL_MIGHT_OF_RAGNAROS = 21154,
SPELL_RAGNAROS_SUBMERGE_EFFECT = 21859, // Applies pacify state and applies all schools immunity SPELL_INTENSE_HEAT = 21155,
SPELL_SUMMON_SONS_FLAME = 21108, // Trigger the eight spells summoning the Son of Flame adds (TODO)
}; };
enum Events enum Events
{ {
EVENT_ERUPTION = 1, EVENT_ERUPTION = 1,
EVENT_WRATH_OF_RAGNAROS, EVENT_WRATH_OF_RAGNAROS,
EVENT_HAND_OF_RAGNAROS, EVENT_HAND_OF_RAGNAROS,
EVENT_MIGHT_OF_RAGNAROS,
EVENT_LAVA_BURST, EVENT_LAVA_BURST,
EVENT_MAGMA_BLAST, EVENT_MAGMA_BLAST,
EVENT_SUBMERGE, EVENT_SUBMERGE,
EVENT_INTRO_1, // Submerge
EVENT_INTRO_2, EVENT_EMERGE,
EVENT_INTRO_3,
EVENT_INTRO_4, // Intro
EVENT_INTRO_5, EVENT_INTRO_SAY,
EVENT_INTRO_MAKE_ATTACKABLE,
// Misc
EVENT_RESET_KNOCKBACK_EMOTE,
}; };
enum Creatures enum Creatures
{ {
NPC_SON_OF_FLAME = 12143, NPC_SON_OF_FLAME = 12143,
NPC_FLAME_OF_RAGNAROS = 13148,
}; };
enum Misc enum Misc
{ {
MAX_SON_OF_FLAME_COUNT = 8, MAX_SON_OF_FLAME_COUNT = 8,
// Event phase
PHASE_INTRO = 1, // Intro events with Majordomu
PHASE_EMERGED = 2, // events which are executed while Ragnaros is visible
PHASE_SUBMERGED = 3, // events which are executed while Ragnaros is submerged (not visible)
}; };
constexpr float DEATH_ORIENTATION = 4.0f; constexpr float DEATH_ORIENTATION = 4.0f;
@@ -86,61 +98,78 @@ public:
struct boss_ragnarosAI : public BossAI struct boss_ragnarosAI : public BossAI
{ {
boss_ragnarosAI(Creature* creature) : BossAI(creature, DATA_RAGNAROS), boss_ragnarosAI(Creature* creature) : BossAI(creature, DATA_RAGNAROS),
attackableTImer(0), _isIntroDone(false),
_emergeTimer(90000),
_hasYelledMagmaBurst(false), _hasYelledMagmaBurst(false),
_hasSubmergedOnce(false), _hasSubmergedOnce(false),
_isBanished(false) _isKnockbackEmoteAllowed(true)
{ {
creature->SetReactState(REACT_PASSIVE);
creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
} }
void Reset() override void Reset() override
{ {
_Reset(); _Reset();
_emergeTimer = 90000;
// Never reset intro events!
if (_isIntroDone && !(extraEvents.GetPhaseMask() & (1 << (PHASE_INTRO - 1))))
{
extraEvents.Reset();
extraEvents.SetPhase(PHASE_EMERGED);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
me->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE);
}
_hasYelledMagmaBurst = false; _hasYelledMagmaBurst = false;
_hasSubmergedOnce = false; _hasSubmergedOnce = false;
_isBanished = false; _isKnockbackEmoteAllowed = true;
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
DoCastSelf(SPELL_MELT_WEAPON, true);
DoCastSelf(SPELL_ELEMENTAL_FIRE, true);
} }
void DoAction(int32 action) override void DoAction(int32 action) override
{ {
if (action == ACTION_FINISH_RAGNAROS_INTRO) if (action == ACTION_FINISH_RAGNAROS_INTRO)
{ {
attackableTImer = 10000; extraEvents.SetPhase(PHASE_INTRO);
extraEvents.ScheduleEvent(EVENT_INTRO_SAY, 5000, 0, PHASE_INTRO);
}
}
void JustSummoned(Creature* summon) override
{
BossAI::JustSummoned(summon);
if (summon->GetEntry() == NPC_FLAME_OF_RAGNAROS)
{
summon->CastSpell((Unit*)nullptr, SPELL_INTENSE_HEAT, true, nullptr, nullptr, me->GetGUID());
} }
} }
void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
{ {
summons.Despawn(summon); summons.Despawn(summon);
if (events.IsInPhase(PHASE_SUBMERGED) && !summons.HasEntry(NPC_SON_OF_FLAME))
{
HandleEmerge();
}
} }
void EnterCombat(Unit* /*victim*/) override void EnterCombat(Unit* /*victim*/) override
{ {
_EnterCombat(); _EnterCombat();
events.ScheduleEvent(EVENT_ERUPTION, 15000); events.SetPhase(PHASE_EMERGED);
events.ScheduleEvent(EVENT_WRATH_OF_RAGNAROS, 30000); ScheduleCombatEvents();
events.ScheduleEvent(EVENT_HAND_OF_RAGNAROS, 25000);
events.ScheduleEvent(EVENT_LAVA_BURST, 10000);
events.ScheduleEvent(EVENT_MAGMA_BLAST, 2000);
events.ScheduleEvent(EVENT_SUBMERGE, 180000);
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
_JustDied(); _JustDied();
extraEvents.Reset();
me->SetFacingTo(DEATH_ORIENTATION); me->SetFacingTo(DEATH_ORIENTATION);
} }
void KilledUnit(Unit* victim) override void KilledUnit(Unit* victim) override
{ {
if (roll_chance_i(25) && victim->GetTypeId() == TYPEID_PLAYER) if (roll_chance_i(25) && victim->IsPlayer())
{ {
Talk(SAY_KILL); Talk(SAY_KILL);
} }
@@ -156,41 +185,44 @@ public:
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (attackableTImer) if (!extraEvents.Empty())
{ {
if (attackableTImer <= diff) extraEvents.Update(diff);
{
me->RemoveAurasDueToSpell(SPELL_RAGNAROS_SUBMERGE_EFFECT);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
attackableTImer = 0;
}
else
{
attackableTImer -= diff;
}
}
if (_isBanished && (_emergeTimer <= diff || !summons.HasEntry(NPC_SON_OF_FLAME))) while (uint32 const eventId = extraEvents.ExecuteEvent())
{
//Become unbanished again
me->SetReactState(REACT_AGGRESSIVE);
me->SetFaction(14);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
me->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE);
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
{ {
AttackStart(target); switch (eventId)
{
// Intro events
case EVENT_INTRO_SAY:
{
Talk(SAY_ARRIVAL5_RAG);
extraEvents.ScheduleEvent(EVENT_INTRO_MAKE_ATTACKABLE, 2500, 0, PHASE_INTRO);
break;
}
case EVENT_INTRO_MAKE_ATTACKABLE:
{
_isIntroDone = true;
extraEvents.SetPhase(PHASE_EMERGED);
me->RemoveAurasDueToSpell(SPELL_RAGNAROS_SUBMERGE_EFFECT);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
break;
}
// Submerge events
case EVENT_EMERGE:
{
HandleEmerge();
break;
}
case EVENT_RESET_KNOCKBACK_EMOTE:
{
_isKnockbackEmoteAllowed = true;
break;
}
}
} }
_isBanished = false;
}
else if (_isBanished)
{
_emergeTimer -= diff;
return;
} }
if (!UpdateVictim()) if (!UpdateVictim())
@@ -204,16 +236,12 @@ public:
{ {
return; return;
} }
// Base combat events - (mainly emerge phase)
while (uint32 const eventId = events.ExecuteEvent()) while (uint32 const eventId = events.ExecuteEvent())
{ {
switch (eventId) switch (eventId)
{ {
case EVENT_ERUPTION:
{
DoCastVictim(SPELL_ERRUPTION);
events.RepeatEvent(urand(20000, 45000));
break;
}
case EVENT_WRATH_OF_RAGNAROS: case EVENT_WRATH_OF_RAGNAROS:
{ {
DoCastVictim(SPELL_WRATH_OF_RAGNAROS); DoCastVictim(SPELL_WRATH_OF_RAGNAROS);
@@ -227,50 +255,95 @@ public:
case EVENT_HAND_OF_RAGNAROS: case EVENT_HAND_OF_RAGNAROS:
{ {
DoCastSelf(SPELL_HAND_OF_RAGNAROS); DoCastSelf(SPELL_HAND_OF_RAGNAROS);
if (urand(0, 1)) if (_isKnockbackEmoteAllowed)
{ {
Talk(SAY_HAND); Talk(SAY_KNOCKBACK);
_isKnockbackEmoteAllowed = false;
extraEvents.RescheduleEvent(EVENT_RESET_KNOCKBACK_EMOTE, 5000);
} }
events.RepeatEvent(20000); events.RepeatEvent(20000);
break; break;
} }
case EVENT_LAVA_BURST: case EVENT_LAVA_BURST:
{ {
DoCastVictim(SPELL_LAVA_BURST); DoCastAOE(SPELL_LAVA_BURST);
events.RepeatEvent(10000); events.RepeatEvent(10000);
break; break;
} }
case EVENT_MAGMA_BLAST: case EVENT_MAGMA_BLAST:
{ {
Unit* victim = me->GetVictim(); Unit const* victim = me->GetVictim();
if (victim && !me->IsWithinMeleeRange(victim)) if (victim && !me->IsWithinMeleeRange(victim))
{ {
DoCast(victim, SPELL_MAGMA_BLAST); if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
{
DoCast(target, SPELL_MAGMA_BLAST);
}
if (!_hasYelledMagmaBurst) if (!_hasYelledMagmaBurst)
{ {
Talk(SAY_MAGMABURST); Talk(SAY_MAGMABURST);
_hasYelledMagmaBurst = true; _hasYelledMagmaBurst = true;
} }
} }
events.RepeatEvent(2500); else if (_hasYelledMagmaBurst)
{
_hasYelledMagmaBurst = false;
}
events.RepeatEvent(500);
break;
}
case EVENT_MIGHT_OF_RAGNAROS:
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, [](Unit const* target)
{
return target->IsPlayer() && target->getPowerType() == POWER_MANA;
}))
{
if (me->CastSpell(target, SPELL_MIGHT_OF_RAGNAROS) == SPELL_CAST_OK && _isKnockbackEmoteAllowed)
{
Talk(SAY_KNOCKBACK, me);
_isKnockbackEmoteAllowed = false;
extraEvents.RescheduleEvent(EVENT_RESET_KNOCKBACK_EMOTE, 5000);
}
}
events.RepeatEvent(urand(11000, 30000));
break; break;
} }
case EVENT_SUBMERGE: case EVENT_SUBMERGE:
{ {
if (!_isBanished) events.CancelEventGroup(PHASE_EMERGED);
events.SetPhase(PHASE_SUBMERGED);
extraEvents.SetPhase(PHASE_SUBMERGED);
me->SetReactState(REACT_PASSIVE);
me->InterruptNonMeleeSpells(false);
me->AttackStop();
DoResetThreat();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SUBMERGED);
DoCastSelf(SPELL_RAGNA_SUBMERGE_VISUAL, true);
//me->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE);
Talk(_hasSubmergedOnce ? SAY_REINFORCEMENTS2 : SAY_REINFORCEMENTS1);
for (uint8 i = 0; i < MAX_SON_OF_FLAME_COUNT; ++i)
{ {
me->InterruptNonMeleeSpells(false); if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
me->AttackStop(); {
DoResetThreat(); if (Creature* summoned = me->SummonCreature(NPC_SON_OF_FLAME, target->GetPosition(), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 900000))
me->SetReactState(REACT_PASSIVE); {
me->SetFaction(35); summoned->AI()->AttackStart(target);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); }
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SUBMERGED); }
DoCastSelf(SPELL_RAGNA_SUBMERGE_VISUAL, true);
//me->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE);
SummonMinions();
} }
events.ScheduleEvent(EVENT_SUBMERGE, 180000);
if (!_hasSubmergedOnce)
{
_hasSubmergedOnce = true;
}
extraEvents.ScheduleEvent(EVENT_EMERGE, 90000, PHASE_SUBMERGED, PHASE_SUBMERGED);
break; break;
} }
} }
@@ -285,34 +358,45 @@ public:
} }
private: private:
uint32 attackableTImer; // used after intro EventMap extraEvents;
uint32 _emergeTimer; bool _isIntroDone;
bool _hasYelledMagmaBurst; bool _hasYelledMagmaBurst;
bool _hasSubmergedOnce; bool _hasSubmergedOnce;
bool _isBanished; bool _isKnockbackEmoteAllowed; // Prevents possible text overlap
void SummonMinions() void HandleEmerge()
{ {
Talk(_hasSubmergedOnce ? SAY_REINFORCEMENTS2 : SAY_REINFORCEMENTS1); if (events.IsInPhase(PHASE_EMERGED))
for (uint8 i = 0; i < MAX_SON_OF_FLAME_COUNT; ++i)
{ {
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) return;
{
if (Creature* summoned = me->SummonCreature(NPC_SON_OF_FLAME, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 900000))
{
summoned->AI()->AttackStart(target);
}
}
} }
_isBanished = true; events.SetPhase(PHASE_EMERGED);
_emergeTimer = 90000; ScheduleCombatEvents();
if (!_hasSubmergedOnce) extraEvents.CancelEventGroup(PHASE_SUBMERGED);
extraEvents.SetPhase(PHASE_EMERGED);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
me->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE);
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
{ {
_hasSubmergedOnce = true; AttackStart(target);
} }
} }
void ScheduleCombatEvents()
{
events.RescheduleEvent(EVENT_ERUPTION, 15000, PHASE_EMERGED, PHASE_EMERGED);
events.RescheduleEvent(EVENT_WRATH_OF_RAGNAROS, 30000, PHASE_EMERGED, PHASE_EMERGED);
events.RescheduleEvent(EVENT_HAND_OF_RAGNAROS, 25000, PHASE_EMERGED, PHASE_EMERGED);
events.RescheduleEvent(EVENT_LAVA_BURST, 10000, PHASE_EMERGED, PHASE_EMERGED);
events.RescheduleEvent(EVENT_MAGMA_BLAST, 500, PHASE_EMERGED, PHASE_EMERGED);
events.RescheduleEvent(EVENT_SUBMERGE, 180000, PHASE_EMERGED, PHASE_EMERGED);
events.RescheduleEvent(EVENT_MIGHT_OF_RAGNAROS, 11000, PHASE_EMERGED, PHASE_EMERGED);
}
}; };
CreatureAI* GetAI(Creature* creature) const override CreatureAI* GetAI(Creature* creature) const override