refactor(Scripts/VioletHold): Modernize Violet Hold dungeon scripts (#25187)

Co-authored-by: joschiwald <joschiwald@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andrew 2026-04-11 13:02:28 -03:00 committed by GitHub
parent 25b928b094
commit 272da9fbad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 1745 additions and 2222 deletions

View file

@ -1810,12 +1810,13 @@ void Map::RemoveAllObjectsInRemoveList()
}
}
uint32 Map::GetPlayersCountExceptGMs() const
uint32 Map::GetPlayersCountExceptGMs(bool aliveOnly /*= false*/) const
{
uint32 count = 0;
for (MapRefMgr::const_iterator itr = m_mapRefMgr.begin(); itr != m_mapRefMgr.end(); ++itr)
if (!itr->GetSource()->IsGameMaster())
++count;
for (auto const& ref : m_mapRefMgr)
if (Player* player = ref.GetSource())
if (!player->IsGameMaster() && (!aliveOnly || (player->IsAlive() && !player->HasSpiritOfRedemptionAura())))
++count;
return count;
}

View file

@ -318,7 +318,8 @@ public:
void markCell(uint32 pCellId) { marked_cells.set(pCellId); }
[[nodiscard]] bool HavePlayers() const { return !m_mapRefMgr.IsEmpty(); }
[[nodiscard]] uint32 GetPlayersCountExceptGMs() const;
// When aliveOnly is true, counts only players that are alive and not in Spirit of Redemption form.
[[nodiscard]] uint32 GetPlayersCountExceptGMs(bool aliveOnly = false) const;
void SendToPlayers(WorldPacket const* data) const;

View file

@ -50,141 +50,95 @@ enum eEvents
EVENT_UNROOT,
};
class boss_cyanigosa : public CreatureScript
struct boss_cyanigosa : public BossAI
{
public:
boss_cyanigosa() : CreatureScript("boss_cyanigosa") { }
boss_cyanigosa(Creature* c) : BossAI(c, DATA_CYANIGOSA) { }
CreatureAI* GetAI(Creature* pCreature) const override
void JustEngagedWith(Unit* who) override
{
return GetVioletHoldAI<boss_cyanigosaAI>(pCreature);
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
events.RescheduleEvent(EVENT_SPELL_ARCANE_VACUUM, 30s);
events.RescheduleEvent(EVENT_SPELL_BLIZZARD, 5s, 10s);
events.RescheduleEvent(EVENT_SPELL_TAIL_SWEEP, 15s, 20s);
events.RescheduleEvent(EVENT_SPELL_UNCONTROLLABLE_ENERGY, 5s, 8s);
if (IsHeroic())
events.RescheduleEvent(EVENT_SPELL_MANA_DESTRUCTION, 20s);
}
struct boss_cyanigosaAI : public ScriptedAI
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
{
boss_cyanigosaAI(Creature* c) : ScriptedAI(c)
if (!target || !spell)
return;
if (spell->Id == SPELL_ARCANE_VACUUM)
target->NearTeleportTo(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, target->GetOrientation());
}
void ExecuteEvent(uint32 eventId) override
{
switch (eventId)
{
pInstance = c->GetInstanceScript();
case EVENT_SPELL_ARCANE_VACUUM:
DoCastAOE(SPELL_ARCANE_VACUUM);
DoResetThreatList();
me->SetControlled(true, UNIT_STATE_ROOT);
me->setAttackTimer(BASE_ATTACK, 3000);
events.Repeat(30s);
events.ScheduleEvent(EVENT_UNROOT, 3s);
break;
case EVENT_UNROOT:
me->SetControlled(false, UNIT_STATE_ROOT);
break;
case EVENT_SPELL_BLIZZARD:
DoCastRandomTarget(SPELL_BLIZZARD, 0, 45.0f);
events.Repeat(15s);
break;
case EVENT_SPELL_MANA_DESTRUCTION:
DoCastRandomTarget(SPELL_MANA_DESTRUCTION, 0, 50.0f);
events.Repeat(20s);
break;
case EVENT_SPELL_TAIL_SWEEP:
DoCastVictim(SPELL_TAIL_SWEEP);
events.Repeat(15s, 20s);
break;
case EVENT_SPELL_UNCONTROLLABLE_ENERGY:
DoCastVictim(SPELL_UNCONTROLLABLE_ENERGY);
events.Repeat(20s, 25s);
break;
}
}
InstanceScript* pInstance;
EventMap events;
void Reset() override
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
float h = me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
if (h != INVALID_HEIGHT && me->GetPositionZ() - h > 3.0f)
{
events.Reset();
me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), h, me->GetOrientation(), true);
me->StopMovingOnCurrentPos();
me->DestroyForVisiblePlayers();
}
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
Talk(SAY_AGGRO);
events.Reset();
events.RescheduleEvent(EVENT_SPELL_ARCANE_VACUUM, 30s);
events.RescheduleEvent(EVENT_SPELL_BLIZZARD, 5s, 10s);
events.RescheduleEvent(EVENT_SPELL_TAIL_SWEEP, 15s, 20s);
events.RescheduleEvent(EVENT_SPELL_UNCONTROLLABLE_ENERGY, 5s, 8s);
if (IsHeroic())
events.RescheduleEvent(EVENT_SPELL_MANA_DESTRUCTION, 20s);
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
{
if (!target || !spell)
return;
switch (spell->Id)
{
case SPELL_ARCANE_VACUUM:
target->NearTeleportTo(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, target->GetOrientation());
break;
}
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_SPELL_ARCANE_VACUUM:
me->CastSpell((Unit*)nullptr, SPELL_ARCANE_VACUUM, false);
DoResetThreatList();
me->SetControlled(true, UNIT_STATE_ROOT);
me->setAttackTimer(BASE_ATTACK, 3000);
events.Repeat(30s);
events.ScheduleEvent(EVENT_UNROOT, 3s);
break;
case EVENT_UNROOT:
me->SetControlled(false, UNIT_STATE_ROOT);
break;
case EVENT_SPELL_BLIZZARD:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f, true))
me->CastSpell(target, SPELL_BLIZZARD, false);
events.Repeat(15s);
break;
case EVENT_SPELL_MANA_DESTRUCTION:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
me->CastSpell(target, SPELL_MANA_DESTRUCTION, false);
events.Repeat(20s);
break;
case EVENT_SPELL_TAIL_SWEEP:
me->CastSpell(me->GetVictim(), SPELL_TAIL_SWEEP, false);
events.Repeat(15s, 20s);
break;
case EVENT_SPELL_UNCONTROLLABLE_ENERGY:
me->CastSpell(me->GetVictim(), SPELL_UNCONTROLLABLE_ENERGY, false);
events.Repeat(20s, 25s);
break;
}
DoMeleeAttackIfReady();
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (pInstance)
pInstance->SetData(DATA_BOSS_DIED, 0);
float h = me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
if (h != INVALID_HEIGHT && me->GetPositionZ() - h > 3.0f)
{
me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), h, me->GetOrientation(), true); // move to ground
me->StopMovingOnCurrentPos();
me->DestroyForVisiblePlayers();
}
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
me->SetControlled(false, UNIT_STATE_ROOT);
ScriptedAI::EnterEvadeMode(why);
events.Reset();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (pInstance)
pInstance->SetData(DATA_FAILED, 1);
}
};
void EnterEvadeMode(EvadeReason why) override
{
me->SetControlled(false, UNIT_STATE_ROOT);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
};
void AddSC_boss_cyanigosa()
{
new boss_cyanigosa();
RegisterVioletHoldCreatureAI(boss_cyanigosa);
}

View file

