Scripts/Gruul's Lair: Update scripts (#30913)

(cherry picked from commit a23262869685e66d68be233b9020d611ffdb5db0)
This commit is contained in:
offl
2026-06-23 22:48:26 +02:00
committed by Shauren
parent 6743e8018a
commit f51383a6b8
4 changed files with 570 additions and 761 deletions
@@ -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)");
@@ -16,331 +16,286 @@
*/
/*
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_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_SHATTER_EFFECT = 33671,
SPELL_HURTFUL_STRIKE_PRIMER = 33812,
SPELL_HURTFUL_STRIKE = 33813,
SPELL_STONED = 33652, // Spell is self cast by target
SPELL_CAVE_IN = 36240,
SPELL_REVERBERATION = 36297,
SPELL_GROWTH = 36300,
SPELL_MAGNETIC_PULL = 28337,
SPELL_KNOCK_BACK = 24199, // Knockback spell until correct implementation is made
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_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);
}
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, CastSpellExtraArgs(TRIGGERED_FULL_MASK)
.SetOriginalCaster(me->GetGUID()));
break;
case 1:
target->CastSpell(target, SPELL_KNOCK_BACK, CastSpellExtraArgs(TRIGGERED_FULL_MASK)
.SetOriginalCaster(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;
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetGruulsLairAI<boss_gruulAI>(creature);
}
SOUND_ID_DEATH = 11363
};
class spell_gruul_shatter : public SpellScriptLoader
// 19044 - Gruul the Dragonkiller
struct boss_gruul : public BossAI
{
public:
spell_gruul_shatter() : SpellScriptLoader("spell_gruul_shatter") { }
boss_gruul(Creature* creature) : BossAI(creature, DATA_GRUUL) { }
class spell_gruul_shatter_SpellScript : public SpellScript
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)
{
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_STONED, SPELL_SHATTER_EFFECT });
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
if (Unit* target = GetHitUnit())
{
target->RemoveAurasDueToSpell(SPELL_STONED);
target->CastSpell(nullptr, SPELL_SHATTER_EFFECT, true);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_gruul_shatter_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_gruul_shatter_SpellScript();
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_effect : public SpellScriptLoader
// 33812 - Hurtful Strike Primer
class spell_gruul_hurtful_strike_primer : public SpellScript
{
public:
spell_gruul_shatter_effect() : SpellScriptLoader("spell_gruul_shatter_effect") { }
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
class spell_gruul_shatter_effect_SpellScript : public SpellScript
// First we get rid of all targets that are not within melee range
targets.remove_if([&](WorldObject* target)
{
bool Validate(SpellInfo const* spellInfo) override
{
return ValidateSpellEffect({ { spellInfo->Id, EFFECT_0 } });
}
if (Unit* unit = target->ToUnit())
return !unit->IsWithinMeleeRange(caster);
void CalculateDamage()
{
if (!GetHitUnit())
return;
return true;
});
float radius = GetEffectInfo(EFFECT_0).CalcRadius(GetCaster(), SpellTargetIndex::TargetB).Max;
if (radius <= 0.0f)
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_SpellScript::CalculateDamage);
}
};
SpellScript* GetSpellScript() const override
if (targets.size() >= 2)
{
return new spell_gruul_shatter_effect_SpellScript();
// Now we sort all targets by threat
targets.sort([&](WorldObject const* left, WorldObject const* right)
{
Unit const* leftTarget = ASSERT_NOTNULL(left->ToUnit());
Unit const* rightTarget = ASSERT_NOTNULL(right->ToUnit());
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);
}
};
// 33525 - Ground Slam
class spell_gruul_ground_slam : public SpellScript
{
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);
}
};
// 33965 - Look Around
class spell_gruul_look_around : public AuraScript
{
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_SHATTER });
}
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);
}
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_gruul_look_around::AfterRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
}
};
// 33654 - Shatter
class spell_gruul_shatter : public SpellScript
{
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);
}
};
// 33671 - Shatter
class spell_gruul_shatter_effect : public SpellScript
{
bool Validate(SpellInfo const* spellInfo) override
{
return ValidateSpellEffect({ { spellInfo->Id, EFFECT_0 } });
}
void CalculateDamage()
{
if (!GetHitUnit())
return;
float radius = GetEffectInfo(EFFECT_0).CalcRadius(GetCaster(), SpellTargetIndex::TargetB).Max;
if (radius <= 0.0f)
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);
}
@@ -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,515 +64,349 @@ 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)
_Reset();
_enraged = false;
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
scheduler.Schedule(10s, [this](TaskContext& task)
{
Initialize();
instance = creature->GetInstanceScript();
}
DoCastVictim(SPELL_ARCING_SMASH);
task.Repeat(10s);
});
void Initialize()
scheduler.Schedule(30s, [this](TaskContext& task)
{
ArcingSmash_Timer = 10000;
MightyBlow_Timer = 40000;
Whirlwind_Timer = 30000;
Charging_Timer = 0;
Roar_Timer = 0;
DoCastSelf(SPELL_WHIRLWIND);
task.Repeat(55s);
});
Phase2 = false;
}
InstanceScript* instance;
uint32 ArcingSmash_Timer;
uint32 MightyBlow_Timer;
uint32 Whirlwind_Timer;
uint32 Charging_Timer;
uint32 Roar_Timer;
bool Phase2;
void Reset() override
scheduler.Schedule(40s, [this](TaskContext& task)
{
Initialize();
DoCastVictim(SPELL_MIGHTY_BLOW);
task.Repeat(30s, 40s);
});
}
DoCast(me, SPELL_DUAL_WIELD, false);
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
void KilledUnit(Unit* /*victim*/) override
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (!_enraged && me->HealthBelowPctDamaged(50, damage))
{
Talk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
instance->SetBossState(DATA_MAULGAR, DONE);
}
void DoAction(int32 actionId) override
{
if (actionId == ACTION_ADD_DEATH)
Talk(SAY_OGRE_DEATH);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
instance->SetBossState(DATA_MAULGAR, IN_PROGRESS);
Talk(SAY_AGGRO);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
//ArcingSmash_Timer
if (ArcingSmash_Timer <= diff)
scheduler.Schedule(0s, [this](TaskContext const& /*task*/)
{
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)
{
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);
}
if (Phase2)
scheduler.Schedule(0s, 5s, [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;
DoCastSelf(SPELL_ROAR);
task.Repeat(40s, 50s);
});
//Intimidating Roar
if (Roar_Timer <= diff)
{
DoCast(me, SPELL_ROAR);
Roar_Timer = 40000 + (rand32() % 10000);
} else Roar_Timer -= diff;
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetGruulsLairAI<boss_high_king_maulgarAI>(creature);
}
};
class boss_olm_the_summoner : public CreatureScript
{
public:
boss_olm_the_summoner() : CreatureScript("boss_olm_the_summoner") { }
struct boss_olm_the_summonerAI : public ScriptedAI
{
boss_olm_the_summonerAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
instance = creature->GetInstanceScript();
}
void Initialize()
{
DarkDecay_Timer = 10000;
Summon_Timer = 15000;
DeathCoil_Timer = 20000;
}
uint32 DarkDecay_Timer;
uint32 Summon_Timer;
uint32 DeathCoil_Timer;
InstanceScript* instance;
void Reset() override
{
Initialize();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
void AttackStart(Unit* who) override
{
if (!who)
return;
if (me->Attack(who, true))
{
AddThreat(who, 0.0f);
me->SetInCombatWith(who);
who->SetInCombatWith(me);
me->GetMotionMaster()->MoveChase(who, 30.0f);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
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);
instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
//DarkDecay_Timer
if (DarkDecay_Timer <= diff)
{
DoCastVictim(SPELL_DARK_DECAY);
DarkDecay_Timer = 20000;
} else DarkDecay_Timer -= diff;
//Summon_Timer
if (Summon_Timer <= diff)
{
DoCast(me, SPELL_SUMMON_WFH);
Summon_Timer = 30000;
} else Summon_Timer -= diff;
//DeathCoil Timer /need correct timer
if (DeathCoil_Timer <= diff)
scheduler.Schedule(10s, 15s, [this](TaskContext& task)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
DoCast(target, SPELL_DEATH_COIL);
DeathCoil_Timer = 20000;
} else DeathCoil_Timer -= diff;
DoCast(target, SPELL_BERSERKER_CHARGE);
task.Repeat(20s);
});
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetGruulsLairAI<boss_olm_the_summonerAI>(creature);
}
void DoAction(int32 actionId) override
{
if (!me->IsAlive())
return;
if (actionId == ACTION_ADD_DEATH)
Talk(SAY_OGRE_DEATH);
}
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
scheduler.Update(diff);
}
private:
bool _enraged;
};
//Kiggler The Crazed AI
class boss_kiggler_the_crazed : public CreatureScript
// 18834 - Olm the Summoner
struct boss_olm_the_summoner : public ScriptedAI
{
public:
boss_kiggler_the_crazed() : CreatureScript("boss_kiggler_the_crazed") { }
boss_olm_the_summoner(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;
InstanceScript* instance;
void Reset() override
{
Initialize();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
void JustEngagedWith(Unit* /*who*/) override
{
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);
instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
//GreaterPolymorph_Timer
if (GreaterPolymorph_Timer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
DoCast(target, SPELL_GREATER_POLYMORPH);
GreaterPolymorph_Timer = urand(15000, 20000);
} else GreaterPolymorph_Timer -= diff;
//LightningBolt_Timer
if (LightningBolt_Timer <= diff)
{
DoCastVictim(SPELL_LIGHTNING_BOLT);
LightningBolt_Timer = 15000;
} else LightningBolt_Timer -= diff;
//ArcaneShock_Timer
if (ArcaneShock_Timer <= diff)
{
DoCastVictim(SPELL_ARCANE_SHOCK);
ArcaneShock_Timer = 20000;
} else ArcaneShock_Timer -= diff;
//ArcaneExplosion_Timer
if (ArcaneExplosion_Timer <= diff)
{
DoCastVictim(SPELL_ARCANE_EXPLOSION);
ArcaneExplosion_Timer = 30000;
} else ArcaneExplosion_Timer -= diff;
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetGruulsLairAI<boss_kiggler_the_crazedAI>(creature);
_scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(10s, [this](TaskContext& task)
{
DoCastVictim(SPELL_DARK_DECAY);
task.Repeat(20s);
});
_scheduler.Schedule(0s, 10s, [this](TaskContext& task)
{
DoCastSelf(SPELL_SUMMON_WILD_FELHUNTER);
task.Repeat(50s, 60s);
});
_scheduler.Schedule(20s, [this](TaskContext& task)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
DoCast(target, SPELL_DEATH_COIL);
task.Repeat(20s);
});
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR)))
maulgar->AI()->DoAction(ACTION_ADD_DEATH);
_instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
_scheduler.Update(diff);
}
private:
TaskScheduler _scheduler;
InstanceScript* _instance;
};
class boss_blindeye_the_seer : public CreatureScript
// 18835 - Kiggler the Crazed
struct boss_kiggler_the_crazed : public ScriptedAI
{
public:
boss_blindeye_the_seer() : CreatureScript("boss_blindeye_the_seer") { }
boss_kiggler_the_crazed(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();
}
void Initialize()
{
GreaterPowerWordShield_Timer = 5000;
Heal_Timer = urand(25000, 40000);
PrayerofHealing_Timer = urand(45000, 55000);
}
uint32 GreaterPowerWordShield_Timer;
uint32 Heal_Timer;
uint32 PrayerofHealing_Timer;
InstanceScript* instance;
void Reset() override
{
Initialize();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
void JustEngagedWith(Unit* /*who*/) override
{
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);
instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
//GreaterPowerWordShield_Timer
if (GreaterPowerWordShield_Timer <= diff)
{
DoCast(me, SPELL_GREATER_PW_SHIELD);
GreaterPowerWordShield_Timer = 40000;
} else GreaterPowerWordShield_Timer -= diff;
//Heal_Timer
if (Heal_Timer <= diff)
{
DoCast(me, SPELL_HEAL);
Heal_Timer = urand(15000, 40000);
} else Heal_Timer -= diff;
//PrayerofHealing_Timer
if (PrayerofHealing_Timer <= diff)
{
DoCast(me, SPELL_PRAYER_OH);
PrayerofHealing_Timer = urand(35000, 50000);
} else PrayerofHealing_Timer -= diff;
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetGruulsLairAI<boss_blindeye_the_seerAI>(creature);
_scheduler.CancelAll();
}
void AttackStart(Unit* who) override
{
ScriptedAI::AttackStartCaster(who, 40.0f);
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(5s, [this](TaskContext& task)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
DoCast(target, SPELL_GREATER_POLYMORPH);
task.Repeat(15s, 20s);
});
_scheduler.Schedule(0s, [this](TaskContext& task)
{
DoCastVictim(SPELL_LIGHTNING_BOLT);
task.Repeat(2s);
});
_scheduler.Schedule(20s, [this](TaskContext& task)
{
DoCastVictim(SPELL_ARCANE_SHOCK);
task.Repeat(20s);
});
_scheduler.Schedule(30s, [this](TaskContext& task)
{
DoCastSelf(SPELL_ARCANE_EXPLOSION);
task.Repeat(30s);
});
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR)))
maulgar->AI()->DoAction(ACTION_ADD_DEATH);
_instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
_scheduler.Update(diff);
}
private:
TaskScheduler _scheduler;
InstanceScript* _instance;
};
class boss_krosh_firehand : public CreatureScript
// 18836 - Blindeye the Seer
struct boss_blindeye_the_seer : public ScriptedAI
{
public:
boss_krosh_firehand() : CreatureScript("boss_krosh_firehand") { }
boss_blindeye_the_seer(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;
void Reset() override
{
Initialize();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
void JustEngagedWith(Unit* /*who*/) override
{
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);
instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
//GreaterFireball_Timer
if (GreaterFireball_Timer < diff || me->IsWithinDist(me->GetVictim(), 30))
{
DoCastVictim(SPELL_GREATER_FIREBALL);
GreaterFireball_Timer = 2000;
} else GreaterFireball_Timer -= diff;
//SpellShield_Timer
if (SpellShield_Timer <= diff)
{
me->InterruptNonMeleeSpells(false);
DoCastVictim(SPELL_SPELLSHIELD);
SpellShield_Timer = 30000;
} else SpellShield_Timer -= diff;
//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;
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetGruulsLairAI<boss_krosh_firehandAI>(creature);
_scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(5s, [this](TaskContext& task)
{
DoCastSelf(SPELL_GREATER_PW_SHIELD);
task.Repeat(40s);
});
_scheduler.Schedule(25s, 40s, [this](TaskContext& task)
{
DoCastSelf(SPELL_HEAL);
task.Repeat(25s, 40s);
});
_scheduler.Schedule(45s, 55s, [this](TaskContext& task)
{
DoCastSelf(SPELL_PRAYER_OH);
task.Repeat(35s, 50s);
});
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR)))
maulgar->AI()->DoAction(ACTION_ADD_DEATH);
_instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
_scheduler.Update(diff);
}
private:
TaskScheduler _scheduler;
InstanceScript* _instance;
};
// 18832 - Krosh Firehand
struct boss_krosh_firehand : public ScriptedAI
{
boss_krosh_firehand(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }
void Reset() override
{
_scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(0s, 5s, [this](TaskContext& task)
{
DoCastVictim(SPELL_GREATER_FIREBALL);
task.Repeat(2s, 5s);
});
_scheduler.Schedule(0s, [this](TaskContext& task)
{
DoCastSelf(SPELL_SPELLSHIELD);
task.Repeat(30s);
});
_scheduler.Schedule(10s, 20s, [this](TaskContext& task)
{
DoCastSelf(SPELL_BLAST_WAVE);
task.Repeat(5s, 15s);
});
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* maulgar = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MAULGAR)))
maulgar->AI()->DoAction(ACTION_ADD_DEATH);
_instance->SetBossState(DATA_MAULGAR, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
_scheduler.Update(diff);
}
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);
}
@@ -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_