diff --git a/data/sql/updates/pending_db_world/rev_1651544130927167200.sql b/data/sql/updates/pending_db_world/rev_1651544130927167200.sql
new file mode 100644
index 000000000..4e19d94f6
--- /dev/null
+++ b/data/sql/updates/pending_db_world/rev_1651544130927167200.sql
@@ -0,0 +1,8 @@
+--
+UPDATE `creature_template` SET `speed_run` = 1.14286, `speed_walk` = 1.32 WHERE `entry` = 14517;
+
+DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_batrider_bomb';
+INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
+(23970, 'spell_batrider_bomb');
+
+UPDATE `gameobject_template` SET `Data2` = 6 WHERE `entry` = 180125;
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp
index b6075f954..ebb69ebee 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp
@@ -15,8 +15,11 @@
* with this program. If not, see .
*/
+#include "GameObjectAI.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellScript.h"
+#include "TaskScheduler.h"
#include "zulgurub.h"
enum Says
@@ -49,7 +52,8 @@ enum Spells
SPELL_GREATER_HEAL = 23954,
// Batriders Spell
- SPELL_BOMB = 40332 // Wrong ID but Magmadars bomb is not working...
+ SPELL_THROW_LIQUID_FIRE = 23970,
+ SPELL_SUMMON_LIQUID_FIRE = 23971
};
enum BatIds
@@ -93,214 +97,227 @@ Position const SpawnBat[6] =
{ -12293.6220f, -1380.2640f, 144.8304f, 5.483f }
};
-class boss_jeklik : public CreatureScript
+struct boss_jeklik : public BossAI
{
-public:
- boss_jeklik() : CreatureScript("boss_jeklik") { }
+ boss_jeklik(Creature* creature) : BossAI(creature, DATA_JEKLIK) { }
- struct boss_jeklikAI : public BossAI
+ void Reset() override
{
- boss_jeklikAI(Creature* creature) : BossAI(creature, DATA_JEKLIK) { }
+ DoCastSelf(SPELL_GREEN_CHANNELING);
+ me->SetHover(false);
+ me->SetDisableGravity(false);
+ _Reset();
+ }
- void Reset() override
+ void JustDied(Unit* /*killer*/) override
+ {
+ _JustDied();
+ Talk(SAY_DEATH);
+ }
+
+ void EnterEvadeMode(EvadeReason why) override
+ {
+ const Position homePos = me->GetHomePosition();
+ me->NearTeleportTo(homePos.GetPositionX(), homePos.GetPositionY(), homePos.GetPositionZ(), homePos.GetOrientation());
+ BossAI::EnterEvadeMode(why);
+ }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _EnterCombat();
+ Talk(SAY_AGGRO);
+ me->RemoveAurasDueToSpell(SPELL_GREEN_CHANNELING);
+ me->SetHover(true);
+ me->SetDisableGravity(true);
+ me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING);
+ DoCastSelf(SPELL_BAT_FORM);
+ events.SetPhase(PHASE_ONE);
+
+ events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(10000, 20000), PHASE_ONE);
+ events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(5000, 15000), PHASE_ONE);
+ events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(5000, 15000), PHASE_ONE);
+ events.ScheduleEvent(EVENT_SONIC_BURST, urand(5000, 15000), PHASE_ONE);
+ events.ScheduleEvent(EVENT_SWOOP, 20000, PHASE_ONE);
+ events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE);
+ }
+
+ void DamageTaken(Unit* /*who*/, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
+ {
+ if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(50))
{
- DoCastSelf(SPELL_GREEN_CHANNELING);
- _Reset();
+ me->RemoveAurasDueToSpell(SPELL_BAT_FORM);
+ me->SetHover(false);
+ me->SetDisableGravity(false);
+ DoResetThreat();
+ events.SetPhase(PHASE_TWO);
+ events.CancelEventGroup(PHASE_ONE);
+
+ events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(5000, 15000), PHASE_TWO);
+ events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(10000, 15000), PHASE_TWO);
+ events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(25000, 35000), PHASE_TWO);
+ events.ScheduleEvent(EVENT_MIND_FLAY, urand(10000, 30000), PHASE_TWO);
+ events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO);
+ events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, 10000, PHASE_TWO);
+
+ return;
}
+ }
- void JustDied(Unit* /*killer*/) override
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
{
- _JustDied();
- Talk(SAY_DEATH);
- }
-
- void EnterCombat(Unit* /*who*/) override
- {
- _EnterCombat();
- Talk(SAY_AGGRO);
- me->RemoveAurasDueToSpell(SPELL_GREEN_CHANNELING);
- me->SetDisableGravity(true);
- DoCastSelf(SPELL_BAT_FORM);
- events.SetPhase(PHASE_ONE);
-
- events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(10000, 20000), PHASE_ONE);
- events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(5000, 15000), PHASE_ONE);
- events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(5000, 15000), PHASE_ONE);
- events.ScheduleEvent(EVENT_SONIC_BURST, urand(5000, 15000), PHASE_ONE);
- events.ScheduleEvent(EVENT_SWOOP, 20000, PHASE_ONE);
- events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE);
- }
-
- void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
- {
- if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(50))
+ switch (eventId)
{
- me->RemoveAurasDueToSpell(SPELL_BAT_FORM);
- me->SetDisableGravity(false);
- DoResetThreat();
- events.SetPhase(PHASE_TWO);
-
- events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(5000, 15000), PHASE_TWO);
- events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(10000, 15000), PHASE_TWO);
- events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(25000, 35000), PHASE_TWO);
- events.ScheduleEvent(EVENT_MIND_FLAY, urand(10000, 30000), PHASE_TWO);
- events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO);
- events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, 10000, PHASE_TWO);
-
- return;
- }
- }
-
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
-
- events.Update(diff);
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- // Phase one
- case EVENT_CHARGE_JEKLIK:
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- {
- DoCast(target, SPELL_CHARGE);
- AttackStart(target);
- }
- events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(15000, 30000), PHASE_ONE);
- break;
- case EVENT_PIERCE_ARMOR:
- DoCastVictim(SPELL_PIERCE_ARMOR);
- events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(20000, 30000), PHASE_ONE);
- break;
- case EVENT_BLOOD_LEECH:
- DoCastVictim(SPELL_BLOOD_LEECH);
- events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(10000, 20000), PHASE_ONE);
- break;
- case EVENT_SONIC_BURST:
- DoCastVictim(SPELL_SONIC_BURST);
- events.ScheduleEvent(EVENT_SONIC_BURST, urand(20000, 30000), PHASE_ONE);
- break;
- case EVENT_SWOOP:
- DoCastVictim(SPELL_SWOOP);
- events.ScheduleEvent(EVENT_SWOOP, urand(20000, 30000), PHASE_ONE);
- break;
- case EVENT_SPAWN_BATS:
- Talk(EMOTE_SUMMON_BATS);
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- for (uint8 i = 0; i < 6; ++i)
- if (Creature* bat = me->SummonCreature(NPC_BLOODSEEKER_BAT, SpawnBat[i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000))
- bat->AI()->AttackStart(target);
- events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE);
- break;
+ // Phase one
+ case EVENT_CHARGE_JEKLIK:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
+ {
+ DoCast(target, SPELL_CHARGE);
+ AttackStart(target);
+ }
+ events.ScheduleEvent(EVENT_CHARGE_JEKLIK, urand(15000, 30000), PHASE_ONE);
+ break;
+ case EVENT_PIERCE_ARMOR:
+ DoCastVictim(SPELL_PIERCE_ARMOR);
+ events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(20000, 30000), PHASE_ONE);
+ break;
+ case EVENT_BLOOD_LEECH:
+ DoCastVictim(SPELL_BLOOD_LEECH);
+ events.ScheduleEvent(EVENT_BLOOD_LEECH, urand(10000, 20000), PHASE_ONE);
+ break;
+ case EVENT_SONIC_BURST:
+ DoCastVictim(SPELL_SONIC_BURST);
+ events.ScheduleEvent(EVENT_SONIC_BURST, urand(20000, 30000), PHASE_ONE);
+ break;
+ case EVENT_SWOOP:
+ DoCastVictim(SPELL_SWOOP);
+ events.ScheduleEvent(EVENT_SWOOP, urand(20000, 30000), PHASE_ONE);
+ break;
+ case EVENT_SPAWN_BATS:
+ Talk(EMOTE_SUMMON_BATS);
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
+ for (uint8 i = 0; i < 6; ++i)
+ if (Creature* bat = me->SummonCreature(NPC_BLOODSEEKER_BAT, SpawnBat[i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000))
+ bat->AI()->AttackStart(target);
+ events.ScheduleEvent(EVENT_SPAWN_BATS, 30000, PHASE_ONE);
+ break;
//Phase two
- case EVENT_CURSE_OF_BLOOD:
- DoCastSelf(SPELL_CURSE_OF_BLOOD);
- events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(25000, 30000), PHASE_TWO);
- break;
- case EVENT_PSYCHIC_SCREAM:
- DoCastVictim(SPELL_PSYCHIC_SCREAM);
- events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(35000, 45000), PHASE_TWO);
- break;
- case EVENT_SHADOW_WORD_PAIN:
- DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN, 0, true);
- events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(12000, 18000), PHASE_TWO);
- break;
- case EVENT_MIND_FLAY:
- DoCastVictim(SPELL_MIND_FLAY);
- events.ScheduleEvent(EVENT_MIND_FLAY, urand(20000, 40000), PHASE_TWO);
- break;
- case EVENT_GREATER_HEAL:
- Talk(EMOTE_GREAT_HEAL);
- me->InterruptNonMeleeSpells(false);
- DoCastSelf(SPELL_GREATER_HEAL);
- events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO);
- break;
- case EVENT_SPAWN_FLYING_BATS:
- Talk(SAY_CALL_RIDERS);
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
- if (Creature* flyingBat = me->SummonCreature(NPC_FRENZIED_BAT, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000))
- flyingBat->AI()->AttackStart(target);
- events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, urand(10000, 15000), PHASE_TWO);
- break;
- default:
- break;
- }
+ case EVENT_CURSE_OF_BLOOD:
+ DoCastSelf(SPELL_CURSE_OF_BLOOD);
+ events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(25000, 30000), PHASE_TWO);
+ break;
+ case EVENT_PSYCHIC_SCREAM:
+ DoCastVictim(SPELL_PSYCHIC_SCREAM);
+ events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, urand(35000, 45000), PHASE_TWO);
+ break;
+ case EVENT_SHADOW_WORD_PAIN:
+ DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN, 0, true);
+ events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(12000, 18000), PHASE_TWO);
+ break;
+ case EVENT_MIND_FLAY:
+ DoCastVictim(SPELL_MIND_FLAY);
+ events.ScheduleEvent(EVENT_MIND_FLAY, urand(20000, 40000), PHASE_TWO);
+ break;
+ case EVENT_GREATER_HEAL:
+ Talk(EMOTE_GREAT_HEAL);
+ me->InterruptNonMeleeSpells(false);
+ DoCastSelf(SPELL_GREATER_HEAL);
+ events.ScheduleEvent(EVENT_GREATER_HEAL, 25000, PHASE_TWO);
+ break;
+ case EVENT_SPAWN_FLYING_BATS:
+ Talk(SAY_CALL_RIDERS);
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
+ if (Creature* flyingBat = me->SummonCreature(NPC_FRENZIED_BAT, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000))
+ flyingBat->AI()->DoZoneInCombat();
+ events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, urand(10000, 15000), PHASE_TWO);
+ break;
+ default:
+ break;
}
-
- DoMeleeAttackIfReady();
}
- };
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetZulGurubAI(creature);
+ DoMeleeAttackIfReady();
}
};
// Flying Bat
-class npc_batrider : public CreatureScript
+struct npc_batrider : public ScriptedAI
{
-public:
- npc_batrider() : CreatureScript("npc_batrider") { }
-
- struct npc_batriderAI : public ScriptedAI
+ npc_batrider(Creature* creature) : ScriptedAI(creature)
{
- npc_batriderAI(Creature* creature) : ScriptedAI(creature) { }
-
- uint32 Bomb_Timer;
-
- void Reset() override
+ _scheduler.SetValidator([this]
{
- Bomb_Timer = 2000;
- me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->AddUnitState(UNIT_STATE_ROOT);
- }
+ return !me->HasUnitState(UNIT_STATE_CASTING);
+ });
+ }
- void EnterCombat(Unit* /*who*/) override { }
-
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
-
- if (Bomb_Timer <= diff)
- {
- std::list targets;
- SelectTargetList(targets, 1, SelectTargetMethod::Random, 500.0f, true);
- if (!targets.empty())
- {
- if (targets.size() > 1)
- {
- targets.resize(1);
- }
- }
-
- for (std::list::iterator itr = targets.begin(); itr != targets.end(); ++itr)
- {
- me->CastSpell((*itr), SPELL_BOMB);
- }
-
- Bomb_Timer = 7000;
- }
- else
- Bomb_Timer -= diff;
- }
- };
-
- CreatureAI* GetAI(Creature* creature) const override
+ void Reset() override
{
- return GetZulGurubAI(creature);
+ _scheduler.CancelAll();
+ me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->SetHover(true);
+ me->SetDisableGravity(true);
+ me->AddUnitState(UNIT_STATE_ROOT);
+ }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _scheduler.Schedule(2s, [this](TaskContext context)
+ {
+ DoCastRandomTarget(SPELL_THROW_LIQUID_FIRE);
+ context.Repeat(7s);
+ });
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ _scheduler.Update(diff);
+ }
+
+private:
+ TaskScheduler _scheduler;
+};
+
+class spell_batrider_bomb : public SpellScript
+{
+ PrepareSpellScript(spell_batrider_bomb);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_SUMMON_LIQUID_FIRE });
+ }
+
+ void HandleScriptEffect(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ if (Unit* target = GetHitUnit())
+ {
+ target->CastSpell(target, SPELL_SUMMON_LIQUID_FIRE, true);
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_batrider_bomb::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
void AddSC_boss_jeklik()
{
- new boss_jeklik();
- new npc_batrider();
+ RegisterCreatureAI(boss_jeklik);
+ RegisterCreatureAI(npc_batrider);
+ RegisterSpellScript(spell_batrider_bomb);
}