mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-19 03:45:43 +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:
@@ -0,0 +1,72 @@
|
||||
-- Gurubashi Bat Rider (14750) waypoints
|
||||
-- Flying loop
|
||||
DELETE FROM `waypoint_data` WHERE `id`=147500;
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
(147500, 0, -12288.004, -1391.931, 145.4913, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 1, -12268.26, -1407.0247, 145.51915, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 2, -12283.021, -1428.5464, 145.07469, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 3, -12293.712, -1449.1431, 143.79681, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 4, -12304.247, -1457.5105, 143.24136, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 5, -12308.79, -1474.1537, 140.71164, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 6, -12301.752, -1483.8573, 139.5728, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 7, -12285.15, -1485.6213, 140.60054, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 8, -12264.889, -1484.9541, 140.40607, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 9, -12243.971, -1478.847, 143.68388, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 10, -12225.232, -1476.0289, 141.3506, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 11, -12205.708, -1460.5085, 141.71165, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 12, -12212.562, -1439.7695, 140.795, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 13, -12218.71, -1420.5781, 141.5173, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 14, -12240.631, -1406.4712, 143.46161, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 15, -12268.599, -1396.2073, 141.85056, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 16, -12290.747, -1411.3853, 142.4061, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 17, -12290.479, -1429.4731, 142.1561, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 18, -12281.226, -1450.1283, 142.65608, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 19, -12259.163, -1461.7659, 145.76721, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 20, -12236.138, -1471.3794, 141.57275, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 21, -12214.372, -1481.1744, 140.37834, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 22, -12194.521, -1472.8463, 147.23947, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 23, -12190.211, -1455.2661, 149.40605, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 24, -12213.919, -1441.9805, 143.76726, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 25, -12236.254, -1427.0173, 145.98946, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 26, -12251.673, -1419.3727, 145.01723, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 27, -12266.985, -1426.0162, 144.40617, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 28, -12276.302, -1439.012, 143.71167, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 29, -12284.435, -1471.691, 140.82281, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 30, -12260.76, -1486.0157, 142.46169, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 31, -12232.035, -1468.1039, 142.18391, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 32, -12219.888, -1455.502, 144.21172, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 33, -12207.583, -1431.8105, 144.26724, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 34, -12211.74, -1418.2014, 141.60057, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 35, -12226.482, -1406.4142, 143.44217, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 36, -12244.903, -1403.1947, 143.1366, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 37, -12264.757, -1411.7229, 144.19214, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 38, -12283.372, -1414.2657, 150.2478, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 39, -12307.016, -1422.0199, 150.44214, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 40, -12313.775, -1448.7852, 148.19214, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 41, -12299.693, -1463.7096, 142.19215, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 42, -12278.83, -1463.4425, 142.74776, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 43, -12268.048, -1451.6599, 143.66444, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 44, -12261.599, -1435.9376, 145.46992, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 45, -12251.38, -1414.5144, 143.24773, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 46, -12234.656, -1409.4224, 146.46994, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 47, -12216.19, -1417.5902, 145.52544, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 48, -12215.774, -1441.7098, 147.94208, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 49, -12215.854, -1461.2819, 142.08109, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 50, -12222.245, -1480.9546, 140.69217, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 51, -12243.042, -1487.8419, 140.10889, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 52, -12254.896, -1478.5782, 141.27551, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 53, -12263.916, -1456.4462, 143.1089, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 54, -12270.662, -1443.2494, 143.99771, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 55, -12286.348, -1430.1548, 142.69221, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 56, -12297.24, -1422.73, 141.74767, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 57, -12302.444, -1406.2711, 142.24767, NULL, 0, 2, 0, 100, 0),
|
||||
(147500, 58, -12288.755, -1392.7551, 145.27551, NULL, 0, 2, 0, 100, 0);
|
||||
|
||||
-- Replace Gurubashi Bat Rider (14750)'s AI setting with script
|
||||
UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_batrider' WHERE (`entry` = 14750);
|
||||
|
||||
-- Remove Gurubashi Bat Rider (14750) SAI entries
|
||||
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 14750);
|
||||
|
||||
-- Remove Frentzied Bloodseeker Bat (14965) script
|
||||
UPDATE `creature_template` SET `ScriptName` = '' WHERE (`entry` = 14965);
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "RandomMovementGenerator.h"
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
#include "WaypointMgr.h"
|
||||
|
||||
inline MovementGenerator* GetIdleMovementGenerator()
|
||||
{
|
||||
@@ -448,6 +449,21 @@ void MotionMaster::MoveSplinePath(Movement::PointsArray* path)
|
||||
}
|
||||
}
|
||||
|
||||
void MotionMaster::MoveSplinePath(uint32 path_id)
|
||||
{
|
||||
// convert the path id to a Movement::PointsArray*
|
||||
Movement::PointsArray* points = new Movement::PointsArray();
|
||||
WaypointPath const* path = sWaypointMgr->GetPath(path_id);
|
||||
for (uint8 i = 0; i < path->size(); ++i)
|
||||
{
|
||||
WaypointData const* node = path->at(i);
|
||||
points->push_back(G3D::Vector3(node->x, node->y, node->z));
|
||||
}
|
||||
|
||||
// pass the new PointsArray* to the appropriate MoveSplinePath function
|
||||
MoveSplinePath(points);
|
||||
}
|
||||
|
||||
void MotionMaster::MoveLand(uint32 id, Position const& pos, float speed /* = 0.0f*/)
|
||||
{
|
||||
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
|
||||
|
||||
@@ -212,6 +212,7 @@ public:
|
||||
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ, generatePath, forceDestination, MOTION_SLOT_ACTIVE, pos.GetOrientation()); }
|
||||
void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true, bool forceDestination = true, MovementSlot slot = MOTION_SLOT_ACTIVE, float orientation = 0.0f);
|
||||
void MoveSplinePath(Movement::PointsArray* path);
|
||||
void MoveSplinePath(uint32 path_id);
|
||||
|
||||
// These two movement types should only be used with creatures having landing/takeoff animations
|
||||
void MoveLand(uint32 id, Position const& pos, float speed = 0.0f);
|
||||
|
||||
@@ -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