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
};
enum Events
struct boss_aeonus : public BossAI
{
EVENT_SANDBREATH = 1,
EVENT_TIMESTOP = 2,
EVENT_FRENZY = 3,
EVENT_CLEAVE = 4
};
boss_aeonus(Creature* creature) : BossAI(creature, DATA_AEONUS) { }
class boss_aeonus : public CreatureScript
{
public:
boss_aeonus() : CreatureScript("boss_aeonus") { }
struct boss_aeonusAI : public ScriptedAI
void JustReachedHome() override
{
boss_aeonusAI(Creature* creature) : ScriptedAI(creature)
if (Creature* medivh = instance->GetCreature(DATA_MEDIVH))
{
instance = creature->GetInstanceScript();
}
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)))
if (me->GetDistance2d(medivh) < 20.0f)
{
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();
DoCastAOE(SPELL_CORRUPT_MEDIVH);
}
}
}
void JustEngagedWith(Unit* /*who*/) 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
void IsSummonedBy(WorldObject* /*summoner*/) 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()
{
new boss_aeonus();
RegisterTheBlackMorassCreatureAI(boss_aeonus);
}

View File

@@ -35,121 +35,84 @@ enum Enums
SPELL_BANISH_DRAGON_HELPER = 31550,
};
enum Events
{
EVENT_ARCANE_BLAST = 1,
EVENT_TIME_LAPSE = 2,
EVENT_ARCANE_DISCHARGE = 3,
EVENT_ATTRACTION = 4
};
struct boss_chrono_lord_deja : public BossAI
{
boss_chrono_lord_deja(Creature* creature) : BossAI(creature, DATA_CHRONO_LORD_DEJA) { }
class boss_chrono_lord_deja : public CreatureScript
{
public:
boss_chrono_lord_deja() : CreatureScript("boss_chrono_lord_deja") { }
void OwnTalk(uint32 id)
{
if (me->GetEntry() == NPC_CHRONO_LORD_DEJA)
{
Talk(id);
}
}
struct boss_chrono_lord_dejaAI : public ScriptedAI
{
boss_chrono_lord_dejaAI(Creature* creature) : ScriptedAI(creature) { }
void InitializeAI() override
{
OwnTalk(SAY_ENTER);
ScriptedAI::InitializeAI();
}
EventMap events;
void JustEngagedWith(Unit* /*who*/) override
{
OwnTalk(SAY_AGGRO);
_JustEngagedWith();
void Reset() override
{
events.Reset();
}
scheduler.Schedule(10s, [this](TaskContext context)
{
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 (me->GetEntry() == NPC_CHRONO_LORD_DEJA)
Talk(id);
}
if (IsHeroic())
{
scheduler.Schedule(20s, [this](TaskContext context)
{
DoCastAOE(SPELL_ATTRACTION);
context.Repeat(30s);
});
}
}
void InitializeAI() override
{
OwnTalk(SAY_ENTER);
ScriptedAI::InitializeAI();
}
void MoveInLineOfSight(Unit* who) override
{
if (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_TIME_KEEPER)
{
if (me->IsWithinDistInMap(who, 20.0f))
{
OwnTalk(SAY_BANISH);
DoCastAOE(SPELL_BANISH_DRAGON_HELPER);
return;
}
}
void JustEngagedWith(Unit* /*who*/) override
{
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);
ScriptedAI::MoveInLineOfSight(who);
}
OwnTalk(SAY_AGGRO);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer())
{
OwnTalk(SAY_SLAY);
}
}
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 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 JustDied(Unit* /*killer*/) override
{
OwnTalk(SAY_DEATH);
_JustDied();
}
};
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
};
enum Events
struct boss_temporus : public BossAI
{
EVENT_HASTEN = 1,
EVENT_MORTAL_WOUND = 2,
EVENT_WING_BUFFET = 3,
EVENT_SPELL_REFLECTION = 4
};
boss_temporus(Creature* creature) : BossAI(creature, DATA_TEMPORUS) { }
class boss_temporus : public CreatureScript
{
public:
boss_temporus() : CreatureScript("boss_temporus") { }
struct boss_temporusAI : public ScriptedAI
void OwnTalk(uint32 id)
{
boss_temporusAI(Creature* creature) : ScriptedAI(creature) { }
if (me->GetEntry() == NPC_TEMPORUS)
Talk(id);
}
EventMap events;
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
void InitializeAI() 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()
{
new boss_temporus();
RegisterTheBlackMorassCreatureAI(boss_temporus);
}

View File

@@ -23,8 +23,7 @@
#include "TemporarySummon.h"
#include "the_black_morass.h"
#define MAX_PORTAL_LOCATIONS 4
const Position PortalLocation[MAX_PORTAL_LOCATIONS] =
const Position PortalLocation[4] =
{
{ -2030.8318f, 7024.9443f, 23.071817f, 3.14159f },
{ -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 }
};
ObjectData const creatureData[1] =
{
{ NPC_MEDIVH, DATA_MEDIVH }
};
class instance_the_black_morass : public InstanceMapScript
{
public:
@@ -44,67 +48,172 @@ public:
struct instance_the_black_morass_InstanceMapScript : public InstanceScript
{
instance_the_black_morass_InstanceMapScript(Map* map) : InstanceScript(map) { }
GuidSet encounterNPCs;
uint32 encounters[MAX_ENCOUNTER];
ObjectGuid _medivhGUID;
uint8 _currentRift;
int8 _shieldPercent;
void Initialize() override
instance_the_black_morass_InstanceMapScript(Map* map) : InstanceScript(map)
{
SetHeaders(DataHeader);
memset(&encounters, 0, sizeof(encounters));
SetBossNumber(EncounterCount);
LoadObjectData(creatureData, nullptr);
_currentRift = 0;
_shieldPercent = 100;
encounterNPCs.clear();
_encounterNPCs.clear();
_timerToNextBoss = 0;
}
void CleanupInstance()
{
Events.Reset();
_currentRift = 0;
_shieldPercent = 100;
_usedRiftPostions.fill(ObjectGuid::Empty);
_availableRiftPositions.clear();
_scheduler.CancelAll();
instance->LoadGrid(-2023.0f, 7121.0f);
if (Creature* medivh = instance->GetCreature(_medivhGUID))
for (Position const& pos : PortalLocation)
{
medivh->DespawnOrUnsummon();
medivh->SetRespawnTime(3);
_availableRiftPositions.push_back(pos);
}
GuidSet eCopy = encounterNPCs;
for (ObjectGuid const& guid : eCopy)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
instance->LoadGrid(-2023.0f, 7121.0f);
if (Creature* medivh = GetCreature(DATA_MEDIVH))
{
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
{
if (instance->GetPlayersCountExceptGMs() <= 1 && GetData(TYPE_AEONUS) != DONE)
if (instance->GetPlayersCountExceptGMs() <= 1 && GetBossState(DATA_AEONUS) != DONE)
{
CleanupInstance();
}
player->SendUpdateWorldState(WORLD_STATE_BM, _currentRift > 0 ? 1 : 0);
player->SendUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
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
{
switch (creature->GetEntry())
{
case NPC_MEDIVH:
_medivhGUID = creature->GetGUID();
break;
case NPC_TIME_RIFT:
case NPC_CHRONO_LORD_DEJA:
case NPC_INFINITE_CHRONO_LORD:
@@ -121,9 +230,11 @@ public:
case NPC_INFINITE_EXECUTIONER:
case NPC_INFINITE_VANQUISHER:
case NPC_DP_BEAM_STALKER:
encounterNPCs.insert(creature->GetGUID());
_encounterNPCs.insert(creature->GetGUID());
break;
}
InstanceScript::OnCreatureCreate(creature);
}
void OnCreatureRemove(Creature* creature) override
@@ -131,6 +242,22 @@ public:
switch (creature->GetEntry())
{
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_INFINITE_CHRONO_LORD:
case NPC_TEMPORUS:
@@ -145,92 +272,28 @@ public:
case NPC_INFINITE_CRONOMANCER:
case NPC_INFINITE_EXECUTIONER:
case NPC_INFINITE_VANQUISHER:
encounterNPCs.erase(creature->GetGUID());
_encounterNPCs.erase(creature->GetGUID());
break;
}
InstanceScript::OnCreatureRemove(creature);
}
void SetData(uint32 type, uint32 data) override
{
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:
{
DoUpdateWorldState(WORLD_STATE_BM, 1);
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 3000);
ScheduleNextPortal(3s);
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
for (ObjectGuid const& guid : encounterNPCs)
for (ObjectGuid const& guid : _encounterNPCs)
{
if (guid.GetEntry() == NPC_DP_BEAM_STALKER)
{
@@ -264,26 +327,73 @@ public:
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->AI()->Talk(SAY_MEDIVH_DEATH);
if (medivh->IsAIEnabled)
{
medivh->AI()->Talk(SAY_MEDIVH_DEATH);
}
Events.ScheduleEvent(EVENT_WIPE_1, 4s);
for (ObjectGuid const& guid : encounterNPCs)
for (ObjectGuid const& guid : _encounterNPCs)
{
if (Creature* creature = instance->GetCreature(guid))
{
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)
{
case TYPE_CHRONO_LORD_DEJA:
case TYPE_TEMPORUS:
case TYPE_AEONUS:
return encounters[type];
case DATA_SHIELD_PERCENT:
return _shieldPercent;
case DATA_RIFT_NUMBER:
@@ -310,66 +416,21 @@ public:
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)
{
return;
}
int32 entry = 0;
switch (_currentRift)
{
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;
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;
case 18:
entry = NPC_AEONUS;
@@ -381,21 +442,22 @@ public:
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)
{
summon->SetLootMode(0);
}
if (summon->GetEntry() != NPC_AEONUS)
{
rift->AI()->SetGUID(summon->GetGUID());
rift->CastSpell(summon, SPELL_RIFT_CHANNEL, false);
}
else
{
summon->SetReactState(REACT_DEFENSIVE);
_scheduler.CancelGroup(CONTEXT_GROUP_RIFTS);
}
}
}
@@ -413,130 +475,16 @@ public:
}
}
Events.Update(diff);
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] << ' ';
_scheduler.Update();
}
protected:
EventMap Events;
std::array<ObjectGuid, MAX_PORTAL_LOCATIONS> _usedRiftPostions;
std::list<Position> _availableRiftPositions;
uint32 _timerToNextBoss;
GuidSet _encounterNPCs;
uint8 _currentRift;
int8 _shieldPercent;
TaskScheduler _scheduler;
};
};

View File

@@ -91,7 +91,7 @@ struct npc_medivh_bm : public ScriptedAI
events.Reset();
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);
}
@@ -101,8 +101,6 @@ struct npc_medivh_bm : public ScriptedAI
void JustSummoned(Creature* summon) override
{
_instance->SetGuidData(DATA_SUMMONED_NPC, summon->GetGUID());
if (summon->GetEntry() == NPC_DP_CRYSTAL_STALKER)
{
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
{
if (!events.Empty() || _instance->GetData(TYPE_AEONUS) == DONE)
if (!events.Empty() || _instance->GetBossState(DATA_AEONUS) == DONE)
{
return;
}
@@ -274,9 +267,12 @@ struct npc_time_rift : public NullCreatureAI
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)
@@ -284,16 +280,15 @@ struct npc_time_rift : public NullCreatureAI
Position pos = me->GetNearPosition(10.0f, 2 * M_PI * rand_norm());
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));
summon->GetMotionMaster()->MoveTargetedHome(true);
summon->SetReactState(REACT_DEFENSIVE);
}
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));
summon->GetMotionMaster()->MoveTargetedHome(true);
summon->SetReactState(REACT_DEFENSIVE);
}
}
}
void DoSelectSummon()
@@ -326,8 +321,6 @@ struct npc_time_rift : public NullCreatureAI
Creature* riftKeeper = ObjectAccessor::GetCreature(*me, _riftKeeperGUID);
if (!riftKeeper || !riftKeeper->IsAlive())
{
_instance->SetGuidData(DATA_RIFT_KILLED, me->GetGUID());
me->DespawnOrUnsummon(0);
break;
}

View File

@@ -24,13 +24,15 @@
#define DataHeader "TBM"
uint32 const EncounterCount = 3;
#define TheBlackMorassScriptName "instance_the_black_morass"
enum DataTypes
{
TYPE_CHRONO_LORD_DEJA = 0,
TYPE_TEMPORUS = 1,
TYPE_AEONUS = 2,
DATA_CHRONO_LORD_DEJA = 0,
DATA_TEMPORUS = 1,
DATA_AEONUS = 2,
MAX_ENCOUNTER = 3,
DATA_MEDIVH = 10,
@@ -89,16 +91,9 @@ enum Misc
SPELL_RIFT_CHANNEL = 31387,
SPELL_TELEPORT_VISUAL = 7791,
EVENT_NEXT_PORTAL = 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,
ACTION_OUTRO = 1
CONTEXT_GROUP_RIFTS = 1
};
enum medivhSays