@ -25,8 +25,6 @@ enum eSpells
SPELL_BREAK_BONDS = 59463,
SPELL_CHAIN_HEAL = 54481,
SPELL_EARTH_SHIELD = 54479,
//SPELL_EARTH_SHIELD_TRIGGERED_N = 54480,
//SPELL_EARTH_SHIELD_TRIGGERED_H = 59472,
SPELL_EARTH_SHOCK = 54511,
SPELL_LIGHTNING_BOLT = 53044,
SPELL_STORMSTRIKE = 51876,
@ -53,164 +51,120 @@ enum eEvents
EVENT_SPELL_STORMSTRIKE,
};
class boss_erekem : public CreatureScript
struct boss_erekem : public BossAI
{
public:
boss_erekem() : CreatureScript("boss_erekem") { }
boss_erekem(Creature* c) : BossAI(c, BOSS_EREKEM) { }
CreatureAI* GetAI(Creature* pCreature) const override
void JustEngagedWith(Unit* who) override
{
return GetVioletHoldAI<boss_erekemAI>(pCreature);
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
DoCastSelf(SPELL_EARTH_SHIELD);
events.RescheduleEvent(EVENT_SPELL_BLOODLUST, 15s);
events.RescheduleEvent(EVENT_SPELL_BREAK_BONDS, 9s, 14s);
events.RescheduleEvent(EVENT_SPELL_CHAIN_HEAL, 0ms);
events.RescheduleEvent(EVENT_SPELL_EARTH_SHIELD, 20s);
events.RescheduleEvent(EVENT_SPELL_EARTH_SHOCK, 2s, 8s);
events.RescheduleEvent(EVENT_SPELL_LIGHTNING_BOLT, 5s, 10s);
if (IsHeroic())
events.RescheduleEvent(EVENT_SPELL_STORMSTRIKE, 3s);
if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)))
if (!c->IsInCombat())
c->AI()->AttackStart(who);
if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)))
if (!c->IsInCombat())
c->AI()->AttackStart(who);
}
struct boss_erekemAI : public ScriptedAI
void ExecuteEvent(uint32 eventId) override
{
boss_erekemAI(Creature* c) : ScriptedAI(c)
switch (eventId)
{
pInstance = c->GetInstanceScript();
}
case EVENT_SPELL_BLOODLUST:
DoCastAOE(SPELL_BLOODLUST);
events.Repeat(35s, 45s);
break;
case EVENT_SPELL_BREAK_BONDS:
DoCastAOE(SPELL_BREAK_BONDS);
events.Repeat(16s, 22s);
break;
case EVENT_SPELL_CHAIN_HEAL:
if (ObjectGuid targetGuid = GetChainHealTargetGuid())
{
if (Creature* target = instance->instance->GetCreature(targetGuid))
DoCast(target, SPELL_CHAIN_HEAL);
InstanceScript* pInstance;
EventMap events;
void Reset() override
{
events.Reset();
}
void JustEngagedWith(Unit* who) override
{
DoZoneInCombat();
Talk(SAY_AGGRO);
DoCast(me, SPELL_EARTH_SHIELD);
events.Reset();
events.RescheduleEvent(EVENT_SPELL_BLOODLUST, 15s);
events.RescheduleEvent(EVENT_SPELL_BREAK_BONDS, 9s, 14s);
events.RescheduleEvent(EVENT_SPELL_CHAIN_HEAL, 0ms);
events.RescheduleEvent(EVENT_SPELL_EARTH_SHIELD, 20s);
events.RescheduleEvent(EVENT_SPELL_EARTH_SHOCK, 2s, 8s);
events.RescheduleEvent(EVENT_SPELL_LIGHTNING_BOLT, 5s, 10s);
if (IsHeroic())
events.RescheduleEvent(EVENT_SPELL_STORMSTRIKE, 3s);
if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)))
if (!c->IsInCombat())
c->AI()->AttackStart(who);
if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)))
if (!c->IsInCombat())
c->AI()->AttackStart(who);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_SPELL_BLOODLUST:
me->CastSpell((Unit*)nullptr, SPELL_BLOODLUST, false);
events.Repeat(35s, 45s);
break;
case EVENT_SPELL_BREAK_BONDS:
me->CastSpell((Unit*)nullptr, SPELL_BREAK_BONDS, false);
events.Repeat(16s, 22s);
break;
case EVENT_SPELL_CHAIN_HEAL:
if (ObjectGuid TargetGUID = GetChainHealTargetGUID())
if (pInstance)
{
if (Creature* target = pInstance->instance->GetCreature(TargetGUID))
me->CastSpell(target, SPELL_CHAIN_HEAL, false);
Creature* pGuard1 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID));
Creature* pGuard2 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID));
if ((pGuard1 && !pGuard1->IsAlive()) || (pGuard2 && !pGuard2->IsAlive()))
{
events.Repeat(3s, 6s);
break;
}
}
events.Repeat(8s, 11s);
break;
case EVENT_SPELL_EARTH_SHIELD:
me->CastSpell(me, SPELL_EARTH_SHIELD, false);
events.Repeat(20s);
break;
case EVENT_SPELL_EARTH_SHOCK:
me->CastSpell(me->GetVictim(), SPELL_EARTH_SHOCK, false);
events.Repeat(8s, 13s);
break;
case EVENT_SPELL_LIGHTNING_BOLT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 35.0f, true))
me->CastSpell(target, SPELL_LIGHTNING_BOLT, false);
events.Repeat(15s, 25s);
break;
case EVENT_SPELL_STORMSTRIKE:
Creature* guard1 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID));
Creature* guard2 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID));
if ((guard1 && !guard1->IsAlive()) || (guard2 && !guard2->IsAlive()))
{
Creature* pGuard1 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID));
Creature* pGuard2 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID));
if (pGuard1 && !pGuard1->IsAlive() && pGuard2 && !pGuard2->IsAlive()) // both dead
me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE, false);
events.Repeat(3s);
events.Repeat(3s, 6s);
break;
}
break;
}
DoMeleeAttackIfReady();
}
events.Repeat(8s, 11s);
break;
case EVENT_SPELL_EARTH_SHIELD:
DoCastSelf(SPELL_EARTH_SHIELD);
events.Repeat(20s);
break;
case EVENT_SPELL_EARTH_SHOCK:
DoCastVictim(SPELL_EARTH_SHOCK);
events.Repeat(8s, 13s);
break;
case EVENT_SPELL_LIGHTNING_BOLT:
DoCastRandomTarget(SPELL_LIGHTNING_BOLT, 0, 35.0f);
events.Repeat(15s, 25s);
break;
case EVENT_SPELL_STORMSTRIKE:
{
Creature* guard1 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID));
Creature* guard2 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID));
if (guard1 && !guard1->IsAlive() && guard2 && !guard2->IsAlive())
DoCastVictim(SPELL_STORMSTRIKE);
events.Repeat(3s);
}
break;
}
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (pInstance)
pInstance->SetData(DATA_BOSS_DIED, 0);
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
ScriptedAI::EnterEvadeMode(why);
events.Reset();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (pInstance)
pInstance->SetData(DATA_FAILED, 1);
}
ObjectGuid GetChainHealTargetGUID()
{
if (HealthBelowPct(85))
return me->GetGUID();
if (pInstance)
{
if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)))
if (c->IsAlive() && !c->HealthAbovePct(75))
return c->GetGUID();
if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)))
if (c->IsAlive() && !c->HealthAbovePct(75))
return c->GetGUID();
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
ObjectGuid GetChainHealTargetGuid()
{
if (HealthBelowPct(85))
return me->GetGUID();
}
};
if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)))
if (c->IsAlive() && !c->HealthAbovePct(75))
return c->GetGUID();
if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)))
if (c->IsAlive() && !c->HealthAbovePct(75))
return c->GetGUID();
return me->GetGUID();
}
};
enum GuardSpells
@ -227,81 +181,69 @@ enum eGuardEvents
EVENT_SPELL_STRIKE
};
class npc_erekem_guard : public CreatureScript
struct npc_erekem_guard : public ScriptedAI
{
public:
npc_erekem_guard() : CreatureScript("npc_erekem_guard") { }
CreatureAI* GetAI(Creature* pCreature) const override
npc_erekem_guard(Creature* c) : ScriptedAI(c)
{
return GetVioletHoldAI<npc_erekem_guardAI>(pCreature);
_instance = c->GetInstanceScript();
}
struct npc_erekem_guardAI : public ScriptedAI
void Reset() override
{
npc_erekem_guardAI(Creature* c) : ScriptedAI(c)
_events.Reset();
}
void JustEngagedWith(Unit* who) override
{
DoZoneInCombat();
_events.Reset();
_events.RescheduleEvent(EVENT_SPELL_GUSHING_WOUND, 1s, 3s);
_events.RescheduleEvent(EVENT_SPELL_HOWLING_SCREECH, 8s, 13s);
_events.RescheduleEvent(EVENT_SPELL_STRIKE, 4s, 8s);
if (Creature* c = _instance->GetCreature(BOSS_EREKEM))
if (!c->IsInCombat())
c->AI()->AttackStart(who);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (_events.ExecuteEvent())
{
pInstance = c->GetInstanceScript();
case EVENT_SPELL_GUSHING_WOUND:
DoCastVictim(SPELL_GUSHING_WOUND);
_events.Repeat(7s, 12s);
break;
case EVENT_SPELL_HOWLING_SCREECH:
DoCastVictim(SPELL_HOWLING_SCREECH);
_events.Repeat(8s, 13s);
break;
case EVENT_SPELL_STRIKE:
DoCastVictim(SPELL_STRIKE);
_events.Repeat(4s, 8s);
break;
}
InstanceScript* pInstance;
EventMap events;
DoMeleeAttackIfReady();
}
void Reset() override
{
events.Reset();
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void JustEngagedWith(Unit* who) override
{
DoZoneInCombat();
events.Reset();
events.RescheduleEvent(EVENT_SPELL_GUSHING_WOUND, 1s, 3s);
events.RescheduleEvent(EVENT_SPELL_HOWLING_SCREECH, 8s, 13s);
events.RescheduleEvent(EVENT_SPELL_STRIKE, 4s, 8s);
if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUID)))
if (!c->IsInCombat())
c->AI()->AttackStart(who);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_SPELL_GUSHING_WOUND:
me->CastSpell(me->GetVictim(), SPELL_GUSHING_WOUND, false);
events.Repeat(7s, 12s);
break;
case EVENT_SPELL_HOWLING_SCREECH:
me->CastSpell(me->GetVictim(), SPELL_HOWLING_SCREECH, false);
events.Repeat(8s, 13s);
break;
case EVENT_SPELL_STRIKE:
me->CastSpell(me->GetVictim(), SPELL_STRIKE, false);
events.Repeat(4s, 8s);
break;
}
DoMeleeAttackIfReady();
}
void MoveInLineOfSight(Unit* /*who*/) override {}
};
private:
InstanceScript* _instance;
EventMap _events;
};
void AddSC_boss_erekem()
{
new boss_erekem();
new npc_erekem_guard();
RegisterVioletHoldCreatureAI(boss_erekem);
RegisterVioletHoldCreatureAI(npc_erekem_guard);
}

View file

