refactor(Scripts/Blackmorass): Rewrite Blackmorass (#15290)

This commit is contained in:
Skjalf
2023-03-06 05:49:51 -03:00
committed by GitHub
parent a8d928e2a2
commit ef0b20928b
6 changed files with 465 additions and 648 deletions

View File

@@ -36,131 +36,87 @@ enum Enums
SPELL_BANISH_DRAGON_HELPER = 31550 SPELL_BANISH_DRAGON_HELPER = 31550
}; };
enum Events struct boss_aeonus : public BossAI
{ {
EVENT_SANDBREATH = 1, boss_aeonus(Creature* creature) : BossAI(creature, DATA_AEONUS) { }
EVENT_TIMESTOP = 2,
EVENT_FRENZY = 3,
EVENT_CLEAVE = 4
};
class boss_aeonus : public CreatureScript void JustReachedHome() override
{
public:
boss_aeonus() : CreatureScript("boss_aeonus") { }
struct boss_aeonusAI : public ScriptedAI
{ {
boss_aeonusAI(Creature* creature) : ScriptedAI(creature) if (Creature* medivh = instance->GetCreature(DATA_MEDIVH))
{ {
instance = creature->GetInstanceScript(); if (me->GetDistance2d(medivh) < 20.0f)
}
EventMap events;
InstanceScript* instance;
void Reset() override
{
events.Reset();
}
void JustReachedHome() override
{
if (Unit* medivh = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_MEDIVH)))
if (me->GetDistance2d(medivh) < 20.0f)
me->CastSpell(me, SPELL_CORRUPT_MEDIVH, false);
}
void InitializeAI() override
{
Talk(SAY_ENTER);
ScriptedAI::InitializeAI();
if (Unit* medivh = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_MEDIVH)))
{ {
me->SetHomePosition(medivh->GetPositionX() + 14.0f * cos(medivh->GetAngle(me)), medivh->GetPositionY() + 14.0f * std::sin(medivh->GetAngle(me)), medivh->GetPositionZ(), me->GetAngle(medivh)); DoCastAOE(SPELL_CORRUPT_MEDIVH);
me->GetMotionMaster()->MoveTargetedHome();
} }
} }
}
void JustEngagedWith(Unit* /*who*/) override void IsSummonedBy(WorldObject* /*summoner*/) override
{
events.ScheduleEvent(EVENT_CLEAVE, 5000);
events.ScheduleEvent(EVENT_SANDBREATH, 20000);
events.ScheduleEvent(EVENT_TIMESTOP, 15000);
events.ScheduleEvent(EVENT_FRENZY, 30000);
Talk(SAY_AGGRO);
}
void MoveInLineOfSight(Unit* who) override
{
if (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_TIME_KEEPER)
{
if (me->IsWithinDistInMap(who, 20.0f))
{
Talk(SAY_BANISH);
me->CastSpell(me, SPELL_BANISH_DRAGON_HELPER, true);
return;
}
}
ScriptedAI::MoveInLineOfSight(who);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
instance->SetData(TYPE_AEONUS, DONE);
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_CLEAVE:
me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
events.ScheduleEvent(EVENT_CLEAVE, 10000);
break;
case EVENT_SANDBREATH:
me->CastSpell(me->GetVictim(), SPELL_SAND_BREATH, false);
events.ScheduleEvent(EVENT_SANDBREATH, 20000);
break;
case EVENT_TIMESTOP:
me->CastSpell(me, SPELL_TIME_STOP, false);
events.ScheduleEvent(EVENT_TIMESTOP, 25000);
break;
case EVENT_FRENZY:
Talk(EMOTE_FRENZY);
me->CastSpell(me, SPELL_ENRAGE, false);
events.ScheduleEvent(EVENT_FRENZY, 30000);
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{ {
return GetTheBlackMorassAI<boss_aeonusAI>(creature); Talk(SAY_ENTER);
if (Creature* medivh = instance->GetCreature(DATA_MEDIVH))
{
me->SetHomePosition(medivh->GetPositionX() + 14.0f * cos(medivh->GetAngle(me)), medivh->GetPositionY() + 14.0f * std::sin(medivh->GetAngle(me)), medivh->GetPositionZ(), me->GetAngle(medivh));
me->GetMotionMaster()->MoveTargetedHome();
}
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
scheduler.Schedule(5s, [this](TaskContext context)
{
DoCastVictim(SPELL_CLEAVE);
context.Repeat(10s);
}).Schedule(20s, [this](TaskContext context)
{
DoCastVictim(SPELL_SAND_BREATH);
context.Repeat(20s);
}).Schedule(15s, [this](TaskContext context)
{
DoCastAOE(SPELL_TIME_STOP);
context.Repeat(25s);
}).Schedule(30s, [this](TaskContext context)
{
Talk(EMOTE_FRENZY);
DoCastSelf(SPELL_ENRAGE);
context.Repeat(30s);
});
}
void MoveInLineOfSight(Unit* who) override
{
if (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_TIME_KEEPER)
{
if (me->IsWithinDistInMap(who, 20.0f))
{
Talk(SAY_BANISH);
DoCastAOE(SPELL_BANISH_DRAGON_HELPER, true);
return;
}
}
ScriptedAI::MoveInLineOfSight(who);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
_JustDied();
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer())
{
Talk(SAY_SLAY);
}
} }
}; };
void AddSC_boss_aeonus() void AddSC_boss_aeonus()
{ {
new boss_aeonus(); RegisterTheBlackMorassCreatureAI(boss_aeonus);
} }

