EverWrath/src/server/scripts/Northrend/VioletHold/violet_hold.cpp

1226 lines
39 KiB
C++

/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "violet_hold.h"
#include "CreatureScript.h"
#include "GameObjectScript.h"
#include "PassiveAI.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
/// @todo: Missing Sinclari Trigger announcements (32204) Look at its creature_text for more info.
/// @todo: Activation Crystals (go_vh_activation_crystal) (193611) are spammable, should be a 1 time use per crystal.
enum Texts
{
GOSSIP_MENU_START_EVENT = 9998,
GOSSIP_MENU_ITEM = 9997,
GOSSIP_MENU_LATE_JOIN = 10275,
NPC_TEXT_SINCLARI_IN = 13853,
NPC_TEXT_SINCLARI_ITEM = 13854,
NPC_TEXT_SINCLARI_DONE = 13910,
NPC_TEXT_SINCLARI_LATE_JOIN = 14271,
};
/***********
** DEFENSE SYSTEM CRYSTAL
***********/
class go_vh_activation_crystal : public GameObjectScript
{
public:
go_vh_activation_crystal() : GameObjectScript("go_vh_activation_crystal") { }
bool OnGossipHello(Player* /*player*/, GameObject* go) override
{
if (InstanceScript* pInstance = go->GetInstanceScript())
pInstance->SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 1);
return true;
}
};
/***********
** SINCLARI
***********/
class npc_vh_sinclari : public CreatureScript
{
public:
npc_vh_sinclari() : CreatureScript("npc_vh_sinclari") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
if (InstanceScript* pInstance = creature->GetInstanceScript())
switch (pInstance->GetData(DATA_ENCOUNTER_STATUS))
{
case NOT_STARTED:
AddGossipItemFor(player, GOSSIP_MENU_ITEM, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
AddGossipItemFor(player, GOSSIP_MENU_START_EVENT, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_IN, creature->GetGUID());
break;
case IN_PROGRESS:
AddGossipItemFor(player, GOSSIP_MENU_LATE_JOIN, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_LATE_JOIN, creature->GetGUID());
break;
default: // DONE or invalid
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_DONE, creature->GetGUID());
}
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*uiSender*/, uint32 uiAction) override
{
ClearGossipMenuFor(player);
switch(uiAction)
{
case GOSSIP_ACTION_INFO_DEF+1:
CloseGossipMenuFor(player);
if (InstanceScript* pInstance = creature->GetInstanceScript())
pInstance->SetData(DATA_START_INSTANCE, 1);
break;
case GOSSIP_ACTION_INFO_DEF+2:
SendGossipMenuFor(player, NPC_TEXT_SINCLARI_ITEM, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF+3:
player->NearTeleportTo(playerTeleportPosition.GetPositionX(), playerTeleportPosition.GetPositionY(), playerTeleportPosition.GetPositionZ(), playerTeleportPosition.GetOrientation(), true);
CloseGossipMenuFor(player);
break;
}
return true;
}
};
/***********
** TELEPORTATION PORTAL
***********/
enum PortalEvents
{
EVENT_SUMMON_KEEPER_OR_GUARDIAN = 1,
EVENT_SUMMON_KEEPER_TRASH,
EVENT_SUMMON_ELITES,
EVENT_SUMMON_SABOTEOUR,
EVENT_CHECK_DEATHS,
};
class npc_vh_teleportation_portal : public CreatureScript
{
public:
npc_vh_teleportation_portal() : CreatureScript("npc_vh_teleportation_portal") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_vh_teleportation_portalAI>(creature);
}
struct npc_vh_teleportation_portalAI : public NullCreatureAI
{
npc_vh_teleportation_portalAI(Creature* c) : NullCreatureAI(c), listOfMobs(me)
{
pInstance = c->GetInstanceScript();
events.Reset();
if (pInstance)
{
wave = pInstance->GetData(DATA_WAVE_COUNT);
bKorG = false;
spawned = false;
if (wave < 12)
addValue = 0;
else
addValue = 1;
if (wave % 6 != 0)
events.RescheduleEvent(RAND(EVENT_SUMMON_KEEPER_OR_GUARDIAN, EVENT_SUMMON_ELITES), 10s);
else
events.RescheduleEvent(EVENT_SUMMON_SABOTEOUR, 3s);
}
}
InstanceScript* pInstance;
SummonList listOfMobs;
EventMap events;
uint8 wave;
uint8 addValue;
bool bKorG;
bool spawned;
void UpdateAI(uint32 diff) override
{
if (!pInstance)
return;
events.Update(diff);
switch(events.ExecuteEvent())
{
case 0:
break;
case EVENT_SUMMON_KEEPER_OR_GUARDIAN:
bKorG = true;
spawned = true;
if (Creature* c = DoSummon(RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER), me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN))
me->CastSpell(c, SPELL_PORTAL_CHANNEL, false);
events.RescheduleEvent(EVENT_SUMMON_KEEPER_TRASH, 20s);
break;
case EVENT_SUMMON_KEEPER_TRASH:
for (uint8 i = 0; i < 3 + addValue; ++i)
{
uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_INVADER_2, NPC_AZURE_SPELLBREAKER_1, NPC_AZURE_SPELLBREAKER_2, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_MAGE_SLAYER_2, NPC_AZURE_BINDER_1, NPC_AZURE_BINDER_2);
DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN);
}
events.Repeat(20s);
break;
case EVENT_SUMMON_ELITES:
spawned = true;
for (uint8 i = 0; i < 2 + addValue; ++i)
{
uint32 entry = RAND(NPC_AZURE_CAPTAIN, NPC_AZURE_RAIDER, NPC_AZURE_STALKER, NPC_AZURE_SORCEROR);
DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN);
}
me->SetVisible(false);
break;
case EVENT_SUMMON_SABOTEOUR:
DoSummon(NPC_SABOTEOUR, me, 2.0f, 0, TEMPSUMMON_CORPSE_DESPAWN);
me->DespawnOrUnsummon(3000);
break;
}
if (!spawned)
return;
if (bKorG)
{
if (!me->IsNonMeleeSpellCast(false)) // keeper/guardian died => channeled spell interrupted
{
// if keeper/guard lost all victims, in enterevademode linking aura is removed, restore it:
if (pInstance)
for (SummonList::iterator itr = listOfMobs.begin(); itr != listOfMobs.end(); ++itr)
if (Creature* c = pInstance->instance->GetCreature(*itr))
if (c->IsAlive() && (c->GetEntry() == NPC_PORTAL_GUARDIAN || c->GetEntry() == NPC_PORTAL_KEEPER))
{
me->CastSpell(c, SPELL_PORTAL_CHANNEL, false);
return;
}
Unit::Kill(me, me, false);
}
}
else
{
if (listOfMobs.empty())
Unit::Kill(me, me, false);
}
}
void JustDied(Unit* /*killer*/) override
{
events.Reset();
if (wave % 6 == 0) // just to be sure, shouln't occur
return;
if (pInstance)
pInstance->SetData(DATA_PORTAL_DEFEATED, 0);
}
void JustSummoned(Creature* pSummoned) override
{
if (pSummoned)
{
listOfMobs.Summon(pSummoned);
pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID());
}
}
void SummonedMobDied(Creature* pSummoned)
{
if (pSummoned)
{
listOfMobs.Despawn(pSummoned);
pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID());
}
}
};
};
/***********
** GENERAL TRASH AI
***********/
struct violet_hold_trashAI : public npc_escortAI
{
violet_hold_trashAI(Creature* c) : npc_escortAI(c)
{
pInstance = c->GetInstanceScript();
if (pInstance)
PLoc = pInstance->GetData(DATA_PORTAL_LOCATION);
bAddedWP = false;
bAlt = false;
}
InstanceScript* pInstance;
bool bAddedWP;
uint32 PLoc;
bool bAlt;
void ClearDoorSealAura()
{
if (pInstance)
if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_DOOR_SEAL_GUID)))
c->RemoveAura(SPELL_DESTROY_DOOR_SEAL, me->GetGUID());
}
void JustEngagedWith(Unit* who) override
{
if (!who->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
me->InterruptNonMeleeSpells(false);
me->SetImmuneToNPC(false);
}
}
void AttackStart(Unit* who) override
{
if (!who->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
ScriptedAI::AttackStart(who);
}
void JustReachedHome() override
{
CreatureStartAttackDoor();
}
void WaypointReached(uint32 id) override
{
if (PLoc < 6)
if (id == uint16(PLocWPCount[PLoc] - 1 - (bAlt ? 1 : 0)))
CreatureStartAttackDoor();
}
void MoveInLineOfSight(Unit* who) override
{
ScriptedAI::MoveInLineOfSight(who);
}
void UpdateAI(uint32 diff) override
{
if (!bAddedWP)
{
bAddedWP = true;
switch(PLoc)
{
case 0:
for(int i = 0; i < 6; i++)
AddWaypoint(i, FirstPortalTrashWPs[i][0] + irand(-1, 1), FirstPortalTrashWPs[i][1] + irand(-1, 1), FirstPortalTrashWPs[i][2] + irand(-1, 1), 0);
me->SetHomePosition(FirstPortalTrashWPs[5][0], FirstPortalTrashWPs[5][1], FirstPortalTrashWPs[5][2], 3.149439f);
break;
case 1:
bAlt = (bool)urand(0, 1);
if (!bAlt)
{
for(int i = 0; i < 9; i++)
AddWaypoint(i, SecondPortalTrashWPs1[i][0] + irand(-1, 1), SecondPortalTrashWPs1[i][1] + irand(-1, 1), SecondPortalTrashWPs1[i][2], 0);
me->SetHomePosition(SecondPortalTrashWPs1[8][0] + irand(-1, 1), SecondPortalTrashWPs1[8][1] + irand(-1, 1), SecondPortalTrashWPs1[8][2] + irand(-1, 1), 3.149439f);
}
else
{
for(int i = 0; i < 8; i++)
AddWaypoint(i, SecondPortalTrashWPs2[i][0] + irand(-1, 1), SecondPortalTrashWPs2[i][1] + irand(-1, 1), SecondPortalTrashWPs2[i][2], 0);
me->SetHomePosition(SecondPortalTrashWPs2[7][0], SecondPortalTrashWPs2[7][1], SecondPortalTrashWPs2[7][2], 3.149439f);
}
break;
case 2:
for(int i = 0; i < 8; i++)
AddWaypoint(i, ThirdPortalTrashWPs[i][0] + irand(-1, 1), ThirdPortalTrashWPs[i][1] + irand(-1, 1), ThirdPortalTrashWPs[i][2], 0);
me->SetHomePosition(ThirdPortalTrashWPs[7][0], ThirdPortalTrashWPs[7][1], ThirdPortalTrashWPs[7][2], 3.149439f);
break;
case 3:
for(int i = 0; i < 9; i++)
AddWaypoint(i, FourthPortalTrashWPs[i][0] + irand(-1, 1), FourthPortalTrashWPs[i][1] + irand(-1, 1), FourthPortalTrashWPs[i][2], 0);
me->SetHomePosition(FourthPortalTrashWPs[8][0], FourthPortalTrashWPs[8][1], FourthPortalTrashWPs[8][2], 3.149439f);
break;
case 4:
for(int i = 0; i < 6; i++)
AddWaypoint(i, FifthPortalTrashWPs[i][0] + irand(-1, 1), FifthPortalTrashWPs[i][1] + irand(-1, 1), FifthPortalTrashWPs[i][2], 0);
me->SetHomePosition(FifthPortalTrashWPs[5][0], FifthPortalTrashWPs[5][1], FifthPortalTrashWPs[5][2], 3.149439f);
break;
case 5:
for(int i = 0; i < 4; i++)
AddWaypoint(i, SixthPoralTrashWPs[i][0] + irand(-1, 1), SixthPoralTrashWPs[i][1] + irand(-1, 1), SixthPoralTrashWPs[i][2], 0);
me->SetHomePosition(SixthPoralTrashWPs[3][0], SixthPoralTrashWPs[3][1], SixthPoralTrashWPs[3][2], 3.149439f);
break;
}
SetDespawnAtEnd(false);
Start(true, true);
}
npc_escortAI::UpdateAI(diff);
}
void JustDied(Unit* /*unit*/) override
{
if (pInstance)
if (Creature* portal = ObjectAccessor::GetCreature((*me), pInstance->GetGuidData(DATA_TELEPORTATION_PORTAL_GUID)))
CAST_AI(npc_vh_teleportation_portal::npc_vh_teleportation_portalAI, portal->AI())->SummonedMobDied(me);
}
void CreatureStartAttackDoor()
{
RemoveEscortState(STATE_ESCORT_ESCORTING | STATE_ESCORT_RETURNING | STATE_ESCORT_PAUSED);
me->SetImmuneToNPC(true);
me->CastSpell((Unit*)nullptr, SPELL_DESTROY_DOOR_SEAL, true);
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
{
me->SetImmuneToNPC(false);
me->SetHomePosition(1845.577759f + rand_norm() * 5 - 2.5f, 800.681152f + rand_norm() * 5 - 2.5f, 44.104248f, M_PI);
}
me->GetThreatMgr().ClearAllThreat();
me->CombatStop(true);
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
AddEscortState(STATE_ESCORT_RETURNING);
ReturnToLastPoint();
}
else
{
me->GetMotionMaster()->MoveTargetedHome();
Reset();
}
me->ClearUnitState(UNIT_STATE_EVADE);
}
};
/***********
** TRASH SPELLS
***********/
enum AzureInvaderSpells
{
SPELL_CLEAVE = 15496,
SPELL_IMPALE_N = 58459,
SPELL_IMPALE_H = 59256,
SPELL_BRUTAL_STRIKE = 58460,
SPELL_SUNDER_ARMOR = 58461,
};
#define SPELL_IMPALE DUNGEON_MODE(SPELL_IMPALE_N, SPELL_IMPALE_H)
enum AzureSpellbreakerSpells
{
SPELL_ARCANE_BLAST_N = 58462,
SPELL_ARCANE_BLAST_H = 59257,
SPELL_SLOW = 25603,
SPELL_CHAINS_OF_ICE = 58464,
SPELL_CONE_OF_COLD_N = 58463,
SPELL_CONE_OF_COLD_H = 59258
};
#define SPELL_ARCANE_BLAST DUNGEON_MODE(SPELL_ARCANE_BLAST_N, SPELL_ARCANE_BLAST_H)
#define SPELL_CONE_OF_COLD DUNGEON_MODE(SPELL_CONE_OF_COLD_N, SPELL_CONE_OF_COLD_H)
enum AzureBinderSpells
{
SPELL_ARCANE_BARRAGE_N = 58456,
SPELL_ARCANE_BARRAGE_H = 59248,
SPELL_ARCANE_EXPLOSION_N = 58455,
SPELL_ARCANE_EXPLOSION_H = 59245,
SPELL_FROST_NOVA_N = 58458,
SPELL_FROST_NOVA_H = 59253,
SPELL_FROSTBOLT_N = 58457,
SPELL_FROSTBOLT_H = 59251,
};
#define SPELL_ARCANE_BARRAGE DUNGEON_MODE(SPELL_ARCANE_BARRAGE_N, SPELL_ARCANE_BARRAGE_H)
#define SPELL_ARCANE_EXPLOSION DUNGEON_MODE(SPELL_ARCANE_EXPLOSION_N, SPELL_ARCANE_EXPLOSION_H)
#define SPELL_FROST_NOVA DUNGEON_MODE(SPELL_FROST_NOVA_N, SPELL_FROST_NOVA_H)
#define SPELL_FROSTBOLT DUNGEON_MODE(SPELL_FROSTBOLT_N, SPELL_FROSTBOLT_H)
enum AzureMageSlayerSpells
{
SPELL_ARCANE_EMPOWERMENT = 58469,
SPELL_SPELL_LOCK = 30849
};
enum AzureCaptainSpells
{
SPELL_MORTAL_STRIKE = 32736,
SPELL_WHIRLWIND_OF_STEEL = 41056
};
enum AzureSorcerorSpells
{
SPELL_ARCANE_STREAM_N = 60181,
SPELL_ARCANE_STREAM_H = 60204,
SPELL_MANA_DETONATION_N = 60182,
SPELL_MANA_DETONATION_H = 60205
};
#define SPELL_ARCANE_STREAM DUNGEON_MODE(SPELL_ARCANE_STREAM_N, SPELL_ARCANE_STREAM_H)
#define SPELL_MANA_DETONATION DUNGEON_MODE(SPELL_MANA_DETONATION_N, SPELL_MANA_DETONATION_H)
enum AzureRaiderSpells
{
SPELL_CONCUSSION_BLOW = 52719,
SPELL_MAGIC_REFLECTION = 60158
};
enum AzureStalkerSpells
{
SPELL_BACKSTAB = 58471,
SPELL_TACTICAL_BLINK = 58470
};
/***********
** TRASH
***********/
class npc_azure_invader : public CreatureScript
{
public:
npc_azure_invader() : CreatureScript("npc_azure_invader") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_invaderAI>(creature);
}
struct npc_azure_invaderAI : public violet_hold_trashAI
{
npc_azure_invaderAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiCleaveTimer;
uint32 uiImpaleTimer;
uint32 uiBrutalStrikeTimer;
uint32 uiSunderArmorTimer;
void Reset() override
{
uiCleaveTimer = 5000;
uiImpaleTimer = 4000;
uiBrutalStrikeTimer = 5000;
uiSunderArmorTimer = 4000;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
if (me->GetEntry() == NPC_AZURE_INVADER_1)
{
if (uiCleaveTimer <= diff)
{
DoCast(me->GetVictim(), SPELL_CLEAVE);
uiCleaveTimer = 5000;
}
else uiCleaveTimer -= diff;
if (uiImpaleTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 5.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_IMPALE);
uiImpaleTimer = 4000;
}
else uiImpaleTimer -= diff;
}
if (me->GetEntry() == NPC_AZURE_INVADER_2)
{
if (uiBrutalStrikeTimer <= diff)
{
DoCast(me->GetVictim(), SPELL_BRUTAL_STRIKE);
uiBrutalStrikeTimer = 5000;
}
else uiBrutalStrikeTimer -= diff;
if (uiSunderArmorTimer <= diff)
{
DoCast(me->GetVictim(), SPELL_SUNDER_ARMOR);
uiSunderArmorTimer = urand(8000, 10000);
}
else uiSunderArmorTimer -= diff;
}
DoMeleeAttackIfReady();
}
};
};
class npc_azure_binder : public CreatureScript
{
public:
npc_azure_binder() : CreatureScript("npc_azure_binder") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_binderAI>(creature);
}
struct npc_azure_binderAI : public violet_hold_trashAI
{
npc_azure_binderAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiArcaneExplosionTimer;
uint32 uiArcainBarrageTimer;
uint32 uiFrostNovaTimer;
uint32 uiFrostboltTimer;
void Reset() override
{
uiArcaneExplosionTimer = 5000;
uiArcainBarrageTimer = 4000;
uiFrostNovaTimer = 5000;
uiFrostboltTimer = 4000;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
if (me->GetEntry() == NPC_AZURE_BINDER_1)
{
if (uiArcaneExplosionTimer <= diff)
{
DoCast(SPELL_ARCANE_EXPLOSION);
uiArcaneExplosionTimer = 5000;
}
else uiArcaneExplosionTimer -= diff;
if (uiArcainBarrageTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_ARCANE_BARRAGE);
uiArcainBarrageTimer = 6000;
}
else uiArcainBarrageTimer -= diff;
}
if (me->GetEntry() == NPC_AZURE_BINDER_2)
{
if (uiFrostNovaTimer <= diff)
{
DoCast(SPELL_FROST_NOVA);
uiFrostNovaTimer = 5000;
}
else uiFrostNovaTimer -= diff;
if (uiFrostboltTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_FROSTBOLT);
uiFrostboltTimer = 6000;
}
else uiFrostboltTimer -= diff;
}
DoMeleeAttackIfReady();
}
};
};
class npc_azure_mage_slayer : public CreatureScript
{
public:
npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_mage_slayerAI>(creature);
}
struct npc_azure_mage_slayerAI : public violet_hold_trashAI
{
npc_azure_mage_slayerAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiArcaneEmpowermentTimer;
uint32 uiSpellLockTimer;
void Reset() override
{
uiArcaneEmpowermentTimer = 5000;
uiSpellLockTimer = 5000;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1)
{
if (uiArcaneEmpowermentTimer <= diff)
{
DoCast(me, SPELL_ARCANE_EMPOWERMENT);
uiArcaneEmpowermentTimer = 14000;
}
else uiArcaneEmpowermentTimer -= diff;
}
if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2)
{
if (uiSpellLockTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_SPELL_LOCK);
uiSpellLockTimer = 9000;
}
else uiSpellLockTimer -= diff;
}
DoMeleeAttackIfReady();
}
};
};
class npc_azure_raider : public CreatureScript
{
public:
npc_azure_raider() : CreatureScript("npc_azure_raider") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_raiderAI> (creature);
}
struct npc_azure_raiderAI : public violet_hold_trashAI
{
npc_azure_raiderAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiConcussionBlowTimer;
uint32 uiMagicReflectionTimer;
void Reset() override
{
uiConcussionBlowTimer = 5000;
uiMagicReflectionTimer = 8000;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
if (uiConcussionBlowTimer <= diff)
{
DoCast(me->GetVictim(), SPELL_CONCUSSION_BLOW);
uiConcussionBlowTimer = 5000;
}
else uiConcussionBlowTimer -= diff;
if (uiMagicReflectionTimer <= diff)
{
DoCast(SPELL_MAGIC_REFLECTION);
uiMagicReflectionTimer = urand(10000, 15000);
}
else uiMagicReflectionTimer -= diff;
DoMeleeAttackIfReady();
}
};
};
class npc_azure_stalker : public CreatureScript
{
public:
npc_azure_stalker() : CreatureScript("npc_azure_stalker") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_stalkerAI>(creature);
}
struct npc_azure_stalkerAI : public violet_hold_trashAI
{
npc_azure_stalkerAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiBackstabTimer;
uint32 uiTacticalBlinkTimer;
bool TacticalBlinkCasted;
void Reset() override
{
uiBackstabTimer = 1300;
uiTacticalBlinkTimer = 8000;
TacticalBlinkCasted = false;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
/*if (!TacticalBlinkCasted)
{
if (uiTacticalBlinkTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_TACTICAL_BLINK);
uiTacticalBlinkTimer = 10000;
TacticalBlinkCasted = true;
} else uiTacticalBlinkTimer -= diff;
}
else*/
{
if (uiBackstabTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0, 5.0f, true);
if (pTarget && !pTarget->HasInArc(M_PI, me))
DoCast(pTarget, SPELL_BACKSTAB);
TacticalBlinkCasted = false;
uiBackstabTimer = 4000;
}
else uiBackstabTimer -= diff;
}
DoMeleeAttackIfReady();
}
};
};
class npc_azure_spellbreaker : public CreatureScript
{
public:
npc_azure_spellbreaker() : CreatureScript("npc_azure_spellbreaker") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_spellbreakerAI>(creature);
}
struct npc_azure_spellbreakerAI : public violet_hold_trashAI
{
npc_azure_spellbreakerAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiArcaneBlastTimer;
uint32 uiSlowTimer;
uint32 uiChainsOfIceTimer;
uint32 uiConeOfColdTimer;
void Reset() override
{
uiArcaneBlastTimer = 5000;
uiSlowTimer = 4000;
uiChainsOfIceTimer = 5000;
uiConeOfColdTimer = 4000;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1)
{
if (uiArcaneBlastTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_ARCANE_BLAST);
uiArcaneBlastTimer = 6000;
}
else uiArcaneBlastTimer -= diff;
if (uiSlowTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_SLOW);
uiSlowTimer = 5000;
}
else uiSlowTimer -= diff;
}
if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2)
{
if (uiChainsOfIceTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_CHAINS_OF_ICE);
uiChainsOfIceTimer = 7000;
}
else uiChainsOfIceTimer -= diff;
if (uiConeOfColdTimer <= diff)
{
DoCast(SPELL_CONE_OF_COLD);
uiConeOfColdTimer = 5000;
}
else uiConeOfColdTimer -= diff;
}
DoMeleeAttackIfReady();
}
};
};
class npc_azure_captain : public CreatureScript
{
public:
npc_azure_captain() : CreatureScript("npc_azure_captain") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_captainAI>(creature);
}
struct npc_azure_captainAI : public violet_hold_trashAI
{
npc_azure_captainAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiMortalStrikeTimer;
uint32 uiWhirlwindTimer;
void Reset() override
{
uiMortalStrikeTimer = 5000;
uiWhirlwindTimer = 8000;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
if (uiMortalStrikeTimer <= diff)
{
DoCast(me->GetVictim(), SPELL_MORTAL_STRIKE);
uiMortalStrikeTimer = 5000;
}
else uiMortalStrikeTimer -= diff;
if (uiWhirlwindTimer <= diff)
{
DoCastAOE(SPELL_WHIRLWIND_OF_STEEL);
uiWhirlwindTimer = 8000;
}
else uiWhirlwindTimer -= diff;
DoMeleeAttackIfReady();
}
};
};
class npc_azure_sorceror : public CreatureScript
{
public:
npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_sorcerorAI>(creature);
}
struct npc_azure_sorcerorAI : public violet_hold_trashAI
{
npc_azure_sorcerorAI(Creature* c) : violet_hold_trashAI(c) {}
uint32 uiArcaneStreamTimer;
uint32 uiArcaneStreamTimerStartingValueHolder;
uint32 uiManaDetonationTimer;
void Reset() override
{
uiArcaneStreamTimer = 4000;
uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer;
uiManaDetonationTimer = 5000;
}
void UpdateAI(uint32 diff) override
{
violet_hold_trashAI::UpdateAI(diff);
if (!UpdateVictim())
return;
if (uiArcaneStreamTimer <= diff)
{
Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 35.0f, true);
if (pTarget)
DoCast(pTarget, SPELL_ARCANE_STREAM);
uiArcaneStreamTimer = urand(0, 5000) + 5000;
uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer;
}
else uiArcaneStreamTimer -= diff;
if (uiManaDetonationTimer <= diff && uiArcaneStreamTimer >= 1500 && uiArcaneStreamTimer <= uiArcaneStreamTimerStartingValueHolder / 2)
{
DoCastAOE(SPELL_MANA_DETONATION);
uiManaDetonationTimer = urand(2000, 6000);
}
else uiManaDetonationTimer -= diff;
DoMeleeAttackIfReady();
}
};
};
/***********
** SABOTEUR
***********/
enum AzureSaboteurSpells
{
SABOTEUR_SHIELD_DISRUPTION = 58291,
SABOTEUR_SHIELD_EFFECT = 45775,
SPELL_TELEPORT_VISUAL = 52096,
};
class npc_azure_saboteur : public CreatureScript
{
public:
npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetVioletHoldAI<npc_azure_saboteurAI>(creature);
}
struct npc_azure_saboteurAI : public npc_escortAI
{
npc_azure_saboteurAI(Creature* c) : npc_escortAI(c)
{
pInstance = c->GetInstanceScript();
uiBoss = 0;
if (pInstance)
uiBoss = pInstance->GetData(DATA_WAVE_COUNT) == 6 ? pInstance->GetData(DATA_FIRST_BOSS_NUMBER) : pInstance->GetData(DATA_SECOND_BOSS_NUMBER);
bAddedWPs = false;
bOpening = false;
}
InstanceScript* pInstance;
bool bAddedWPs;
uint8 uiBoss;
bool bOpening;
uint32 timer;
uint8 count;
void WaypointReached(uint32 uiWPointId) override
{
if (!pInstance)
return;
switch(uiBoss)
{
case 1:
if(uiWPointId == 2)
FinishPointReached();
break;
case 2:
if(uiWPointId == 2)
FinishPointReached();
break;
case 3:
if(uiWPointId == 1)
FinishPointReached();
break;
case 4:
if(uiWPointId == 0)
FinishPointReached();
break;
case 5:
if(uiWPointId == 0)
FinishPointReached();
break;
case 6:
if(uiWPointId == 4)
FinishPointReached();
break;
}
}
void UpdateAI(uint32 diff) override
{
npc_escortAI::UpdateAI(diff);
if(!bAddedWPs)
{
bAddedWPs = true;
switch(uiBoss)
{
case 1:
for(int i = 0; i < 3; i++)
AddWaypoint(i, SaboteurFinalPos1[i][0], SaboteurFinalPos1[i][1], SaboteurFinalPos1[i][2], 0);
me->SetHomePosition(SaboteurFinalPos1[2][0], SaboteurFinalPos1[2][1], SaboteurFinalPos1[2][2], 4.762346f);
break;
case 2:
for(int i = 0; i < 3; i++)
AddWaypoint(i, SaboteurFinalPos2[i][0], SaboteurFinalPos2[i][1], SaboteurFinalPos2[i][2], 0);
me->SetHomePosition(SaboteurFinalPos2[2][0], SaboteurFinalPos2[2][1], SaboteurFinalPos2[2][2], 1.862674f);
break;
case 3:
for(int i = 0; i < 2; i++)
AddWaypoint(i, SaboteurFinalPos3[i][0], SaboteurFinalPos3[i][1], SaboteurFinalPos3[i][2], 0);
me->SetHomePosition(SaboteurFinalPos3[1][0], SaboteurFinalPos3[1][1], SaboteurFinalPos3[1][2], 5.500638f);
break;
case 4:
AddWaypoint(0, SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 0);
me->SetHomePosition(SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 3.991108f);
break;
case 5:
AddWaypoint(0, SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 0);
me->SetHomePosition(SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 1.100841f);
break;
case 6:
for(int i = 0; i < 5; i++)
AddWaypoint(i, SaboteurFinalPos6[i][0], SaboteurFinalPos6[i][1], SaboteurFinalPos6[i][2], 0);
me->SetHomePosition(SaboteurFinalPos6[4][0], SaboteurFinalPos6[4][1], SaboteurFinalPos6[4][2], 0.983031f);
break;
}
SetDespawnAtEnd(false);
Start(true, true);
}
if (bOpening)
{
if (timer <= diff)
{
if (count < 2)
{
me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false);
timer = 1000;
}
else if (count == 2)
{
me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false);
if (pInstance)
pInstance->SetData(DATA_RELEASE_BOSS, 0);
timer = 500;
}
else
{
bOpening = false;
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetDisplayId(11686);
me->CastSpell(me, SPELL_TELEPORT_VISUAL, true);
me->DespawnOrUnsummon(1000);
}
++count;
}
else timer -= diff;
}
}
void FinishPointReached()
{
bOpening = true;
timer = 1000;
count = 0;
me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
};
};
/***********
** DESTROY DOOR SEAL SPELL SCRIPT
***********/
class spell_destroy_door_seal_aura : public AuraScript
{
PrepareAuraScript(spell_destroy_door_seal_aura);
void HandleEffectPeriodic(AuraEffect const* /*aurEff*/)
{
PreventDefaultAction();
if (Unit* target = GetTarget())
if (InstanceScript* pInstance = target->GetInstanceScript())
pInstance->SetData(DATA_DECRASE_DOOR_HEALTH, 0);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_destroy_door_seal_aura::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
struct npc_violet_hold_defense_system : public ScriptedAI
{
npc_violet_hold_defense_system(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
DoCast(RAND(SPELL_DEFENSE_SYSTEM_SPAWN_EFFECT, SPELL_DEFENSE_SYSTEM_VISUAL));
events.ScheduleEvent(EVENT_ARCANE_LIGHTNING, 4s);
events.ScheduleEvent(EVENT_ARCANE_LIGHTNING_INSTAKILL, 4s);
me->DespawnOrUnsummon(7s, 0s);
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_ARCANE_LIGHTNING:
DoCastAOE(RAND(SPELL_ARCANE_LIGHTNING, SPELL_ARCANE_LIGHTNING_VISUAL));
events.RepeatEvent(2000);
break;
case EVENT_ARCANE_LIGHTNING_INSTAKILL:
DoCastAOE(SPELL_ARCANE_LIGHTNING_INSTAKILL);
events.RepeatEvent(1000);
break;
}
}
};
void AddSC_violet_hold()
{
new go_vh_activation_crystal();
new npc_vh_sinclari();
new npc_vh_teleportation_portal();
new npc_azure_saboteur();
new npc_azure_invader();
new npc_azure_spellbreaker();
new npc_azure_binder();
new npc_azure_mage_slayer();
new npc_azure_captain();
new npc_azure_sorceror();
new npc_azure_raider();
new npc_azure_stalker();
RegisterSpellScript(spell_destroy_door_seal_aura);
RegisterCreatureAI(npc_violet_hold_defense_system);
}