@ -58,302 +58,278 @@ enum eSpells
SPELL_WATER_BLAST = 54237,
SPELL_WATER_BOLT_VOLLEY = 54241,
SPELL_SPLASH = 59516, // casted by globule upon death
SPELL_WATER_GLOBULE = 54268, // casted when hit by visual
SPELL_CREATE_GLOBULE_VISUAL = 54260, // tar 25
SPELL_SPLASH = 59516,
SPELL_WATER_GLOBULE = 54268,
SPELL_CREATE_GLOBULE_VISUAL = 54260,
};
class boss_ichoron : public CreatureScript
enum eEvents
{
public:
boss_ichoron() : CreatureScript("boss_ichoron") { }
EVENT_WATER_BOLT_VOLLEY = 1,
EVENT_DRAINED_CHECK,
};
CreatureAI* GetAI(Creature* pCreature) const override
struct boss_ichoron : public BossAI
{
boss_ichoron(Creature* c) : BossAI(c, BOSS_ICHORON) { }
void Reset() override
{
return GetVioletHoldAI<boss_ichoronAI>(pCreature);
BossAI::Reset();
_isExploded = false;
_isFrenzy = false;
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
}
struct boss_ichoronAI : public ScriptedAI
void DoAction(int32 param) override
{
boss_ichoronAI(Creature* c) : ScriptedAI(c), globules(me)
if (!me->IsAlive())
return;
switch (param)
{
pInstance = c->GetInstanceScript();
case ACTION_WATER_ELEMENT_HIT:
instance->SetData(DATA_ACHIEV, 0);
me->ModifyHealth(int32(me->CountPctFromMaxHealth(1)));
if (_isExploded)
DoExplodeCompleted();
break;
case ACTION_WATER_ELEMENT_KILLED:
uint32 damage = me->CountPctFromMaxHealth(3);
damage = std::min(damage, me->GetHealth() - 1);
me->ModifyHealth(-int32(damage));
me->LowerPlayerDamageReq(damage);
break;
}
}
void DoExplodeCompleted()
{
_isExploded = false;
me->RemoveAura(SPELL_DRAINED);
if (!HealthBelowPct(25))
{
Talk(SAY_BUBBLE);
DoCastSelf(SPELL_PROTECTIVE_BUBBLE, true);
}
InstanceScript* pInstance;
SummonList globules;
bool bIsExploded;
bool bIsFrenzy;
uint32 uiWaterBoltVolleyTimer;
uint32 uiDrainedTimer;
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
}
void Reset() override
void IchoronDoCastToAllHostilePlayers(uint32 spellId, bool triggered)
{
Map::PlayerList const& playerList = me->GetMap()->GetPlayers();
if (playerList.IsEmpty())
return;
for (Map::PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i)
if (Player* plr = i->GetSource())
DoCast(plr, spellId, triggered);
}
void JustEngagedWith(Unit* who) override
{
_isExploded = false;
_isFrenzy = false;
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
DoCastSelf(SPELL_PROTECTIVE_BUBBLE, true);
events.RescheduleEvent(EVENT_WATER_BOLT_VOLLEY, 7s, 12s);
instance->SetData(DATA_ACHIEV, 1);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (!_isFrenzy && !_isExploded && HealthBelowPct(25))
{
globules.DespawnAll();
bIsExploded = false;
bIsFrenzy = false;
uiDrainedTimer = 15000;
uiWaterBoltVolleyTimer = urand(7000, 12000);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
Talk(SAY_ENRAGE);
DoCastSelf(SPELL_FRENZY, true);
_isFrenzy = true;
}
void DoAction(int32 param) override
if (!_isFrenzy)
{
if (!me->IsAlive())
return;
switch (param)
if (!_isExploded)
{
case ACTION_WATER_ELEMENT_HIT:
if (pInstance)
pInstance->SetData(DATA_ACHIEV, 0);
me->ModifyHealth(int32(me->CountPctFromMaxHealth(1)));
if (bIsExploded)
DoExplodeCompleted();
break;
case ACTION_WATER_ELEMENT_KILLED:
uint32 damage = me->CountPctFromMaxHealth(3);
damage = std::min(damage, me->GetHealth() - 1);
me->ModifyHealth(-int32(damage));
me->LowerPlayerDamageReq(damage);
break;
}
}
void DoExplodeCompleted()
{
bIsExploded = false;
me->RemoveAura(SPELL_DRAINED);
if (!HealthBelowPct(25))
{
Talk(SAY_BUBBLE);
me->CastSpell(me, SPELL_PROTECTIVE_BUBBLE, true);
}
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
}
void IchoronDoCastToAllHostilePlayers(uint32 spellId, bool triggered)
{
Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers();
if (PlayerList.IsEmpty())
return;
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (Player* plr = i->GetSource())
me->CastSpell(plr, spellId, triggered);
}
void JustEngagedWith(Unit* /*who*/) override
{
bIsExploded = false;
bIsFrenzy = false;
uiDrainedTimer = 15000;
uiWaterBoltVolleyTimer = urand(7000, 12000);
DoZoneInCombat();
Talk(SAY_AGGRO);
me->CastSpell(me, SPELL_PROTECTIVE_BUBBLE, true);
if (pInstance)
pInstance->SetData(DATA_ACHIEV, 1);
}
void UpdateAI(uint32 uiDiff) override
{
if (!UpdateVictim())
return;
if (!bIsFrenzy && !bIsExploded && HealthBelowPct(25))
{
Talk(SAY_ENRAGE);
me->CastSpell(me, SPELL_FRENZY, true);
bIsFrenzy = true;
}
if (!bIsFrenzy)
{
if (!bIsExploded)
if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE))
{
if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE))
me->InterruptNonMeleeSpells(false);
Talk(SAY_SHATTER);
DoZoneInCombat();
IchoronDoCastToAllHostilePlayers(SPELL_WATER_BLAST, true);
DoCastSelf(SPELL_DRAINED, true);
_isExploded = true;
events.CancelEvent(EVENT_WATER_BOLT_VOLLEY);
events.RescheduleEvent(EVENT_DRAINED_CHECK, 15s);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(11686);
for (uint8 i = 0; i < MAX_SPAWN_LOC; ++i)
{
me->InterruptNonMeleeSpells(false);
Talk(SAY_SHATTER);
DoZoneInCombat();
IchoronDoCastToAllHostilePlayers(SPELL_WATER_BLAST, true);
me->CastSpell(me, SPELL_DRAINED, true);
bIsExploded = true;
uiDrainedTimer = 15000;
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(11686);
for (uint8 i = 0; i < MAX_SPAWN_LOC; ++i)
{
float angle = rand_norm() * 2 * M_PI;
Position p1(SpawnLoc[i]), p2(SpawnLoc[i]);
p1.m_positionX += 2.5f * cos(angle);
p1.m_positionY += 2.5f * std::sin(angle);
p2.m_positionX -= 2.5f * cos(angle);
p2.m_positionY -= 2.5f * std::sin(angle);
DoSummon(NPC_ICHOR_GLOBULE, p1, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN);
DoSummon(NPC_ICHOR_GLOBULE, p2, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN);
}
float angle = rand_norm() * 2 * M_PI;
Position p1(SpawnLoc[i]), p2(SpawnLoc[i]);
p1.m_positionX += 2.5f * cos(angle);
p1.m_positionY += 2.5f * std::sin(angle);
p2.m_positionX -= 2.5f * cos(angle);
p2.m_positionY -= 2.5f * std::sin(angle);
DoSummon(NPC_ICHOR_GLOBULE, p1, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN);
DoSummon(NPC_ICHOR_GLOBULE, p2, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN);
}
}
}
else
{
if (events.ExecuteEvent() == EVENT_DRAINED_CHECK)
DoExplodeCompleted();
else
{
if (uiDrainedTimer <= uiDiff)
DoExplodeCompleted();
else
bool isWaterElementsAlive = false;
if (!summons.empty())
{
uiDrainedTimer -= uiDiff;
bool bIsWaterElementsAlive = false;
if (!globules.empty())
{
for (ObjectGuid const& guid : globules)
if (Creature* pTemp = ObjectAccessor::GetCreature(*me, guid))
if (pTemp->IsAlive())
{
bIsWaterElementsAlive = true;
break;
}
}
if (!bIsWaterElementsAlive)
DoExplodeCompleted();
for (ObjectGuid const& guid : summons)
if (Creature* temp = ObjectAccessor::GetCreature(*me, guid))
if (temp->IsAlive())
{
isWaterElementsAlive = true;
break;
}
}
if (!isWaterElementsAlive)
DoExplodeCompleted();
}
}
if (!bIsExploded)
{
if (uiWaterBoltVolleyTimer <= uiDiff)
{
me->CastSpell((Unit*)nullptr, SPELL_WATER_BOLT_VOLLEY, false);
uiWaterBoltVolleyTimer = urand(10000, 15000);
}
else uiWaterBoltVolleyTimer -= uiDiff;
}
DoMeleeAttackIfReady();
}
void JustSummoned(Creature* pSummoned) override
if (!_isExploded)
{
if (pSummoned)
if (events.ExecuteEvent() == EVENT_WATER_BOLT_VOLLEY)
{
pSummoned->SetSpeed(MOVE_RUN, 0.3f);
pSummoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
pSummoned->GetMotionMaster()->MoveFollow(me, 0, 0, MOTION_SLOT_ACTIVE, false, false);
me->CastSpell(pSummoned, SPELL_CREATE_GLOBULE_VISUAL, true); // triggered should ignore los
globules.Summon(pSummoned);
if (pInstance)
pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID());
DoCastAOE(SPELL_WATER_BOLT_VOLLEY);
events.Repeat(10s, 15s);
}
}
void SummonedCreatureDespawn(Creature* pSummoned) override
{
if (pSummoned)
{
globules.Despawn(pSummoned);
if (pInstance)
pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID());
}
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
bIsExploded = false;
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
globules.DespawnAll();
if (pInstance)
pInstance->SetData(DATA_BOSS_DIED, 0);
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
ScriptedAI::EnterEvadeMode(why);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (pInstance)
pInstance->SetData(DATA_FAILED, 1);
}
};
};
class npc_ichor_globule : public CreatureScript
{
public:
npc_ichor_globule() : CreatureScript("npc_ichor_globule") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetVioletHoldAI<npc_ichor_globuleAI>(pCreature);
DoMeleeAttackIfReady();
}
struct npc_ichor_globuleAI : public ScriptedAI
void JustSummoned(Creature* summoned) override
{
npc_ichor_globuleAI(Creature* c) : ScriptedAI(c)
if (summoned)
{
pInstance = c->GetInstanceScript();
uiRangeCheck_Timer = 1000;
summoned->SetSpeed(MOVE_RUN, 0.3f);
summoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
summoned->GetMotionMaster()->MoveFollow(me, 0, 0, MOTION_SLOT_ACTIVE, false, false);
DoCast(summoned, SPELL_CREATE_GLOBULE_VISUAL, true);
BossAI::JustSummoned(summoned);
instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID());
}
}
InstanceScript* pInstance;
uint32 uiRangeCheck_Timer;
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
void SummonedCreatureDespawn(Creature* summoned) override
{
if (summoned)
{
if (spell->Id == SPELL_CREATE_GLOBULE_VISUAL)
me->CastSpell(me, SPELL_WATER_GLOBULE, true);
BossAI::SummonedCreatureDespawn(summoned);
instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID());
}
}
void UpdateAI(uint32 uiDiff) override
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
_isExploded = false;
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(me->GetNativeDisplayId());
BossAI::JustDied(killer);
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
private:
bool _isExploded;
bool _isFrenzy;
};
enum eGlobuleEvents
{
EVENT_RANGE_CHECK = 1,
};
struct npc_ichor_globule : public ScriptedAI
{
npc_ichor_globule(Creature* c) : ScriptedAI(c)
{
_instance = c->GetInstanceScript();
}
void Reset() override
{
_events.RescheduleEvent(EVENT_RANGE_CHECK, 1s);
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_CREATE_GLOBULE_VISUAL)
DoCastSelf(SPELL_WATER_GLOBULE, true);
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
if (_events.ExecuteEvent() == EVENT_RANGE_CHECK)
{
if (uiRangeCheck_Timer < uiDiff)
{
if (pInstance)
if (Creature* pIchoron = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_ICHORON_GUID)))
if (me->IsWithinDist(pIchoron, 2.0f, false))
{
if (pIchoron->AI())
pIchoron->AI()->DoAction(ACTION_WATER_ELEMENT_HIT);
me->DespawnOrUnsummon();
}
uiRangeCheck_Timer = 1000;
}
else uiRangeCheck_Timer -= uiDiff;
if (Creature* ichoron = _instance->GetCreature(BOSS_ICHORON))
if (me->IsWithinDist(ichoron, 2.0f, false))
{
if (ichoron->AI())
ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_HIT);
me->DespawnOrUnsummon();
return;
}
_events.Repeat(1s);
}
}
void JustDied(Unit* /*killer*/) override
{
me->CastSpell(me, SPELL_SPLASH, true);
if (pInstance)
if (Creature* pIchoron = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_ICHORON_GUID)))
if (pIchoron->AI())
pIchoron->AI()->DoAction(ACTION_WATER_ELEMENT_KILLED);
me->DespawnOrUnsummon(2500ms);
}
void JustDied(Unit* /*killer*/) override
{
DoCastSelf(SPELL_SPLASH, true);
if (Creature* ichoron = _instance->GetCreature(BOSS_ICHORON))
if (ichoron->AI())
ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_KILLED);
me->DespawnOrUnsummon(2500ms);
}
void AttackStart(Unit* /*who*/) override {}
void MoveInLineOfSight(Unit* /*who*/) override {}
};
void AttackStart(Unit* /*who*/) override {}
void MoveInLineOfSight(Unit* /*who*/) override {}
private:
InstanceScript* _instance;
EventMap _events;
};
void AddSC_boss_ichoron()
{
new boss_ichoron();
new npc_ichor_globule();
RegisterVioletHoldCreatureAI(boss_ichoron);
RegisterVioletHoldCreatureAI(npc_ichor_globule);
}

View file

@ -35,97 +35,53 @@ enum eEvents
EVENT_SPELL_CAUTERIZING_FLAMES,
};
class boss_lavanthor : public CreatureScript
struct boss_lavanthor : public BossAI
{
public:
boss_lavanthor() : CreatureScript("boss_lavanthor") { }
boss_lavanthor(Creature* c) : BossAI(c, BOSS_LAVANTHOR) { }
CreatureAI* GetAI(Creature* pCreature) const override
void JustEngagedWith(Unit* who) override
{
return GetVioletHoldAI<boss_lavanthorAI>(pCreature);
BossAI::JustEngagedWith(who);
events.RescheduleEvent(EVENT_SPELL_FIREBOLT, 1s);
events.RescheduleEvent(EVENT_SPELL_FLAME_BREATH, 5s);
events.RescheduleEvent(EVENT_SPELL_LAVA_BURN, 10s);
if (IsHeroic())
events.RescheduleEvent(EVENT_SPELL_CAUTERIZING_FLAMES, 3s);
}
struct boss_lavanthorAI : public ScriptedAI
void ExecuteEvent(uint32 eventId) override
{
boss_lavanthorAI(Creature* c) : ScriptedAI(c)
switch (eventId)
{
pInstance = c->GetInstanceScript();
case EVENT_SPELL_FIREBOLT:
DoCastVictim(SPELL_FIREBOLT);
events.Repeat(5s, 13s);
break;
case EVENT_SPELL_FLAME_BREATH:
DoCastVictim(SPELL_FLAME_BREATH);
events.Repeat(10s, 15s);
break;
case EVENT_SPELL_LAVA_BURN:
DoCastVictim(SPELL_LAVA_BURN);
events.Repeat(14s, 20s);
break;
case EVENT_SPELL_CAUTERIZING_FLAMES:
DoCastAOE(SPELL_CAUTERIZING_FLAMES);
events.Repeat(10s, 16s);
break;
}
}
InstanceScript* pInstance;
EventMap events;
void MoveInLineOfSight(Unit* /*who*/) override {}
void Reset() override
{
events.Reset();
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
events.Reset();
events.RescheduleEvent(EVENT_SPELL_FIREBOLT, 1s);
events.RescheduleEvent(EVENT_SPELL_FLAME_BREATH, 5s);
events.RescheduleEvent(EVENT_SPELL_LAVA_BURN, 10s);
if (IsHeroic())
events.RescheduleEvent(EVENT_SPELL_CAUTERIZING_FLAMES, 3s);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_SPELL_FIREBOLT:
me->CastSpell(me->GetVictim(), SPELL_FIREBOLT, false);
events.Repeat(5s, 13s);
break;
case EVENT_SPELL_FLAME_BREATH:
me->CastSpell(me->GetVictim(), SPELL_FLAME_BREATH, false);
events.Repeat(10s, 15s);
break;
case EVENT_SPELL_LAVA_BURN:
me->CastSpell(me->GetVictim(), SPELL_LAVA_BURN, false);
events.Repeat(14s, 20s);
break;
case EVENT_SPELL_CAUTERIZING_FLAMES:
me->CastSpell((Unit*)nullptr, SPELL_FLAME_BREATH, false);
events.Repeat(10s, 16s);
break;
}
DoMeleeAttackIfReady();
}
void JustDied(Unit* /*killer*/) override
{
if (pInstance)
pInstance->SetData(DATA_BOSS_DIED, 0);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
ScriptedAI::EnterEvadeMode(why);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (pInstance)
pInstance->SetData(DATA_FAILED, 1);
}
};
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
};
void AddSC_boss_lavanthor()
{
new boss_lavanthor();
RegisterVioletHoldCreatureAI(boss_lavanthor);
}