View File

@@ -35,121 +35,84 @@ enum Enums
SPELL_BANISH_DRAGON_HELPER = 31550, SPELL_BANISH_DRAGON_HELPER = 31550,
}; };
enum Events struct boss_chrono_lord_deja : public BossAI
{ {
EVENT_ARCANE_BLAST = 1, boss_chrono_lord_deja(Creature* creature) : BossAI(creature, DATA_CHRONO_LORD_DEJA) { }
EVENT_TIME_LAPSE = 2,
EVENT_ARCANE_DISCHARGE = 3,
EVENT_ATTRACTION = 4
};
class boss_chrono_lord_deja : public CreatureScript void OwnTalk(uint32 id)
{ {
public: if (me->GetEntry() == NPC_CHRONO_LORD_DEJA)
boss_chrono_lord_deja() : CreatureScript("boss_chrono_lord_deja") { } {
Talk(id);
}
}
struct boss_chrono_lord_dejaAI : public ScriptedAI void InitializeAI() override
{ {
boss_chrono_lord_dejaAI(Creature* creature) : ScriptedAI(creature) { } OwnTalk(SAY_ENTER);
ScriptedAI::InitializeAI();
}
EventMap events; void JustEngagedWith(Unit* /*who*/) override
{
OwnTalk(SAY_AGGRO);
_JustEngagedWith();
void Reset() override scheduler.Schedule(10s, [this](TaskContext context)
{ {
events.Reset(); DoCastVictim(SPELL_ARCANE_BLAST);
} context.Repeat(20s);
}).Schedule(15s, [this](TaskContext context)
{
DoCastAOE(SPELL_TIME_LAPSE);
context.Repeat(20s);
}).Schedule(20s, [this](TaskContext context)
{
DoCastAOE(SPELL_ARCANE_DISCHARGE);
context.Repeat(25s);
});
void OwnTalk(uint32 id) if (IsHeroic())
{ {
if (me->GetEntry() == NPC_CHRONO_LORD_DEJA) scheduler.Schedule(20s, [this](TaskContext context)
Talk(id); {
} DoCastAOE(SPELL_ATTRACTION);
context.Repeat(30s);
});
}
}
void InitializeAI() override void MoveInLineOfSight(Unit* who) override
{ {
OwnTalk(SAY_ENTER); if (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_TIME_KEEPER)
ScriptedAI::InitializeAI(); {
} if (me->IsWithinDistInMap(who, 20.0f))
{
OwnTalk(SAY_BANISH);
DoCastAOE(SPELL_BANISH_DRAGON_HELPER);
return;
}
}
void JustEngagedWith(Unit* /*who*/) override ScriptedAI::MoveInLineOfSight(who);
{ }
events.ScheduleEvent(EVENT_ARCANE_BLAST, 10000);
events.ScheduleEvent(EVENT_TIME_LAPSE, 15000);
events.ScheduleEvent(EVENT_ARCANE_DISCHARGE, 25000);
if (IsHeroic())
events.ScheduleEvent(EVENT_ATTRACTION, 20000);
OwnTalk(SAY_AGGRO); void KilledUnit(Unit* victim) override
} {
if (victim->IsPlayer())
{
OwnTalk(SAY_SLAY);
}
}
void MoveInLineOfSight(Unit* who) override void JustDied(Unit* /*killer*/) override
{ {
if (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_TIME_KEEPER) OwnTalk(SAY_DEATH);
{ _JustDied();
if (me->IsWithinDistInMap(who, 20.0f)) }
{ };
OwnTalk(SAY_BANISH);
me->CastSpell(me, SPELL_BANISH_DRAGON_HELPER, true);
return;
}
}
ScriptedAI::MoveInLineOfSight(who);
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
OwnTalk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
OwnTalk(SAY_DEATH);
if (InstanceScript* instance = me->GetInstanceScript())
instance->SetData(TYPE_CHRONO_LORD_DEJA, DONE);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_ARCANE_BLAST:
me->CastSpell(me->GetVictim(), SPELL_ARCANE_BLAST, false);
events.ScheduleEvent(EVENT_ARCANE_BLAST, 20000);
break;
case EVENT_TIME_LAPSE:
me->CastSpell(me, SPELL_TIME_LAPSE, false);
events.ScheduleEvent(EVENT_TIME_LAPSE, 20000);
break;
case EVENT_ARCANE_DISCHARGE:
me->CastSpell(me, SPELL_ARCANE_DISCHARGE, false);
events.ScheduleEvent(EVENT_ARCANE_DISCHARGE, 25000);
break;
case EVENT_ATTRACTION:
me->CastSpell(me, SPELL_ATTRACTION, false);
events.ScheduleEvent(EVENT_ATTRACTION, 30000);
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetTheBlackMorassAI<boss_chrono_lord_dejaAI>(creature);
}
};
void AddSC_boss_chrono_lord_deja() void AddSC_boss_chrono_lord_deja()
{ {
new boss_chrono_lord_deja(); RegisterTheBlackMorassCreatureAI(boss_chrono_lord_deja);
} }

View File

@@ -34,121 +34,83 @@ enum Enums
SPELL_BANISH_DRAGON_HELPER = 31550 SPELL_BANISH_DRAGON_HELPER = 31550
}; };
enum Events struct boss_temporus : public BossAI
{ {
EVENT_HASTEN = 1, boss_temporus(Creature* creature) : BossAI(creature, DATA_TEMPORUS) { }
EVENT_MORTAL_WOUND = 2,
EVENT_WING_BUFFET = 3,
EVENT_SPELL_REFLECTION = 4
};
class boss_temporus : public CreatureScript void OwnTalk(uint32 id)
{
public:
boss_temporus() : CreatureScript("boss_temporus") { }
struct boss_temporusAI : public ScriptedAI
{ {
boss_temporusAI(Creature* creature) : ScriptedAI(creature) { } if (me->GetEntry() == NPC_TEMPORUS)
Talk(id);
}
EventMap events; void InitializeAI() override
void OwnTalk(uint32 id)
{
if (me->GetEntry() == NPC_TEMPORUS)
Talk(id);
}
void Reset() override
{
events.Reset();
}
void InitializeAI() override
{
OwnTalk(SAY_ENTER);
ScriptedAI::InitializeAI();
}
void JustEngagedWith(Unit* /*who*/) override
{
events.ScheduleEvent(EVENT_HASTEN, 12000);
events.ScheduleEvent(EVENT_MORTAL_WOUND, 5000);
events.ScheduleEvent(EVENT_WING_BUFFET, 20000);
if (IsHeroic())
events.ScheduleEvent(EVENT_SPELL_REFLECTION, 28000);
OwnTalk(SAY_AGGRO);
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER)
OwnTalk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
OwnTalk(SAY_DEATH);
if (InstanceScript* instance = me->GetInstanceScript())
instance->SetData(TYPE_TEMPORUS, DONE);
}
void MoveInLineOfSight(Unit* who) override
{
if (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_TIME_KEEPER)
{
if (me->IsWithinDistInMap(who, 20.0f))
{
OwnTalk(SAY_BANISH);
me->CastSpell(me, SPELL_BANISH_DRAGON_HELPER, true);
return;
}
}
ScriptedAI::MoveInLineOfSight(who);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_HASTEN:
me->CastSpell(me, SPELL_HASTEN, false);
events.ScheduleEvent(EVENT_HASTEN, 20000);
break;
case EVENT_MORTAL_WOUND:
me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false);
events.ScheduleEvent(EVENT_MORTAL_WOUND, 10000);
break;
case EVENT_WING_BUFFET:
me->CastSpell(me, SPELL_WING_BUFFET, false);
events.ScheduleEvent(EVENT_WING_BUFFET, 20000);
break;
case EVENT_SPELL_REFLECTION:
me->CastSpell(me, SPELL_REFLECT, false);
events.ScheduleEvent(EVENT_SPELL_REFLECTION, 30000);
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{ {
return GetTheBlackMorassAI<boss_temporusAI>(creature); OwnTalk(SAY_ENTER);
ScriptedAI::InitializeAI();
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
scheduler.Schedule(12s, [this](TaskContext context)
{
DoCastSelf(SPELL_HASTEN);
context.Repeat(20s);
}).Schedule(5s, [this](TaskContext context)
{
DoCastVictim(SPELL_MORTAL_WOUND);
context.Repeat(10s);
}).Schedule(20s, [this](TaskContext context)
{
DoCastAOE(SPELL_WING_BUFFET);
context.Repeat(20s);
});
if (IsHeroic())
{
scheduler.Schedule(28s, [this](TaskContext context)
{
DoCastSelf(SPELL_REFLECT);
context.Repeat(30s);
});
}
OwnTalk(SAY_AGGRO);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer())
{
OwnTalk(SAY_SLAY);
}
}
void JustDied(Unit* /*killer*/) override
{
OwnTalk(SAY_DEATH);
_JustDied();
}
void MoveInLineOfSight(Unit* who) override
{
if (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_TIME_KEEPER)
{
if (me->IsWithinDistInMap(who, 20.0f))
{
OwnTalk(SAY_BANISH);
me->CastSpell(me, SPELL_BANISH_DRAGON_HELPER, true);
return;
}
}
ScriptedAI::MoveInLineOfSight(who);
} }
}; };
void AddSC_boss_temporus() void AddSC_boss_temporus()
{ {
new boss_temporus(); RegisterTheBlackMorassCreatureAI(boss_temporus);
} }

