refactor(Scripts/ZulAman): refactor Jan'alai (#20195)

* Janalai (#13)

* wip

* nullcreature

* wipppp

* first attempt at working state

* hatcherai

* test

* conditions for hatching

* some potential fixes

* some debuggo

* some small fixes

* general fixes

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* more debug

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* fixed?

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* remove dumb bomb count

it is not needed

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* other debug

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* remove script for egg

* F

* Revert "F"

This reverts commit 246c66ccdac52e38c95c63f0ee59f1add97f4d64.

* Revert "remove script for egg"

This reverts commit b85933583fd480f941b839623c7291b6d5887246.

* different spell ids

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* Update boss_janalai.cpp

* restore data sets

changing this in a different PR, but data is still being set here
This commit is contained in:
Dan
2024-10-26 00:44:25 +02:00
committed by GitHub
parent f93a500400
commit d9e2773519
@@ -26,6 +26,7 @@ EndScriptData */
#include "CreatureScript.h" #include "CreatureScript.h"
#include "GridNotifiers.h" #include "GridNotifiers.h"
#include "GridNotifiersImpl.h" #include "GridNotifiersImpl.h"
#include "PassiveAI.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "zulaman.h" #include "zulaman.h"
@@ -60,7 +61,8 @@ enum Spells
SPELL_FIRE_BOMB_DAMAGE = 42630, SPELL_FIRE_BOMB_DAMAGE = 42630,
// Hatcher Spells // Hatcher Spells
SPELL_HATCH_EGG = 42471, // 43734 SPELL_HATCH_EGG_ALL = 42471,
SPELL_HATCH_EGG_SINGULAR = 43734,
SPELL_SUMMON_HATCHLING = 42493, SPELL_SUMMON_HATCHLING = 42493,
// Hatchling Spells // Hatchling Spells
@@ -78,135 +80,170 @@ enum Creatures
const int area_dx = 44; const int area_dx = 44;
const int area_dy = 51; const int area_dy = 51;
float JanalainPos[1][3] = const Position janalainPos = {-33.93f, 1149.27f, 19.0f, 0.0f};
{
{-33.93f, 1149.27f, 19}
};
float FireWallCoords[4][4] = const Position fireWallCoords[4] =
{ {
{-10.13f, 1149.27f, 19, 3.1415f}, {-10.13f, 1149.27f, 19, 3.1415f},
{-33.93f, 1123.90f, 19, 0.5f * 3.1415f}, {-33.93f, 1123.90f, 19, 0.5f * 3.1415f},
{-54.80f, 1150.08f, 19, 0}, {-54.80f, 1150.08f, 19, 0.0f},
{-33.93f, 1175.68f, 19, 1.5f * 3.1415f} {-33.93f, 1175.68f, 19, 1.5f * 3.1415f}
}; };
float hatcherway[2][5][3] = const Position hatcherway[2][5] =
{ {
{ {
{-87.46f, 1170.09f, 6}, {-87.46f, 1170.09f, 6.0f, 0.0f},
{-74.41f, 1154.75f, 6}, {-74.41f, 1154.75f, 6.0f, 0.0f},
{-52.74f, 1153.32f, 19}, {-52.74f, 1153.32f, 19.0f, 0.0f},
{-33.37f, 1172.46f, 19}, {-33.37f, 1172.46f, 19.0f, 0.0f},
{-33.09f, 1203.87f, 19} {-33.09f, 1203.87f, 19.0f, 0.0f}
}, },
{ {
{-86.57f, 1132.85f, 6}, {-86.57f, 1132.85f, 6.0f, 0.0f},
{-73.94f, 1146.00f, 6}, {-73.94f, 1146.00f, 6.0f, 0.0f},
{-52.29f, 1146.51f, 19}, {-52.29f, 1146.51f, 19.0f, 0.0f},
{-33.57f, 1125.72f, 19}, {-33.57f, 1125.72f, 19.0f, 0.0f},
{-34.29f, 1095.22f, 19} {-34.29f, 1095.22f, 19.0f, 0.0f}
} }
}; };
class boss_janalai : public CreatureScript
enum HatchActions
{ {
public: HATCH_RESET = 0,
boss_janalai() HATCH_ALL = 1
: CreatureScript("boss_janalai") };
enum Misc
{ {
} MAX_BOMB_COUNT = 40,
struct boss_janalaiAI : public ScriptedAI SCHEDULER_GROUP_HATCHING = 1,
EVENT_BERSERK = 0
};
struct boss_janalai : public BossAI
{ {
boss_janalaiAI(Creature* creature) : ScriptedAI(creature) boss_janalai(Creature* creature) : BossAI(creature, DATA_JANALAIEVENT) { }
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
uint32 FireBreathTimer;
uint32 BombTimer;
uint32 BombSequenceTimer;
uint32 BombCount;
uint32 HatcherTimer;
uint32 EnrageTimer;
bool noeggs;
bool enraged;
bool isBombing;
bool isFlameBreathing;
ObjectGuid FireBombGUIDs[40];
void Reset() override void Reset() override
{ {
BossAI::Reset();
instance->SetData(DATA_JANALAIEVENT, NOT_STARTED); instance->SetData(DATA_JANALAIEVENT, NOT_STARTED);
HatchAllEggs(HATCH_RESET);
_isBombing = false;
_isFlameBreathing = false;
FireBreathTimer = 8000; ScheduleHealthCheckEvent(25, [&]{
BombTimer = 30000; DoCastSelf(SPELL_ENRAGE, true);
BombSequenceTimer = 1000; });
BombCount = 0;
HatcherTimer = 10000;
EnrageTimer = MINUTE * 5 * IN_MILLISECONDS;
noeggs = false; ScheduleHealthCheckEvent(35, [&]{
isBombing = false; Talk(SAY_ALL_EGGS);
enraged = false; me->AttackStop();
me->GetMotionMaster()->Clear();
isFlameBreathing = false; me->SetPosition(janalainPos);
me->StopMovingOnCurrentPos();
for (uint8 i = 0; i < 40; ++i) DoCastSelf(SPELL_HATCH_ALL);
FireBombGUIDs[i].Clear(); HatchAllEggs(HATCH_ALL);
});
HatchAllEggs(1);
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* killer) override
{ {
Talk(SAY_DEATH); Talk(SAY_DEATH);
instance->SetData(DATA_JANALAIEVENT, DONE); instance->SetData(DATA_JANALAIEVENT, DONE);
} BossAI::JustDied(killer);
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_SLAY);
}
void JustEngagedWith(Unit* /*who*/) override
{
instance->SetData(DATA_JANALAIEVENT, IN_PROGRESS);
Talk(SAY_AGGRO);
// DoZoneInCombat();
} }
void DamageDealt(Unit* target, uint32& damage, DamageEffectType /*damagetype*/) override void DamageDealt(Unit* target, uint32& damage, DamageEffectType /*damagetype*/) override
{ {
if (isFlameBreathing) if (_isFlameBreathing)
{ {
if (!me->HasInArc(M_PI / 6, target)) if (!me->HasInArc(M_PI / 6, target))
damage = 0; damage = 0;
} }
} }
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
instance->SetData(DATA_JANALAIEVENT, IN_PROGRESS);
Talk(SAY_AGGRO);
//schedule abilities
ScheduleTimedEvent(30s, [&]{
StartBombing();
}, 20s, 40s);
ScheduleTimedEvent(10s, [&]{
if (HatchAllEggs(HATCH_RESET))
{
Talk(SAY_SUMMON_HATCHER);
me->SummonCreature(NPC_AMANI_HATCHER, hatcherway[0][0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
me->SummonCreature(NPC_AMANI_HATCHER, hatcherway[1][0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
}
}, 90s);
ScheduleTimedEvent(8s, [&]{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->AttackStop();
me->GetMotionMaster()->Clear();
DoCast(target, SPELL_FLAME_BREATH);
me->StopMoving();
_isFlameBreathing = true;
// placeholder time idk yet
scheduler.Schedule(2s, [this](TaskContext)
{
_isFlameBreathing = false;
});
}
}, 8s);
ScheduleUniqueTimedEvent(5min, [&]{
Talk(SAY_BERSERK);
DoCastSelf(SPELL_BERSERK);
}, EVENT_BERSERK);
}
bool HatchAllEggs(uint32 hatchAction)
{
std::list<Creature* > eggList;
me->GetCreaturesWithEntryInRange(eggList, 100.0f, NPC_EGG);
if (eggList.empty())
return false;
if (hatchAction == HATCH_RESET)
for (Creature* egg : eggList)
egg->Respawn();
else if (hatchAction == HATCH_ALL)
DoCastSelf(SPELL_HATCH_EGG_ALL);
if (hatchAction == HATCH_RESET)
{
std::list<Creature* > hatchlingList;
me->GetCreaturesWithEntryInRange(hatchlingList, 100.0f, NPC_HATCHLING);
for (Creature* hatchling : hatchlingList)
hatchling->DespawnOrUnsummon();
hatchlingList.clear();
}
eggList.clear();
return true;
}
void FireWall() void FireWall()
{ {
for (uint8 i = 0; i < 4; ++i) for (uint8 i = 0; i < 4; ++i)
{ {
uint8 WallNum = i == 0 || i == 2 ? 3 : 2; uint8 wallNum = i == 0 || i == 2 ? 3 : 2;
for (uint8 j = 0; j < WallNum; j++) for (uint8 j = 0; j < wallNum; j++)
{ {
Creature* wall = WallNum == 3 Creature* wall = wallNum == 3
? me->SummonCreature(NPC_FIRE_BOMB, FireWallCoords[i][0], FireWallCoords[i][1] + 5 * (j - 1), FireWallCoords[i][2], FireWallCoords[i][3], TEMPSUMMON_TIMED_DESPAWN, 15000) ? me->SummonCreature(NPC_FIRE_BOMB, fireWallCoords[i].GetPositionX(), fireWallCoords[i].GetPositionY() + 5 * (j - 1), fireWallCoords[i].GetPositionZ(), fireWallCoords[i].GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 15000)
: me->SummonCreature(NPC_FIRE_BOMB, FireWallCoords[i][0] - 2 + 4 * j, FireWallCoords[i][1], FireWallCoords[i][2], FireWallCoords[i][3], TEMPSUMMON_TIMED_DESPAWN, 15000); : me->SummonCreature(NPC_FIRE_BOMB, fireWallCoords[i].GetPositionX() - 2 + 4 * j, fireWallCoords[i].GetPositionY(), fireWallCoords[i].GetPositionZ(), fireWallCoords[i].GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 15000);
if (wall) if (wall)
{ {
wall->CastSpell(wall, SPELL_FIRE_WALL, true); wall->AI()->DoCastSelf(SPELL_FIRE_WALL, true);
} }
} }
} }
@@ -215,472 +252,243 @@ public:
void SpawnBombs() void SpawnBombs()
{ {
float dx, dy; float dx, dy;
for (int i(0); i < 40; ++i) for (int i = 0; i < MAX_BOMB_COUNT; ++i)
{ {
dx = float(irand(-area_dx / 2, area_dx / 2)); dx = float(irand(-area_dx / 2, area_dx / 2));
dy = float(irand(-area_dy / 2, area_dy / 2)); dy = float(irand(-area_dy / 2, area_dy / 2));
DoSpawnCreature(NPC_FIRE_BOMB, dx, dy, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 15000);
Creature* bomb = DoSpawnCreature(NPC_FIRE_BOMB, dx, dy, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 15000);
if (bomb)
FireBombGUIDs[i] = bomb->GetGUID();
} }
BombCount = 0;
}
bool HatchAllEggs(uint32 action) //1: reset, 2: isHatching all
{
std::list<Creature*> templist;
float x, y, z;
me->GetPosition(x, y, z);
Acore::AllCreaturesOfEntryInRange check(me, NPC_EGG, 100);
Acore::CreatureListSearcher<Acore::AllCreaturesOfEntryInRange> searcher(me, templist, check);
Cell::VisitGridObjects(me, searcher, me->GetGridActivationRange());
//LOG_ERROR("scripts", "Eggs {} at middle", templist.size());
if (templist.empty())
return false;
for (std::list<Creature*>::const_iterator i = templist.begin(); i != templist.end(); ++i)
{
if (action == 1)
(*i)->SetDisplayId(10056);
else if (action == 2 && (*i)->GetDisplayId() != 11686)
(*i)->CastSpell(*i, SPELL_HATCH_EGG, false);
}
return true;
} }
void Boom() void Boom()
{ {
std::list<Creature*> templist; std::list<Creature*> fireBombs;
float x, y, z; me->GetCreaturesWithEntryInRange(fireBombs, 100.0f, NPC_FIRE_BOMB);
me->GetPosition(x, y, z);
Acore::AllCreaturesOfEntryInRange check(me, NPC_FIRE_BOMB, 100); if (fireBombs.empty())
Acore::CreatureListSearcher<Acore::AllCreaturesOfEntryInRange> searcher(me, templist, check);
Cell::VisitGridObjects(me, searcher, me->GetGridActivationRange());
for (std::list<Creature*>::const_iterator i = templist.begin(); i != templist.end(); ++i)
{
(*i)->CastSpell(*i, SPELL_FIRE_BOMB_DAMAGE, true);
(*i)->RemoveAllAuras();
}
}
void HandleBombSequence()
{
if (BombCount < 40)
{
if (Unit* FireBomb = ObjectAccessor::GetUnit(*me, FireBombGUIDs[BombCount]))
{
FireBomb->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
DoCast(FireBomb, SPELL_FIRE_BOMB_THROW, true);
FireBomb->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
++BombCount;
if (BombCount == 40)
{
BombSequenceTimer = 5000;
}
else BombSequenceTimer = 100;
}
else
{
Boom();
isBombing = false;
BombTimer = urand(20000, 40000);
me->RemoveAurasDueToSpell(SPELL_FIRE_BOMB_CHANNEL);
if (EnrageTimer <= 10000)
EnrageTimer = 0;
else
EnrageTimer -= 10000;
}
}
void UpdateAI(uint32 diff) override
{
if (isFlameBreathing)
{
if (!me->IsNonMeleeSpellCast(false))
isFlameBreathing = false;
else
return;
}
if (isBombing)
{
if (BombSequenceTimer <= diff)
HandleBombSequence();
else
BombSequenceTimer -= diff;
return;
}
if (!UpdateVictim())
return; return;
//enrage if under 25% hp before 5 min. for (Creature* bomb : fireBombs)
if (!enraged && HealthBelowPct(25)) {
EnrageTimer = 0; bomb->AI()->DoCastSelf(SPELL_FIRE_BOMB_DAMAGE, true);
bomb->RemoveAllAuras();
}
fireBombs.clear();
}
if (EnrageTimer <= diff) void StartBombing()
{
if (!enraged)
{
DoCast(me, SPELL_ENRAGE, true);
enraged = true;
EnrageTimer = 300000;
}
else
{
Talk(SAY_BERSERK);
DoCast(me, SPELL_BERSERK, true);
EnrageTimer = 300000;
}
}
else EnrageTimer -= diff;
if (BombTimer <= diff)
{ {
Talk(SAY_FIRE_BOMBS); Talk(SAY_FIRE_BOMBS);
me->AttackStop(); me->AttackStop();
me->GetMotionMaster()->Clear(); me->GetMotionMaster()->Clear();
me->NearTeleportTo(JanalainPos[0][0], JanalainPos[0][1], JanalainPos[0][2], me->GetOrientation()); me->SetPosition(janalainPos);
me->StopMovingOnCurrentPos(); me->StopMovingOnCurrentPos();
DoCast(me, SPELL_FIRE_BOMB_CHANNEL, false); DoCastSelf(SPELL_FIRE_BOMB_CHANNEL);
//DoTeleportPlayer(me, JanalainPos[0][0], JanalainPos[0][1], JanalainPos[0][2], 0);
//DoCast(me, SPELL_TELE_TO_CENTER, true);
FireWall(); FireWall();
SpawnBombs(); SpawnBombs();
isBombing = true; _isBombing = true;
BombSequenceTimer = 100;
//Teleport every Player into the middle if (Map* map = me->GetMap())
Map* map = me->GetMap(); {
if (!map->IsDungeon()) map->DoForAllPlayers([&](Player* player)
return; {
if (player->IsAlive())
Map::PlayerList const& PlayerList = map->GetPlayers(); DoTeleportPlayer(player, janalainPos.GetPositionX() - 5 + rand() % 10, janalainPos.GetPositionY() - 5 + rand() % 10, janalainPos.GetPositionZ(), 0.0f);
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) });
if (Player* i_pl = i->GetSource()) }
if (i_pl->IsAlive())
DoTeleportPlayer(i_pl, JanalainPos[0][0] - 5 + rand() % 10, JanalainPos[0][1] - 5 + rand() % 10, JanalainPos[0][2], 0);
//DoCast(Temp, SPELL_SUMMON_PLAYERS, true) // core bug, spell does not work if too far //DoCast(Temp, SPELL_SUMMON_PLAYERS, true) // core bug, spell does not work if too far
return; ThrowBombs();
}
else BombTimer -= diff;
if (!noeggs) scheduler.Schedule(11s, [this](TaskContext)
{ {
if (HealthBelowPct(35)) Boom();
{ _isBombing = false;
Talk(SAY_ALL_EGGS);
me->AttackStop(); me->RemoveAurasDueToSpell(SPELL_FIRE_BOMB_CHANNEL);
me->GetMotionMaster()->Clear(); });
me->NearTeleportTo(JanalainPos[0][0], JanalainPos[0][1], JanalainPos[0][2], me->GetOrientation());
me->StopMovingOnCurrentPos();
DoCast(me, SPELL_HATCH_ALL, false);
HatchAllEggs(2);
noeggs = true;
}
else if (HatcherTimer <= diff)
{
if (HatchAllEggs(0))
{
Talk(SAY_SUMMON_HATCHER);
me->SummonCreature(NPC_AMANI_HATCHER, hatcherway[0][0][0], hatcherway[0][0][1], hatcherway[0][0][2], 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
me->SummonCreature(NPC_AMANI_HATCHER, hatcherway[1][0][0], hatcherway[1][0][1], hatcherway[1][0][2], 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
HatcherTimer = 90000;
}
else
noeggs = true;
}
else HatcherTimer -= diff;
} }
DoMeleeAttackIfReady(); void ThrowBombs()
if (FireBreathTimer <= diff)
{ {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) std::list<Creature*> fireBombs;
std::chrono::milliseconds bombTimer = 100ms;
me->GetCreaturesWithEntryInRange(fireBombs, 100.0f, NPC_FIRE_BOMB);
for (Creature* bomb : fireBombs)
{ {
me->AttackStop(); scheduler.Schedule(bombTimer, [this, bomb](TaskContext)
me->GetMotionMaster()->Clear(); {
DoCast(target, SPELL_FLAME_BREATH, false); bomb->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->StopMoving(); DoCast(bomb, SPELL_FIRE_BOMB_THROW, true);
isFlameBreathing = true; bomb->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
});
bombTimer = bombTimer + 100ms;
} }
FireBreathTimer = 8000; fireBombs.clear();
}
else FireBreathTimer -= diff;
} }
bool CheckEvadeIfOutOfCombatArea() const override bool CheckEvadeIfOutOfCombatArea() const override
{ {
return me->GetPositionZ() <= 12.0f; return me->GetPositionZ() <= 12.0f;
} }
private:
bool _isBombing;
bool _isFlameBreathing;
}; };
CreatureAI* GetAI(Creature* creature) const override struct npc_janalai_firebomb : public NullCreatureAI
{ {
return GetZulAmanAI<boss_janalaiAI>(creature); npc_janalai_firebomb(Creature* creature) : NullCreatureAI(creature) { }
}
};
class npc_janalai_firebomb : public CreatureScript
{
public:
npc_janalai_firebomb()
: CreatureScript("npc_janalai_firebomb")
{
}
struct npc_janalai_firebombAI : public ScriptedAI
{
npc_janalai_firebombAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override { }
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{ {
if (spell->Id == SPELL_FIRE_BOMB_THROW) if (spell->Id == SPELL_FIRE_BOMB_THROW)
DoCast(me, SPELL_FIRE_BOMB_DUMMY, true); DoCastSelf(SPELL_FIRE_BOMB_DUMMY, true);
}
void JustEngagedWith(Unit* /*who*/) override { }
void AttackStart(Unit* /*who*/) override { }
void MoveInLineOfSight(Unit* /*who*/) override { }
void UpdateAI(uint32 /*diff*/) override { }
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulAmanAI<npc_janalai_firebombAI>(creature);
} }
}; };
class npc_janalai_hatcher : public CreatureScript struct npc_janalai_hatcher : public ScriptedAI
{ {
public: npc_janalai_hatcher(Creature* creature) : ScriptedAI(creature)
npc_janalai_hatcher()
: CreatureScript("npc_janalai_hatcher")
{ {
_instance = creature->GetInstanceScript();
} }
struct npc_janalai_hatcherAI : public ScriptedAI
{
npc_janalai_hatcherAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
uint32 waypoint;
uint32 HatchNum;
uint32 WaitTimer;
bool side;
bool hasChangedSide;
bool isHatching;
void Reset() override void Reset() override
{ {
me->SetWalk(true); ScriptedAI::Reset();
side = (me->GetPositionY() < 1150); scheduler.CancelAll();
waypoint = 0; _side = (me->GetPositionY() < 1150);
isHatching = false; _waypoint = 0;
hasChangedSide = false; _isHatching = false;
WaitTimer = 1; _hasChangedSide = false;
HatchNum = 0; me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(0, hatcherway[_side][0]);
} }
bool HatchEggs(uint32 num)
{
std::list<Creature*> templist;
float x, y, z;
me->GetPosition(x, y, z);
Acore::AllCreaturesOfEntryInRange check(me, 23817, 50);
Acore::CreatureListSearcher<Acore::AllCreaturesOfEntryInRange> searcher(me, templist, check);
Cell::VisitGridObjects(me, searcher, me->GetGridActivationRange());
//LOG_ERROR("scripts", "Eggs {} at {}", templist.size(), side);
for (std::list<Creature*>::const_iterator i = templist.begin(); i != templist.end() && num > 0; ++i)
if ((*i)->GetDisplayId() != 11686)
{
(*i)->CastSpell(*i, SPELL_HATCH_EGG, false);
num--;
}
return num == 0; // if num == 0, no more templist
}
void JustEngagedWith(Unit* /*who*/) override { }
void AttackStart(Unit* /*who*/) override { }
void MoveInLineOfSight(Unit* /*who*/) override { }
void MovementInform(uint32, uint32) override void MovementInform(uint32, uint32) override
{ {
if (waypoint == 5) if (_waypoint == 5)
{ {
isHatching = true; _isHatching = true;
HatchNum = 1; std::list<Creature* > eggList;
WaitTimer = 5000; me->GetCreaturesWithEntryInRange(eggList, 50.0f, NPC_EGG);
scheduler.Schedule(1500ms, SCHEDULER_GROUP_HATCHING, [this, eggList](TaskContext context)
{
std::list<Creature* > unhatchedEggs;
for (Creature* egg : eggList)
{
if (egg->IsAlive())
unhatchedEggs.emplace_front(egg);
}
Acore::Containers::RandomResize(unhatchedEggs, 1);
if (Creature* egg = unhatchedEggs.front())
egg->AI()->DoCastSelf(SPELL_HATCH_EGG_SINGULAR);
else if (!_hasChangedSide)
{
_side = _side ? 0 : 1;
_isHatching = false;
_waypoint = 3;
MoveToNewWaypoint(_waypoint);
_hasChangedSide = true;
context.CancelGroup(SCHEDULER_GROUP_HATCHING);
}
context.Repeat(1500ms);
});
} }
else else
WaitTimer = 1; {
MoveToNewWaypoint(_waypoint);
++_waypoint;
}
}
void MoveToNewWaypoint(uint8 waypoint)
{
if (!_isHatching)
{
scheduler.Schedule(100ms, [this, waypoint](TaskContext)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(0, hatcherway[_side][waypoint]);
});
}
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (!instance || instance->GetData(DATA_JANALAIEVENT) != IN_PROGRESS) scheduler.Update(diff);
{
me->DisappearAndDie();
return;
} }
if (!isHatching) void JustEngagedWith(Unit* /*who*/) override { }
{ void AttackStart(Unit* /*who*/) override { }
if (WaitTimer) void MoveInLineOfSight(Unit* /*who*/) override { }
{
me->GetMotionMaster()->Clear(); private:
me->GetMotionMaster()->MovePoint(0, hatcherway[side][waypoint][0], hatcherway[side][waypoint][1], hatcherway[side][waypoint][2]); InstanceScript* _instance;
++waypoint; uint8 _side;
WaitTimer = 0; uint8 _waypoint;
} bool _isHatching;
} bool _hasChangedSide;
else
{
if (WaitTimer <= diff)
{
if (HatchEggs(HatchNum))
{
++HatchNum;
WaitTimer = 10000;
}
else if (!hasChangedSide)
{
side = side ? 0 : 1;
isHatching = false;
waypoint = 3;
WaitTimer = 1;
hasChangedSide = true;
}
else
me->DisappearAndDie();
}
else WaitTimer -= diff;
}
}
}; };
CreatureAI* GetAI(Creature* creature) const override struct npc_janalai_hatchling : public ScriptedAI
{ {
return GetZulAmanAI<npc_janalai_hatcherAI>(creature); npc_janalai_hatchling(Creature* creature) : ScriptedAI(creature)
{
_instance = creature->GetInstanceScript();
} }
};
class npc_janalai_hatchling : public CreatureScript
{
public:
npc_janalai_hatchling()
: CreatureScript("npc_janalai_hatchling")
{
}
struct npc_janalai_hatchlingAI : public ScriptedAI
{
npc_janalai_hatchlingAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
uint32 BuffetTimer;
void Reset() override void Reset() override
{ {
BuffetTimer = 7000; scheduler.CancelAll();
if (me->GetPositionY() > 1150) if (me->GetPositionY() > 1150)
me->GetMotionMaster()->MovePoint(0, hatcherway[0][3][0] + rand() % 4 - 2, 1150.0f + rand() % 4 - 2, hatcherway[0][3][2]); me->GetMotionMaster()->MovePoint(0, hatcherway[0][3].GetPositionX() + rand() % 4 - 2, 1150.0f + rand() % 4 - 2, hatcherway[0][3].GetPositionY());
else else
me->GetMotionMaster()->MovePoint(0, hatcherway[1][3][0] + rand() % 4 - 2, 1150.0f + rand() % 4 - 2, hatcherway[1][3][2]); me->GetMotionMaster()->MovePoint(0, hatcherway[1][3].GetPositionX() + rand() % 4 - 2, 1150.0f + rand() % 4 - 2, hatcherway[1][3].GetPositionY());
me->SetDisableGravity(true); me->SetDisableGravity(true);
me->SetInCombatWithZone();
} }
void JustEngagedWith(Unit* /*who*/) override {/*DoZoneInCombat();*/ } void JustEngagedWith(Unit* who) override
{
ScriptedAI::JustEngagedWith(who);
ScheduleTimedEvent(7s, [&]{
DoCastVictim(SPELL_FLAMEBUFFET);
}, 10s);
}
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
if (!instance || instance->GetData(DATA_JANALAIEVENT) != IN_PROGRESS)
{
me->DisappearAndDie();
return;
}
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
if (BuffetTimer <= diff) scheduler.Update(diff);
{
DoCastVictim(SPELL_FLAMEBUFFET, false);
BuffetTimer = 10000;
}
else BuffetTimer -= diff;
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
private:
InstanceScript* _instance;
}; };
CreatureAI* GetAI(Creature* creature) const override struct npc_janalai_egg : public NullCreatureAI
{ {
return GetZulAmanAI<npc_janalai_hatchlingAI>(creature); npc_janalai_egg(Creature* creature) : NullCreatureAI(creature) { }
}
};
class npc_janalai_egg : public CreatureScript
{
public:
npc_janalai_egg(): CreatureScript("npc_janalai_egg") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulAmanAI<npc_janalai_eggAI>(creature);
}
struct npc_janalai_eggAI : public ScriptedAI
{
npc_janalai_eggAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override { }
void UpdateAI(uint32 /*diff*/) override { }
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{ {
if (spell->Id == SPELL_HATCH_EGG) if (spell->Id == SPELL_HATCH_EGG_ALL || spell->Id == SPELL_HATCH_EGG_SINGULAR)
{ DoCastSelf(SPELL_SUMMON_HATCHLING);
DoCast(SPELL_SUMMON_HATCHLING);
} }
}
};
}; };
void AddSC_boss_janalai() void AddSC_boss_janalai()
{ {
new boss_janalai(); RegisterZulAmanCreatureAI(boss_janalai);
new npc_janalai_firebomb(); RegisterZulAmanCreatureAI(npc_janalai_firebomb);
new npc_janalai_hatcher(); RegisterZulAmanCreatureAI(npc_janalai_hatcher);
new npc_janalai_hatchling(); RegisterZulAmanCreatureAI(npc_janalai_hatchling);
new npc_janalai_egg(); RegisterZulAmanCreatureAI(npc_janalai_egg);
} }