View file

@ -25,12 +25,7 @@
enum eSpells
{
SPELL_RAY_OF_SUFFERING = 54442,
//SPELL_RAY_OF_SUFFERING_TRIGGERED = 54417,
SPELL_RAY_OF_PAIN = 54438,
//SPELL_RAY_OF_PAIN_TRIGGERED_N = 54416,
//SPELL_RAY_OF_PAIN_TRIGGERED_H = 59525,
SPELL_CORROSIVE_SALIVA = 54527,
SPELL_OPTIC_LINK = 54396,
};
@ -41,90 +36,46 @@ enum eEvents
EVENT_SPELL_OPTIC_LINK,
};
class boss_moragg : public CreatureScript
struct boss_moragg : public BossAI
{
public:
boss_moragg() : CreatureScript("boss_moragg") { }
boss_moragg(Creature* c) : BossAI(c, BOSS_MORAGG) { }
CreatureAI* GetAI(Creature* pCreature) const override
void JustEngagedWith(Unit* who) override
{
return GetVioletHoldAI<boss_moraggAI>(pCreature);
BossAI::JustEngagedWith(who);
DoCastSelf(SPELL_RAY_OF_SUFFERING, true);
DoCastSelf(SPELL_RAY_OF_PAIN, true);
events.RescheduleEvent(EVENT_SPELL_CORROSIVE_SALIVA, 4s, 6s);
events.RescheduleEvent(EVENT_SPELL_OPTIC_LINK, 10s, 11s);
}
struct boss_moraggAI : public ScriptedAI
void ExecuteEvent(uint32 eventId) override
{
boss_moraggAI(Creature* c) : ScriptedAI(c)
switch (eventId)
{
pInstance = c->GetInstanceScript();
case EVENT_SPELL_CORROSIVE_SALIVA:
DoCastVictim(SPELL_CORROSIVE_SALIVA);
events.Repeat(8s, 10s);
break;
case EVENT_SPELL_OPTIC_LINK:
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 40.0f, true))
{
DoCast(target, SPELL_OPTIC_LINK);
events.Repeat(18s, 21s);
}
else
events.Repeat(5s);
break;
}
}
InstanceScript* pInstance;
EventMap events;
void MoveInLineOfSight(Unit* /*who*/) override {}
void Reset() override
{
events.Reset();
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
me->CastSpell(me, SPELL_RAY_OF_SUFFERING, true);
me->CastSpell(me, SPELL_RAY_OF_PAIN, true);
events.Reset();
events.RescheduleEvent(EVENT_SPELL_CORROSIVE_SALIVA, 4s, 6s);
events.RescheduleEvent(EVENT_SPELL_OPTIC_LINK, 10s, 11s);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_SPELL_CORROSIVE_SALIVA:
me->CastSpell(me->GetVictim(), SPELL_CORROSIVE_SALIVA, false);
events.Repeat(8s, 10s);
break;
case EVENT_SPELL_OPTIC_LINK:
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 40.0f, true))
{
me->CastSpell(target, SPELL_OPTIC_LINK, false);
events.Repeat(18s, 21s);
}
else
events.Repeat(5s);
break;
}
DoMeleeAttackIfReady();
}
void JustDied(Unit* /*killer*/) override
{
if (pInstance)
pInstance->SetData(DATA_BOSS_DIED, 0);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
ScriptedAI::EnterEvadeMode(why);
events.Reset();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (pInstance)
pInstance->SetData(DATA_FAILED, 1);
}
};
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
};
class spell_optic_link_aura : public AuraScript
@ -147,6 +98,6 @@ class spell_optic_link_aura : public AuraScript
void AddSC_boss_moragg()
{
new boss_moragg();
RegisterVioletHoldCreatureAI(boss_moragg);
RegisterSpellScript(spell_optic_link_aura);
}

View file

@ -39,8 +39,6 @@ enum eSpells
SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138,
SPELL_ARCANE_POWER = 54160
//SPELL_SUMMON_PLAYERS = 54164, // not used
//SPELL_POWER_BALL_VISUAL = 54141,
};
enum eEvents
@ -51,159 +49,114 @@ enum eEvents
EVENT_CHECK_DISTANCE,
};
class boss_xevozz : public CreatureScript
struct boss_xevozz : public BossAI
{
public:
boss_xevozz() : CreatureScript("boss_xevozz") { }
boss_xevozz(Creature* c) : BossAI(c, BOSS_XEVOZZ) { }
CreatureAI* GetAI(Creature* pCreature) const override
void JustEngagedWith(Unit* who) override
{
return GetVioletHoldAI<boss_xevozzAI>(pCreature);
Talk(SAY_AGGRO);
BossAI::JustEngagedWith(who);
events.RescheduleEvent(EVENT_SPELL_ARCANE_BARRAGE_VOLLEY, 16s, 20s);
events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s);
}
struct boss_xevozzAI : public ScriptedAI
void ExecuteEvent(uint32 eventId) override
{
boss_xevozzAI(Creature* c) : ScriptedAI(c), spheres(me)
switch (eventId)
{
pInstance = c->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList spheres;
void Reset() override
{
events.Reset();
spheres.DespawnAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
DoZoneInCombat();
events.Reset();
events.RescheduleEvent(EVENT_SPELL_ARCANE_BARRAGE_VOLLEY, 16s, 20s);
events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_SPELL_ARCANE_BARRAGE_VOLLEY:
me->CastSpell((Unit*)nullptr, SPELL_ARCANE_BARRAGE_VOLLEY, false);
events.Repeat(20s);
break;
case EVENT_SPELL_ARCANE_BUFFET:
me->CastSpell(me->GetVictim(), SPELL_ARCANE_BUFFET, false);
break;
case EVENT_SUMMON_SPHERES:
case EVENT_SPELL_ARCANE_BARRAGE_VOLLEY:
DoCastAOE(SPELL_ARCANE_BARRAGE_VOLLEY);
events.Repeat(20s);
break;
case EVENT_SPELL_ARCANE_BUFFET:
DoCastVictim(SPELL_ARCANE_BUFFET);
break;
case EVENT_SUMMON_SPHERES:
{
Talk(SAY_SUMMON_ENERGY);
summons.DespawnAll();
uint32 entry1 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3);
DoCastAOE(entry1, true);
if (IsHeroic())
{
Talk(SAY_SUMMON_ENERGY);
spheres.DespawnAll();
uint32 entry1 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3);
me->CastSpell((Unit*)nullptr, entry1, true);
if (IsHeroic())
{
uint32 entry2;
do { entry2 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3); }
while (entry1 == entry2);
me->CastSpell((Unit*)nullptr, entry2, true);
}
events.Repeat(45s);
events.RescheduleEvent(EVENT_SPELL_ARCANE_BUFFET, 5s);
events.RescheduleEvent(EVENT_CHECK_DISTANCE, 6s);
uint32 entry2;
do { entry2 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3); }
while (entry1 == entry2);
DoCastAOE(entry2, true);
}
break;
case EVENT_CHECK_DISTANCE:
events.Repeat(45s);
events.RescheduleEvent(EVENT_SPELL_ARCANE_BUFFET, 5s);
events.RescheduleEvent(EVENT_CHECK_DISTANCE, 6s);
}
break;
case EVENT_CHECK_DISTANCE:
{
bool found = false;
for (ObjectGuid const& guid : summons)
if (Creature* sphere = instance->instance->GetCreature(guid))
if (me->GetDistance(sphere) < 3.0f)
{
sphere->CastSpell(me, SPELL_ARCANE_POWER, false);
sphere->DespawnOrUnsummon(8s);
found = true;
}
if (found)
{
bool found = false;
if (pInstance)
for (ObjectGuid const& guid : spheres)
if (Creature* c = pInstance->instance->GetCreature(guid))
if (me->GetDistance(c) < 3.0f)
{
c->CastSpell(me, SPELL_ARCANE_POWER, false);
c->DespawnOrUnsummon(8s);
found = true;
}
if (found)
{
Talk(SAY_CHARGED);
events.Repeat(9s);
events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s);
}
else
events.Repeat(2s);
Talk(SAY_CHARGED);
events.Repeat(9s);
events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s);
}
break;
}
DoMeleeAttackIfReady();
else
events.Repeat(2s);
}
break;
}
}
void JustSummoned(Creature* pSummoned) override
void JustSummoned(Creature* summoned) override
{
if (summoned)
{
if (pSummoned)
{
pSummoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f, MOTION_SLOT_ACTIVE, true, false);
spheres.Summon(pSummoned);
if (pInstance)
pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID());
}
summoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f, MOTION_SLOT_ACTIVE, true, false);
BossAI::JustSummoned(summoned);
instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID());
}
}
void SummonedCreatureDespawn(Creature* pSummoned) override
void SummonedCreatureDespawn(Creature* summoned) override
{
if (summoned)
{
if (pSummoned)
{
spheres.Despawn(pSummoned);
if (pInstance)
pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID());
}
BossAI::SummonedCreatureDespawn(summoned);
instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID());
}
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
spheres.DespawnAll();
if (pInstance)
pInstance->SetData(DATA_BOSS_DIED, 0);
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void KilledUnit(Unit* pVictim) override
{
if (pVictim && pVictim->GetGUID() == me->GetGUID())
return;
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
Talk(SAY_SLAY);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
ScriptedAI::EnterEvadeMode(why);
events.Reset();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (pInstance)
pInstance->SetData(DATA_FAILED, 1);
}
};
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
};
void AddSC_boss_xevozz()
{
new boss_xevozz();
RegisterVioletHoldCreatureAI(boss_xevozz);
}

View file