View File

@@ -23,8 +23,7 @@
#include "TemporarySummon.h" #include "TemporarySummon.h"
#include "the_black_morass.h" #include "the_black_morass.h"
#define MAX_PORTAL_LOCATIONS 4 const Position PortalLocation[4] =
const Position PortalLocation[MAX_PORTAL_LOCATIONS] =
{ {
{ -2030.8318f, 7024.9443f, 23.071817f, 3.14159f }, { -2030.8318f, 7024.9443f, 23.071817f, 3.14159f },
{ -1961.7335f, 7029.5280f, 21.811401f, 2.12931f }, { -1961.7335f, 7029.5280f, 21.811401f, 2.12931f },
@@ -32,6 +31,11 @@ const Position PortalLocation[MAX_PORTAL_LOCATIONS] =
{ -1930.9106f, 7183.5970f, 23.007639f, 3.59537f } { -1930.9106f, 7183.5970f, 23.007639f, 3.59537f }
}; };
ObjectData const creatureData[1] =
{
{ NPC_MEDIVH, DATA_MEDIVH }
};
class instance_the_black_morass : public InstanceMapScript class instance_the_black_morass : public InstanceMapScript
{ {
public: public:
@@ -44,67 +48,172 @@ public:
struct instance_the_black_morass_InstanceMapScript : public InstanceScript struct instance_the_black_morass_InstanceMapScript : public InstanceScript
{ {
instance_the_black_morass_InstanceMapScript(Map* map) : InstanceScript(map) { } instance_the_black_morass_InstanceMapScript(Map* map) : InstanceScript(map)
GuidSet encounterNPCs;
uint32 encounters[MAX_ENCOUNTER];
ObjectGuid _medivhGUID;
uint8 _currentRift;
int8 _shieldPercent;
void Initialize() override
{ {
SetHeaders(DataHeader); SetHeaders(DataHeader);
memset(&encounters, 0, sizeof(encounters)); SetBossNumber(EncounterCount);
LoadObjectData(creatureData, nullptr);
_currentRift = 0; _currentRift = 0;
_shieldPercent = 100; _shieldPercent = 100;
encounterNPCs.clear(); _encounterNPCs.clear();
_timerToNextBoss = 0; _timerToNextBoss = 0;
} }
void CleanupInstance() void CleanupInstance()
{ {
Events.Reset();
_currentRift = 0; _currentRift = 0;
_shieldPercent = 100; _shieldPercent = 100;
_usedRiftPostions.fill(ObjectGuid::Empty); _availableRiftPositions.clear();
_scheduler.CancelAll();
instance->LoadGrid(-2023.0f, 7121.0f); for (Position const& pos : PortalLocation)
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{ {
medivh->DespawnOrUnsummon(); _availableRiftPositions.push_back(pos);
medivh->SetRespawnTime(3);
} }
GuidSet eCopy = encounterNPCs; instance->LoadGrid(-2023.0f, 7121.0f);
for (ObjectGuid const& guid : eCopy) if (Creature* medivh = GetCreature(DATA_MEDIVH))
if (Creature* creature = instance->GetCreature(guid)) {
creature->DespawnOrUnsummon(); medivh->DespawnOrUnsummon(0ms, 3s);
}
} }
bool IsEncounterInProgress() const override bool SetBossState(uint32 type, EncounterState state) override
{ {
return false; if (!InstanceScript::SetBossState(type, state))
{
return false;
}
if (state == DONE)
{
switch (type)
{
case DATA_AEONUS:
{
if (Creature* medivh = GetCreature(DATA_MEDIVH))
{
medivh->AI()->DoAction(ACTION_OUTRO);
}
instance->DoForAllPlayers([&](Player* player)
{
if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
{
player->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);
}
if (player->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
{
player->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
}
});
break;
}
case DATA_CHRONO_LORD_DEJA:
case DATA_TEMPORUS:
{
for (ObjectGuid const& guid : _encounterNPCs)
{
if (Creature* creature = instance->GetCreature(guid))
{
switch (creature->GetEntry())
{
case NPC_RIFT_KEEPER_WARLOCK:
case NPC_RIFT_KEEPER_MAGE:
case NPC_RIFT_LORD:
case NPC_RIFT_LORD_2:
case NPC_TIME_RIFT:
creature->DespawnOrUnsummon();
break;
default:
break;
}
}
}
if (!_timerToNextBoss || _timerToNextBoss > 30 * IN_MILLISECONDS)
{
ScheduleNextPortal(30s);
}
else
{
ScheduleNextPortal(Milliseconds(_timerToNextBoss));
}
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
break;
}
default:
break;
}
}
return true;
} }
void OnPlayerEnter(Player* player) override void OnPlayerEnter(Player* player) override
{ {
if (instance->GetPlayersCountExceptGMs() <= 1 && GetData(TYPE_AEONUS) != DONE) if (instance->GetPlayersCountExceptGMs() <= 1 && GetBossState(DATA_AEONUS) != DONE)
{
CleanupInstance(); CleanupInstance();
}
player->SendUpdateWorldState(WORLD_STATE_BM, _currentRift > 0 ? 1 : 0); player->SendUpdateWorldState(WORLD_STATE_BM, _currentRift > 0 ? 1 : 0);
player->SendUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent); player->SendUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
player->SendUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift); player->SendUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
} }
void ScheduleNextPortal(Milliseconds time)
{
_scheduler.CancelGroup(CONTEXT_GROUP_RIFTS);
_scheduler.Schedule(time, [this](TaskContext context)
{
if (GetCreature(DATA_MEDIVH))
{
Position spawnPos;
if (!_availableRiftPositions.empty())
{
spawnPos = Acore::Containers::SelectRandomContainerElement(_availableRiftPositions);
_availableRiftPositions.remove(spawnPos);
DoUpdateWorldState(WORLD_STATE_BM_RIFT, ++_currentRift);
if (Creature* rift = instance->SummonCreature(NPC_TIME_RIFT, spawnPos))
{
_scheduler.Schedule(6s, [this, rift](TaskContext)
{
SummonPortalKeeper(rift);
});
}
// Here we check if we have available rift spots.
// If there are spots available, spawn a rift instantly.
if (!_availableRiftPositions.empty())
{
context.Repeat((_currentRift >= 13 ? 2min : 90s));
}
else
{
context.Repeat(3s);
}
}
else
{
context.Repeat(3s);
}
context.SetGroup(CONTEXT_GROUP_RIFTS);
}
});
}
void OnCreatureCreate(Creature* creature) override void OnCreatureCreate(Creature* creature) override
{ {
switch (creature->GetEntry()) switch (creature->GetEntry())
{ {
case NPC_MEDIVH:
_medivhGUID = creature->GetGUID();
break;
case NPC_TIME_RIFT: case NPC_TIME_RIFT:
case NPC_CHRONO_LORD_DEJA: case NPC_CHRONO_LORD_DEJA:
case NPC_INFINITE_CHRONO_LORD: case NPC_INFINITE_CHRONO_LORD:
@@ -121,9 +230,11 @@ public:
case NPC_INFINITE_EXECUTIONER: case NPC_INFINITE_EXECUTIONER:
case NPC_INFINITE_VANQUISHER: case NPC_INFINITE_VANQUISHER:
case NPC_DP_BEAM_STALKER: case NPC_DP_BEAM_STALKER:
encounterNPCs.insert(creature->GetGUID()); _encounterNPCs.insert(creature->GetGUID());
break; break;
} }
InstanceScript::OnCreatureCreate(creature);
} }
void OnCreatureRemove(Creature* creature) override void OnCreatureRemove(Creature* creature) override
@@ -131,6 +242,22 @@ public:
switch (creature->GetEntry()) switch (creature->GetEntry())
{ {
case NPC_TIME_RIFT: case NPC_TIME_RIFT:
_availableRiftPositions.push_back(creature->GetHomePosition());
if (GetBossState(DATA_AEONUS) != DONE)
{
// Here we check if we have available rift spots.
// If there are spots available, spawn a rift instantly.
if (!_availableRiftPositions.empty())
{
ScheduleNextPortal(4s);
}
else
{
ScheduleNextPortal((_currentRift >= 13 ? 2min : 90s));
}
}
[[fallthrough]];
case NPC_CHRONO_LORD_DEJA: case NPC_CHRONO_LORD_DEJA:
case NPC_INFINITE_CHRONO_LORD: case NPC_INFINITE_CHRONO_LORD:
case NPC_TEMPORUS: case NPC_TEMPORUS:
@@ -145,92 +272,28 @@ public:
case NPC_INFINITE_CRONOMANCER: case NPC_INFINITE_CRONOMANCER:
case NPC_INFINITE_EXECUTIONER: case NPC_INFINITE_EXECUTIONER:
case NPC_INFINITE_VANQUISHER: case NPC_INFINITE_VANQUISHER:
encounterNPCs.erase(creature->GetGUID()); _encounterNPCs.erase(creature->GetGUID());
break; break;
} }
InstanceScript::OnCreatureRemove(creature);
} }
void SetData(uint32 type, uint32 data) override void SetData(uint32 type, uint32 data) override
{ {
switch (type) switch (type)
{ {
case TYPE_AEONUS:
{
encounters[type] = DONE;
SaveToDB();
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{
medivh->AI()->DoAction(ACTION_OUTRO);
}
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
{
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* player = itr->GetSource())
{
if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
{
player->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);
}
if (player->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
{
player->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
}
}
}
}
break;
}
case TYPE_CHRONO_LORD_DEJA:
case TYPE_TEMPORUS:
{
GuidSet eCopy = encounterNPCs;
for (ObjectGuid const& guid : eCopy)
{
if (Creature* creature = instance->GetCreature(guid))
{
switch (creature->GetEntry())
{
case NPC_RIFT_KEEPER_WARLOCK:
case NPC_RIFT_KEEPER_MAGE:
case NPC_RIFT_LORD:
case NPC_RIFT_LORD_2:
case NPC_TIME_RIFT:
creature->DespawnOrUnsummon();
break;
default:
break;
}
}
}
encounters[type] = DONE;
if (!_timerToNextBoss || _timerToNextBoss > 30 * IN_MILLISECONDS)
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 30 * IN_MILLISECONDS);
}
else
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, _timerToNextBoss);
}
Events.SetPhase(1);
SaveToDB();
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
break;
}
case DATA_MEDIVH: case DATA_MEDIVH:
{ {
DoUpdateWorldState(WORLD_STATE_BM, 1); DoUpdateWorldState(WORLD_STATE_BM, 1);
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent); DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift); DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 3000);
ScheduleNextPortal(3s);
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS; _timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
for (ObjectGuid const& guid : encounterNPCs) for (ObjectGuid const& guid : _encounterNPCs)
{ {
if (guid.GetEntry() == NPC_DP_BEAM_STALKER) if (guid.GetEntry() == NPC_DP_BEAM_STALKER)
{ {
@@ -264,26 +327,73 @@ public:
if (!_shieldPercent) if (!_shieldPercent)
{ {
if (Creature* medivh = instance->GetCreature(_medivhGUID)) if (Creature* medivh = GetCreature(DATA_MEDIVH))
{ {
if (medivh->IsAlive()) if (medivh->IsAlive() && medivh->IsAIEnabled)
{ {
medivh->SetImmuneToNPC(true); medivh->SetImmuneToNPC(true);
medivh->AI()->Talk(SAY_MEDIVH_DEATH);
if (medivh->IsAIEnabled) for (ObjectGuid const& guid : _encounterNPCs)
{
medivh->AI()->Talk(SAY_MEDIVH_DEATH);
}
Events.ScheduleEvent(EVENT_WIPE_1, 4s);
for (ObjectGuid const& guid : encounterNPCs)
{ {
if (Creature* creature = instance->GetCreature(guid)) if (Creature* creature = instance->GetCreature(guid))
{ {
creature->InterruptNonMeleeSpells(true); creature->InterruptNonMeleeSpells(true);
} }
} }
// Step 1 - Medivh loses all auras.
_scheduler.Schedule(4s, [this](TaskContext)
{
if (Creature* medivh = GetCreature(DATA_MEDIVH))
{
medivh->RemoveAllAuras();
}
// Step 2 - Medivh dies and visual effect NPCs are despawned.
_scheduler.Schedule(500ms, [this](TaskContext)
{
if (Creature* medivh = GetCreature(DATA_MEDIVH))
{
medivh->KillSelf(false);
GuidSet encounterNPCSCopy = _encounterNPCs;
for (ObjectGuid const& guid : encounterNPCSCopy)
{
switch (guid.GetEntry())
{
case NPC_TIME_RIFT:
case NPC_DP_EMITTER_STALKER:
case NPC_DP_CRYSTAL_STALKER:
case NPC_DP_BEAM_STALKER:
if (Creature* creature = instance->GetCreature(guid))
{
creature->DespawnOrUnsummon();
}
break;
default:
break;
}
}
}
// Step 3 - All summoned creatures despawn
_scheduler.Schedule(2s, [this](TaskContext)
{
GuidSet encounterNPCSCopy = _encounterNPCs;
for (ObjectGuid const& guid : encounterNPCSCopy)
{
if (Creature* creature = instance->GetCreature(guid))
{
creature->CastSpell(creature, SPELL_TELEPORT_VISUAL, true);
creature->DespawnOrUnsummon(1200ms, 0s);
}
}
_scheduler.CancelAll();
});
});
});
} }
} }
} }
@@ -298,10 +408,6 @@ public:
{ {
switch (type) switch (type)
{ {
case TYPE_CHRONO_LORD_DEJA:
case TYPE_TEMPORUS:
case TYPE_AEONUS:
return encounters[type];
case DATA_SHIELD_PERCENT: case DATA_SHIELD_PERCENT:
return _shieldPercent; return _shieldPercent;
case DATA_RIFT_NUMBER: case DATA_RIFT_NUMBER:
@@ -310,66 +416,21 @@ public:
return 0; return 0;
} }
void SetGuidData(uint32 type, ObjectGuid data) override void SummonPortalKeeper(Creature* rift)
{ {
if (type == DATA_SUMMONED_NPC)
encounterNPCs.insert(data);
else if (type == DATA_DELETED_NPC)
encounterNPCs.erase(data);
else if (type == DATA_RIFT_KILLED)
{
if (!Events.IsInPhase(1))
{
uint8 emptySpots = 0;
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
{
if (!_usedRiftPostions[i])
{
++emptySpots;
}
if (_usedRiftPostions[i] == data)
{
_usedRiftPostions[i].Clear();
}
}
if (emptySpots >= MAX_PORTAL_LOCATIONS - 1)
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 4000);
}
else if (!emptySpots)
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, (_currentRift >= 13 ? 120 : 90) * IN_MILLISECONDS);
}
}
}
}
ObjectGuid GetGuidData(uint32 data) const override
{
if (data == DATA_MEDIVH)
return _medivhGUID;
return ObjectGuid::Empty;
}
void SummonPortalKeeper(uint32 eventId)
{
uint8 riftPosition = eventId - EVENT_SUMMON_KEEPER_1;
ObjectGuid const& riftGUID = _usedRiftPostions[riftPosition];
Creature* rift = instance->GetCreature(riftGUID);
if (!rift) if (!rift)
{
return; return;
}
int32 entry = 0; int32 entry = 0;
switch (_currentRift) switch (_currentRift)
{ {
case 6: case 6:
entry = GetData(TYPE_CHRONO_LORD_DEJA) == DONE ? (instance->IsHeroic() ? NPC_INFINITE_CHRONO_LORD : -NPC_CHRONO_LORD_DEJA) : NPC_CHRONO_LORD_DEJA; entry = GetBossState(DATA_CHRONO_LORD_DEJA) == DONE ? (instance->IsHeroic() ? NPC_INFINITE_CHRONO_LORD : -NPC_CHRONO_LORD_DEJA) : NPC_CHRONO_LORD_DEJA;
break; break;
case 12: case 12:
entry = GetData(TYPE_TEMPORUS) == DONE ? (instance->IsHeroic() ? NPC_INFINITE_TIMEREAVER : -NPC_TEMPORUS) : NPC_TEMPORUS; entry = GetBossState(DATA_TEMPORUS) == DONE ? (instance->IsHeroic() ? NPC_INFINITE_TIMEREAVER : -NPC_TEMPORUS) : NPC_TEMPORUS;
break; break;
case 18: case 18:
entry = NPC_AEONUS; entry = NPC_AEONUS;
@@ -381,21 +442,22 @@ public:
Position pos = rift->GetNearPosition(10.0f, 2 * M_PI * rand_norm()); Position pos = rift->GetNearPosition(10.0f, 2 * M_PI * rand_norm());
if (TempSummon* summon = instance->SummonCreature(std::abs(entry), pos)) if (Creature* summon = rift->SummonCreature(std::abs(entry), pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3 * MINUTE * IN_MILLISECONDS))
{ {
summon->SetTempSummonType(TEMPSUMMON_CORPSE_TIMED_DESPAWN);
summon->SetTimer(3 * MINUTE * IN_MILLISECONDS);
if (entry < 0) if (entry < 0)
{
summon->SetLootMode(0); summon->SetLootMode(0);
}
if (summon->GetEntry() != NPC_AEONUS) if (summon->GetEntry() != NPC_AEONUS)
{ {
rift->AI()->SetGUID(summon->GetGUID());
rift->CastSpell(summon, SPELL_RIFT_CHANNEL, false); rift->CastSpell(summon, SPELL_RIFT_CHANNEL, false);
} }
else else
{
summon->SetReactState(REACT_DEFENSIVE); summon->SetReactState(REACT_DEFENSIVE);
_scheduler.CancelGroup(CONTEXT_GROUP_RIFTS);
}
} }
} }
@@ -413,130 +475,16 @@ public:
} }
} }
Events.Update(diff); _scheduler.Update();
uint32 eventId = Events.ExecuteEvent();
switch (eventId)
{
case EVENT_NEXT_PORTAL:
{
if (instance->GetCreature(_medivhGUID))
{
uint8 position = MAX_PORTAL_LOCATIONS;
std::vector<uint8> possibleSpots;
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
{
if (!_usedRiftPostions[i])
{
possibleSpots.push_back(i);
}
}
if (!possibleSpots.empty())
{
position = Acore::Containers::SelectRandomContainerElement(possibleSpots);
}
if (position < MAX_PORTAL_LOCATIONS)
{
++_currentRift;
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
Events.ScheduleEvent(EVENT_SUMMON_KEEPER_1 + position, 6000);
Events.SetPhase(0);
if (Creature* rift = instance->SummonCreature(NPC_TIME_RIFT, PortalLocation[position]))
{
_usedRiftPostions[position] = rift->GetGUID();
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
{
if (!_usedRiftPostions[i])
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, (_currentRift >= 13 ? 120 : 90) * IN_MILLISECONDS);
break;
}
}
}
}
}
break;
}
case EVENT_SUMMON_KEEPER_1:
case EVENT_SUMMON_KEEPER_2:
case EVENT_SUMMON_KEEPER_3:
case EVENT_SUMMON_KEEPER_4:
SummonPortalKeeper(eventId);
break;
case EVENT_WIPE_1:
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{
medivh->RemoveAllAuras();
}
Events.ScheduleEvent(EVENT_WIPE_2, 500ms);
break;
case EVENT_WIPE_2:
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{
medivh->KillSelf(false);
GuidSet encounterNPCSCopy = encounterNPCs;
for (ObjectGuid const& guid : encounterNPCSCopy)
{
switch (guid.GetEntry())
{
case NPC_TIME_RIFT:
case NPC_DP_EMITTER_STALKER:
case NPC_DP_CRYSTAL_STALKER:
case NPC_DP_BEAM_STALKER:
if (Creature* creature = instance->GetCreature(guid))
{
creature->DespawnOrUnsummon();
}
break;
default:
break;
}
}
}
Events.ScheduleEvent(EVENT_WIPE_3, 2s);
break;
case EVENT_WIPE_3:
{
GuidSet encounterNPCSCopy = encounterNPCs;
for (ObjectGuid const& guid : encounterNPCSCopy)
{
if (Creature* creature = instance->GetCreature(guid))
{
creature->CastSpell(creature, SPELL_TELEPORT_VISUAL, true);
creature->DespawnOrUnsummon(1200ms, 0s);
}
}
break;
}
default:
break;
}
}
void ReadSaveDataMore(std::istringstream& data) override
{
data >> encounters[0];
data >> encounters[1];
data >> encounters[2];
}
void WriteSaveDataMore(std::ostringstream& data) override
{
data << encounters[0] << ' '
<< encounters[1] << ' '
<< encounters[2] << ' ';
} }
protected: protected:
EventMap Events; std::list<Position> _availableRiftPositions;
std::array<ObjectGuid, MAX_PORTAL_LOCATIONS> _usedRiftPostions;
uint32 _timerToNextBoss; uint32 _timerToNextBoss;
GuidSet _encounterNPCs;
uint8 _currentRift;
int8 _shieldPercent;
TaskScheduler _scheduler;
}; };
}; };

