From edf7b54315df9bf7441c1834a0b62e6b7ec71522 Mon Sep 17 00:00:00 2001 From: Andrius Peleckas <32540208+sanctum32@users.noreply.github.com> Date: Mon, 22 Nov 2021 17:17:32 +0200 Subject: [PATCH] fix(Scripts/MoltenCore): Ragnaros rewrite (#8935) --- .../rev_1635972083556511200.sql | 12 + .../MoltenCore/boss_majordomo_executus.cpp | 8 +- .../MoltenCore/boss_ragnaros.cpp | 302 +++++++++++------- 3 files changed, 211 insertions(+), 111 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1635972083556511200.sql diff --git a/data/sql/updates/pending_db_world/rev_1635972083556511200.sql b/data/sql/updates/pending_db_world/rev_1635972083556511200.sql new file mode 100644 index 000000000..89424a5fc --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1635972083556511200.sql @@ -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; diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp index 585fd151b..d02913f47 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp @@ -40,6 +40,7 @@ enum Texts // Majordomo SAY_RAG_SUM_1 = 9, SAY_RAG_SUM_2 = 10, + SAY_DEATH = 11, // Ragnaros SAY_ARRIVAL1_RAG = 1, @@ -130,8 +131,11 @@ public: { 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 { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp index 464b36ae6..874b7657c 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp @@ -28,52 +28,64 @@ enum Texts SAY_ARRIVAL5_RAG = 4, SAY_REINFORCEMENTS1 = 5, 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_KILL = 9, - SAY_MAGMABURST = 10 + SAY_MAGMABURST = 10, }; enum Spells { SPELL_HAND_OF_RAGNAROS = 19780, SPELL_WRATH_OF_RAGNAROS = 20566, - SPELL_LAVA_BURST = 21158, - SPELL_MAGMA_BLAST = 20565, // Ranged attack - SPELL_SONS_OF_FLAME_DUMMY = 21108, // Server side effect - SPELL_RAGSUBMERGE = 21107, // Stealth aura - SPELL_RAGNA_SUBMERGE_VISUAL = 20567, // Visual for submerging into lava + 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_SONS_OF_FLAME_DUMMY = 21108, // Server side effect + SPELL_RAGSUBMERGE = 21107, // Stealth aura + SPELL_RAGNA_SUBMERGE_VISUAL = 20567, // Visual for submerging into lava SPELL_RAGEMERGE = 20568, - SPELL_MELT_WEAPON = 21387, - SPELL_ELEMENTAL_FIRE = 20563, - SPELL_ERRUPTION = 17731, - SPELL_RAGNAROS_SUBMERGE_EFFECT = 21859, // Applies pacify state and applies all schools immunity + SPELL_RAGNAROS_SUBMERGE_EFFECT = 21859, // Applies pacify state and applies all schools immunity (server side) + SPELL_ELEMENTAL_FIRE_KILL = 19773, // Spell is used only on Majordomo + SPELL_MIGHT_OF_RAGNAROS = 21154, + SPELL_INTENSE_HEAT = 21155, + SPELL_SUMMON_SONS_FLAME = 21108, // Trigger the eight spells summoning the Son of Flame adds (TODO) }; enum Events { - EVENT_ERUPTION = 1, + EVENT_ERUPTION = 1, EVENT_WRATH_OF_RAGNAROS, EVENT_HAND_OF_RAGNAROS, + EVENT_MIGHT_OF_RAGNAROS, EVENT_LAVA_BURST, EVENT_MAGMA_BLAST, EVENT_SUBMERGE, - EVENT_INTRO_1, - EVENT_INTRO_2, - EVENT_INTRO_3, - EVENT_INTRO_4, - EVENT_INTRO_5, + // Submerge + EVENT_EMERGE, + + // Intro + EVENT_INTRO_SAY, + EVENT_INTRO_MAKE_ATTACKABLE, + + // Misc + EVENT_RESET_KNOCKBACK_EMOTE, }; enum Creatures { - NPC_SON_OF_FLAME = 12143, + NPC_SON_OF_FLAME = 12143, + NPC_FLAME_OF_RAGNAROS = 13148, }; 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; @@ -86,61 +98,78 @@ public: struct boss_ragnarosAI : public BossAI { boss_ragnarosAI(Creature* creature) : BossAI(creature, DATA_RAGNAROS), - attackableTImer(0), - _emergeTimer(90000), + _isIntroDone(false), _hasYelledMagmaBurst(false), _hasSubmergedOnce(false), - _isBanished(false) + _isKnockbackEmoteAllowed(true) { - creature->SetReactState(REACT_PASSIVE); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); } void Reset() override { _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; _hasSubmergedOnce = false; - _isBanished = false; + _isKnockbackEmoteAllowed = true; me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); - DoCastSelf(SPELL_MELT_WEAPON, true); - DoCastSelf(SPELL_ELEMENTAL_FIRE, true); } void DoAction(int32 action) override { 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 { summons.Despawn(summon); + if (events.IsInPhase(PHASE_SUBMERGED) && !summons.HasEntry(NPC_SON_OF_FLAME)) + { + HandleEmerge(); + } } void EnterCombat(Unit* /*victim*/) override { _EnterCombat(); - events.ScheduleEvent(EVENT_ERUPTION, 15000); - events.ScheduleEvent(EVENT_WRATH_OF_RAGNAROS, 30000); - events.ScheduleEvent(EVENT_HAND_OF_RAGNAROS, 25000); - events.ScheduleEvent(EVENT_LAVA_BURST, 10000); - events.ScheduleEvent(EVENT_MAGMA_BLAST, 2000); - events.ScheduleEvent(EVENT_SUBMERGE, 180000); + events.SetPhase(PHASE_EMERGED); + ScheduleCombatEvents(); } void JustDied(Unit* /*killer*/) override { _JustDied(); + extraEvents.Reset(); me->SetFacingTo(DEATH_ORIENTATION); } void KilledUnit(Unit* victim) override { - if (roll_chance_i(25) && victim->GetTypeId() == TYPEID_PLAYER) + if (roll_chance_i(25) && victim->IsPlayer()) { Talk(SAY_KILL); } @@ -156,41 +185,44 @@ public: void UpdateAI(uint32 diff) override { - if (attackableTImer) + if (!extraEvents.Empty()) { - if (attackableTImer <= 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; - } - } + extraEvents.Update(diff); - if (_isBanished && (_emergeTimer <= diff || !summons.HasEntry(NPC_SON_OF_FLAME))) - { - //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)) + while (uint32 const eventId = extraEvents.ExecuteEvent()) { - 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()) @@ -204,16 +236,12 @@ public: { return; } + + // Base combat events - (mainly emerge phase) while (uint32 const eventId = events.ExecuteEvent()) { switch (eventId) { - case EVENT_ERUPTION: - { - DoCastVictim(SPELL_ERRUPTION); - events.RepeatEvent(urand(20000, 45000)); - break; - } case EVENT_WRATH_OF_RAGNAROS: { DoCastVictim(SPELL_WRATH_OF_RAGNAROS); @@ -227,50 +255,95 @@ public: case EVENT_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); break; } case EVENT_LAVA_BURST: { - DoCastVictim(SPELL_LAVA_BURST); + DoCastAOE(SPELL_LAVA_BURST); events.RepeatEvent(10000); break; } case EVENT_MAGMA_BLAST: { - Unit* victim = me->GetVictim(); + Unit const* victim = me->GetVictim(); 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) { Talk(SAY_MAGMABURST); _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; } 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); - me->AttackStop(); - DoResetThreat(); - me->SetReactState(REACT_PASSIVE); - me->SetFaction(35); - 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(); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + { + if (Creature* summoned = me->SummonCreature(NPC_SON_OF_FLAME, target->GetPosition(), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 900000)) + { + summoned->AI()->AttackStart(target); + } + } } - events.ScheduleEvent(EVENT_SUBMERGE, 180000); + + if (!_hasSubmergedOnce) + { + _hasSubmergedOnce = true; + } + + extraEvents.ScheduleEvent(EVENT_EMERGE, 90000, PHASE_SUBMERGED, PHASE_SUBMERGED); break; } } @@ -285,34 +358,45 @@ public: } private: - uint32 attackableTImer; // used after intro - uint32 _emergeTimer; + EventMap extraEvents; + bool _isIntroDone; bool _hasYelledMagmaBurst; bool _hasSubmergedOnce; - bool _isBanished; + bool _isKnockbackEmoteAllowed; // Prevents possible text overlap - void SummonMinions() + void HandleEmerge() { - Talk(_hasSubmergedOnce ? SAY_REINFORCEMENTS2 : SAY_REINFORCEMENTS1); - - for (uint8 i = 0; i < MAX_SON_OF_FLAME_COUNT; ++i) + if (events.IsInPhase(PHASE_EMERGED)) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) - { - 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); - } - } + return; } - _isBanished = true; - _emergeTimer = 90000; - if (!_hasSubmergedOnce) + events.SetPhase(PHASE_EMERGED); + ScheduleCombatEvents(); + 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