@ -36,12 +36,12 @@ enum eSpells
SPELL_VOID_SHIFT = 54361,
SPELL_SUMMON_VOID_SENTRY = 54369,
SPELL_SUMMON_VOID_SENTRY_BALL = 58650,
//SPELL_ZURAMAT_ADD_2_N = 54342,
//SPELL_ZURAMAT_ADD_2_H = 59747,
};
#define NPC_VOID_SENTRY_BALL 29365
enum eCreatures
{
NPC_VOID_SENTRY_BALL = 29365,
};
enum eEvents
{
@ -50,208 +50,154 @@ enum eEvents
EVENT_SPELL_SUMMON_VOID_SENTRY,
};
class boss_zuramat : public CreatureScript
enum eVoidSentryEvents
{
public:
boss_zuramat() : CreatureScript("boss_zuramat") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetVioletHoldAI<boss_zuramatAI>(pCreature);
}
struct boss_zuramatAI : public ScriptedAI
{
boss_zuramatAI(Creature* c) : ScriptedAI(c), summons(me)
{
pInstance = c->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
void Reset() override
{
events.Reset();
summons.DespawnAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
DoZoneInCombat();
events.Reset();
events.RescheduleEvent(EVENT_SPELL_SHROUD_OF_DARKNESS, 5s, 7s);
events.RescheduleEvent(EVENT_SPELL_VOID_SHIFT, 23s, 25s);
events.RescheduleEvent(EVENT_SPELL_SUMMON_VOID_SENTRY, 10s);
if (pInstance)
pInstance->SetData(DATA_ACHIEV, 1);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case 0:
break;
case EVENT_SPELL_SHROUD_OF_DARKNESS:
me->CastSpell(me, SPELL_SHROUD_OF_DARKNESS, false);
Talk(SAY_SHIELD);
events.Repeat(20s);
break;
case EVENT_SPELL_VOID_SHIFT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true))
{
me->CastSpell(target, SPELL_VOID_SHIFT, false);
me->Whisper("Gaze... into the void.", LANG_UNIVERSAL, target->ToPlayer());
}
events.Repeat(18s, 22s);
break;
case EVENT_SPELL_SUMMON_VOID_SENTRY:
me->CastSpell((Unit*)nullptr, SPELL_SUMMON_VOID_SENTRY, false);
events.Repeat(12s);
break;
}
DoMeleeAttackIfReady();
}
void JustDied(Unit* /*killer*/) override
{
summons.DespawnAll();
Talk(SAY_DEATH);
if (pInstance)
pInstance->SetData(DATA_BOSS_DIED, 0);
}
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void JustSummoned(Creature* pSummoned) override
{
if (pSummoned)
{
summons.Summon(pSummoned);
pSummoned->SetPhaseMask(16, true);
if (pInstance)
pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID());
}
}
void SummonedCreatureDespawn(Creature* pSummoned) override
{
if (pSummoned)
{
summons.Despawn(pSummoned);
if (pSummoned->IsAIEnabled)
pSummoned->AI()->DoAction(-1337);
if (pInstance)
pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID());
}
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
ScriptedAI::EnterEvadeMode(why);
events.Reset();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (pInstance)
pInstance->SetData(DATA_FAILED, 1);
}
};
EVENT_CHECK_SUMMONER = 1,
};
class npc_vh_void_sentry : public CreatureScript
struct boss_zuramat : public BossAI
{
public:
npc_vh_void_sentry() : CreatureScript("npc_vh_void_sentry") { }
boss_zuramat(Creature* c) : BossAI(c, BOSS_ZURAMAT) { }
CreatureAI* GetAI(Creature* pCreature) const override
void JustEngagedWith(Unit* who) override
{
return GetVioletHoldAI<npc_vh_void_sentryAI>(pCreature);
Talk(SAY_AGGRO);
BossAI::JustEngagedWith(who);
events.RescheduleEvent(EVENT_SPELL_SHROUD_OF_DARKNESS, 5s, 7s);
events.RescheduleEvent(EVENT_SPELL_VOID_SHIFT, 23s, 25s);
events.RescheduleEvent(EVENT_SPELL_SUMMON_VOID_SENTRY, 10s);
instance->SetData(DATA_ACHIEV, 1);
}
struct npc_vh_void_sentryAI : public NullCreatureAI
void ExecuteEvent(uint32 eventId) override
{
npc_vh_void_sentryAI(Creature* c) : NullCreatureAI(c)
switch (eventId)
{
pInstance = c->GetInstanceScript();
SummonedGUID.Clear();
checkTimer = 5000;
//me->CastSpell(me, SPELL_SUMMON_VOID_SENTRY_BALL, true);
if (Creature* pSummoned = me->SummonCreature(NPC_VOID_SENTRY_BALL, *me, TEMPSUMMON_TIMED_DESPAWN, 300000))
{
pSummoned->SetPhaseMask(1, true);
SummonedGUID = pSummoned->GetGUID();
pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID());
}
case EVENT_SPELL_SHROUD_OF_DARKNESS:
DoCastSelf(SPELL_SHROUD_OF_DARKNESS);
Talk(SAY_SHIELD);
events.Repeat(20s);
break;
case EVENT_SPELL_VOID_SHIFT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true))
{
DoCast(target, SPELL_VOID_SHIFT);
me->Whisper("Gaze... into the void.", LANG_UNIVERSAL, target->ToPlayer());
}
events.Repeat(18s, 22s);
break;
case EVENT_SPELL_SUMMON_VOID_SENTRY:
DoCastAOE(SPELL_SUMMON_VOID_SENTRY);
events.Repeat(12s);
break;
}
}
InstanceScript* pInstance;
ObjectGuid SummonedGUID;
uint16 checkTimer;
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void DoAction(int32 a) override
void KilledUnit(Unit* victim) override
{
if (victim && victim->GetGUID() == me->GetGUID())
return;
Talk(SAY_SLAY);
}
void JustSummoned(Creature* summoned) override
{
if (summoned)
{
if (a == -1337)
if (Creature* c = pInstance->instance->GetCreature(SummonedGUID))
c->DespawnOrUnsummon();
BossAI::JustSummoned(summoned);
summoned->SetPhaseMask(16, true);
instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID());
}
}
void JustDied(Unit* /*killer*/) override
void SummonedCreatureDespawn(Creature* summoned) override
{
if (summoned)
{
if (pInstance)
{
pInstance->SetData(DATA_ACHIEV, 0);
if (Creature* c = pInstance->instance->GetCreature(SummonedGUID))
c->DespawnOrUnsummon();
}
me->DespawnOrUnsummon(5s);
BossAI::SummonedCreatureDespawn(summoned);
if (summoned->IsAIEnabled)
summoned->AI()->DoAction(-1337);
instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID());
}
}
void SummonedCreatureDespawn(Creature* pSummoned) override
{
if (pSummoned)
pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID());
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void UpdateAI(uint32 diff) override
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
_EnterEvadeMode(why);
}
};
struct npc_vh_void_sentry : public NullCreatureAI
{
npc_vh_void_sentry(Creature* c) : NullCreatureAI(c)
{
_instance = c->GetInstanceScript();
_summonedGuid.Clear();
_events.ScheduleEvent(EVENT_CHECK_SUMMONER, 5s);
if (Creature* summoned = me->SummonCreature(NPC_VOID_SENTRY_BALL, *me, TEMPSUMMON_TIMED_DESPAWN, 300000))
{
if (checkTimer <= diff)
{
checkTimer = 5000;
bool good = false;
if (me->IsSummon())
if (Unit* s = me->ToTempSummon()->GetSummonerUnit())
if (s->IsAlive())
good = true;
if (!good)
me->KillSelf();
}
else
checkTimer -= diff;
summoned->SetPhaseMask(1, true);
_summonedGuid = summoned->GetGUID();
_instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID());
}
};
}
void DoAction(int32 action) override
{
if (action == -1337)
if (Creature* voidSentry = _instance->instance->GetCreature(_summonedGuid))
voidSentry->DespawnOrUnsummon();
}
void JustDied(Unit* /*killer*/) override
{
_instance->SetData(DATA_ACHIEV, 0);
if (Creature* voidSentry = _instance->instance->GetCreature(_summonedGuid))
voidSentry->DespawnOrUnsummon();
me->DespawnOrUnsummon(5s);
}
void SummonedCreatureDespawn(Creature* summoned) override
{
if (summoned)
_instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID());
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
if (_events.ExecuteEvent() == EVENT_CHECK_SUMMONER)
{
bool good = false;
if (me->IsSummon())
if (Unit* s = me->ToTempSummon()->GetSummonerUnit())
if (s->IsAlive())
good = true;
if (!good)
me->KillSelf();
_events.Repeat(5s);
}
}
private:
InstanceScript* _instance;
ObjectGuid _summonedGuid;
EventMap _events;
};
void AddSC_boss_zuramat()
{
new boss_zuramat();
new npc_vh_void_sentry();
RegisterVioletHoldCreatureAI(boss_zuramat);
RegisterVioletHoldCreatureAI(npc_vh_void_sentry);
}

View file