View File

@@ -91,7 +91,7 @@ struct npc_medivh_bm : public ScriptedAI
events.Reset(); events.Reset();
me->CastSpell(me, SPELL_MANA_SHIELD, true); me->CastSpell(me, SPELL_MANA_SHIELD, true);
if (_instance->GetData(TYPE_AEONUS) != DONE) if (_instance->GetBossState(DATA_AEONUS) != DONE)
{ {
me->CastSpell(me, SPELL_MEDIVH_CHANNEL, false); me->CastSpell(me, SPELL_MEDIVH_CHANNEL, false);
} }
@@ -101,8 +101,6 @@ struct npc_medivh_bm : public ScriptedAI
void JustSummoned(Creature* summon) override void JustSummoned(Creature* summon) override
{ {
_instance->SetGuidData(DATA_SUMMONED_NPC, summon->GetGUID());
if (summon->GetEntry() == NPC_DP_CRYSTAL_STALKER) if (summon->GetEntry() == NPC_DP_CRYSTAL_STALKER)
{ {
summon->DespawnOrUnsummon(25000); summon->DespawnOrUnsummon(25000);
@@ -119,14 +117,9 @@ struct npc_medivh_bm : public ScriptedAI
} }
} }
void SummonedCreatureDespawn(Creature* summon) override
{
_instance->SetGuidData(DATA_DELETED_NPC, summon->GetGUID());
}
void MoveInLineOfSight(Unit* who) override void MoveInLineOfSight(Unit* who) override
{ {
if (!events.Empty() || _instance->GetData(TYPE_AEONUS) == DONE) if (!events.Empty() || _instance->GetBossState(DATA_AEONUS) == DONE)
{ {
return; return;
} }
@@ -274,9 +267,12 @@ struct npc_time_rift : public NullCreatureAI
events.ScheduleEvent(EVENT_CHECK_DEATH, 8000); events.ScheduleEvent(EVENT_CHECK_DEATH, 8000);
} }
void SetGUID(ObjectGuid guid, int32) override void JustSummoned(Creature* creature) override
{ {
_riftKeeperGUID = guid; if (creature->GetEntry() != NPC_AEONUS)
{
_riftKeeperGUID = creature->GetGUID();
}
} }
void DoSummonAtRift(uint32 entry) void DoSummonAtRift(uint32 entry)
@@ -284,16 +280,15 @@ struct npc_time_rift : public NullCreatureAI
Position pos = me->GetNearPosition(10.0f, 2 * M_PI * rand_norm()); Position pos = me->GetNearPosition(10.0f, 2 * M_PI * rand_norm());
if (Creature* summon = me->SummonCreature(entry, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 150000)) if (Creature* summon = me->SummonCreature(entry, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 150000))
if (_instance) {
if (Creature* medivh = _instance->GetCreature(DATA_MEDIVH))
{ {
if (Unit* medivh = ObjectAccessor::GetUnit(*me, _instance->GetGuidData(DATA_MEDIVH))) float o = medivh->GetAngle(summon) + frand(-1.0f, 1.0f);
{ summon->SetHomePosition(medivh->GetPositionX() + 14.0f * cos(o), medivh->GetPositionY() + 14.0f * std::sin(o), medivh->GetPositionZ(), summon->GetAngle(medivh));
float o = medivh->GetAngle(summon) + frand(-1.0f, 1.0f); summon->GetMotionMaster()->MoveTargetedHome(true);
summon->SetHomePosition(medivh->GetPositionX() + 14.0f * cos(o), medivh->GetPositionY() + 14.0f * std::sin(o), medivh->GetPositionZ(), summon->GetAngle(medivh)); summon->SetReactState(REACT_DEFENSIVE);
summon->GetMotionMaster()->MoveTargetedHome(true);
summon->SetReactState(REACT_DEFENSIVE);
}
} }
}
} }
void DoSelectSummon() void DoSelectSummon()
@@ -326,8 +321,6 @@ struct npc_time_rift : public NullCreatureAI
Creature* riftKeeper = ObjectAccessor::GetCreature(*me, _riftKeeperGUID); Creature* riftKeeper = ObjectAccessor::GetCreature(*me, _riftKeeperGUID);
if (!riftKeeper || !riftKeeper->IsAlive()) if (!riftKeeper || !riftKeeper->IsAlive())
{ {
_instance->SetGuidData(DATA_RIFT_KILLED, me->GetGUID());
me->DespawnOrUnsummon(0); me->DespawnOrUnsummon(0);
break; break;
} }

