diff options
author | offl <11556157+offl@users.noreply.github.com> | 2025-06-10 22:05:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-10 21:05:55 +0200 |
commit | a23262869685e66d68be233b9020d611ffdb5db0 (patch) | |
tree | 60e0c7c6a55cd44171207e6c746389dd879cebda | |
parent | 0bd56da09b1f0ae34605a1a6fdd14ca26cd42d4c (diff) |
Scripts/Gruul's Lair: Update scripts (#30913)
4 files changed, 513 insertions, 696 deletions
diff --git a/sql/updates/world/3.3.5/2025_06_10_06_world.sql b/sql/updates/world/3.3.5/2025_06_10_06_world.sql new file mode 100644 index 00000000000..fb111f71de1 --- /dev/null +++ b/sql/updates/world/3.3.5/2025_06_10_06_world.sql @@ -0,0 +1,19 @@ +-- +DELETE FROM `spell_script_names` WHERE +(`spell_id` = 33525 AND `ScriptName` = "spell_gruul_ground_slam") OR +(`spell_id` = 33965 AND `ScriptName` = "spell_gruul_look_around") OR +(`spell_id` = 33812 AND `ScriptName` = "spell_gruul_hurtful_strike_primer"); +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(33525,"spell_gruul_ground_slam"), +(33965,"spell_gruul_look_around"), +(33812,"spell_gruul_hurtful_strike_primer"); + +UPDATE `creature_text` SET `Text` = "%s roars!", `Type` = 16, `Sound` = 0, `BroadcastTextId` = 14029, `comment` = "gruul EMOTE_ROAR" WHERE `CreatureID` = 19044 AND `GroupID` = 4 AND `ID` = 0; +UPDATE `creature_text` SET `TextRange` = 3 WHERE `CreatureID` = 19044; + +UPDATE `creature_template` SET `AIName` = "SmartAI" WHERE `entry` = 19198; +DELETE FROM `smart_scripts` WHERE `entryorguid` = 19198 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 +(19198,0,0,0,60,0,100,1,0,0,0,0,0,11,33496,0,0,0,0,0,1,0,0,0,0,0,0,0,0,"Invisible Tractor Beam Source - On Update - Cast 'Tractor Beam Creator' (No Repeat)"), +(19198,0,1,0,60,0,100,1,500,500,0,0,0,11,33497,0,0,0,0,0,23,0,0,0,0,0,0,0,0,"Invisible Tractor Beam Source - On Update - Cast 'Tractor Beam' (No Repeat)"), +(19198,0,2,0,60,0,100,1,1000,1000,0,0,0,41,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,"Invisible Tractor Beam Source - On Update - Despawn Instant (No Repeat)"); diff --git a/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp b/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp index 3b519ae2bc0..1a15d9a7a35 100644 --- a/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp +++ b/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp @@ -16,330 +16,291 @@ */ /* -TO-DO: -Slighly(400ms) after spell cast 33965 creatures 19198 are spawned. I guess he forces all enemies including pets(9 summoned units -and 9 units in his threatlist) to cast 39186(19198 were created by that spell(sniff)). Summoned by that spell creature 19198 casts 33496 -on self after being summoned. Then probably they casts 33497(Pull Towards: (150)) on their creators and that's how that knockback is handled. -If you look closely, players are knocked to random destinations with random angles, means there is no only one spell which handles knockback. -19198 despawns after 800ms after being summoned. -*/ + * Dummy spell 39188 apparently handles actions from EVENT_GROUND_SLAM but maybe something else + * The way Reverberation & Ground Slam timers are handled may be wrong. Both timers are random but sometimes + because of that first Reverberation cast may be skipped while so far I never seen it in movies. He can cast + it twice between Ground Slam sequences or skip one of 2 casts but never skips all. Maybe it's just random + * The way knock back is handled should be re-checked + */ #include "ScriptMgr.h" #include "gruuls_lair.h" -#include "MotionMaster.h" #include "ScriptedCreature.h" #include "SpellInfo.h" #include "SpellScript.h" -enum Yells +enum GruulTexts { SAY_AGGRO = 0, SAY_SLAM = 1, SAY_SHATTER = 2, SAY_SLAY = 3, - SAY_DEATH = 4, - + EMOTE_ROAR = 4, EMOTE_GROW = 5 }; -enum Spells +enum GruulSpells { - SPELL_GROWTH = 36300, + SPELL_HURTFUL_STRIKE_PRIMER = 33812, + SPELL_HURTFUL_STRIKE = 33813, SPELL_CAVE_IN = 36240, - SPELL_GROUND_SLAM = 33525, // AoE Ground Slam applying Ground Slam to everyone with a script effect (most likely the knock back, we can code it to a set knockback) SPELL_REVERBERATION = 36297, - SPELL_SHATTER = 33654, + SPELL_GROWTH = 36300, + SPELL_GROUND_SLAM_DUMMY = 39188, + SPELL_GROUND_SLAM = 33525, + SPELL_LOOK_AROUND = 33965, + SPELL_SUMMON_RANDOM_TRACTOR = 39186, + SPELL_SHATTER = 33654, SPELL_SHATTER_EFFECT = 33671, - SPELL_HURTFUL_STRIKE = 33813, - SPELL_STONED = 33652, // Spell is self cast by target - - SPELL_MAGNETIC_PULL = 28337, - SPELL_KNOCK_BACK = 24199, // Knockback spell until correct implementation is made + SPELL_STONED = 33652 }; -enum Events +enum GruulEvents { - EVENT_GROWTH = 1, + EVENT_HURTFUL_STRIKE = 1, EVENT_CAVE_IN, - EVENT_CAVE_IN_STATIC, - EVENT_GROUND_SLAM, - EVENT_HURTFUL_STRIKE, - EVENT_REVERBERATION + EVENT_REVERBERATION, + EVENT_GROWTH, + EVENT_GROUND_SLAM }; -class boss_gruul : public CreatureScript +enum GruulMisc { - public: - boss_gruul() : CreatureScript("boss_gruul") { } - - struct boss_gruulAI : public BossAI - { - boss_gruulAI(Creature* creature) : BossAI(creature, DATA_GRUUL) - { - Initialize(); - } - - void Initialize() - { - m_uiGrowth_Timer = 30000; - m_uiCaveIn_Timer = 27000; - m_uiCaveIn_StaticTimer = 30000; - m_uiGroundSlamTimer = 35000; - m_bPerformingGroundSlam = false; - m_uiHurtfulStrike_Timer = 8000; - m_uiReverberation_Timer = 60000 + 45000; - } - - uint32 m_uiGrowth_Timer; - uint32 m_uiCaveIn_Timer; - uint32 m_uiCaveIn_StaticTimer; - uint32 m_uiGroundSlamTimer; - uint32 m_uiHurtfulStrike_Timer; - uint32 m_uiReverberation_Timer; - - bool m_bPerformingGroundSlam; - - void Reset() override - { - _Reset(); - Initialize(); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - Talk(SAY_AGGRO); - } - - void KilledUnit(Unit* who) override - { - if (who->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - Talk(SAY_DEATH); - } + SOUND_ID_DEATH = 11363 +}; - void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override - { - //This to emulate effect1 (77) of SPELL_GROUND_SLAM, knock back to any direction - //It's initially wrong, since this will cause fall damage, which is by comments, not intended. - if (spellInfo->Id == SPELL_GROUND_SLAM) - { - if (target->GetTypeId() == TYPEID_PLAYER) - { - switch (urand(0, 1)) - { - case 0: - target->CastSpell(target, SPELL_MAGNETIC_PULL, me->GetGUID()); - break; - - case 1: - target->CastSpell(target, SPELL_KNOCK_BACK, me->GetGUID()); - break; - } - } - } - - //this part should be in the core - if (spellInfo->Id == SPELL_SHATTER) - { - /// @todo use eventmap to kill this stuff - //clear this, if we are still performing - if (m_bPerformingGroundSlam) - { - m_bPerformingGroundSlam = false; - - //and correct movement, if not already - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) - { - if (me->GetVictim()) - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } - } - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - /// @todo: convert this shit to eventmap - - // Growth - // Gruul can cast this spell up to 30 times - if (m_uiGrowth_Timer <= diff) - { - Talk(EMOTE_GROW); - DoCast(me, SPELL_GROWTH); - m_uiGrowth_Timer = 30000; - } - else - m_uiGrowth_Timer -= diff; - - if (m_bPerformingGroundSlam) - { - if (m_uiGroundSlamTimer <= diff) - { - m_uiGroundSlamTimer =120000; - m_uiHurtfulStrike_Timer= 8000; - - if (m_uiReverberation_Timer < 10000) //Give a little time to the players to undo the damage from shatter - m_uiReverberation_Timer += 10000; - - DoCast(me, SPELL_SHATTER); - } - else - m_uiGroundSlamTimer -= diff; - } - else - { - // Hurtful Strike - if (m_uiHurtfulStrike_Timer <= diff) - { - Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1); - - if (target && me->IsWithinMeleeRange(me->GetVictim())) - DoCast(target, SPELL_HURTFUL_STRIKE); - else - DoCastVictim(SPELL_HURTFUL_STRIKE); - - m_uiHurtfulStrike_Timer= 8000; - } - else - m_uiHurtfulStrike_Timer -= diff; - - // Reverberation - if (m_uiReverberation_Timer <= diff) - { - DoCastVictim(SPELL_REVERBERATION, true); - m_uiReverberation_Timer = urand(15000, 25000); - } - else - m_uiReverberation_Timer -= diff; - - // Cave In - if (m_uiCaveIn_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - DoCast(target, SPELL_CAVE_IN); - - if (m_uiCaveIn_StaticTimer >= 4000) - m_uiCaveIn_StaticTimer -= 2000; - - m_uiCaveIn_Timer = m_uiCaveIn_StaticTimer; - } - else - m_uiCaveIn_Timer -= diff; - - // Ground Slam, Gronn Lord's Grasp, Stoned, Shatter - if (m_uiGroundSlamTimer <= diff) - { - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - - m_bPerformingGroundSlam= true; - m_uiGroundSlamTimer = 10000; - - DoCast(me, SPELL_GROUND_SLAM); - } - else - m_uiGroundSlamTimer -= diff; - - DoMeleeAttackIfReady(); - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override +// 19044 - Gruul the Dragonkiller +struct boss_gruul : public BossAI +{ + boss_gruul(Creature* creature) : BossAI(creature, DATA_GRUUL) { } + + void Reset() override + { + _Reset(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + + events.ScheduleEvent(EVENT_HURTFUL_STRIKE, 6s); + events.ScheduleEvent(EVENT_CAVE_IN, 8s); + events.ScheduleEvent(EVENT_REVERBERATION, 105s, 115s); + events.ScheduleEvent(EVENT_GROWTH, 30s); + events.ScheduleEvent(EVENT_GROUND_SLAM, 40s); + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + DoPlaySoundToSet(me, SOUND_ID_DEATH); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - return GetGruulsLairAI<boss_gruulAI>(creature); + case EVENT_HURTFUL_STRIKE: + DoCastSelf(SPELL_HURTFUL_STRIKE_PRIMER); + events.Repeat(8s); + break; + case EVENT_CAVE_IN: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + DoCast(target, SPELL_CAVE_IN); + events.Repeat(8s); + break; + case EVENT_REVERBERATION: + DoCastSelf(SPELL_REVERBERATION); + events.Repeat(30s, 45s); + break; + case EVENT_GROWTH: + DoCastSelf(SPELL_GROWTH); + Talk(EMOTE_GROW); + events.Repeat(30s); + break; + case EVENT_GROUND_SLAM: + DoCastSelf(SPELL_GROUND_SLAM_DUMMY); + Talk(SAY_SLAM); + DoCastSelf(SPELL_GROUND_SLAM); + events.RescheduleEvent(EVENT_HURTFUL_STRIKE, 21s); + events.RescheduleEvent(EVENT_CAVE_IN, 15s); + events.Repeat(70s, 90s); + break; + default: + break; } + } }; -class spell_gruul_shatter : public SpellScriptLoader +// 33812 - Hurtful Strike Primer +class spell_gruul_hurtful_strike_primer : public SpellScript { - public: - spell_gruul_shatter() : SpellScriptLoader("spell_gruul_shatter") { } + PrepareSpellScript(spell_gruul_hurtful_strike_primer); + + void FilterTargets(std::list<WorldObject*>& targets) + { + Unit* caster = GetCaster(); - class spell_gruul_shatter_SpellScript : public SpellScript + // First we get rid of all targets that are not within melee range + targets.remove_if([&](WorldObject* target) { - PrepareSpellScript(spell_gruul_shatter_SpellScript); + if (Unit* unit = target->ToUnit()) + return !unit->IsWithinMeleeRange(caster); - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_STONED, SPELL_SHATTER_EFFECT }); - } + return true; + }); - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (Unit* target = GetHitUnit()) - { - target->RemoveAurasDueToSpell(SPELL_STONED); - target->CastSpell(nullptr, SPELL_SHATTER_EFFECT, true); - } - } - - void Register() override + if (targets.size() >= 2) + { + // Now we sort all targets by threat + targets.sort([&](WorldObject const* left, WorldObject const* right) { - OnEffectHitTarget += SpellEffectFn(spell_gruul_shatter_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + Unit const* leftTarget = ASSERT_NOTNULL(left->ToUnit()); + Unit const* rightTarget = ASSERT_NOTNULL(right->ToUnit()); - SpellScript* GetSpellScript() const override - { - return new spell_gruul_shatter_SpellScript(); + return caster->GetThreatManager().GetThreat(leftTarget) > caster->GetThreatManager().GetThreat(rightTarget); + }); + + // Now we nuke the top threat target so we are only left with the 2nd top victim + targets.pop_front(); + + if (targets.size() >= 2) + targets.resize(1); } + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_HURTFUL_STRIKE); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gruul_hurtful_strike_primer::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_gruul_hurtful_strike_primer::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gruul_shatter_effect : public SpellScriptLoader +// 33525 - Ground Slam +class spell_gruul_ground_slam : public SpellScript { - public: - spell_gruul_shatter_effect() : SpellScriptLoader("spell_gruul_shatter_effect") { } + PrepareSpellScript(spell_gruul_ground_slam); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_LOOK_AROUND, SPELL_SUMMON_RANDOM_TRACTOR }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + // Stuns Gruul for 8 seconds + GetCaster()->CastSpell(GetCaster(), SPELL_LOOK_AROUND); + /* I guess he forces all enemies including pets to summon creature 19198 by spell 39186(9 summoned units and + 9 units in his threat list). Summoned by that spell creature 19198 casts 33496 on self after being summoned. + Then after small delay they casts 33497(Pull Towards: (150)) (guess) on their creators and that's how that + knockback without fall damage is handled. If you look closely, players are knocked to random destinations + with random angles, means there is no only one spell which handles knockback. However the spell to summon + that trigger is TARGET_DEST_CASTER_RADIUS, so creature may be spawned just few yards away from player. In + that case player will be knocked back for a really small distance. It may look weird and wrong. + Script for 19198 is handled in SAI. 19198 probably is used in Cata dungeons or raids too, also at least in + one TBC raid or dungeon since there are more spells to summon that creature. */ + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_RANDOM_TRACTOR); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gruul_ground_slam::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; - class spell_gruul_shatter_effect_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gruul_shatter_effect_SpellScript); +// 33965 - Look Around +class spell_gruul_look_around : public AuraScript +{ + PrepareAuraScript(spell_gruul_look_around); - void CalculateDamage() - { - if (!GetHitUnit()) - return; + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_SHATTER }); + } - float radius = GetEffectInfo(EFFECT_0).CalcRadius(GetCaster()); - if (!radius) - return; + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* creature = GetTarget()->ToCreature()) + { + creature->AI()->Talk(EMOTE_ROAR); + creature->AI()->Talk(SAY_SHATTER); + creature->CastSpell(creature, SPELL_SHATTER); + } + } - float distance = GetCaster()->GetDistance2d(GetHitUnit()); - if (distance > 1.0f) - SetHitDamage(int32(GetHitDamage() * ((radius - distance) / radius))); - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_gruul_look_around::AfterRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + } +}; - void Register() override - { - OnHit += SpellHitFn(spell_gruul_shatter_effect_SpellScript::CalculateDamage); - } - }; +// 33654 - Shatter +class spell_gruul_shatter : public SpellScript +{ + PrepareSpellScript(spell_gruul_shatter); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_STONED, SPELL_SHATTER_EFFECT }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->RemoveAurasDueToSpell(SPELL_STONED); + target->CastSpell(nullptr, SPELL_SHATTER_EFFECT, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gruul_shatter::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; - SpellScript* GetSpellScript() const override - { - return new spell_gruul_shatter_effect_SpellScript(); - } +// 33671 - Shatter +class spell_gruul_shatter_effect : public SpellScript +{ + PrepareSpellScript(spell_gruul_shatter_effect); + + void CalculateDamage() + { + if (!GetHitUnit()) + return; + + float radius = GetEffectInfo(EFFECT_0).CalcRadius(GetCaster()); + if (!radius) + return; + + float distance = GetCaster()->GetDistance2d(GetHitUnit()); + if (distance > 1.0f) + SetHitDamage(int32(GetHitDamage() * ((radius - distance) / radius))); + } + + void Register() override + { + OnHit += SpellHitFn(spell_gruul_shatter_effect::CalculateDamage); + } }; void AddSC_boss_gruul() { - new boss_gruul(); - new spell_gruul_shatter(); - new spell_gruul_shatter_effect(); + RegisterGruulsLairCreatureAI(boss_gruul); + RegisterSpellScript(spell_gruul_hurtful_strike_primer); + RegisterSpellScript(spell_gruul_ground_slam); + RegisterSpellScript(spell_gruul_look_around); + RegisterSpellScript(spell_gruul_shatter); + RegisterSpellScript(spell_gruul_shatter_effect); } diff --git a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp index 78c3e190c50..dc1a29955ba 100644 --- a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp +++ b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp @@ -15,41 +15,40 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_High_King_Maulgar -SD%Complete: 90 -SDComment: Correct timers, after whirlwind melee attack bug, prayer of healing -SDCategory: Gruul's Lair -EndScriptData */ +/* + * Timers requires update + * The door should open after Maulgar's death, not after all NPCs are dead + */ #include "ScriptMgr.h" #include "gruuls_lair.h" #include "InstanceScript.h" -#include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" -enum HighKingMaulgar +enum MaulgarTexts { SAY_AGGRO = 0, SAY_ENRAGE = 1, SAY_OGRE_DEATH = 2, SAY_SLAY = 3, - SAY_DEATH = 4, + SAY_DEATH = 4 +}; +enum MaulgarSpells +{ // High King Maulgar SPELL_ARCING_SMASH = 39144, SPELL_MIGHTY_BLOW = 33230, SPELL_WHIRLWIND = 33238, - SPELL_BERSERKER_C = 26561, + SPELL_BERSERKER_CHARGE = 26561, SPELL_ROAR = 16508, SPELL_FLURRY = 33232, - SPELL_DUAL_WIELD = 29651, // Olm the Summoner SPELL_DARK_DECAY = 33129, SPELL_DEATH_COIL = 33130, - SPELL_SUMMON_WFH = 33131, + SPELL_SUMMON_WILD_FELHUNTER = 33131, // Kiggler the Craed SPELL_GREATER_POLYMORPH = 33173, @@ -65,523 +64,359 @@ enum HighKingMaulgar // Krosh Firehand SPELL_GREATER_FIREBALL = 33051, SPELL_SPELLSHIELD = 33054, - SPELL_BLAST_WAVE = 33061, + SPELL_BLAST_WAVE = 33061 +}; +enum MaulgarMisc +{ ACTION_ADD_DEATH = 1 }; -class boss_high_king_maulgar : public CreatureScript +// 18831 - High King Maulgar +struct boss_high_king_maulgar : public BossAI { -public: - boss_high_king_maulgar() : CreatureScript("boss_high_king_maulgar") { } + boss_high_king_maulgar(Creature* creature) : BossAI(creature, DATA_MAULGAR), _enraged(false) { } - struct boss_high_king_maulgarAI : public ScriptedAI + void Reset() override { - boss_high_king_maulgarAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - ArcingSmash_Timer = 10000; - MightyBlow_Timer = 40000; - Whirlwind_Timer = 30000; - Charging_Timer = 0; - Roar_Timer = 0; - - Phase2 = false; - } - - InstanceScript* instance; - - uint32 ArcingSmash_Timer; - uint32 MightyBlow_Timer; - uint32 Whirlwind_Timer; - uint32 Charging_Timer; - uint32 Roar_Timer; - - bool Phase2; - - void Reset() override - { - Initialize(); - - DoCast(me, SPELL_DUAL_WIELD, false); - - instance->SetBossState(DATA_MAULGAR, NOT_STARTED); - } + _Reset(); + _enraged = false; + } - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_SLAY); - } + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); - void JustDied(Unit* /*killer*/) override + scheduler.Schedule(10s, [this](TaskContext task) { - Talk(SAY_DEATH); - - instance->SetBossState(DATA_MAULGAR, DONE); - } + DoCastVictim(SPELL_ARCING_SMASH); + task.Repeat(10s); + }); - void DoAction(int32 actionId) override + scheduler.Schedule(30s, [this](TaskContext task) { - if (actionId == ACTION_ADD_DEATH) - Talk(SAY_OGRE_DEATH); - } + DoCastSelf(SPELL_WHIRLWIND); + task.Repeat(55s); + }); - void JustEngagedWith(Unit* /*who*/) override + scheduler.Schedule(40s, [this](TaskContext task) { - DoZoneInCombat(); - instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - Talk(SAY_AGGRO); - } + DoCastVictim(SPELL_MIGHTY_BLOW); + task.Repeat(30s, 40s); + }); + } - void UpdateAI(uint32 diff) override + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override + { + if (!_enraged && me->HealthBelowPctDamaged(50, damage)) { - if (!UpdateVictim()) - return; - - //ArcingSmash_Timer - if (ArcingSmash_Timer <= diff) - { - DoCastVictim(SPELL_ARCING_SMASH); - ArcingSmash_Timer = 10000; - } else ArcingSmash_Timer -= diff; - - //Whirlwind_Timer - if (Whirlwind_Timer <= diff) - { - DoCastVictim(SPELL_WHIRLWIND); - Whirlwind_Timer = 55000; - } else Whirlwind_Timer -= diff; - - //MightyBlow_Timer - if (MightyBlow_Timer <= diff) + scheduler.Schedule(0s, [this](TaskContext /*task*/) { - DoCastVictim(SPELL_MIGHTY_BLOW); - MightyBlow_Timer = 30000 + rand32() % 10000; - } else MightyBlow_Timer -= diff; - - //Entering Phase 2 - if (!Phase2 && HealthBelowPct(50)) - { - Phase2 = true; + _enraged = true; Talk(SAY_ENRAGE); + DoCastSelf(SPELL_FLURRY); + SetEquipmentSlots(false, EQUIP_UNEQUIP); + }); - DoCast(me, SPELL_DUAL_WIELD, true); - me->SetVirtualItem(0, 0); - me->SetVirtualItem(1, 0); - } + scheduler.Schedule(0s, 5s, [this](TaskContext task) + { + DoCastSelf(SPELL_ROAR); + task.Repeat(40s, 50s); + }); - if (Phase2) + scheduler.Schedule(10s, 15s, [this](TaskContext task) { - //Charging_Timer - if (Charging_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - AttackStart(target); - DoCast(target, SPELL_BERSERKER_C); - } - Charging_Timer = 20000; - } else Charging_Timer -= diff; - - //Intimidating Roar - if (Roar_Timer <= diff) - { - DoCast(me, SPELL_ROAR); - Roar_Timer = 40000 + (rand32() % 10000); - } else Roar_Timer -= diff; - } - - DoMeleeAttackIfReady(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + DoCast(target, SPELL_BERSERKER_CHARGE); + task.Repeat(20s); + }); } - }; + } - CreatureAI* GetAI(Creature* creature) const override + void DoAction(int32 actionId) override { - return GetGruulsLairAI<boss_high_king_maulgarAI>(creature); - } -}; + if (!me->IsAlive()) + return; -class boss_olm_the_summoner : public CreatureScript -{ -public: - boss_olm_the_summoner() : CreatureScript("boss_olm_the_summoner") { } + if (actionId == ACTION_ADD_DEATH) + Talk(SAY_OGRE_DEATH); + } - struct boss_olm_the_summonerAI : public ScriptedAI + void KilledUnit(Unit* /*victim*/) override { - boss_olm_the_summonerAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + Talk(SAY_SLAY); + } - void Initialize() - { - DarkDecay_Timer = 10000; - Summon_Timer = 15000; - DeathCoil_Timer = 20000; - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } - uint32 DarkDecay_Timer; - uint32 Summon_Timer; - uint32 DeathCoil_Timer; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - InstanceScript* instance; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void Reset() override - { - Initialize(); + scheduler.Update(diff); - instance->SetBossState(DATA_MAULGAR, NOT_STARTED); - } + DoMeleeAttackIfReady(); + } - void AttackStart(Unit* who) override - { - if (!who) - return; +private: + bool _enraged; +}; - if (me->Attack(who, true)) - { - AddThreat(who, 0.0f); - me->SetInCombatWith(who); - who->SetInCombatWith(me); +// 18834 - Olm the Summoner +struct boss_olm_the_summoner : public ScriptedAI +{ + boss_olm_the_summoner(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - me->GetMotionMaster()->MoveChase(who, 30.0f); - } - } + void Reset() override + { + _scheduler.CancelAll(); + } - void JustEngagedWith(Unit* /*who*/) override + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.Schedule(10s, [this](TaskContext task) { - DoZoneInCombat(); - instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - } + DoCastVictim(SPELL_DARK_DECAY); + task.Repeat(20s); + }); - void JustDied(Unit* /*killer*/) override + _scheduler.Schedule(0s, 10s, [this](TaskContext task) { - if (Creature* maulgar = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MAULGAR))) - maulgar->AI()->DoAction(ACTION_ADD_DEATH); + DoCastSelf(SPELL_SUMMON_WILD_FELHUNTER); + task.Repeat(50s, 60s); + }); - instance->SetBossState(DATA_MAULGAR, DONE); - } - - void UpdateAI(uint32 diff) override + _scheduler.Schedule(20s, [this](TaskContext task) { - if (!UpdateVictim()) - return; + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + DoCast(target, SPELL_DEATH_COIL); + task.Repeat(20s); + }); + } - //DarkDecay_Timer - if (DarkDecay_Timer <= diff) - { - DoCastVictim(SPELL_DARK_DECAY); - DarkDecay_Timer = 20000; - } else DarkDecay_Timer -= diff; + void JustDied(Unit* /*killer*/) override + { + if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR))) + maulgar->AI()->DoAction(ACTION_ADD_DEATH); - //Summon_Timer - if (Summon_Timer <= diff) - { - DoCast(me, SPELL_SUMMON_WFH); - Summon_Timer = 30000; - } else Summon_Timer -= diff; + _instance->SetBossState(DATA_MAULGAR, DONE); + } - //DeathCoil Timer /need correct timer - if (DeathCoil_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - DoCast(target, SPELL_DEATH_COIL); - DeathCoil_Timer = 20000; - } else DeathCoil_Timer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - DoMeleeAttackIfReady(); - } - }; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - CreatureAI* GetAI(Creature* creature) const override - { - return GetGruulsLairAI<boss_olm_the_summonerAI>(creature); + _scheduler.Update(diff); + + DoMeleeAttackIfReady(); } + +private: + TaskScheduler _scheduler; + InstanceScript* _instance; }; -//Kiggler The Crazed AI -class boss_kiggler_the_crazed : public CreatureScript +// 18835 - Kiggler the Crazed +struct boss_kiggler_the_crazed : public ScriptedAI { -public: - boss_kiggler_the_crazed() : CreatureScript("boss_kiggler_the_crazed") { } + boss_kiggler_the_crazed(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct boss_kiggler_the_crazedAI : public ScriptedAI + void Reset() override { - boss_kiggler_the_crazedAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - GreaterPolymorph_Timer = 5000; - LightningBolt_Timer = 10000; - ArcaneShock_Timer = 20000; - ArcaneExplosion_Timer = 30000; - } - - uint32 GreaterPolymorph_Timer; - uint32 LightningBolt_Timer; - uint32 ArcaneShock_Timer; - uint32 ArcaneExplosion_Timer; + _scheduler.CancelAll(); + } - InstanceScript* instance; + void AttackStart(Unit* who) override + { + ScriptedAI::AttackStartCaster(who, 40.0f); + } - void Reset() override + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.Schedule(5s, [this](TaskContext task) { - Initialize(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + DoCast(target, SPELL_GREATER_POLYMORPH); + task.Repeat(15s, 20s); + }); - instance->SetBossState(DATA_MAULGAR, NOT_STARTED); - } - - void JustEngagedWith(Unit* /*who*/) override + _scheduler.Schedule(0s, [this](TaskContext task) { - DoZoneInCombat(); - instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - } + DoCastVictim(SPELL_LIGHTNING_BOLT); + task.Repeat(2s); + }); - void JustDied(Unit* /*killer*/) override + _scheduler.Schedule(20s, [this](TaskContext task) { - if (Creature* maulgar = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MAULGAR))) - maulgar->AI()->DoAction(ACTION_ADD_DEATH); - - instance->SetBossState(DATA_MAULGAR, DONE); - } + DoCastVictim(SPELL_ARCANE_SHOCK); + task.Repeat(20s); + }); - void UpdateAI(uint32 diff) override + _scheduler.Schedule(30s, [this](TaskContext task) { - if (!UpdateVictim()) - return; - - //GreaterPolymorph_Timer - if (GreaterPolymorph_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - DoCast(target, SPELL_GREATER_POLYMORPH); + DoCastSelf(SPELL_ARCANE_EXPLOSION); + task.Repeat(30s); + }); + } - GreaterPolymorph_Timer = urand(15000, 20000); - } else GreaterPolymorph_Timer -= diff; + void JustDied(Unit* /*killer*/) override + { + if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR))) + maulgar->AI()->DoAction(ACTION_ADD_DEATH); - //LightningBolt_Timer - if (LightningBolt_Timer <= diff) - { - DoCastVictim(SPELL_LIGHTNING_BOLT); - LightningBolt_Timer = 15000; - } else LightningBolt_Timer -= diff; + _instance->SetBossState(DATA_MAULGAR, DONE); + } - //ArcaneShock_Timer - if (ArcaneShock_Timer <= diff) - { - DoCastVictim(SPELL_ARCANE_SHOCK); - ArcaneShock_Timer = 20000; - } else ArcaneShock_Timer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - //ArcaneExplosion_Timer - if (ArcaneExplosion_Timer <= diff) - { - DoCastVictim(SPELL_ARCANE_EXPLOSION); - ArcaneExplosion_Timer = 30000; - } else ArcaneExplosion_Timer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - DoMeleeAttackIfReady(); - } - }; + _scheduler.Update(diff); - CreatureAI* GetAI(Creature* creature) const override - { - return GetGruulsLairAI<boss_kiggler_the_crazedAI>(creature); + DoMeleeAttackIfReady(); } + +private: + TaskScheduler _scheduler; + InstanceScript* _instance; }; -class boss_blindeye_the_seer : public CreatureScript +// 18836 - Blindeye the Seer +struct boss_blindeye_the_seer : public ScriptedAI { -public: - boss_blindeye_the_seer() : CreatureScript("boss_blindeye_the_seer") { } + boss_blindeye_the_seer(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct boss_blindeye_the_seerAI : public ScriptedAI + void Reset() override { - boss_blindeye_the_seerAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + _scheduler.CancelAll(); + } - void Initialize() + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.Schedule(5s, [this](TaskContext task) { - GreaterPowerWordShield_Timer = 5000; - Heal_Timer = urand(25000, 40000); - PrayerofHealing_Timer = urand(45000, 55000); - } - - uint32 GreaterPowerWordShield_Timer; - uint32 Heal_Timer; - uint32 PrayerofHealing_Timer; - - InstanceScript* instance; + DoCastSelf(SPELL_GREATER_PW_SHIELD); + task.Repeat(40s); + }); - void Reset() override + _scheduler.Schedule(25s, 40s, [this](TaskContext task) { - Initialize(); + DoCastSelf(SPELL_HEAL); + task.Repeat(25s, 40s); + }); - instance->SetBossState(DATA_MAULGAR, NOT_STARTED); - } - - void JustEngagedWith(Unit* /*who*/) override + _scheduler.Schedule(45s, 55s, [this](TaskContext task) { - DoZoneInCombat(); - instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - } - - void JustDied(Unit* /*killer*/) override - { - if (Creature* maulgar = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MAULGAR))) - maulgar->AI()->DoAction(ACTION_ADD_DEATH); + DoCastSelf(SPELL_PRAYER_OH); + task.Repeat(35s, 50s); + }); + } - instance->SetBossState(DATA_MAULGAR, DONE); - } + void JustDied(Unit* /*killer*/) override + { + if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR))) + maulgar->AI()->DoAction(ACTION_ADD_DEATH); - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + _instance->SetBossState(DATA_MAULGAR, DONE); + } - //GreaterPowerWordShield_Timer - if (GreaterPowerWordShield_Timer <= diff) - { - DoCast(me, SPELL_GREATER_PW_SHIELD); - GreaterPowerWordShield_Timer = 40000; - } else GreaterPowerWordShield_Timer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - //Heal_Timer - if (Heal_Timer <= diff) - { - DoCast(me, SPELL_HEAL); - Heal_Timer = urand(15000, 40000); - } else Heal_Timer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - //PrayerofHealing_Timer - if (PrayerofHealing_Timer <= diff) - { - DoCast(me, SPELL_PRAYER_OH); - PrayerofHealing_Timer = urand(35000, 50000); - } else PrayerofHealing_Timer -= diff; + _scheduler.Update(diff); - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetGruulsLairAI<boss_blindeye_the_seerAI>(creature); + DoMeleeAttackIfReady(); } + +private: + TaskScheduler _scheduler; + InstanceScript* _instance; }; -class boss_krosh_firehand : public CreatureScript +// 18832 - Krosh Firehand +struct boss_krosh_firehand : public ScriptedAI { -public: - boss_krosh_firehand() : CreatureScript("boss_krosh_firehand") { } + boss_krosh_firehand(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct boss_krosh_firehandAI : public ScriptedAI + void Reset() override { - boss_krosh_firehandAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - GreaterFireball_Timer = 1000; - SpellShield_Timer = 5000; - BlastWave_Timer = 20000; - } - - uint32 GreaterFireball_Timer; - uint32 SpellShield_Timer; - uint32 BlastWave_Timer; - - InstanceScript* instance; + _scheduler.CancelAll(); + } - void Reset() override + void JustEngagedWith(Unit* /*who*/) override + { + _scheduler.Schedule(0s, 5s, [this](TaskContext task) { - Initialize(); - - instance->SetBossState(DATA_MAULGAR, NOT_STARTED); - } + DoCastVictim(SPELL_GREATER_FIREBALL); + task.Repeat(2s, 5s); + }); - void JustEngagedWith(Unit* /*who*/) override + _scheduler.Schedule(0s, [this](TaskContext task) { - DoZoneInCombat(); - instance->SetBossState(DATA_MAULGAR, IN_PROGRESS); - } + DoCastSelf(SPELL_SPELLSHIELD); + task.Repeat(30s); + }); - void JustDied(Unit* /*killer*/) override + _scheduler.Schedule(10s, 20s, [this](TaskContext task) { - if (Creature* maulgar = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MAULGAR))) - maulgar->AI()->DoAction(ACTION_ADD_DEATH); + DoCastSelf(SPELL_BLAST_WAVE); + task.Repeat(5s, 15s); + }); + } - instance->SetBossState(DATA_MAULGAR, DONE); - } + void JustDied(Unit* /*killer*/) override + { + if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR))) + maulgar->AI()->DoAction(ACTION_ADD_DEATH); - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + _instance->SetBossState(DATA_MAULGAR, DONE); + } - //GreaterFireball_Timer - if (GreaterFireball_Timer < diff || me->IsWithinDist(me->GetVictim(), 30)) - { - DoCastVictim(SPELL_GREATER_FIREBALL); - GreaterFireball_Timer = 2000; - } else GreaterFireball_Timer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - //SpellShield_Timer - if (SpellShield_Timer <= diff) - { - me->InterruptNonMeleeSpells(false); - DoCastVictim(SPELL_SPELLSHIELD); - SpellShield_Timer = 30000; - } else SpellShield_Timer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - //BlastWave_Timer - if (BlastWave_Timer <= diff) - { - std::vector<Unit*> target_list; - for (auto* ref : me->GetThreatManager().GetUnsortedThreatList()) - { - Unit* target = ref->GetVictim(); - if (target && target->IsWithinDist(me, 15, false)) // 15 yard radius minimum - target_list.push_back(target); - } - Unit* target = nullptr; - if (!target_list.empty()) - target = *(target_list.begin() + rand32() % target_list.size()); - - me->InterruptNonMeleeSpells(false); - DoCast(target, SPELL_BLAST_WAVE); - BlastWave_Timer = 60000; - } else BlastWave_Timer -= diff; - } - }; + _scheduler.Update(diff); - CreatureAI* GetAI(Creature* creature) const override - { - return GetGruulsLairAI<boss_krosh_firehandAI>(creature); + DoMeleeAttackIfReady(); } + +private: + TaskScheduler _scheduler; + InstanceScript* _instance; }; void AddSC_boss_high_king_maulgar() { - new boss_high_king_maulgar(); - new boss_kiggler_the_crazed(); - new boss_blindeye_the_seer(); - new boss_olm_the_summoner(); - new boss_krosh_firehand(); + RegisterGruulsLairCreatureAI(boss_high_king_maulgar); + RegisterGruulsLairCreatureAI(boss_kiggler_the_crazed); + RegisterGruulsLairCreatureAI(boss_blindeye_the_seer); + RegisterGruulsLairCreatureAI(boss_olm_the_summoner); + RegisterGruulsLairCreatureAI(boss_krosh_firehand); } diff --git a/src/server/scripts/Outland/GruulsLair/gruuls_lair.h b/src/server/scripts/Outland/GruulsLair/gruuls_lair.h index 79ef246f518..dfe95fb9db2 100644 --- a/src/server/scripts/Outland/GruulsLair/gruuls_lair.h +++ b/src/server/scripts/Outland/GruulsLair/gruuls_lair.h @@ -53,4 +53,6 @@ inline AI* GetGruulsLairAI(T* obj) return GetInstanceAI<AI>(obj, GLScriptName); } +#define RegisterGruulsLairCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetGruulsLairAI) + #endif // GRUULS_LAIR_H_ |