@ -22,7 +22,7 @@
#include "WorldStateDefines.h"
#include "violet_hold.h"
enum vYells
enum VHYells
{
CYANIGOSA_SAY_SPAWN = 3,
SAY_SINCLARI_LEAVING = 0,
@ -30,175 +30,163 @@ enum vYells
SAY_SINCLARI_COMPLETE = 2,
};
ObjectData const creatureData[] =
{
{ NPC_MORAGG, BOSS_MORAGG },
{ NPC_EREKEM, BOSS_EREKEM },
{ NPC_ICHORON, BOSS_ICHORON },
{ NPC_LAVANTHOR, BOSS_LAVANTHOR },
{ NPC_XEVOZZ, BOSS_XEVOZZ },
{ NPC_ZURAMAT, BOSS_ZURAMAT },
{ NPC_CYANIGOSA, DATA_CYANIGOSA },
{ NPC_SINCLARI, DATA_SINCLARI },
{ NPC_PRISON_DOOR_SEAL, DATA_DOOR_SEAL },
{ NPC_TELEPORTATION_PORTAL, DATA_TELEPORTATION_PORTAL },
{ 0, 0 }
};
ObjectData const goData[] =
{
{ GO_MAIN_DOOR, DATA_MAIN_DOOR },
{ GO_MORAGG_DOOR, DATA_MORAGG_CELL },
{ GO_EREKEM_DOOR, DATA_EREKEM_CELL },
{ GO_EREKEM_GUARD_1_DOOR, DATA_EREKEM_GUARD_1_CELL },
{ GO_EREKEM_GUARD_2_DOOR, DATA_EREKEM_GUARD_2_CELL },
{ GO_ICHORON_DOOR, DATA_ICHORON_CELL },
{ GO_LAVANTHOR_DOOR, DATA_LAVANTHOR_CELL },
{ GO_XEVOZZ_DOOR, DATA_XEVOZZ_CELL },
{ GO_ZURAMAT_DOOR, DATA_ZURAMAT_CELL },
{ 0, 0 }
};
class instance_violet_hold : public InstanceMapScript
{
public:
instance_violet_hold() : InstanceMapScript("instance_violet_hold", MAP_VIOLET_HOLD) { }
InstanceScript* GetInstanceScript(InstanceMap* pMap) const override
InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
return new instance_violet_hold_InstanceMapScript(pMap);
return new instance_violet_hold_InstanceMapScript(map);
}
struct instance_violet_hold_InstanceMapScript : public InstanceScript
{
instance_violet_hold_InstanceMapScript(Map* pMap) : InstanceScript(pMap) {}
uint32 m_auiEncounter[MAX_ENCOUNTER];
bool CLEANED;
uint8 EncounterStatus;
uint32 uiFirstBoss, uiSecondBoss;
std::string str_data;
EventMap events;
uint8 GateHealth;
uint8 WaveCount;
uint8 PortalLocation;
bool bAchiev;
bool bDefensesUsed;
GuidVector GO_ActivationCrystalGUID;
ObjectGuid GO_MainGateGUID;
ObjectGuid GO_MoraggCellGUID;
ObjectGuid GO_ErekemCellGUID;
ObjectGuid GO_ErekemRightGuardCellGUID;
ObjectGuid GO_ErekemLeftGuardCellGUID;
ObjectGuid GO_IchoronCellGUID;
ObjectGuid GO_LavanthorCellGUID;
ObjectGuid GO_XevozzCellGUID;
ObjectGuid GO_ZuramatCellGUID;
GuidSet trashMobs;
ObjectGuid NPC_SinclariGUID;
ObjectGuid NPC_GuardGUID[4];
ObjectGuid NPC_PortalGUID;
ObjectGuid NPC_DoorSealGUID;
ObjectGuid NPC_MoraggGUID;
ObjectGuid NPC_ErekemGUID;
ObjectGuid NPC_ErekemGuardGUID[2];
ObjectGuid NPC_IchoronGUID;
ObjectGuid NPC_LavanthorGUID;
ObjectGuid NPC_XevozzGUID;
ObjectGuid NPC_ZuramatGUID;
ObjectGuid NPC_CyanigosaGUID;
instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map)
{
SetHeaders(DataHeader);
SetBossNumber(MAX_BOSS);
SetPersistentDataCount(PERSISTENT_DATA_COUNT);
LoadObjectData(creatureData, goData);
}
void Initialize() override
{
SetHeaders(DataHeader);
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
CLEANED = false;
EncounterStatus = NOT_STARTED;
uiFirstBoss = 0;
uiSecondBoss = 0;
events.Reset();
events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0ms);
GateHealth = 100;
WaveCount = 0;
PortalLocation = 0;
bDefensesUsed = false;
_cleaned = false;
_encounterStatus = NOT_STARTED;
_events.Reset();
_events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0ms);
_gateHealth = 100;
_waveCount = 0;
_portalLocation = 0;
_defensesUsed = false;
GO_ActivationCrystalGUID.clear();
_activationCrystalGuidList.clear();
}
bool IsEncounterInProgress() const override
{
return false;
return _encounterStatus == IN_PROGRESS;
}
void OnCreatureCreate(Creature* creature) override
{
InstanceScript::OnCreatureCreate(creature);
switch (creature->GetEntry())
{
case NPC_SINCLARI:
NPC_SinclariGUID = creature->GetGUID();
break;
case NPC_VIOLET_HOLD_GUARD:
for (uint8 i = 0; i < 4; ++i)
if (!NPC_GuardGUID[i])
for (ObjectGuid& guid : _guardGuid)
if (!guid)
{
NPC_GuardGUID[i] = creature->GetGUID();
guid = creature->GetGUID();
break;
}
break;
case NPC_DEFENSE_DUMMY_TARGET:
creature->ApplySpellImmune(0, IMMUNITY_ID, SPELL_ARCANE_LIGHTNING, true);
break;
case NPC_TELEPORTATION_PORTAL:
NPC_PortalGUID = creature->GetGUID();
break;
case NPC_PRISON_DOOR_SEAL:
NPC_DoorSealGUID = creature->GetGUID();
break;
// BOSSES BELOW:
case NPC_XEVOZZ:
NPC_XevozzGUID = creature->GetGUID();
break;
case NPC_LAVANTHOR:
NPC_LavanthorGUID = creature->GetGUID();
break;
case NPC_ICHORON:
NPC_IchoronGUID = creature->GetGUID();
break;
case NPC_ZURAMAT:
NPC_ZuramatGUID = creature->GetGUID();
break;
case NPC_EREKEM:
NPC_ErekemGUID = creature->GetGUID();
break;
case NPC_EREKEM_GUARD:
if (!NPC_ErekemGuardGUID[0])
NPC_ErekemGuardGUID[0] = creature->GetGUID();
if (!_erekemGuardGuid[0])
_erekemGuardGuid[0] = creature->GetGUID();
else
NPC_ErekemGuardGUID[1] = creature->GetGUID();
break;
case NPC_MORAGG:
NPC_MoraggGUID = creature->GetGUID();
break;
case NPC_CYANIGOSA:
NPC_CyanigosaGUID = creature->GetGUID();
_erekemGuardGuid[1] = creature->GetGUID();
break;
}
}
void OnGameObjectCreate(GameObject* go) override
{
switch (go->GetEntry())
InstanceScript::OnGameObjectCreate(go);
if (go->GetEntry() == GO_ACTIVATION_CRYSTAL)
{
case GO_ACTIVATION_CRYSTAL:
HandleGameObject(ObjectGuid::Empty, false, go); // make go not used yet
go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); // not useable at the beginning
GO_ActivationCrystalGUID.push_back(go->GetGUID());
HandleGameObject(ObjectGuid::Empty, false, go);
go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
_activationCrystalGuidList.push_back(go->GetGUID());
}
}
bool SetBossState(uint32 id, EncounterState state) override
{
if (!InstanceScript::SetBossState(id, state))
return false;
switch (id)
{
case BOSS_MORAGG:
case BOSS_EREKEM:
case BOSS_ICHORON:
case BOSS_LAVANTHOR:
case BOSS_XEVOZZ:
case BOSS_ZURAMAT:
if (state == DONE)
{
if (_waveCount == 6)
SetBossState(DATA_1ST_BOSS, DONE);
else if (_waveCount == 12)
SetBossState(DATA_2ND_BOSS, DONE);
_events.RescheduleEvent(EVENT_SUMMON_PORTAL, 35s);
}
else if (state == FAIL || state == NOT_STARTED)
{
_cleaned = false;
InstanceCleanup();
}
break;
case GO_MAIN_DOOR:
GO_MainGateGUID = go->GetGUID();
case DATA_CYANIGOSA:
if (state == DONE)
{
_encounterStatus = DONE;
HandleGameObject(DATA_MAIN_DOOR, true);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0);
if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
sinclari->AI()->Talk(SAY_SINCLARI_COMPLETE);
sinclari->DespawnOrUnsummon();
sinclari->SetRespawnTime(3);
}
}
else if (state == FAIL || state == NOT_STARTED)
{
_cleaned = false;
InstanceCleanup();
}
break;
// BOSS GATES BELOW:
case GO_EREKEM_GUARD_1_DOOR:
GO_ErekemLeftGuardCellGUID = go->GetGUID();
break;
case GO_EREKEM_GUARD_2_DOOR:
GO_ErekemRightGuardCellGUID = go->GetGUID();
break;
case GO_EREKEM_DOOR:
GO_ErekemCellGUID = go->GetGUID();
break;
case GO_ZURAMAT_DOOR:
GO_ZuramatCellGUID = go->GetGUID();
break;
case GO_LAVANTHOR_DOOR:
GO_LavanthorCellGUID = go->GetGUID();
break;
case GO_MORAGG_DOOR:
GO_MoraggCellGUID = go->GetGUID();
break;
case GO_ICHORON_DOOR:
GO_IchoronCellGUID = go->GetGUID();
break;
case GO_XEVOZZ_DOOR:
GO_XevozzCellGUID = go->GetGUID();
default:
break;
}
return true;
}
void SetData(uint32 type, uint32 data) override
@ -208,90 +196,57 @@ public:
case DATA_ACTIVATE_DEFENSE_SYSTEM:
{
if (data)
bDefensesUsed = true;
const Position pos = {1919.09546f, 812.29724f, 86.2905f, M_PI};
_defensesUsed = true;
Position const pos = {1919.09546f, 812.29724f, 86.2905f, M_PI};
instance->SummonCreature(NPC_DEFENSE_SYSTEM, pos, 0, 6499);
}
break;
case DATA_START_INSTANCE:
if (EncounterStatus == NOT_STARTED)
if (_encounterStatus == NOT_STARTED)
{
EncounterStatus = IN_PROGRESS;
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
_encounterStatus = IN_PROGRESS;
if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
c->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
c->AI()->Talk(SAY_SINCLARI_LEAVING);
/// @todo: Missing orientation for Sinclari's movement and "interaction" animation with the nearby crystal.
sinclari->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
sinclari->AI()->Talk(SAY_SINCLARI_LEAVING);
}
events.RescheduleEvent(EVENT_GUARDS_FALL_BACK, 4s);
_events.RescheduleEvent(EVENT_GUARDS_FALL_BACK, 4s);
}
break;
case DATA_PORTAL_DEFEATED:
events.RescheduleEvent(EVENT_SUMMON_PORTAL, 3s);
_events.RescheduleEvent(EVENT_SUMMON_PORTAL, 3s);
break;
case DATA_PORTAL_LOCATION:
PortalLocation = data;
_portalLocation = data;
break;
case DATA_DECRASE_DOOR_HEALTH:
if (GateHealth > 0)
--GateHealth;
if (GateHealth == 0)
case DATA_DECREASE_DOOR_HEALTH:
if (_gateHealth > 0)
--_gateHealth;
if (_gateHealth == 0)
{
CLEANED = false;
_cleaned = false;
InstanceCleanup();
}
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)GateHealth);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)_gateHealth);
break;
case DATA_RELEASE_BOSS:
if (WaveCount == 6)
StartBossEncounter(uiFirstBoss);
if (_waveCount == 6)
StartBossEncounter(GetPersistentData(PERSISTENT_DATA_FIRST_BOSS));
else
StartBossEncounter(uiSecondBoss);
break;
case DATA_BOSS_DIED:
if (WaveCount == 6)
m_auiEncounter[0] = DONE;
else if (WaveCount == 12)
m_auiEncounter[1] = DONE;
else if (WaveCount == 18)
{
m_auiEncounter[2] = DONE;
EncounterStatus = DONE;
HandleGameObject(GO_MainGateGUID, true);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0);
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
{
c->AI()->Talk(SAY_SINCLARI_COMPLETE);
c->DespawnOrUnsummon();
c->SetRespawnTime(3);
}
}
SaveToDB();
if (WaveCount < 18)
events.RescheduleEvent(EVENT_SUMMON_PORTAL, 35s);
break;
case DATA_FAILED:
CLEANED = false;
InstanceCleanup();
StartBossEncounter(GetPersistentData(PERSISTENT_DATA_SECOND_BOSS));
break;
case DATA_ACHIEV:
bAchiev = !!data;
_achievementCompleted = !!data;
break;
}
}
void SetGuidData(uint32 type, ObjectGuid data) override
{
switch (type)
{
case DATA_ADD_TRASH_MOB:
trashMobs.insert(data);
break;
case DATA_DELETE_TRASH_MOB:
if (!CLEANED)
trashMobs.erase(data);
break;
}
if (type == DATA_ADD_TRASH_MOB)
_trashMobs.insert(data);
else if (type == DATA_DELETE_TRASH_MOB && !_cleaned)
_trashMobs.erase(data);
}
uint32 GetData(uint32 type) const override
@ -299,15 +254,11 @@ public:
switch (type)
{
case DATA_ENCOUNTER_STATUS:
return (uint32)EncounterStatus;
return (uint32)_encounterStatus;
case DATA_WAVE_COUNT:
return (uint32)WaveCount;
return (uint32)_waveCount;
case DATA_PORTAL_LOCATION:
return PortalLocation;
case DATA_FIRST_BOSS_NUMBER:
return uiFirstBoss;
case DATA_SECOND_BOSS_NUMBER:
return uiSecondBoss;
return _portalLocation;
}
return 0;
@ -315,212 +266,180 @@ public:
ObjectGuid GetGuidData(uint32 identifier) const override
{
switch (identifier)
{
case DATA_TELEPORTATION_PORTAL_GUID:
return NPC_PortalGUID;
case DATA_DOOR_SEAL_GUID:
return NPC_DoorSealGUID;
case DATA_EREKEM_GUID:
return NPC_ErekemGUID;
case DATA_EREKEM_GUARD_1_GUID:
return NPC_ErekemGuardGUID[0];
case DATA_EREKEM_GUARD_2_GUID:
return NPC_ErekemGuardGUID[1];
case DATA_ICHORON_GUID:
return NPC_IchoronGUID;
}
if (identifier == DATA_EREKEM_GUARD_1_GUID)
return _erekemGuardGuid[0];
if (identifier == DATA_EREKEM_GUARD_2_GUID)
return _erekemGuardGuid[1];
return ObjectGuid::Empty;
}
void StartBossEncounter(uint8 uiBoss)
void StartBossEncounter(uint32 bossId)
{
Creature* pBoss = nullptr;
switch (uiBoss)
switch (bossId)
{
case BOSS_MORAGG:
HandleGameObject(GO_MoraggCellGUID, true);
pBoss = instance->GetCreature(NPC_MoraggGUID);
if (pBoss)
pBoss->GetMotionMaster()->MovePoint(0, BossStartMove1);
HandleGameObject(DATA_MORAGG_CELL, true);
break;
case BOSS_EREKEM:
HandleGameObject(GO_ErekemCellGUID, true);
HandleGameObject(GO_ErekemRightGuardCellGUID, true);
HandleGameObject(GO_ErekemLeftGuardCellGUID, true);
pBoss = instance->GetCreature(NPC_ErekemGUID);
if (pBoss)
pBoss->GetMotionMaster()->MovePoint(0, BossStartMove2);
if (Creature* pGuard1 = instance->GetCreature(NPC_ErekemGuardGUID[0]))
HandleGameObject(DATA_EREKEM_CELL, true);
HandleGameObject(DATA_EREKEM_GUARD_2_CELL, true);
HandleGameObject(DATA_EREKEM_GUARD_1_CELL, true);
if (Creature* guard1 = instance->GetCreature(_erekemGuardGuid[0]))
{
pGuard1->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
pGuard1->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
pGuard1->SetImmuneToNPC(false);
pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21);
guard1->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
guard1->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
guard1->SetImmuneToNPC(false);
guard1->GetMotionMaster()->MovePoint(0, BossStartMove21);
}
if (Creature* pGuard2 = instance->GetCreature(NPC_ErekemGuardGUID[1]))
if (Creature* guard2 = instance->GetCreature(_erekemGuardGuid[1]))
{
pGuard2->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
pGuard2->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
pGuard2->SetImmuneToNPC(false);
pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22);
guard2->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
guard2->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
guard2->SetImmuneToNPC(false);
guard2->GetMotionMaster()->MovePoint(0, BossStartMove22);
}
break;
case BOSS_ICHORON:
HandleGameObject(GO_IchoronCellGUID, true);
pBoss = instance->GetCreature(NPC_IchoronGUID);
if (pBoss)
pBoss->GetMotionMaster()->MovePoint(0, BossStartMove3);
HandleGameObject(DATA_ICHORON_CELL, true);
break;
case BOSS_LAVANTHOR:
HandleGameObject(GO_LavanthorCellGUID, true);
pBoss = instance->GetCreature(NPC_LavanthorGUID);
if (pBoss)
pBoss->GetMotionMaster()->MovePoint(0, BossStartMove4);
HandleGameObject(DATA_LAVANTHOR_CELL, true);
break;
case BOSS_XEVOZZ:
HandleGameObject(GO_XevozzCellGUID, true);
pBoss = instance->GetCreature(NPC_XevozzGUID);
if (pBoss)
pBoss->GetMotionMaster()->MovePoint(0, BossStartMove5);
HandleGameObject(DATA_XEVOZZ_CELL, true);
break;
case BOSS_ZURAMAT:
HandleGameObject(GO_ZuramatCellGUID, true);
pBoss = instance->GetCreature(NPC_ZuramatGUID);
if (pBoss)
pBoss->GetMotionMaster()->MovePoint(0, BossStartMove6);
HandleGameObject(DATA_ZURAMAT_CELL, true);
break;
}
if (pBoss)
static Position const BossStartPositions[] =
{
pBoss->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
pBoss->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
pBoss->SetImmuneToNPC(false);
pBoss->SetReactState(REACT_AGGRESSIVE);
if ((WaveCount == 6 && m_auiEncounter[0] == DONE) || (WaveCount == 12 && m_auiEncounter[1] == DONE))
pBoss->SetLootMode(0);
BossStartMove1, // BOSS_MORAGG
BossStartMove2, // BOSS_EREKEM
BossStartMove3, // BOSS_ICHORON
BossStartMove4, // BOSS_LAVANTHOR
BossStartMove5, // BOSS_XEVOZZ
BossStartMove6, // BOSS_ZURAMAT
};
Creature* boss = GetCreature(bossId);
if (boss)
{
boss->GetMotionMaster()->MovePoint(0, BossStartPositions[bossId - BOSS_MORAGG]);
boss->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
boss->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
boss->SetImmuneToNPC(false);
boss->SetReactState(REACT_AGGRESSIVE);
if ((_waveCount == 6 && GetBossState(DATA_1ST_BOSS) == DONE) || (_waveCount == 12 && GetBossState(DATA_2ND_BOSS) == DONE))
boss->SetLootMode(0);
}
}
void Update(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
_events.Update(diff);
switch (_events.ExecuteEvent())
{
case 0:
break;
case EVENT_CHECK_PLAYERS:
{
if (DoNeedCleanup(false))
InstanceCleanup();
events.Repeat(5s);
}
if (DoNeedCleanup(false))
InstanceCleanup();
_events.Repeat(5s);
break;
case EVENT_GUARDS_FALL_BACK:
{
for (uint8 i = 0; i < 4; ++i)
if (Creature* c = instance->GetCreature(NPC_GuardGUID[i]))
{
c->SetReactState(REACT_PASSIVE);
c->CombatStop();
c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
c->GetMotionMaster()->MovePoint(0, guardMovePosition);
}
events.RescheduleEvent(EVENT_GUARDS_DISAPPEAR, 5s);
}
for (ObjectGuid const& guid : _guardGuid)
if (Creature* guard = instance->GetCreature(guid))
{
guard->SetReactState(REACT_PASSIVE);
guard->CombatStop();
guard->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
guard->GetMotionMaster()->MovePoint(0, guardMovePosition);
}
_events.RescheduleEvent(EVENT_GUARDS_DISAPPEAR, 5s);
break;
case EVENT_GUARDS_DISAPPEAR:
{
for (uint8 i = 0; i < 4; ++i)
if (Creature* c = instance->GetCreature(NPC_GuardGUID[i]))
c->SetVisible(false);
events.RescheduleEvent(EVENT_SINCLARI_FALL_BACK, 2s);
}
for (ObjectGuid const& guid : _guardGuid)
if (Creature* guard = instance->GetCreature(guid))
guard->SetVisible(false);
_events.RescheduleEvent(EVENT_SINCLARI_FALL_BACK, 2s);
break;
case EVENT_SINCLARI_FALL_BACK:
if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
{
c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
c->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition);
}
SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 0);
events.RescheduleEvent(EVENT_START_ENCOUNTER, 4s);
sinclari->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
sinclari->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition);
}
SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 0);
_events.RescheduleEvent(EVENT_START_ENCOUNTER, 4s);
break;
case EVENT_START_ENCOUNTER:
if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
{
c->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
c->AI()->Talk(SAY_SINCLARI_DOOR_LOCK);
}
if (Creature* c = instance->GetCreature(NPC_DoorSealGUID))
{
c->RemoveAllAuras(); // just to be sure...
}
GateHealth = 100;
HandleGameObject(GO_MainGateGUID, false);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 1);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)GateHealth);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)WaveCount);
for (ObjectGuid const& guid : GO_ActivationCrystalGUID)
if (GameObject* go = instance->GetGameObject(guid))
{
HandleGameObject(ObjectGuid::Empty, false, go); // not used yet
go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); // make it useable
}
events.RescheduleEvent(EVENT_SUMMON_PORTAL, 4s);
sinclari->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
sinclari->AI()->Talk(SAY_SINCLARI_DOOR_LOCK);
}
if (Creature* doorSeal = GetCreature(DATA_DOOR_SEAL))
doorSeal->RemoveAllAuras();
_gateHealth = 100;
HandleGameObject(DATA_MAIN_DOOR, false);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 1);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)_gateHealth);
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)_waveCount);
for (ObjectGuid const& guid : _activationCrystalGuidList)
if (GameObject* go = instance->GetGameObject(guid))
{
HandleGameObject(ObjectGuid::Empty, false, go);
go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
}
_events.RescheduleEvent(EVENT_SUMMON_PORTAL, 4s);
break;
case EVENT_SUMMON_PORTAL:
++WaveCount;
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)WaveCount);
++_waveCount;
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)_waveCount);
SetData(DATA_PORTAL_LOCATION, (GetData(DATA_PORTAL_LOCATION) + urand(1, 5)) % 6);
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
if (WaveCount % 6 != 0)
c->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocations[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN);
else if (WaveCount == 6 || WaveCount == 12) // first or second boss
if (_waveCount % 6 != 0)
sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocations[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN);
else if (_waveCount == 6 || _waveCount == 12)
{
if (!uiFirstBoss || !uiSecondBoss)
if (!GetPersistentData(PERSISTENT_DATA_FIRST_BOSS) || !GetPersistentData(PERSISTENT_DATA_SECOND_BOSS))
{
uiFirstBoss = urand(1, 6);
do { uiSecondBoss = urand(1, 6); }
while (uiFirstBoss == uiSecondBoss);
SaveToDB();
uint32 firstBoss = urand(BOSS_MORAGG, BOSS_ZURAMAT);
uint32 secondBoss;
do { secondBoss = urand(BOSS_MORAGG, BOSS_ZURAMAT); }
while (firstBoss == secondBoss);
StorePersistentData(PERSISTENT_DATA_FIRST_BOSS, firstBoss);
StorePersistentData(PERSISTENT_DATA_SECOND_BOSS, secondBoss);
}
c->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN);
sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN);
}
else // cyanigossa
else
{
if (Creature* cyanigosa = c->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN))
if (Creature* cyanigosa = sinclari->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN))
{
cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_BLUE_AURA, false);
cyanigosa->AI()->Talk(CYANIGOSA_SAY_SPAWN);
cyanigosa->GetMotionMaster()->MoveJump(MiddleRoomLocation.GetPositionX(), MiddleRoomLocation.GetPositionY(), MiddleRoomLocation.GetPositionZ(), 10.0f, 20.0f);
}
events.RescheduleEvent(EVENT_CYANIGOSSA_TRANSFORM, 10s);
_events.RescheduleEvent(EVENT_CYANIGOSA_TRANSFORM, 10s);
}
}
break;
case EVENT_CYANIGOSSA_TRANSFORM:
if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID))
case EVENT_CYANIGOSA_TRANSFORM:
if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA))
{
c->RemoveAurasDueToSpell(SPELL_CYANIGOSA_BLUE_AURA);
c->CastSpell(c, SPELL_CYANIGOSA_TRANSFORM, 0);
events.RescheduleEvent(EVENT_CYANIGOSA_ATTACK, 2500ms);
cyanigosa->RemoveAurasDueToSpell(SPELL_CYANIGOSA_BLUE_AURA);
cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_TRANSFORM, 0);
_events.RescheduleEvent(EVENT_CYANIGOSA_ATTACK, 2500ms);
}
break;
case EVENT_CYANIGOSA_ATTACK:
if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID))
if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA))
{
c->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
c->SetImmuneToNPC(false);
cyanigosa->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
cyanigosa->SetImmuneToNPC(false);
}
break;
}
@ -531,121 +450,118 @@ public:
if (DoNeedCleanup(plr->IsAlive()))
InstanceCleanup();
if (EncounterStatus == IN_PROGRESS)
if (_encounterStatus == IN_PROGRESS)
{
plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 1);
plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)GateHealth);
plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)WaveCount);
plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)_gateHealth);
plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)_waveCount);
}
else
plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0);
events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s);
_events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s);
}
bool DoNeedCleanup(bool enter)
{
uint8 aliveCount = 0;
Map::PlayerList const& pl = instance->GetPlayers();
for( Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr )
if (Player* plr = itr->GetSource())
if (plr->IsAlive() && !plr->IsGameMaster() && !plr->HasAura(27827)/*spirit of redemption aura*/ )
++aliveCount;
uint32 aliveCount = instance->GetPlayersCountExceptGMs(true);
bool need = enter ? aliveCount <= 1 : aliveCount == 0;
if (!need && CLEANED)
CLEANED = false;
if (!need && _cleaned)
_cleaned = false;
return need;
}
void InstanceCleanup()
{
if (CLEANED)
if (_cleaned)
return;
CLEANED = true;
_cleaned = true;
// reset defense crystals
for (ObjectGuid const& guid : GO_ActivationCrystalGUID)
for (ObjectGuid const& guid : _activationCrystalGuidList)
if (GameObject* go = instance->GetGameObject(guid))
{
HandleGameObject(ObjectGuid::Empty, false, go); // not used yet
go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); // not useable at the beginning
HandleGameObject(ObjectGuid::Empty, false, go);
go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
}
// reset positions of Sinclari and Guards
if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
c->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
c->DespawnOrUnsummon();
c->SetRespawnTime(3);
sinclari->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
sinclari->DespawnOrUnsummon();
sinclari->SetRespawnTime(3);
}
for (uint8 i = 0; i < 4; ++i)
if (Creature* c = instance->GetCreature(NPC_GuardGUID[i]))
for (ObjectGuid const& guid : _guardGuid)
if (Creature* guard = instance->GetCreature(guid))
{
c->DespawnOrUnsummon();
c->SetRespawnTime(3);
if (m_auiEncounter[MAX_ENCOUNTER - 1] == DONE)
c->SetVisible(false);
else
c->SetVisible(true);
c->SetReactState(REACT_AGGRESSIVE);
guard->DespawnOrUnsummon();
guard->SetRespawnTime(3);
guard->SetVisible(GetBossState(DATA_CYANIGOSA) != DONE);
guard->SetReactState(REACT_AGGRESSIVE);
}
// remove portal if any
if (Creature* c = instance->GetCreature(NPC_PortalGUID))
c->DespawnOrUnsummon();
NPC_PortalGUID.Clear();
if (Creature* portal = GetCreature(DATA_TELEPORTATION_PORTAL))
portal->DespawnOrUnsummon();
// remove trash
for (ObjectGuid const& guid : trashMobs)
if (Creature* c = instance->GetCreature(guid))
c->DespawnOrUnsummon();
for (ObjectGuid const& guid : _trashMobs)
if (Creature* trash = instance->GetCreature(guid))
trash->DespawnOrUnsummon();
trashMobs.clear();
_trashMobs.clear();
// clear door seal damaging auras:
if (Creature* c = instance->GetCreature(NPC_DoorSealGUID))
c->RemoveAllAuras();
if (Creature* doorSeal = GetCreature(DATA_DOOR_SEAL))
doorSeal->RemoveAllAuras();
// open main gate
HandleGameObject(GO_MainGateGUID, true);
HandleGameObject(DATA_MAIN_DOOR, true);
if (m_auiEncounter[MAX_ENCOUNTER - 1] != DONE) // instance not finished
if (GetBossState(DATA_CYANIGOSA) != DONE)
{
// close all cells
HandleGameObject(GO_MoraggCellGUID, false);
HandleGameObject(GO_ErekemCellGUID, false);
HandleGameObject(GO_ErekemRightGuardCellGUID, false);
HandleGameObject(GO_ErekemLeftGuardCellGUID, false);
HandleGameObject(GO_IchoronCellGUID, false);
HandleGameObject(GO_LavanthorCellGUID, false);
HandleGameObject(GO_XevozzCellGUID, false);
HandleGameObject(GO_ZuramatCellGUID, false);
HandleGameObject(DATA_MORAGG_CELL, false);
HandleGameObject(DATA_EREKEM_CELL, false);
HandleGameObject(DATA_EREKEM_GUARD_2_CELL, false);
HandleGameObject(DATA_EREKEM_GUARD_1_CELL, false);
HandleGameObject(DATA_ICHORON_CELL, false);
HandleGameObject(DATA_LAVANTHOR_CELL, false);
HandleGameObject(DATA_XEVOZZ_CELL, false);
HandleGameObject(DATA_ZURAMAT_CELL, false);
// respawn bosses
if (Creature* c = instance->GetCreature(NPC_MoraggGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_MoraggGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_ErekemGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_ErekemGuardGUID[0])) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_ErekemGuardGUID[1])) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_IchoronGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_LavanthorGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_XevozzGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_ZuramatGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); }
if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID)) { c->DespawnOrUnsummon(); }
for (uint32 id = BOSS_MORAGG; id <= BOSS_ZURAMAT; ++id)
{
if (Creature* boss = GetCreature(id))
{
boss->DespawnOrUnsummon();
boss->SetRespawnTime(3);
boss->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
boss->SetImmuneToNPC(true);
}
}
if (Creature* guard1 = instance->GetCreature(_erekemGuardGuid[0]))
{
guard1->DespawnOrUnsummon();
guard1->SetRespawnTime(3);
guard1->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
guard1->SetImmuneToNPC(true);
}
if (Creature* guard2 = instance->GetCreature(_erekemGuardGuid[1]))
{
guard2->DespawnOrUnsummon();
guard2->SetRespawnTime(3);
guard2->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
guard2->SetImmuneToNPC(true);
}
if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA))
cyanigosa->DespawnOrUnsummon();
}
// reinitialize variables and events
DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0);
EncounterStatus = NOT_STARTED;
GateHealth = 100;
WaveCount = 0;
bDefensesUsed = false;
if (m_auiEncounter[MAX_ENCOUNTER - 1] == DONE)
EncounterStatus = DONE;
events.Reset();
events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s);
_encounterStatus = NOT_STARTED;
_gateHealth = 100;
_waveCount = 0;
_defensesUsed = false;
if (GetBossState(DATA_CYANIGOSA) == DONE)
_encounterStatus = DONE;
_events.Reset();
_events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s);
}
bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override
@ -653,35 +569,31 @@ public:
switch (criteria_id)
{
case CRITERIA_DEFENSELESS:
return GateHealth == 100 && !bDefensesUsed;
return _gateHealth == 100 && !_defensesUsed;
case CRITERIA_A_VOID_DANCE:
case CRITERIA_DEHYDRATION:
return bAchiev;
return _achievementCompleted;
}
return false;
}
void ReadSaveDataMore(std::istringstream& data) override
{
EncounterStatus = NOT_STARTED;
CLEANED = false;
events.Reset();
events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0ms);
private:
bool _cleaned{ false };
uint8 _encounterStatus{ NOT_STARTED };
EventMap _events;
uint8 _gateHealth{ 100 };
uint8 _waveCount{ 0 };
uint8 _portalLocation{ 0 };
bool _achievementCompleted{ false };
bool _defensesUsed{ false };
data >> m_auiEncounter[0];
data >> m_auiEncounter[1];
data >> m_auiEncounter[2];
data >> uiFirstBoss;
}
GuidVector _activationCrystalGuidList;
void WriteSaveDataMore(std::ostringstream& data) override
{
data << m_auiEncounter[0] << ' '
<< m_auiEncounter[1] << ' '
<< m_auiEncounter[2] << ' '
<< uiFirstBoss << ' '
<< uiSecondBoss << ' ';
}
// Multi-instance creature tracking (can't use ObjectData)
ObjectGuid _guardGuid[4];
ObjectGuid _erekemGuardGuid[2];
GuidSet _trashMobs;
};
};