View File

@@ -24,13 +24,15 @@
#define DataHeader "TBM" #define DataHeader "TBM"
uint32 const EncounterCount = 3;
#define TheBlackMorassScriptName "instance_the_black_morass" #define TheBlackMorassScriptName "instance_the_black_morass"
enum DataTypes enum DataTypes
{ {
TYPE_CHRONO_LORD_DEJA = 0, DATA_CHRONO_LORD_DEJA = 0,
TYPE_TEMPORUS = 1, DATA_TEMPORUS = 1,
TYPE_AEONUS = 2, DATA_AEONUS = 2,
MAX_ENCOUNTER = 3, MAX_ENCOUNTER = 3,
DATA_MEDIVH = 10, DATA_MEDIVH = 10,
@@ -89,16 +91,9 @@ enum Misc
SPELL_RIFT_CHANNEL = 31387, SPELL_RIFT_CHANNEL = 31387,
SPELL_TELEPORT_VISUAL = 7791, SPELL_TELEPORT_VISUAL = 7791,
EVENT_NEXT_PORTAL = 1, ACTION_OUTRO = 1,
EVENT_SUMMON_KEEPER_1 = 2,
EVENT_SUMMON_KEEPER_2 = 3,
EVENT_SUMMON_KEEPER_3 = 4,
EVENT_SUMMON_KEEPER_4 = 5,
EVENT_WIPE_1 = 6,
EVENT_WIPE_2 = 7,
EVENT_WIPE_3 = 8,
ACTION_OUTRO = 1 CONTEXT_GROUP_RIFTS = 1
}; };
enum medivhSays enum medivhSays