diff options
author | offl <11556157+offl@users.noreply.github.com> | 2025-09-14 01:19:03 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-09-14 00:19:03 +0200 |
commit | de6a77c535208a4bd71ebe6f508a49ad011d4efe (patch) | |
tree | 365c17600b646797908d5790c878f0c05a2a6430 | |
parent | 5c6bf610663b620f3eeba41d05082401eb1b995d (diff) |
Scripts/AQ20: Rewrite Moam (#31216)
* New register model
* Repeat events instead of scheduling them
* Added unique names for enums
* Added comments for script names
* Added AI for Mana Fiend
* Added missing emote
* Use all emotes
* Create master-script to summon Mana Fiends
* Implement & use Zero Mana/Full Health spell
* Implement Energize script to end stoned phase
* Implement Drain Mana master spell script with correct amount of targets and checks to ensure only players and mana-users will be targeted
* Implement Drain Mana visual effect
* Now, once all Mana Fiends are dead, stone phase is finished
* Rework the way stone phase is started and finished
* Moam now drops obsidian mineral once dead
* Added a check to ensure all combat spells will be used
* Added event to handle Arcane Eruption instead of trying to cast it every update tick
Credit for a lot of things goes to CMaNGOS
-rw-r--r-- | sql/updates/world/3.3.5/2025_09_13_01_world.sql | 26 | ||||
-rw-r--r-- | src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_moam.cpp | 375 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_generic.cpp | 18 |
3 files changed, 279 insertions, 140 deletions
diff --git a/sql/updates/world/3.3.5/2025_09_13_01_world.sql b/sql/updates/world/3.3.5/2025_09_13_01_world.sql new file mode 100644 index 00000000000..2467ed8bc29 --- /dev/null +++ b/sql/updates/world/3.3.5/2025_09_13_01_world.sql @@ -0,0 +1,26 @@ +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ( +'spell_moam_summon_mana_fiends', +'spell_moam_energize', +'spell_moam_drain_mana', +'spell_moam_drain_mana_effect', +'spell_gen_zero_mana_full_health'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(25684, 'spell_moam_summon_mana_fiends'), +(25685, 'spell_moam_energize'), +(25676, 'spell_moam_drain_mana'), +(25671, 'spell_moam_drain_mana_effect'), +(25755, 'spell_moam_drain_mana_effect'), +(23777, 'spell_gen_zero_mana_full_health'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 15340 AND `GroupID` = 2; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(15340,2,0,"%s drains your mana and turns to stone.",16,0,100,0,0,0,11474,0,"moam EMOTE_ENERGIZE"); + +UPDATE `creature_template` SET `AIName` = 'SmartAI', `flags_extra` = `flags_extra`&~2 WHERE `entry` = 15527; +DELETE FROM `smart_scripts` WHERE `entryorguid` = 15527 AND `source_type` = 0; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`event_param5`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_param4`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(15527,0,0,0,37,0,100,0,0,0,0,0,0,116,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,"Mana Fiend - On AI Initialize - Set Corpse Delay"), +(15527,0,1,0,11,0,100,0,0,0,0,0,0,38,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,"Mana Fiend - On Spawn - Set In Combat With Zone"), +(15527,0,2,0,0,0,100,0,5000,7000,5000,7000,0,11,25679,0,0,0,0,0,1,0,0,0,0,0,0,0,0,"Mana Fiend - In Combat - Cast 'Arcane Explosion'"), +(15527,0,3,0,13,0,100,0,10000,20000,0,0,0,11,15122,0,0,0,0,0,2,0,0,0,0,0,0,0,0,"Mana Fiend - Target Casting - Cast 'Counterspell'"); diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_moam.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_moam.cpp index 484144c389b..74d89d99ab2 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_moam.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_moam.cpp @@ -17,177 +17,272 @@ #include "ScriptMgr.h" #include "Containers.h" +#include "Player.h" #include "ScriptedCreature.h" +#include "SpellInfo.h" +#include "SpellScript.h" #include "ruins_of_ahnqiraj.h" -enum Texts +enum MoamTexts { - EMOTE_AGGRO = 0, - EMOTE_MANA_FULL = 1 + EMOTE_AGGRO = 0, + EMOTE_MANA_FULL = 1, + EMOTE_ENERGIZE = 2 }; -enum Spells +enum MoamSpells { + // Combat SPELL_TRAMPLE = 15550, - SPELL_DRAIN_MANA = 25671, + SPELL_DRAIN_MANA = 25676, SPELL_ARCANE_ERUPTION = 25672, - SPELL_SUMMON_MANA_FIEND_1 = 25681, // TARGET_DEST_CASTER_FRONT - SPELL_SUMMON_MANA_FIEND_2 = 25682, // TARGET_DEST_CASTER_LEFT - SPELL_SUMMON_MANA_FIEND_3 = 25683, // TARGET_DEST_CASTER_RIGHT - SPELL_ENERGIZE = 25685 + SPELL_SUMMON_MANA_FIENDS = 25684, + SPELL_ENERGIZE = 25685, + + // Misc + SPELL_ZERO_MANA_FULL_HEALTH = 23777, + SPELL_DROP_OBSIDIAN = 27630, + + // Scripts + SPELL_SUMMON_MANA_FIEND_1 = 25681, + SPELL_SUMMON_MANA_FIEND_2 = 25682, + SPELL_SUMMON_MANA_FIEND_3 = 25683, + SPELL_DRAIN_MANA_EFFECT = 25671, + SPELL_DRAIN_MANA_VISUAL = 26639 }; -enum Events +enum MoamEvents { - EVENT_TRAMPLE = 1, - EVENT_DRAIN_MANA = 2, - EVENT_STONE_PHASE = 3, - EVENT_STONE_PHASE_END = 4, - EVENT_WIDE_SLASH = 5, + EVENT_TRAMPLE = 1, + EVENT_DRAIN_MANA, + EVENT_STONE_PHASE, + EVENT_ARCANE_ERUPTION }; -enum Actions +enum MoamMisc { - ACTION_STONE_PHASE_START = 1, - ACTION_STONE_PHASE_END = 2, + MAX_MANA_FIENDS = 3 }; -class boss_moam : public CreatureScript +// 15340 - Moam +struct boss_moam : public BossAI { - public: - boss_moam() : CreatureScript("boss_moam") { } + boss_moam(Creature* creature) : BossAI(creature, DATA_MOAM), _deadManaFiendCount(0) { } - struct boss_moamAI : public BossAI - { - boss_moamAI(Creature* creature) : BossAI(creature, DATA_MOAM) - { - Initialize(); - } + void JustAppeared() override + { + DoCastSelf(SPELL_ZERO_MANA_FULL_HEALTH); + } - void Initialize() - { - _isStonePhase = false; - } + void Reset() override + { + _Reset(); + _deadManaFiendCount = 0; + } - void Reset() override - { - _Reset(); + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + + Talk(EMOTE_AGGRO); + + events.ScheduleEvent(EVENT_TRAMPLE, 10s, 25s); + events.ScheduleEvent(EVENT_DRAIN_MANA, 6s); + events.ScheduleEvent(EVENT_STONE_PHASE, 90s); + events.ScheduleEvent(EVENT_ARCANE_ERUPTION, 1s); + } + + void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override + { + _deadManaFiendCount++; + + if (_deadManaFiendCount == MAX_MANA_FIENDS) + { + me->RemoveAurasDueToSpell(SPELL_ENERGIZE); + _deadManaFiendCount = 0; + } + } + + void OnSpellCast(SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_ARCANE_ERUPTION: + // Is there a spell for that? Maybe Zero Mana/Full Health but with a check to prevent setting full health if creature is in combat? me->SetPower(POWER_MANA, 0); - Initialize(); - events.ScheduleEvent(EVENT_STONE_PHASE, 90s); - //events.ScheduleEvent(EVENT_WIDE_SLASH, 11s); - } + Talk(EMOTE_MANA_FULL); + break; + case SPELL_SUMMON_MANA_FIENDS: + DoCastSelf(SPELL_ENERGIZE); + Talk(EMOTE_ENERGIZE); + break; + default: + break; + } + } - void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override - { - if (!_isStonePhase && HealthBelowPct(45)) - { - _isStonePhase = true; - DoAction(ACTION_STONE_PHASE_START); - } - } + void JustReachedHome() override + { + _JustReachedHome(); + DoCastSelf(SPELL_ZERO_MANA_FULL_HEALTH); + } - void DoAction(int32 action) override - { - switch (action) - { - case ACTION_STONE_PHASE_END: - { - me->RemoveAurasDueToSpell(SPELL_ENERGIZE); - events.ScheduleEvent(EVENT_STONE_PHASE, 90s); - _isStonePhase = false; - break; - } - case ACTION_STONE_PHASE_START: - { - DoCast(me, SPELL_SUMMON_MANA_FIEND_1); - DoCast(me, SPELL_SUMMON_MANA_FIEND_2); - DoCast(me, SPELL_SUMMON_MANA_FIEND_3); - DoCast(me, SPELL_ENERGIZE); - events.ScheduleEvent(EVENT_STONE_PHASE_END, 90s); - break; - } - default: - break; - } - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + DoCastSelf(SPELL_DROP_OBSIDIAN, true); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void UpdateAI(uint32 diff) override + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->GetPower(POWER_MANA) == me->GetMaxPower(POWER_MANA)) - { - if (_isStonePhase) - DoAction(ACTION_STONE_PHASE_END); - DoCastAOE(SPELL_ARCANE_ERUPTION); - me->SetPower(POWER_MANA, 0); - } - - if (_isStonePhase) - { - if (events.ExecuteEvent() == EVENT_STONE_PHASE_END) - DoAction(ACTION_STONE_PHASE_END); - return; - } - - // Messing up mana-drain channel - //if (me->HasUnitState(UNIT_STATE_CASTING)) - // return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_STONE_PHASE: - DoAction(ACTION_STONE_PHASE_START); - break; - case EVENT_DRAIN_MANA: - { - std::list<Unit*> targetList; - { - for (ThreatReference const* ref : me->GetThreatManager().GetUnsortedThreatList()) - if (ref->GetVictim()->GetTypeId() == TYPEID_PLAYER && ref->GetVictim()->GetPowerType() == POWER_MANA) - targetList.push_back(ref->GetVictim()); - } - - Trinity::Containers::RandomResize(targetList, 5); - - for (std::list<Unit*>::iterator itr = targetList.begin(); itr != targetList.end(); ++itr) - DoCast(*itr, SPELL_DRAIN_MANA); - - events.ScheduleEvent(EVENT_DRAIN_MANA, 5s, 15s); - break; - }/* - case EVENT_WIDE_SLASH: - DoCast(me, SPELL_WIDE_SLASH); - events.ScheduleEvent(EVENT_WIDE_SLASH, 11s); - break; - case EVENT_TRASH: - DoCast(me, SPELL_TRASH); - events.ScheduleEvent(EVENT_WIDE_SLASH, 15s); - break;*/ - default: - break; - } - } - - DoMeleeAttackIfReady(); + case EVENT_TRAMPLE: + DoCastSelf(SPELL_TRAMPLE); + events.Repeat(10s, 15s); + break; + case EVENT_DRAIN_MANA: + DoCastSelf(SPELL_DRAIN_MANA); + events.Repeat(6s); + break; + case EVENT_STONE_PHASE: + _deadManaFiendCount = 0; + DoCastSelf(SPELL_SUMMON_MANA_FIENDS); + events.Repeat(130s); + break; + case EVENT_ARCANE_ERUPTION: + if (me->GetPower(POWER_MANA) == me->GetMaxPower(POWER_MANA)) + DoCastSelf(SPELL_ARCANE_ERUPTION); + events.Repeat(1s); + break; + default: + break; } - private: - bool _isStonePhase; - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetAQ20AI<boss_moamAI>(creature); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } + + DoMeleeAttackIfReady(); + } + +private: + uint8 _deadManaFiendCount; +}; + +// 25684 - Summon Mana Fiends +class spell_moam_summon_mana_fiends : public SpellScript +{ + PrepareSpellScript(spell_moam_summon_mana_fiends); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_SUMMON_MANA_FIEND_1, + SPELL_SUMMON_MANA_FIEND_2, + SPELL_SUMMON_MANA_FIEND_3 + }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + caster->CastSpell(caster, SPELL_SUMMON_MANA_FIEND_1, true); + caster->CastSpell(caster, SPELL_SUMMON_MANA_FIEND_2, true); + caster->CastSpell(caster, SPELL_SUMMON_MANA_FIEND_3, true); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_moam_summon_mana_fiends::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 25685 - Energize +class spell_moam_energize : public AuraScript +{ + PrepareAuraScript(spell_moam_energize); + + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + if (GetTarget()->GetPower(POWER_MANA) == GetTarget()->GetMaxPower(POWER_MANA)) + Remove(); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_moam_energize::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_ENERGIZE); + } +}; + +// 25676 - Drain Mana +class spell_moam_drain_mana : public SpellScript +{ + PrepareSpellScript(spell_moam_drain_mana); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DRAIN_MANA_EFFECT }); + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + Trinity::Containers::RandomResize(targets, [](WorldObject* target) + { + return target->IsPlayer() && target->ToPlayer()->GetPowerType() == POWER_MANA; + }, 6); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_DRAIN_MANA_EFFECT, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_moam_drain_mana::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_moam_drain_mana::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 25671 - Drain Mana +// 25755 - Drain Mana +class spell_moam_drain_mana_effect : public SpellScript +{ + PrepareSpellScript(spell_moam_drain_mana_effect); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DRAIN_MANA_VISUAL }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetCaster(), SPELL_DRAIN_MANA_VISUAL, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_moam_drain_mana_effect::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } }; void AddSC_boss_moam() { - new boss_moam(); + RegisterAQ20CreatureAI(boss_moam); + RegisterSpellScript(spell_moam_summon_mana_fiends); + RegisterSpellScript(spell_moam_energize); + RegisterSpellScript(spell_moam_drain_mana); + RegisterSpellScript(spell_moam_drain_mana_effect); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index ab852ecba75..22028fa1d39 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -3922,6 +3922,23 @@ private: uint32 _text; }; +// 23777 - Zero Mana/Full Health DND +class spell_gen_zero_mana_full_health : public SpellScript +{ + PrepareSpellScript(spell_gen_zero_mana_full_health); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->SetFullHealth(); + GetCaster()->SetPower(POWER_MANA, 0); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_gen_zero_mana_full_health::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + class spell_gen_eject_all_passengers : public SpellScript { PrepareSpellScript(spell_gen_eject_all_passengers); @@ -4828,6 +4845,7 @@ void AddSC_generic_spell_scripts() RegisterSpellScriptWithArgs(spell_gen_whisper_to_controller_random, "spell_future_you_whisper_to_controller_random", WHISPER_FUTURE_YOU); RegisterSpellScriptWithArgs(spell_gen_whisper_to_controller_random, "spell_wyrmrest_defender_whisper_to_controller_random", WHISPER_DEFENDER); RegisterSpellScriptWithArgs(spell_gen_whisper_to_controller_random, "spell_past_you_whisper_to_controller_random", WHISPER_PAST_YOU); + RegisterSpellScript(spell_gen_zero_mana_full_health); RegisterSpellScript(spell_gen_eject_all_passengers); RegisterSpellScript(spell_gen_eject_passenger); RegisterSpellScriptWithArgs(spell_gen_eject_passenger_with_seatId, "spell_gen_eject_passenger_1", 0); |