mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-16 18:40:28 +00:00
(Scripts/AI) ZG20: Jeklik (Bat Boss) - Implement Bat Riders, other fixes and improvements (#17573)
--------- Co-authored-by: AG <43139552+AGandrup@users.noreply.github.com>
This commit is contained in:
@@ -16,75 +16,70 @@
|
||||
*/
|
||||
|
||||
#include "GameObjectAI.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SmartAI.h"
|
||||
#include "SpellScript.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "WaypointMgr.h"
|
||||
#include "zulgurub.h"
|
||||
|
||||
enum Says
|
||||
{
|
||||
SAY_AGGRO = 0,
|
||||
SAY_CALL_RIDERS = 1,
|
||||
SAY_DEATH = 2,
|
||||
EMOTE_SUMMON_BATS = 3,
|
||||
EMOTE_GREAT_HEAL = 4
|
||||
// Jeklik
|
||||
SAY_AGGRO = 0,
|
||||
SAY_CALL_RIDERS = 1,
|
||||
SAY_DEATH = 2,
|
||||
EMOTE_SUMMON_BATS = 3,
|
||||
EMOTE_GREAT_HEAL = 4,
|
||||
|
||||
// Bat Rider
|
||||
EMOTE_BATRIDER_LOW_HEALTH = 0
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
// Intro
|
||||
SPELL_GREEN_CHANNELING = 13540,
|
||||
SPELL_BAT_FORM = 23966,
|
||||
SPELL_GREEN_CHANNELING = 13540,
|
||||
SPELL_BAT_FORM = 23966,
|
||||
|
||||
// Phase one
|
||||
SPELL_PIERCE_ARMOR = 12097,
|
||||
SPELL_BLOOD_LEECH = 22644,
|
||||
SPELL_CHARGE = 22911,
|
||||
SPELL_SONIC_BURST = 23918,
|
||||
SPELL_SWOOP = 23919,
|
||||
SPELL_PIERCE_ARMOR = 12097,
|
||||
SPELL_BLOOD_LEECH = 22644,
|
||||
SPELL_CHARGE = 22911,
|
||||
SPELL_SONIC_BURST = 23918,
|
||||
SPELL_SWOOP = 23919,
|
||||
|
||||
// Phase two
|
||||
SPELL_CURSE_OF_BLOOD = 16098,
|
||||
SPELL_PSYCHIC_SCREAM = 22884,
|
||||
SPELL_SHADOW_WORD_PAIN = 23952,
|
||||
SPELL_MIND_FLAY = 23953,
|
||||
SPELL_GREATER_HEAL = 23954,
|
||||
SPELL_CURSE_OF_BLOOD = 16098,
|
||||
SPELL_PSYCHIC_SCREAM = 22884,
|
||||
SPELL_SHADOW_WORD_PAIN = 23952,
|
||||
SPELL_MIND_FLAY = 23953,
|
||||
SPELL_GREATER_HEAL = 23954,
|
||||
|
||||
// Batriders Spell
|
||||
SPELL_THROW_LIQUID_FIRE = 23970,
|
||||
SPELL_SUMMON_LIQUID_FIRE = 23971
|
||||
// Bat Rider (Boss)
|
||||
SPELL_BATRIDER_THROW_LIQUID_FIRE = 23970,
|
||||
SPELL_BATRIDER_SUMMON_LIQUID_FIRE = 23971,
|
||||
|
||||
// Bat Rider (Trash)
|
||||
SPELL_BATRIDER_DEMO_SHOUT = 23511,
|
||||
SPELL_BATRIDER_BATTLE_COMMAND = 5115,
|
||||
SPELL_BATRIDER_INFECTED_BITE = 16128,
|
||||
SPELL_BATRIDER_PASSIVE_THRASH = 8876,
|
||||
SPELL_BATRIDER_UNSTABLE_CONCOCTION = 24024
|
||||
};
|
||||
|
||||
enum BatIds
|
||||
{
|
||||
NPC_BLOODSEEKER_BAT = 11368,
|
||||
NPC_FRENZIED_BAT = 14965
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
// Phase one
|
||||
EVENT_CHARGE_JEKLIK = 1,
|
||||
EVENT_PIERCE_ARMOR,
|
||||
EVENT_BLOOD_LEECH,
|
||||
EVENT_SONIC_BURST,
|
||||
EVENT_SWOOP,
|
||||
EVENT_SPAWN_BATS,
|
||||
|
||||
// Phase two
|
||||
EVENT_CURSE_OF_BLOOD,
|
||||
EVENT_PSYCHIC_SCREAM,
|
||||
EVENT_SHADOW_WORD_PAIN,
|
||||
EVENT_MIND_FLAY,
|
||||
EVENT_GREATER_HEAL,
|
||||
EVENT_SPAWN_FLYING_BATS
|
||||
NPC_BLOODSEEKER_BAT = 11368,
|
||||
NPC_BATRIDER = 14750
|
||||
};
|
||||
|
||||
enum Phase
|
||||
{
|
||||
PHASE_ONE = 1,
|
||||
PHASE_TWO = 2
|
||||
PHASE_ONE = 1,
|
||||
PHASE_TWO = 2
|
||||
};
|
||||
|
||||
Position const SpawnBat[6] =
|
||||
@@ -97,213 +92,360 @@ Position const SpawnBat[6] =
|
||||
{ -12293.6220f, -1380.2640f, 144.8304f, 5.483f }
|
||||
};
|
||||
|
||||
enum Misc
|
||||
Position const SpawnBatRider = { -12301.689, -1371.2921, 145.09244 };
|
||||
Position const JeklikCaveHomePosition = { -12291.9f, -1380.08f, 144.902f, 2.28638f };
|
||||
|
||||
enum PathID
|
||||
{
|
||||
PATH_JEKLIK_INTRO = 145170
|
||||
PATH_JEKLIK_INTRO = 145170,
|
||||
PATH_BATRIDER_LOOP = 147500
|
||||
};
|
||||
|
||||
Position const homePosition = { -12291.9f, -1380.08f, 144.902f, 2.28638f };
|
||||
enum BatRiderMode
|
||||
{
|
||||
BATRIDER_MODE_TRASH = 1,
|
||||
BATRIDER_MODE_BOSS
|
||||
};
|
||||
|
||||
// High Priestess Jeklik (14517)
|
||||
struct boss_jeklik : public BossAI
|
||||
{
|
||||
// Bat Riders (14750) counter
|
||||
uint8 batRidersCount = 0;
|
||||
|
||||
boss_jeklik(Creature* creature) : BossAI(creature, DATA_JEKLIK) { }
|
||||
|
||||
void InitializeAI() override
|
||||
{
|
||||
BossAI::InitializeAI();
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
DoCastSelf(SPELL_GREEN_CHANNELING);
|
||||
me->SetHover(false);
|
||||
BossAI::Reset();
|
||||
|
||||
// allow the scheduler to interrupt casting
|
||||
scheduler.ClearValidator();
|
||||
|
||||
// start invisible so we can setup the green channeling effect
|
||||
me->SetVisible(false);
|
||||
|
||||
me->SetHomePosition(JeklikCaveHomePosition);
|
||||
|
||||
me->SetDisableGravity(false);
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
_Reset();
|
||||
SetCombatMovement(false);
|
||||
BossAI::SetCombatMovement(false);
|
||||
batRidersCount = 0;
|
||||
|
||||
// once the path for her to come down to the ground starts, it appears to be near-impossible to stop it
|
||||
// instead, simply wait the 3 seconds it takes the path to complete, then teleport her home
|
||||
scheduler.Schedule(3s, [this](TaskContext)
|
||||
{
|
||||
float x, y, z, o;
|
||||
JeklikCaveHomePosition.GetPosition(x, y, z, o);
|
||||
|
||||
me->NearTeleportTo(x, y, z, o);
|
||||
});
|
||||
|
||||
scheduler.Schedule(4s, [this](TaskContext)
|
||||
{
|
||||
DoCastSelf(SPELL_GREEN_CHANNELING, true);
|
||||
|
||||
});
|
||||
|
||||
// restore visibility and unlock root
|
||||
scheduler.Schedule(5s, [this](TaskContext)
|
||||
{
|
||||
me->SetVisible(true);
|
||||
me->ClearUnitState(UNIT_STATE_ROOT);
|
||||
});
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
_JustDied();
|
||||
Talk(SAY_DEATH);
|
||||
}
|
||||
BossAI::JustEngagedWith(who);
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->SetHomePosition(homePosition);
|
||||
me->NearTeleportTo(homePosition.GetPositionX(), homePosition.GetPositionY(), homePosition.GetPositionZ(), homePosition.GetOrientation());
|
||||
BossAI::EnterEvadeMode(why);
|
||||
}
|
||||
scheduler.SetValidator([this]
|
||||
{
|
||||
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||
});
|
||||
|
||||
scheduler.CancelAll();
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
Talk(SAY_AGGRO);
|
||||
DoZoneInCombat();
|
||||
|
||||
me->RemoveAurasDueToSpell(SPELL_GREEN_CHANNELING);
|
||||
me->SetHover(true);
|
||||
me->SetDisableGravity(true);
|
||||
DoCastSelf(SPELL_BAT_FORM, true);
|
||||
|
||||
me->GetMotionMaster()->MovePath(PATH_JEKLIK_INTRO, false);
|
||||
}
|
||||
|
||||
void PathEndReached(uint32 /*pathId*/) override
|
||||
void PathEndReached(uint32 pathId) override
|
||||
{
|
||||
me->SetHover(false);
|
||||
BossAI::PathEndReached(pathId);
|
||||
|
||||
me->SetDisableGravity(false);
|
||||
_JustEngagedWith();
|
||||
SetCombatMovement(true);
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
events.SetPhase(PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_CHARGE_JEKLIK, 10s, 20s, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_PIERCE_ARMOR, 5s, 15s, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_BLOOD_LEECH, 5s, 15s, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_SONIC_BURST, 5s, 15s, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_SWOOP, 20s, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_SPAWN_BATS, 30s, PHASE_ONE);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* /*who*/, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (events.IsInPhase(PHASE_ONE) && !HealthAbovePct(50))
|
||||
scheduler.CancelAll();
|
||||
|
||||
//
|
||||
// Phase 1
|
||||
//
|
||||
scheduler.Schedule(10s, 20s, PHASE_ONE, [this](TaskContext context)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, -8.0f, false, false))
|
||||
{
|
||||
DoCast(target, SPELL_CHARGE);
|
||||
AttackStart(target);
|
||||
}
|
||||
context.Repeat(15s, 30s);
|
||||
}).Schedule(5s, 15s, PHASE_ONE, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_PIERCE_ARMOR);
|
||||
context.Repeat(20s, 30s);
|
||||
}).Schedule(5s, 15s, PHASE_ONE, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_BLOOD_LEECH);
|
||||
context.Repeat(10s, 20s);
|
||||
}).Schedule(5s, 15s, PHASE_ONE, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_SONIC_BURST);
|
||||
context.Repeat(20s, 30s);
|
||||
}).Schedule(20s, PHASE_ONE, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_SWOOP);
|
||||
context.Repeat(20s, 30s);
|
||||
}).Schedule(30s, PHASE_ONE, [this](TaskContext context)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
context.Repeat(30s);
|
||||
});
|
||||
|
||||
//
|
||||
// Phase 2 (@ 50% health)
|
||||
//
|
||||
ScheduleHealthCheckEvent(50, [&]
|
||||
{
|
||||
me->RemoveAurasDueToSpell(SPELL_BAT_FORM);
|
||||
DoResetThreatList();
|
||||
events.SetPhase(PHASE_TWO);
|
||||
events.CancelEventGroup(PHASE_ONE);
|
||||
|
||||
events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, 5s, 15s, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 10s, 15s, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, 25s, 35s, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_MIND_FLAY, 10s, 30s, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_GREATER_HEAL, 25s, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_SPAWN_FLYING_BATS, 10s, PHASE_TWO);
|
||||
scheduler.CancelGroup(PHASE_ONE);
|
||||
|
||||
return;
|
||||
scheduler.Schedule(5s, 15s, PHASE_TWO, [this](TaskContext context)
|
||||
{
|
||||
DoCastSelf(SPELL_CURSE_OF_BLOOD);
|
||||
context.Repeat(25s, 30s);
|
||||
}).Schedule(25s, 35s, PHASE_TWO, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_PSYCHIC_SCREAM);
|
||||
context.Repeat(35s, 45s);
|
||||
}).Schedule(10s, 15s, PHASE_TWO, [this](TaskContext context)
|
||||
{
|
||||
DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN, 0, true);
|
||||
context.Repeat(12s, 18s);
|
||||
}).Schedule(10s, 30s, PHASE_TWO, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_MIND_FLAY);
|
||||
context.Repeat(20s, 40s);
|
||||
}).Schedule(25s, PHASE_TWO, [this](TaskContext context)
|
||||
{
|
||||
Talk(EMOTE_GREAT_HEAL);
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
DoCastSelf(SPELL_GREATER_HEAL);
|
||||
context.Repeat(25s);
|
||||
}).Schedule(10s, PHASE_TWO, [this](TaskContext context)
|
||||
{
|
||||
if (me->GetThreatMgr().GetThreatListSize())
|
||||
{
|
||||
// summon up to 2 bat riders
|
||||
if (batRidersCount < 2)
|
||||
{
|
||||
Talk(SAY_CALL_RIDERS);
|
||||
// only if the bat rider was successfully created
|
||||
if (me->SummonCreature(NPC_BATRIDER, SpawnBatRider, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT))
|
||||
{
|
||||
batRidersCount++;
|
||||
}
|
||||
if (batRidersCount == 1)
|
||||
{
|
||||
context.Repeat(10s, 15s);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
BossAI::EnterEvadeMode(why);
|
||||
|
||||
if (why != EvadeReason::EVADE_REASON_NO_PATH)
|
||||
{
|
||||
// make invisible to hide wonky-looking movement
|
||||
me->SetVisible(false);
|
||||
|
||||
// cancel any pending moves and stop moving
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->AddUnitState(UNIT_STATE_ROOT);
|
||||
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
// ensures that the scheduler gets updated even out of combat
|
||||
scheduler.Update(diff);
|
||||
|
||||
events.Update(diff);
|
||||
BossAI::UpdateAI(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, 15s, 30s, PHASE_ONE);
|
||||
break;
|
||||
case EVENT_PIERCE_ARMOR:
|
||||
DoCastVictim(SPELL_PIERCE_ARMOR);
|
||||
events.ScheduleEvent(EVENT_PIERCE_ARMOR, 20s, 30s, PHASE_ONE);
|
||||
break;
|
||||
case EVENT_BLOOD_LEECH:
|
||||
DoCastVictim(SPELL_BLOOD_LEECH);
|
||||
events.ScheduleEvent(EVENT_BLOOD_LEECH, 10s, 20s, PHASE_ONE);
|
||||
break;
|
||||
case EVENT_SONIC_BURST:
|
||||
DoCastVictim(SPELL_SONIC_BURST);
|
||||
events.ScheduleEvent(EVENT_SONIC_BURST, 20s, 30s, PHASE_ONE);
|
||||
break;
|
||||
case EVENT_SWOOP:
|
||||
DoCastVictim(SPELL_SWOOP);
|
||||
events.ScheduleEvent(EVENT_SWOOP, 20s, 30s, 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, 30s, PHASE_ONE);
|
||||
break;
|
||||
//Phase two
|
||||
case EVENT_CURSE_OF_BLOOD:
|
||||
DoCastSelf(SPELL_CURSE_OF_BLOOD);
|
||||
events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, 25s, 30s, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_PSYCHIC_SCREAM:
|
||||
DoCastVictim(SPELL_PSYCHIC_SCREAM);
|
||||
events.ScheduleEvent(EVENT_PSYCHIC_SCREAM, 35s, 45s, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_SHADOW_WORD_PAIN:
|
||||
DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN, 0, true);
|
||||
events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 12s, 18s, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_MIND_FLAY:
|
||||
DoCastVictim(SPELL_MIND_FLAY);
|
||||
events.ScheduleEvent(EVENT_MIND_FLAY, 20s, 40s, PHASE_TWO);
|
||||
break;
|
||||
case EVENT_GREATER_HEAL:
|
||||
Talk(EMOTE_GREAT_HEAL);
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
DoCastSelf(SPELL_GREATER_HEAL);
|
||||
events.ScheduleEvent(EVENT_GREATER_HEAL, 25s, 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, 10s, 15s, PHASE_TWO);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
Talk(SAY_DEATH);
|
||||
}
|
||||
};
|
||||
|
||||
// Flying Bat
|
||||
struct npc_batrider : public ScriptedAI
|
||||
// Gurubashi Bat Rider (14750) - trash and boss summon are same creature ID
|
||||
struct npc_batrider : public CreatureAI
|
||||
{
|
||||
npc_batrider(Creature* creature) : ScriptedAI(creature)
|
||||
BatRiderMode _mode; // the version of this creature (trash or boss)
|
||||
TaskScheduler _scheduler;
|
||||
|
||||
npc_batrider(Creature* creature) : CreatureAI(creature)
|
||||
{
|
||||
_scheduler.SetValidator([this]
|
||||
// if this is a summon of Jeklik, it is in boss mode
|
||||
if
|
||||
(
|
||||
me->GetEntry() == NPC_BATRIDER &&
|
||||
me->IsSummon() &&
|
||||
me->ToTempSummon() &&
|
||||
me->ToTempSummon()->GetSummoner() &&
|
||||
me->ToTempSummon()->GetSummoner()->GetEntry() == NPC_PRIESTESS_JEKLIK
|
||||
)
|
||||
{
|
||||
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||
});
|
||||
_mode = BATRIDER_MODE_BOSS;
|
||||
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
me->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_PC);
|
||||
me->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC);
|
||||
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
|
||||
me->SetSpeed(MOVE_WALK, 5.0f, true);
|
||||
|
||||
me->SetCanFly(true);
|
||||
me->GetMotionMaster()->MoveSplinePath(PATH_BATRIDER_LOOP);
|
||||
}
|
||||
else
|
||||
{
|
||||
_mode = BATRIDER_MODE_TRASH;
|
||||
|
||||
me->SetReactState(REACT_DEFENSIVE);
|
||||
|
||||
// don't interrupt casting
|
||||
_scheduler.SetValidator([this]
|
||||
{
|
||||
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
CreatureAI::Reset();
|
||||
|
||||
_scheduler.CancelAll();
|
||||
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||
me->SetHover(true);
|
||||
me->SetDisableGravity(true);
|
||||
me->AddUnitState(UNIT_STATE_ROOT);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*who*/) override
|
||||
{
|
||||
_scheduler.Schedule(2s, [this](TaskContext context)
|
||||
if (_mode == BATRIDER_MODE_BOSS)
|
||||
{
|
||||
DoCastRandomTarget(SPELL_THROW_LIQUID_FIRE);
|
||||
context.Repeat(7s);
|
||||
});
|
||||
me->GetMotionMaster()->Clear();
|
||||
}
|
||||
else if (_mode == BATRIDER_MODE_TRASH)
|
||||
{
|
||||
me->CastSpell(me, SPELL_BATRIDER_PASSIVE_THRASH);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
CreatureAI::JustEngagedWith(who);
|
||||
|
||||
_scheduler.Update(diff);
|
||||
if (_mode == BATRIDER_MODE_BOSS)
|
||||
{
|
||||
_scheduler.Schedule(2s, [this](TaskContext context)
|
||||
{
|
||||
DoCastRandomTarget(SPELL_BATRIDER_THROW_LIQUID_FIRE);
|
||||
context.Repeat(8s);
|
||||
});
|
||||
}
|
||||
else if (_mode == BATRIDER_MODE_TRASH)
|
||||
{
|
||||
_scheduler.Schedule(1s, [this](TaskContext /*context*/)
|
||||
{
|
||||
DoCastSelf(SPELL_BATRIDER_DEMO_SHOUT);
|
||||
}).Schedule(8s, [this](TaskContext context)
|
||||
{
|
||||
DoCastSelf(SPELL_BATRIDER_BATTLE_COMMAND);
|
||||
context.Repeat(25s);
|
||||
}).Schedule(6500ms, [this](TaskContext context)
|
||||
{
|
||||
DoCastVictim(SPELL_BATRIDER_INFECTED_BITE);
|
||||
context.Repeat(8s);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (_mode == BATRIDER_MODE_TRASH)
|
||||
{
|
||||
if (me->HealthBelowPctDamaged(30, damage))
|
||||
{
|
||||
_scheduler.CancelAll();
|
||||
DoCastSelf(SPELL_BATRIDER_UNSTABLE_CONCOCTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 /*diff*/) override
|
||||
{
|
||||
if (_mode == BATRIDER_MODE_BOSS)
|
||||
{
|
||||
if (!me->isMoving())
|
||||
{
|
||||
me->SetCanFly(true);
|
||||
me->GetMotionMaster()->MoveSplinePath(PATH_BATRIDER_LOOP);
|
||||
}
|
||||
}
|
||||
else if (_mode == BATRIDER_MODE_TRASH)
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
_scheduler.Update();
|
||||
}
|
||||
};
|
||||
|
||||
class spell_batrider_bomb : public SpellScript
|
||||
@@ -312,7 +454,7 @@ class spell_batrider_bomb : public SpellScript
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_SUMMON_LIQUID_FIRE });
|
||||
return ValidateSpellInfo({ SPELL_BATRIDER_SUMMON_LIQUID_FIRE });
|
||||
}
|
||||
|
||||
void HandleScriptEffect(SpellEffIndex effIndex)
|
||||
@@ -321,7 +463,7 @@ class spell_batrider_bomb : public SpellScript
|
||||
|
||||
if (Unit* target = GetHitUnit())
|
||||
{
|
||||
target->CastSpell(target, SPELL_SUMMON_LIQUID_FIRE, true);
|
||||
target->CastSpell(target, SPELL_BATRIDER_SUMMON_LIQUID_FIRE, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ enum CreatureIds
|
||||
NPC_ZULIAN_PROWLER = 15101, // Arlokk Event
|
||||
NPC_ZEALOT_LORKHAN = 11347,
|
||||
NPC_ZEALOT_ZATH = 11348,
|
||||
NPC_PRIESTESS_JEKLIK = 14517,
|
||||
NPC_PRIESTESS_MARLI = 14510,
|
||||
NPC_SPAWN_OF_MARLI = 15041,
|
||||
NPC_HIGH_PRIEST_THEKAL = 14509,
|
||||
|
||||
Reference in New Issue
Block a user