File diff suppressed because it is too large Load diff

View file

@ -18,15 +18,57 @@
#ifndef DEF_VIOLET_HOLD_H
#define DEF_VIOLET_HOLD_H
#define MAX_ENCOUNTER 3
#include "CreatureAIImpl.h"
#define DataHeader "VIO"
#define VioletHoldScriptName "instance_violet_hold"
enum Creatures
enum VHData
{
// Creature ObjectData IDs
// Boss creatures use VHBosses values directly (BOSS_MORAGG .. BOSS_ZURAMAT, DATA_CYANIGOSA)
DATA_SINCLARI = 10,
DATA_DOOR_SEAL,
DATA_TELEPORTATION_PORTAL,
// GO ObjectData IDs
DATA_MAIN_DOOR = 20,
DATA_MORAGG_CELL,
DATA_EREKEM_CELL,
DATA_EREKEM_GUARD_1_CELL,
DATA_EREKEM_GUARD_2_CELL,
DATA_ICHORON_CELL,
DATA_LAVANTHOR_CELL,
DATA_XEVOZZ_CELL,
DATA_ZURAMAT_CELL,
// Instance action/state IDs (used by SetData/GetData)
DATA_ENCOUNTER_STATUS = 30,
DATA_ACTIVATE_DEFENSE_SYSTEM,
DATA_START_INSTANCE,
DATA_ADD_TRASH_MOB,
DATA_DELETE_TRASH_MOB,
DATA_PORTAL_DEFEATED,
DATA_WAVE_COUNT,
DATA_PORTAL_LOCATION,
DATA_RELEASE_BOSS,
DATA_DECREASE_DOOR_HEALTH,
DATA_ACHIEV,
// Manual GUID tracking (multi-instance entries)
DATA_EREKEM_GUARD_1_GUID,
DATA_EREKEM_GUARD_2_GUID,
};
enum VHPersistentData
{
PERSISTENT_DATA_FIRST_BOSS,
PERSISTENT_DATA_SECOND_BOSS,
PERSISTENT_DATA_COUNT
};
enum VHCreatures
{
NPC_TELEPORTATION_PORTAL = 31011,
NPC_DEFENSE_SYSTEM = 30837,
@ -63,7 +105,7 @@ enum Creatures
NPC_AZURE_STALKER = 32191,
};
enum GameObjects
enum VHGameObjects
{
GO_MAIN_DOOR = 191723,
GO_XEVOZZ_DOOR = 191556,
@ -78,19 +120,25 @@ enum GameObjects
GO_ACTIVATION_CRYSTAL = 193611,
};
enum Bosses
enum VHBosses
{
BOSS_NONE,
BOSS_MORAGG,
// Encounter slot IDs (used by SetBossState/GetBossState)
DATA_1ST_BOSS,
DATA_2ND_BOSS,
DATA_CYANIGOSA,
MAX_ENCOUNTER,
// Individual boss IDs (used by ObjectData and BossAI)
BOSS_MORAGG = MAX_ENCOUNTER,
BOSS_EREKEM,
BOSS_ICHORON,
BOSS_LAVANTHOR,
BOSS_XEVOZZ,
BOSS_ZURAMAT,
BOSS_CYANIGOSA
MAX_BOSS
};
enum Spells
enum VHSpells
{
SPELL_CONTROL_CRYSTAL_ACTIVATION = 57804,
SPELL_DEFENSE_SYSTEM_SPAWN_EFFECT = 57886,
@ -104,7 +152,7 @@ enum Spells
SPELL_CYANIGOSA_BLUE_AURA = 45870
};
enum Events
enum VHInstanceEvents
{
EVENT_CHECK_PLAYERS = 1,
EVENT_GUARDS_FALL_BACK,
@ -112,7 +160,7 @@ enum Events
EVENT_SINCLARI_FALL_BACK,
EVENT_START_ENCOUNTER,
EVENT_SUMMON_PORTAL,
EVENT_CYANIGOSSA_TRANSFORM,
EVENT_CYANIGOSA_TRANSFORM,
EVENT_CYANIGOSA_ATTACK,
// Event defense system
@ -120,32 +168,7 @@ enum Events
EVENT_ARCANE_LIGHTNING_INSTAKILL
};
enum Data
{
DATA_ACTIVATE_DEFENSE_SYSTEM = 1,
DATA_ENCOUNTER_STATUS,
DATA_START_INSTANCE,
DATA_ADD_TRASH_MOB,
DATA_DELETE_TRASH_MOB,
DATA_PORTAL_DEFEATED,
DATA_WAVE_COUNT,
DATA_PORTAL_LOCATION,
DATA_TELEPORTATION_PORTAL_GUID,
DATA_DOOR_SEAL_GUID,
DATA_FIRST_BOSS_NUMBER,
DATA_SECOND_BOSS_NUMBER,
DATA_RELEASE_BOSS,
DATA_DECRASE_DOOR_HEALTH,
DATA_BOSS_DIED,
DATA_FAILED,
DATA_EREKEM_GUID,
DATA_EREKEM_GUARD_1_GUID,
DATA_EREKEM_GUARD_2_GUID,
DATA_ICHORON_GUID,
DATA_ACHIEV,
};
enum AchievCriteria
enum VHAchievCriteria
{
CRITERIA_DEFENSELESS = 6803,
CRITERIA_A_VOID_DANCE = 7587,
@ -308,4 +331,6 @@ inline AI* GetVioletHoldAI(T* obj)
return GetInstanceAI<AI>(obj, VioletHoldScriptName);
}
#define RegisterVioletHoldCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetVioletHoldAI)
#endif