aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjoschiwald <joschiwald.trinity@gmail.com>2015-08-23 14:43:11 +0200
committerShauren <shauren.trinity@gmail.com>2015-11-01 13:09:30 +0100
commit92b08fc74b368e9512c5238d91e381b6df644219 (patch)
treef5e6bba399cdca2d41a89006f6efbd0673fb5cda /src
parent7b684e8813376a82526d78c44d00f282229fe471 (diff)
Scripts/VioletHold: rewrote the whole instance
thanks @MitchesD for help (cherry picked from commit df21162fe44d2ff29c201a9004586f560789c38b) (cherry picked from commit 3a4f54197c05ce4209c7e659acfd382896607265) (cherry picked from commit f50df728201849ee7185d33c5a061c91e781c8de) (cherry picked from commit 95bae7143f0f4bb7d5975f8a61daa3c533f48225) (cherry picked from commit 5406f82044e7bd6ceb024a67f33c1674400cdc1f)
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp18
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h4
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.h4
-rw-r--r--src/server/game/Entities/Player/Player.cpp11
-rw-r--r--src/server/game/Instances/InstanceScript.cpp5
-rw-r--r--src/server/game/Instances/InstanceScript.h2
-rw-r--r--src/server/game/Movement/MotionMaster.cpp18
-rw-r--r--src/server/game/Movement/MotionMaster.h1
-rw-r--r--src/server/game/Spells/SpellMgr.cpp13
-rw-r--r--src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_the_barrens.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_winterspring.cpp2
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp2
-rw-r--r--src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp3
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp3
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp185
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_erekem.cpp479
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp697
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp160
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_moragg.cpp256
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp425
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp269
-rw-r--r--src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp1340
-rw-r--r--src/server/scripts/Northrend/VioletHold/violet_hold.cpp1929
-rw-r--r--src/server/scripts/Northrend/VioletHold/violet_hold.h114
-rw-r--r--src/server/scripts/Northrend/zone_borean_tundra.cpp2
-rw-r--r--src/server/scripts/Northrend/zone_howling_fjord.cpp2
-rw-r--r--src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp2
31 files changed, 2896 insertions, 3062 deletions
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 512385a67b6..28f9ad591df 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -446,7 +446,13 @@ BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
summons(creature),
_boundary(instance ? instance->GetBossBoundary(bossId) : NULL),
- _bossId(bossId) { }
+ _bossId(bossId)
+{
+ scheduler.SetValidator([this]
+ {
+ return !me->HasUnitState(UNIT_STATE_CASTING);
+ });
+}
void BossAI::_Reset()
{
@@ -457,6 +463,7 @@ void BossAI::_Reset()
me->ResetLootMode();
events.Reset();
summons.DespawnAll();
+ scheduler.CancelAll();
if (instance)
instance->SetBossState(_bossId, NOT_STARTED);
}
@@ -465,15 +472,13 @@ void BossAI::_JustDied()
{
events.Reset();
summons.DespawnAll();
+ scheduler.CancelAll();
if (instance)
instance->SetBossState(_bossId, DONE);
}
void BossAI::_EnterCombat()
{
- me->SetCombatPulseDelay(5);
- me->setActive(true);
- DoZoneInCombat();
if (instance)
{
// bosses do not respawn, check only on enter combat
@@ -484,6 +489,11 @@ void BossAI::_EnterCombat()
}
instance->SetBossState(_bossId, IN_PROGRESS);
}
+
+ me->SetCombatPulseDelay(5);
+ me->setActive(true);
+ DoZoneInCombat();
+ ScheduleTasks();
}
void BossAI::TeleportCheaters()
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 74d0654dff2..49e2e0e8518 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -23,6 +23,7 @@
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "InstanceScript.h"
+#include "TaskScheduler.h"
#define CAST_AI(a, b) (dynamic_cast<a*>(b))
#define ENSURE_AI(a,b) (EnsureAI<a>(b))
@@ -356,6 +357,8 @@ class BossAI : public ScriptedAI
// is supposed to run more than once
virtual void ExecuteEvent(uint32 /*eventId*/) { }
+ virtual void ScheduleTasks() { }
+
void Reset() override { _Reset(); }
void EnterCombat(Unit* /*who*/) override { _EnterCombat(); }
void JustDied(Unit* /*killer*/) override { _JustDied(); }
@@ -382,6 +385,7 @@ class BossAI : public ScriptedAI
EventMap events;
SummonList summons;
+ TaskScheduler scheduler;
private:
BossBoundaryMap const* const _boundary;
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
index 1d71652c948..deabd1dc484 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
@@ -68,8 +68,8 @@ struct npc_escortAI : public ScriptedAI
void EnterEvadeMode() override;
- void UpdateAI(uint32 diff) override; //the "internal" update, calls UpdateEscortAI()
- virtual void UpdateEscortAI(uint32 const diff); //used when it's needed to add code in update (abilities, scripted events, etc)
+ void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI()
+ virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc)
void MovementInform(uint32, uint32) override;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index ab2d4fbdbd0..a1acbb9d7aa 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -9267,6 +9267,17 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
packet.Worldstates.emplace_back(4131, 0); // 10 WORLDSTATE_ALGALON_DESPAWN_TIMER
}
break;
+ // Violet Hold
+ case 4415:
+ if (instance && mapid == 608)
+ instance->FillInitialWorldStates(packet);
+ else
+ {
+ packet.Worldstates.emplace_back(3816, 0); // 9 WORLD_STATE_VH_SHOW
+ packet.Worldstates.emplace_back(3815, 100); // 10 WORLD_STATE_VH_PRISON_STATE
+ packet.Worldstates.emplace_back(3810, 0); // 11 WORLD_STATE_VH_WAVE_COUNT
+ }
+ break;
// Halls of Refection
case 4820:
if (instance && mapid == 668)
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 80024879a43..01a82ecb5af 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -319,6 +319,11 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
return false;
}
+bool InstanceScript::_SkipCheckRequiredBosses(Player const* player /*= nullptr*/) const
+{
+ return player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES);
+}
+
void InstanceScript::Load(char const* data)
{
if (!data)
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index d932c24abf3..609c43417d5 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -280,6 +280,8 @@ class InstanceScript : public ZoneScript
void WriteSaveDataBossStates(std::ostringstream& data);
virtual void WriteSaveDataMore(std::ostringstream& /*data*/) { }
+ bool _SkipCheckRequiredBosses(Player const* player = nullptr) const;
+
private:
static void LoadObjectData(ObjectData const* creatureData, ObjectInfoMap& objectInfo);
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 117f4b675bf..ad6f1b457aa 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -417,6 +417,24 @@ void MotionMaster::MoveCirclePath(float x, float y, float z, float radius, bool
init.Launch();
}
+void MotionMaster::MoveSmoothPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize, bool walk)
+{
+ Movement::PointsArray path(pathPoints, pathPoints + pathSize);
+
+ Movement::MoveSplineInit init(_owner);
+ init.MovebyPath(path);
+ init.SetSmooth();
+ init.SetWalk(walk);
+ init.Launch();
+
+ // This code is not correct
+ // EffectMovementGenerator does not affect UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE
+ // need to call PointMovementGenerator with various pointIds
+ Mutate(new EffectMovementGenerator(pointId), MOTION_SLOT_ACTIVE);
+ //Position pos(pathPoints[pathSize - 1].x, pathPoints[pathSize - 1].y, pathPoints[pathSize - 1].z);
+ //MovePoint(EVENT_CHARGE_PREPATH, pos, false);
+}
+
void MotionMaster::MoveFall(uint32 id /*=0*/)
{
// use larger distance for vmap height search than in most other cases
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index c8da50364ba..9a8950de9f7 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -185,6 +185,7 @@ class MotionMaster //: private std::stack<MovementGenerator *>
{ MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); }
void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = EVENT_JUMP);
void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount);
+ void MoveSmoothPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize, bool walk);
void MoveFall(uint32 id = 0);
void MoveSeekAssistance(float x, float y, float z);
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 9d6466dfab7..75f419dc99e 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3228,6 +3228,19 @@ void SpellMgr::LoadSpellInfoCorrections()
//! HACK: This spell break quest complete for alliance and on retail not used °_O
const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->Effect = 0;
break;
+ // VIOLET HOLD SPELLS
+ //
+ case 54258: // Water Globule (Ichoron)
+ case 54264: // Water Globule (Ichoron)
+ case 54265: // Water Globule (Ichoron)
+ case 54266: // Water Globule (Ichoron)
+ case 54267: // Water Globule (Ichoron)
+ // in 3.3.5 there is only one radius in dbc which is 0 yards in this case
+ // use max radius from 4.3.4
+ const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_25_YARDS);
+ break;
+ // ENDOF VIOLET HOLD
+ //
// ULDUAR SPELLS
//
case 62374: // Pursued (Flame Leviathan)
diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
index 87dadd44dd7..c1470d3bc47 100644
--- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
+++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
@@ -361,7 +361,7 @@ public:
}
}
- void UpdateEscortAI(const uint32 uiDiff) override
+ void UpdateEscortAI(uint32 uiDiff) override
{
if (uiPhase)
{
diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
index 1c9d5cf2c62..978fc6d96fc 100644
--- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp
+++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
@@ -607,7 +607,7 @@ public:
summoned->AI()->AttackStart(me);
}
- void UpdateEscortAI(const uint32 Diff) override
+ void UpdateEscortAI(uint32 Diff) override
{
if (!UpdateVictim())
{
diff --git a/src/server/scripts/Kalimdor/zone_winterspring.cpp b/src/server/scripts/Kalimdor/zone_winterspring.cpp
index b287e5e9a91..067f36afad6 100644
--- a/src/server/scripts/Kalimdor/zone_winterspring.cpp
+++ b/src/server/scripts/Kalimdor/zone_winterspring.cpp
@@ -568,7 +568,7 @@ public:
}
- void UpdateEscortAI(const uint32 diff) override
+ void UpdateEscortAI(uint32 diff) override
{
DialogueUpdate(diff);
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
index eb6230fabfc..eb004505bab 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
@@ -263,7 +263,7 @@ class npc_onyx_flamecaller : public CreatureScript
}
}
- void UpdateEscortAI(uint32 const diff) override
+ void UpdateEscortAI(uint32 diff) override
{
if (!UpdateVictim())
return;
diff --git a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp
index 5111247b84c..4438c4ab199 100644
--- a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp
+++ b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp
@@ -18,7 +18,6 @@
#include "InstanceScript.h"
#include "Player.h"
#include "ScriptMgr.h"
-#include "WorldSession.h"
#include "gundrak.h"
#include "EventMap.h"
@@ -191,7 +190,7 @@ class instance_gundrak : public InstanceMapScript
bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override
{
- if (player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES))
+ if (_SkipCheckRequiredBosses(player))
return true;
switch (bossId)
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
index 30b797bf98d..8676b6c0506 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp
@@ -1140,7 +1140,7 @@ class npc_crok_scourgebane : public CreatureScript
}
}
- void UpdateEscortAI(uint32 const diff) override
+ void UpdateEscortAI(uint32 diff) override
{
if (_wipeCheckTimer <= diff)
_wipeCheckTimer = 0;
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index d38d630f775..555ed40e805 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -17,7 +17,6 @@
#include "AccountMgr.h"
#include "InstanceScript.h"
-#include "Map.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "PoolMgr.h"
@@ -26,7 +25,6 @@
#include "Transport.h"
#include "TransportMgr.h"
#include "WorldPacket.h"
-#include "WorldSession.h"
#include "icecrown_citadel.h"
enum EventIds
@@ -1118,7 +1116,7 @@ class instance_icecrown_citadel : public InstanceMapScript
bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override
{
- if (player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES))
+ if (_SkipCheckRequiredBosses(player))
return true;
switch (bossId)
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp
index 6233c7e8953..86dbe6c16fb 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp
@@ -437,7 +437,7 @@ public:
return 0;
}
- void UpdateEscortAI(const uint32 uiDiff) override
+ void UpdateEscortAI(uint32 uiDiff) override
{
if (uiPhaseTimer <= uiDiff)
{
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp
index c67e31c4cc0..227b9c208cc 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/instance_halls_of_stone.cpp
@@ -18,7 +18,6 @@
#include "InstanceScript.h"
#include "Player.h"
#include "ScriptMgr.h"
-#include "WorldSession.h"
#include "halls_of_stone.h"
DoorData const doorData[] =
@@ -172,7 +171,7 @@ class instance_halls_of_stone : public InstanceMapScript
bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override
{
- if (player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES))
+ if (_SkipCheckRequiredBosses(player))
return true;
switch (bossId)
diff --git a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp
index dc923e534b0..d3868b8df9c 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp
@@ -16,11 +16,13 @@
*/
#include "ScriptMgr.h"
+#include "SpellScript.h"
#include "ScriptedCreature.h"
#include "violet_hold.h"
enum Spells
{
+ SPELL_SUMMON_PLAYER = 21150,
SPELL_ARCANE_VACUUM = 58694,
SPELL_BLIZZARD = 58693,
SPELL_MANA_DESTRUCTION = 59374,
@@ -42,119 +44,91 @@ enum Yells
class boss_cyanigosa : public CreatureScript
{
-public:
- boss_cyanigosa() : CreatureScript("boss_cyanigosa") { }
-
- struct boss_cyanigosaAI : public BossAI
- {
- boss_cyanigosaAI(Creature* creature) : BossAI(creature, DATA_CYANIGOSA)
- {
- Initialize();
- }
-
- void Initialize()
- {
- uiArcaneVacuumTimer = 10000;
- uiBlizzardTimer = 15000;
- uiManaDestructionTimer = 30000;
- uiTailSweepTimer = 20000;
- uiUncontrollableEnergyTimer = 25000;
- }
-
- uint32 uiArcaneVacuumTimer;
- uint32 uiBlizzardTimer;
- uint32 uiManaDestructionTimer;
- uint32 uiTailSweepTimer;
- uint32 uiUncontrollableEnergyTimer;
+ public:
+ boss_cyanigosa() : CreatureScript("boss_cyanigosa") { }
- void Reset() override
+ struct boss_cyanigosaAI : public BossAI
{
- Initialize();
- BossAI::Reset();
- }
+ boss_cyanigosaAI(Creature* creature) : BossAI(creature, DATA_CYANIGOSA) { }
- void EnterCombat(Unit* who) override
- {
- BossAI::EnterCombat(who);
- Talk(SAY_AGGRO);
- }
+ void EnterCombat(Unit* who) override
+ {
+ BossAI::EnterCombat(who);
+ Talk(SAY_AGGRO);
+ }
- void MoveInLineOfSight(Unit* /*who*/) override { }
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
- void UpdateAI(uint32 diff) override
- {
- if (instance->GetData(DATA_REMOVE_NPC) == 1)
+ void JustDied(Unit* killer) override
{
- me->DespawnOrUnsummon();
- instance->SetData(DATA_REMOVE_NPC, 0);
+ BossAI::JustDied(killer);
+ Talk(SAY_DEATH);
}
- if (!UpdateVictim())
- return;
+ void MoveInLineOfSight(Unit* /*who*/) override { }
- if (uiArcaneVacuumTimer <= diff)
+ void UpdateAI(uint32 diff) override
{
- DoCastAOE(SPELL_ARCANE_VACUUM);
- uiArcaneVacuumTimer = 10000;
- } else uiArcaneVacuumTimer -= diff;
+ if (!UpdateVictim())
+ return;
- if (uiBlizzardTimer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_BLIZZARD);
- uiBlizzardTimer = 15000;
- } else uiBlizzardTimer -= diff;
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
+ }
- if (uiTailSweepTimer <= diff)
+ void ScheduleTasks() override
{
- DoCastVictim(SPELL_TAIL_SWEEP);
- uiTailSweepTimer = 20000;
- } else uiTailSweepTimer -= diff;
+ scheduler.Schedule(Seconds(10), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_ARCANE_VACUUM);
+ task.Repeat();
+ });
- if (uiUncontrollableEnergyTimer <= diff)
- {
- DoCastVictim(SPELL_UNCONTROLLABLE_ENERGY);
- uiUncontrollableEnergyTimer = 25000;
- } else uiUncontrollableEnergyTimer -= diff;
+ scheduler.Schedule(Seconds(15), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true))
+ DoCast(target, SPELL_BLIZZARD);
+ task.Repeat();
+ });
- if (IsHeroic())
- {
- if (uiManaDestructionTimer <= diff)
+ scheduler.Schedule(Seconds(20), [this](TaskContext task)
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_MANA_DESTRUCTION);
- uiManaDestructionTimer = 30000;
- } else uiManaDestructionTimer -= diff;
- }
+ DoCastVictim(SPELL_TAIL_SWEEP);
+ task.Repeat();
+ });
- DoMeleeAttackIfReady();
- }
+ scheduler.Schedule(Seconds(25), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_UNCONTROLLABLE_ENERGY);
+ task.Repeat();
+ });
- void JustDied(Unit* killer) override
- {
- BossAI::JustDied(killer);
- Talk(SAY_DEATH);
- }
+ if (IsHeroic())
+ {
+ scheduler.Schedule(Seconds(30), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true))
+ DoCast(target, SPELL_MANA_DESTRUCTION);
+ task.Repeat();
+ });
+ }
+ }
+ };
- void KilledUnit(Unit* victim) override
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (victim->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
+ return GetVioletHoldAI<boss_cyanigosaAI>(creature);
}
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<boss_cyanigosaAI>(creature);
- }
};
class achievement_defenseless : public AchievementCriteriaScript
{
public:
- achievement_defenseless() : AchievementCriteriaScript("achievement_defenseless")
- {
- }
+ achievement_defenseless() : AchievementCriteriaScript("achievement_defenseless") { }
bool OnCheck(Player* /*player*/, Unit* target) override
{
@@ -165,10 +139,40 @@ class achievement_defenseless : public AchievementCriteriaScript
if (!instance)
return false;
- if (!instance->GetData(DATA_DEFENSELESS))
- return false;
+ return instance->GetData(DATA_DEFENSELESS) != 0;
+ }
+};
+
+class spell_cyanigosa_arcane_vacuum : public SpellScriptLoader
+{
+ public:
+ spell_cyanigosa_arcane_vacuum() : SpellScriptLoader("spell_cyanigosa_arcane_vacuum") { }
- return true;
+ class spell_cyanigosa_arcane_vacuum_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_cyanigosa_arcane_vacuum_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_PLAYER))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_PLAYER, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_cyanigosa_arcane_vacuum_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_cyanigosa_arcane_vacuum_SpellScript();
}
};
@@ -176,4 +180,5 @@ void AddSC_boss_cyanigosa()
{
new boss_cyanigosa();
new achievement_defenseless();
+ new spell_cyanigosa_arcane_vacuum();
}
diff --git a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp
index 8ead8ab559e..cc27bf52118 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp
@@ -41,269 +41,210 @@ enum Yells
SAY_BOTH_ADDS_KILLED = 5
};
-enum ErekemEvents
-{
- EVENT_EARTH_SHIELD = 1,
- EVENT_CHAIN_HEAL,
- EVENT_BLOODLUST,
- EVENT_LIGHTNING_BOLT,
- EVENT_EARTH_SHOCK,
- EVENT_WINDFURY,
- EVENT_STORMSTRIKE
-};
-
class boss_erekem : public CreatureScript
{
-public:
- boss_erekem() : CreatureScript("boss_erekem") { }
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<boss_erekemAI>(creature);
- }
-
- struct boss_erekemAI : public ScriptedAI
- {
- boss_erekemAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- phase = 0;
- breakBondsCd = 0;
- }
+ public:
+ boss_erekem() : CreatureScript("boss_erekem") { }
- void Reset() override
+ struct boss_erekemAI : public BossAI
{
- Initialize();
-
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED);
-
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS)
+ boss_erekemAI(Creature* creature) : BossAI(creature, DATA_EREKEM)
{
- if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)))
- pGuard1->DespawnOrUnsummon();
- if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)))
- pGuard2->DespawnOrUnsummon();
+ Initialize();
}
- else
+
+ void Initialize()
{
- if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)))
- {
- if (!pGuard1->IsAlive())
- pGuard1->Respawn();
- }
- if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)))
- {
- if (!pGuard2->IsAlive())
- pGuard2->Respawn();
- }
+ _phase = 0;
}
- events.Reset();
- }
-
- void JustReachedHome() override
- {
- if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)))
- pGuard1->Respawn();
+ void Reset() override
+ {
+ Initialize();
+ BossAI::Reset();
+ me->SetCanDualWield(false);
+ }
- if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)))
- pGuard2->Respawn();
- }
+ void EnterCombat(Unit* who) override
+ {
+ BossAI::EnterCombat(who);
+ Talk(SAY_AGGRO);
+ DoCast(me, SPELL_EARTH_SHIELD);
+ }
- void AttackStart(Unit* who) override
- {
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return;
+ void MovementInform(uint32 type, uint32 pointId) override
+ {
+ if (type == EFFECT_MOTION_TYPE && pointId == POINT_INTRO)
+ me->SetFacingTo(4.921828f);
+ }
- if (me->Attack(who, true))
+ void JustReachedHome() override
{
- me->AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
- DoStartMovement(who);
+ BossAI::JustReachedHome();
+ instance->SetData(DATA_HANDLE_CELLS, DATA_EREKEM);
+ }
- if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)))
- {
- pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
- if (!pGuard1->GetVictim() && pGuard1->AI())
- pGuard1->AI()->AttackStart(who);
- }
- if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)))
- {
- pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
- if (!pGuard2->GetVictim() && pGuard2->AI())
- pGuard2->AI()->AttackStart(who);
- }
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
}
- }
- void EnterCombat(Unit* /*who*/) override
- {
- Talk(SAY_AGGRO);
- DoCast(me, SPELL_EARTH_SHIELD);
+ void JustDied(Unit* killer) override
+ {
+ BossAI::JustDied(killer);
+ Talk(SAY_DEATH);
+ }
- if (GameObject* door = instance->GetGameObject(DATA_EREKEM_CELL))
- if (door->GetGoState() == GO_STATE_READY)
+ bool CheckGuardAuras(Creature* guard) const
+ {
+ static uint32 const MechanicImmunityList =
+ (1 << MECHANIC_SNARE)
+ | (1 << MECHANIC_ROOT)
+ | (1 << MECHANIC_FEAR)
+ | (1 << MECHANIC_STUN)
+ | (1 << MECHANIC_SLEEP)
+ | (1 << MECHANIC_CHARM)
+ | (1 << MECHANIC_SAPPED)
+ | (1 << MECHANIC_HORROR)
+ | (1 << MECHANIC_POLYMORPH)
+ | (1 << MECHANIC_DISORIENTED)
+ | (1 << MECHANIC_FREEZE)
+ | (1 << MECHANIC_TURN);
+
+ static std::list<AuraType> const AuraImmunityList =
{
- EnterEvadeMode();
- return;
- }
+ SPELL_AURA_MOD_STUN,
+ SPELL_AURA_MOD_DECREASE_SPEED,
+ SPELL_AURA_MOD_ROOT,
+ SPELL_AURA_MOD_CONFUSE,
+ SPELL_AURA_MOD_FEAR
+ };
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
+ if (guard->HasAuraWithMechanic(MechanicImmunityList))
+ return true;
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC);
-
- events.ScheduleEvent(EVENT_EARTH_SHIELD, 20000);
- events.ScheduleEvent(EVENT_BLOODLUST, 15000);
- events.ScheduleEvent(EVENT_CHAIN_HEAL, 10000);
- events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 2000);
- }
+ for (AuraType type : AuraImmunityList)
+ if (guard->HasAuraType(type))
+ return true;
- void JustDied(Unit* /*killer*/) override
- {
- Talk(SAY_DEATH);
-
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- {
- instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 7);
+ return false;
}
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
+
+ bool CheckGuardAlive() const
{
- instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 13);
- }
- }
+ for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i)
+ {
+ if (Creature* guard = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i)))
+ if (guard->IsAlive())
+ return true;
+ }
- void KilledUnit(Unit* victim) override
- {
- if (victim->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
- }
+ return false;
+ }
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
+ Unit* GetChainHealTarget() const
+ {
+ if (HealthBelowPct(85))
+ return me;
- if (phase == 0)
- if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)))
+ for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i)
{
- if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)))
- {
- if (!pGuard1->IsAlive() && !pGuard2->IsAlive())
- {
- phase = 1;
- DoCastVictim(SPELL_STORMSTRIKE);
- DoCast(SPELL_WINDFURY);
- events.Reset();
- events.ScheduleEvent(EVENT_EARTH_SHOCK, urand(2000, 8000));
- events.ScheduleEvent(EVENT_WINDFURY, urand(1500, 2000));
- events.ScheduleEvent(EVENT_STORMSTRIKE, urand(1500, 2000));
- }
- }
+ if (Creature* guard = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i)))
+ if (guard->IsAlive() && !guard->HealthAbovePct(75))
+ return guard;
}
- events.Update(diff);
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ return nullptr;
+ }
- if (breakBondsCd <= 0)
+ void UpdateAI(uint32 diff) override
{
- if (Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1)))
+ if (!UpdateVictim())
+ return;
+
+ if (_phase == 0 && !CheckGuardAlive())
{
- if (Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2)))
- {
- if (pGuard1->IsAlive())
- {
- if (pGuard1->HasAuraType(SPELL_AURA_MOD_STUN) || pGuard1->HasAuraType(SPELL_AURA_MOD_ROOT)
- || pGuard1->HasAuraType(SPELL_AURA_MOD_CONFUSE) || pGuard1->HasAuraType(SPELL_AURA_MOD_PACIFY)
- || pGuard1->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED))
- {
- DoCast(SPELL_BREAK_BONDS);
- breakBondsCd = 10000;
- return;
- }
- }
- if (pGuard2->IsAlive())
- {
- if (pGuard2->HasAuraType(SPELL_AURA_MOD_STUN) || pGuard2->HasAuraType(SPELL_AURA_MOD_ROOT)
- || pGuard2->HasAuraType(SPELL_AURA_MOD_CONFUSE) || pGuard2->HasAuraType(SPELL_AURA_MOD_PACIFY)
- || pGuard2->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED))
- {
- DoCast(SPELL_BREAK_BONDS);
- breakBondsCd = 10000;
- return;
- }
- }
- }
+ _phase = 1;
+ me->SetCanDualWield(true);
+ DoCast(me, SPELL_WINDFURY, true);
}
+
+ scheduler.Update(diff, [this]
+ {
+ if (_phase == 1)
+ DoSpellAttackIfReady(SPELL_STORMSTRIKE);
+ else
+ DoMeleeAttackIfReady();
+ });
}
- else
- breakBondsCd -= diff;
- switch (events.ExecuteEvent())
+ void ScheduleTasks() override
{
- case EVENT_EARTH_SHIELD:
+ scheduler.Schedule(Seconds(20), [this](TaskContext task)
+ {
if (Unit* ally = DoSelectLowestHpFriendly(30.0f))
DoCast(ally, SPELL_EARTH_SHIELD);
- events.ScheduleEvent(EVENT_EARTH_SHIELD, 20000);
- break;
- case EVENT_BLOODLUST:
+
+ task.Repeat(Seconds(20));
+ });
+
+ scheduler.Schedule(Seconds(2), [this](TaskContext task)
+ {
DoCast(SPELL_BLOODLUST);
- events.ScheduleEvent(EVENT_BLOODLUST, urand(35000, 45000));
- break;
- case EVENT_LIGHTNING_BOLT:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ task.Repeat(Seconds(35), Seconds(45));
+ });
+
+ scheduler.Schedule(Seconds(2), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f))
DoCast(target, SPELL_LIGHTNING_BOLT);
- events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 2500);
- break;
- case EVENT_CHAIN_HEAL:
+
+ task.Repeat(Milliseconds(2500));
+ });
+
+ scheduler.Schedule(Seconds(10), [this](TaskContext task)
+ {
if (Unit* ally = DoSelectLowestHpFriendly(40.0f))
DoCast(ally, SPELL_CHAIN_HEAL);
+
+ if (!CheckGuardAlive())
+ task.Repeat(Seconds(3));
+ else
+ task.Repeat(Seconds(8), Seconds(11));
+ });
+
+ scheduler.Schedule(Seconds(2), Seconds(8), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_EARTH_SHOCK);
+ task.Repeat(Seconds(8), Seconds(13));
+ });
+
+ scheduler.Schedule(Seconds(0), [this](TaskContext task)
+ {
+ for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i)
{
- Creature* pGuard1 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_1));
- Creature* pGuard2 = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EREKEM_GUARD_2));
- events.ScheduleEvent(EVENT_CHAIN_HEAL, ((pGuard1 && !pGuard1->IsAlive()) || (pGuard2 && !pGuard2->IsAlive()) ? 3000 : 8000 + rand() % 3000));
+ Creature* guard = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i));
+
+ if (guard && guard->IsAlive() && CheckGuardAuras(guard))
+ {
+ DoCastAOE(SPELL_BREAK_BONDS);
+ task.Repeat(Seconds(10));
+ return;
+ }
}
- break;
- case EVENT_EARTH_SHOCK:
- DoCastVictim(SPELL_EARTH_SHOCK);
- events.ScheduleEvent(EVENT_EARTH_SHOCK, urand(8000, 13000));
- break;
- case EVENT_WINDFURY:
- DoCast(SPELL_WINDFURY);
- events.ScheduleEvent(EVENT_WINDFURY, urand(1500, 2000));
- break;
- case EVENT_STORMSTRIKE:
- DoCastVictim(SPELL_STORMSTRIKE);
- events.ScheduleEvent(EVENT_STORMSTRIKE, urand(1500, 2000));
- break;
- default:
- break;
+ task.Repeat(Milliseconds(500));
+ });
}
- DoMeleeAttackIfReady();
- }
+ private:
+ uint8 _phase;
+ };
- private:
- EventMap events;
- InstanceScript* instance;
- uint8 phase;
- int32 breakBondsCd;
- };
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<boss_erekemAI>(creature);
+ }
};
enum GuardSpells
@@ -315,85 +256,61 @@ enum GuardSpells
class npc_erekem_guard : public CreatureScript
{
-public:
- npc_erekem_guard() : CreatureScript("npc_erekem_guard") { }
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_erekem_guardAI>(creature);
- }
+ public:
+ npc_erekem_guard() : CreatureScript("npc_erekem_guard") { }
- struct npc_erekem_guardAI : public ScriptedAI
- {
- npc_erekem_guardAI(Creature* creature) : ScriptedAI(creature)
+ struct npc_erekem_guardAI : public ScriptedAI
{
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ npc_erekem_guardAI(Creature* creature) : ScriptedAI(creature) { }
- void Initialize()
- {
- uiStrikeTimer = urand(4000, 8000);
- uiHowlingScreechTimer = urand(8000, 13000);
- uiGushingWoundTimer = urand(1000, 3000);
- }
+ void Reset() override
+ {
+ scheduler.CancelAll();
+ }
- uint32 uiGushingWoundTimer;
- uint32 uiHowlingScreechTimer;
- uint32 uiStrikeTimer;
+ void EnterCombat(Unit* /*who*/) override
+ {
+ DoZoneInCombat();
+ }
- InstanceScript* instance;
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- void Reset() override
- {
- Initialize();
+ scheduler.Update(diff,
+ std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
+ }
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
- }
+ void ScheduledTasks()
+ {
+ scheduler.Schedule(Seconds(4), Seconds(8), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_STRIKE);
+ task.Repeat(Seconds(4), Seconds(8));
+ });
- void AttackStart(Unit* who) override
- {
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return;
+ scheduler.Schedule(Seconds(8), Seconds(13), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_HOWLING_SCREECH);
+ task.Repeat(Seconds(8), Seconds(13));
+ });
- if (me->Attack(who, true))
- {
- me->AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
- DoStartMovement(who);
+ scheduler.Schedule(Seconds(1), Seconds(3), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_GUSHING_WOUND);
+ task.Repeat(Seconds(7), Seconds(12));
+ });
}
- }
- void MoveInLineOfSight(Unit* /*who*/) override { }
+ private:
+ TaskScheduler scheduler;
+ };
- void UpdateAI(uint32 diff) override
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
-
- if (uiStrikeTimer <= diff)
- {
- DoCastVictim(SPELL_STRIKE);
- uiStrikeTimer = urand(4000, 8000);
- } else uiStrikeTimer -= diff;
-
- if (uiHowlingScreechTimer <= diff)
- {
- DoCastVictim(SPELL_HOWLING_SCREECH);
- uiHowlingScreechTimer = urand(8000, 13000);
- } else uiHowlingScreechTimer -= diff;
-
- if (uiGushingWoundTimer <= diff)
- {
- DoCastVictim(SPELL_GUSHING_WOUND);
- uiGushingWoundTimer = urand(7000, 12000);
- } else uiGushingWoundTimer -= diff;
+ return GetVioletHoldAI<npc_erekem_guardAI>(creature);
}
- };
};
void AddSC_boss_erekem()
diff --git a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp
index caf1392ea38..3c29cc1123c 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp
@@ -17,26 +17,32 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellAuraEffects.h"
+#include "SpellScript.h"
#include "violet_hold.h"
enum Spells
{
- SPELL_DRAINED = 59820,
- SPELL_FRENZY = 54312,
- SPELL_PROTECTIVE_BUBBLE = 54306,
SPELL_WATER_BLAST = 54237,
SPELL_WATER_BOLT_VOLLEY = 54241,
- SPELL_SPLASH = 59516,
+ SPELL_SPLATTER = 54259,
+ SPELL_PROTECTIVE_BUBBLE = 54306,
+ SPELL_FRENZY = 54312,
SPELL_BURST = 54379,
- SPELL_WATER_GLOBULE = 54268,
- SPELL_MERGE = 54269,
- SPELL_WATER_GLOBULE_VISUAL = 54260
-};
+ SPELL_DRAINED = 59820,
+ SPELL_THREAT_PROC = 61732,
+ SPELL_SHRINK = 54297,
-enum IchoronCreatures
-{
- NPC_ICHOR_GLOBULE = 29321,
- NPC_ICHORON_SUMMON_TARGET = 29326
+ SPELL_WATER_GLOBULE_SUMMON_1 = 54258,
+ SPELL_WATER_GLOBULE_SUMMON_2 = 54264,
+ SPELL_WATER_GLOBULE_SUMMON_3 = 54265,
+ SPELL_WATER_GLOBULE_SUMMON_4 = 54266,
+ SPELL_WATER_GLOBULE_SUMMON_5 = 54267,
+ SPELL_WATER_GLOBULE_TRANSFORM = 54268,
+ SPELL_WATER_GLOBULE_VISUAL = 54260,
+
+ SPELL_MERGE = 54269,
+ SPELL_SPLASH = 59516
};
enum Yells
@@ -47,483 +53,412 @@ enum Yells
SAY_SPAWN = 3,
SAY_ENRAGE = 4,
SAY_SHATTER = 5,
- SAY_BUBBLE = 6
+ SAY_BUBBLE = 6,
+ EMOTE_SHATTER = 7
};
enum Actions
{
- ACTION_WATER_ELEMENT_HIT = 1
-};
-
-enum IchoronEvents
-{
- EVENT_WATER_BLAST = 1,
- EVENT_WATER_BOLT_VOLLEY
-};
-
-enum GlobuleEvents
-{
- EVENT_GLOBULE_MOVE = 1
+ ACTION_WATER_GLOBULE_HIT = 1,
+ ACTION_PROTECTIVE_BUBBLE_SHATTERED = 2,
+ ACTION_DRAINED = 3
};
enum Misc
{
- DATA_GLOBULE_PATH = 0,
DATA_DEHYDRATION = 1
};
-
-#define MAX_GLOBULE_PATHS 10
-
-Position const globulePaths[MAX_GLOBULE_PATHS] =
-{
- // first target
- { 1861.357f, 804.039f, 44.008f, 6.268f },
- { 1869.375f, 803.976f, 38.781f, 0.009f },
- // second target
- { 1888.063f, 763.488f, 47.667f, 1.744f },
- { 1882.865f, 776.385f, 38.824f, 1.882f },
- // third target
- { 1935.140f, 817.752f, 52.181f, 1.885f },
- { 1916.642f, 826.337f, 39.139f, 2.851f },
- // fourth target
- { 1930.257f, 833.053f, 46.906f, 4.579f },
- { 1916.642f, 826.337f, 39.139f, 2.851f },
- // fifth target
- { 1878.248f, 841.883f, 43.334f, 4.717f },
- { 1879.438f, 834.443f, 38.699f, 4.831f }
-};
-
class boss_ichoron : public CreatureScript
{
-public:
- boss_ichoron() : CreatureScript("boss_ichoron") { }
+ public:
+ boss_ichoron() : CreatureScript("boss_ichoron") { }
- struct boss_ichoronAI : public ScriptedAI
- {
- boss_ichoronAI(Creature* creature) : ScriptedAI(creature), m_waterElements(creature)
+ struct boss_ichoronAI : public BossAI
{
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ boss_ichoronAI(Creature* creature) : BossAI(creature, DATA_ICHORON)
+ {
+ Initialize();
- void Initialize()
- {
- bIsExploded = false;
- bIsFrenzy = false;
- bIsDrained = false;
- dehydration = true;
- drainedTimer = 50;
- burstTimer = 15000;
- }
+ /// for some reason ichoron can't walk back to it's water basin on evade
+ me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING);
+ }
- void Reset() override
- {
- Initialize();
+ void Initialize()
+ {
+ _isFrenzy = false;
+ _dehydration = true;
+ }
- events.Reset();
- me->SetVisible(true);
- DespawnWaterElements();
+ void Reset() override
+ {
+ Initialize();
+ BossAI::Reset();
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED);
- }
+ DoCast(me, SPELL_THREAT_PROC, true);
+ }
- void EnterCombat(Unit* /*who*/) override
- {
- Talk(SAY_AGGRO);
+ void EnterCombat(Unit* who) override
+ {
+ BossAI::EnterCombat(who);
+ Talk(SAY_AGGRO);
+ }
- DoCast(me, SPELL_PROTECTIVE_BUBBLE);
+ void JustReachedHome() override
+ {
+ BossAI::JustReachedHome();
+ instance->SetData(DATA_HANDLE_CELLS, DATA_ICHORON);
+ }
- if (GameObject* door = instance->GetGameObject(DATA_ICHORON_CELL))
- if (door->GetGoState() == GO_STATE_READY)
+ void DoAction(int32 actionId) override
+ {
+ switch (actionId)
{
- EnterEvadeMode();
- return;
- }
+ case ACTION_WATER_GLOBULE_HIT:
+ if (!me->IsAlive())
+ break;
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
+ me->ModifyHealth(int32(me->CountPctFromMaxHealth(3)));
+ _dehydration = false;
+ break;
+ case ACTION_PROTECTIVE_BUBBLE_SHATTERED:
+ {
+ Talk(SAY_SHATTER);
+ Talk(EMOTE_SHATTER);
- events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000));
- events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000));
- }
+ DoCastAOE(SPELL_SPLATTER, true);
+ DoCastAOE(SPELL_BURST, true);
+ DoCast(me, SPELL_DRAINED, true);
- void AttackStart(Unit* who) override
- {
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return;
+ uint32 damage = me->CountPctFromMaxHealth(30);
+ me->LowerPlayerDamageReq(damage);
+ me->ModifyHealth(-std::min<int32>(damage, me->GetHealth() - 1));
- if (me->Attack(who, true))
- {
- me->AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
- DoStartMovement(who);
+ scheduler.DelayAll(Seconds(15));
+ break;
+ }
+ case ACTION_DRAINED:
+ if (HealthAbovePct(30))
+ {
+ Talk(SAY_BUBBLE);
+ DoCast(me, SPELL_PROTECTIVE_BUBBLE, true);
+ }
+ break;
+ default:
+ break;
+ }
}
- }
- void DoAction(int32 param) override
- {
- if (!me->IsAlive())
- return;
+ uint32 GetData(uint32 type) const override
+ {
+ if (type == DATA_DEHYDRATION)
+ return _dehydration ? 1 : 0;
+ return 0;
+ }
- switch (param)
+ void KilledUnit(Unit* victim) override
{
- case ACTION_WATER_ELEMENT_HIT:
- {
- if (bIsExploded)
- DoExplodeCompleted();
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
- me->SetHealth(me->GetHealth() + me->CountPctFromMaxHealth(3));
- dehydration = false;
- }
- break;
+ void JustDied(Unit* killer) override
+ {
+ BossAI::JustDied(killer);
+ Talk(SAY_DEATH);
}
- }
- void DespawnWaterElements()
- {
- m_waterElements.DespawnAll();
- }
+ void JustSummoned(Creature* summon) override
+ {
+ summons.Summon(summon);
- // call when explode shall stop.
- // either when "hit" by a bubble, or when there is no bubble left.
- void DoExplodeCompleted()
- {
- bIsExploded = false;
- bIsDrained = false;
+ if (summon->GetEntry() == NPC_ICHOR_GLOBULE)
+ DoCast(summon, SPELL_WATER_GLOBULE_VISUAL);
+ }
- if (!HealthBelowPct(25))
+ void SummonedCreatureDespawn(Creature* summon) override
{
- Talk(SAY_BUBBLE);
- DoCast(me, SPELL_PROTECTIVE_BUBBLE, true);
+ BossAI::SummonedCreatureDespawn(summon);
+
+ if (summons.empty())
+ me->RemoveAurasDueToSpell(SPELL_DRAINED, ObjectGuid::Empty, 0, AURA_REMOVE_BY_EXPIRE);
}
- me->SetVisible(true);
- me->GetMotionMaster()->MoveChase(me->GetVictim());
- }
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- uint32 GetData(uint32 type) const override
- {
- if (type == DATA_DEHYDRATION)
- return dehydration ? 1 : 0;
+ if (!_isFrenzy && HealthBelowPct(25) && !me->HasAura(SPELL_DRAINED))
+ {
+ Talk(SAY_ENRAGE);
+ DoCast(me, SPELL_FRENZY, true);
+ _isFrenzy = true;
+ }
- return 0;
- }
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
+ }
- void MoveInLineOfSight(Unit* who) override
- {
- if (!who->ToCreature())
- return;
+ void ScheduleTasks() override
+ {
+ scheduler.Async([this]
+ {
+ DoCast(me, SPELL_SHRINK);
+ DoCast(me, SPELL_PROTECTIVE_BUBBLE);
+ });
- if (who->GetEntry() != NPC_ICHOR_GLOBULE)
- return;
+ scheduler.Schedule(Seconds(10), Seconds(15), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_WATER_BOLT_VOLLEY);
+ task.Repeat(Seconds(10), Seconds(15));
+ });
- if (!me->IsWithinDist(who, 4.0f, false))
- return;
+ scheduler.Schedule(Seconds(6), Seconds(9), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f))
+ DoCast(target, SPELL_WATER_BLAST);
+ task.Repeat(Seconds(6), Seconds(9));
+ });
+ }
- if (who->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
- return;
+ private:
+ bool _isFrenzy;
+ bool _dehydration;
+ };
- who->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- who->CastSpell(who, SPELL_MERGE);
- DoAction(ACTION_WATER_ELEMENT_HIT);
- who->ToCreature()->DespawnOrUnsummon(1000);
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<boss_ichoronAI>(creature);
}
+};
- void JustDied(Unit* /*killer*/) override
- {
- Talk(SAY_DEATH);
+class npc_ichor_globule : public CreatureScript
+{
+ public:
+ npc_ichor_globule() : CreatureScript("npc_ichor_globule") { }
- if (bIsExploded)
+ struct npc_ichor_globuleAI : public ScriptedAI
+ {
+ npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature)
{
- bIsExploded = false;
- me->SetVisible(true);
+ _instance = creature->GetInstanceScript();
+ creature->SetReactState(REACT_PASSIVE);
}
- DespawnWaterElements();
-
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- {
- instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 7);
- }
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
+ void SpellHit(Unit* caster, SpellInfo const* spellInfo) override
{
- instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 13);
+ if (spellInfo->Id == SPELL_WATER_GLOBULE_VISUAL)
+ {
+ DoCast(me, SPELL_WATER_GLOBULE_TRANSFORM);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->GetMotionMaster()->MoveFollow(caster, 0.0f, 0.0f);
+ }
}
- }
- void JustSummoned(Creature* summoned) override
- {
- summoned->SetSpeed(MOVE_RUN, 0.3f);
- m_waterElements.Summon(summoned);
+ void MovementInform(uint32 type, uint32 id) override
+ {
+ if (type != FOLLOW_MOTION_TYPE)
+ return;
- instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID());
- }
+ if (_instance->GetObjectGuid(DATA_ICHORON).GetCounter() != id)
+ return;
- void SummonedCreatureDespawn(Creature* summoned) override
- {
- m_waterElements.Despawn(summoned);
+ me->CastSpell(me, SPELL_MERGE);
+ me->DespawnOrUnsummon(1);
+ }
- if (m_waterElements.empty() && bIsExploded)
+ // on retail spell casted on a creature's death are not casted after death but keeping mob at 1 health, casting it and then letting the mob die.
+ // this feature should be still implemented
+ void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
- me->RemoveAllAuras();
- DoExplodeCompleted();
+ if (damage >= me->GetHealth())
+ DoCastAOE(SPELL_SPLASH);
}
- instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID());
- }
+ void UpdateAI(uint32 /*diff*/) override { }
+
+ private:
+ InstanceScript* _instance;
+ };
- void KilledUnit(Unit* victim) override
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (victim->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
+ return GetVioletHoldAI<npc_ichor_globuleAI>(creature);
}
+};
+
+// 59820 - Drained
+class spell_ichoron_drained : public SpellScriptLoader
+{
+ public:
+ spell_ichoron_drained() : SpellScriptLoader("spell_ichoron_drained") { }
- void UpdateAI(uint32 diff) override
+ class spell_ichoron_drained_AuraScript : public AuraScript
{
- if (!UpdateVictim())
- return;
+ PrepareAuraScript(spell_ichoron_drained_AuraScript);
- if (!bIsFrenzy && HealthBelowPct(25) && !bIsExploded)
+ bool Load() override
{
- Talk(SAY_ENRAGE);
- DoCast(me, SPELL_FRENZY, true);
- bIsFrenzy = true;
+ return GetOwner()->GetEntry() == NPC_ICHORON || GetOwner()->GetEntry() == NPC_DUMMY_ICHORON;
}
- if (!bIsFrenzy)
+ void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
- if (!bIsExploded)
- {
- if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE))
- {
- bIsExploded = true;
- Talk(SAY_SHATTER);
- DoCast(SPELL_BURST);
- me->RemoveAllAuras();
- burstTimer = 15000;
+ GetTarget()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_UNK_31);
+ GetTarget()->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
+ }
- std::list<Creature*> summonTargets;
- GetCreatureListWithEntryInGrid(summonTargets, me, NPC_ICHORON_SUMMON_TARGET, 200.0f);
- std::list<Creature*>::iterator itr = summonTargets.begin();
+ void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ GetTarget()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_UNK_31);
+ GetTarget()->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
- for (uint8 i = 0; i < MAX_GLOBULE_PATHS; i++)
- {
- std::advance(itr, urand(0, summonTargets.size() - 1)); // I take a random minion in the list
- Position targetPos = (*itr)->GetRandomNearPosition(10.0f);
- itr = summonTargets.begin();
- TempSummon* globule = me->SummonCreature(NPC_ICHOR_GLOBULE, targetPos, TEMPSUMMON_CORPSE_DESPAWN);
- DoCast(globule, SPELL_WATER_GLOBULE_VISUAL);
-
- float minDistance = 1000.0f;
- uint8 nextPath = 0;
- // I move the globules to next position. the 10 positions are in couples, defined in globulePaths, so i have to increase by 2.
- for (uint8 gpath = 0; gpath < MAX_GLOBULE_PATHS; gpath += 2)
- {
- if (globule->GetDistance(globulePaths[gpath]) < minDistance)
- {
- minDistance = globule->GetDistance(globulePaths[gpath]);
- nextPath = gpath;
- }
- }
-
- globule->GetAI()->SetData(DATA_GLOBULE_PATH, nextPath);
- }
- return;
- }
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
+ if (GetTarget()->IsAIEnabled)
+ GetTarget()->GetAI()->DoAction(ACTION_DRAINED);
+ }
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ void Register() override
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_ichoron_drained_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_ichoron_drained_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
- events.Update(diff);
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_ichoron_drained_AuraScript();
+ }
+};
- switch (events.ExecuteEvent())
- {
- case EVENT_WATER_BLAST:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_WATER_BLAST);
- events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000));
- break;
- case EVENT_WATER_BOLT_VOLLEY:
- DoCast(SPELL_WATER_BOLT_VOLLEY);
- events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000));
- break;
- }
+// 54269 - Merge
+class spell_ichoron_merge : public SpellScriptLoader
+{
+ public:
+ spell_ichoron_merge() : SpellScriptLoader("spell_ichoron_merge") { }
- DoMeleeAttackIfReady();
- }
- else if (!bIsDrained)
- {
- if (drainedTimer <= 0)
- {
- bIsDrained = true;
- drainedTimer = 50;
- uint32 damage = me->CountPctFromMaxHealth(30);
- if (me->GetHealth() < damage)
- me->SetHealth(me->CountPctFromMaxHealth(1));
- else
- {
- me->SetHealth(me->GetHealth() - damage);
- me->LowerPlayerDamageReq(damage);
- }
- DoCast(SPELL_DRAINED);
- me->SetVisible(false);
- me->AttackStop();
- }
- else
- drainedTimer -= diff;
- }
- else if (bIsDrained)
- {
- if (burstTimer <= 0)
- {
- DoExplodeCompleted();
- }
- else
- burstTimer -= diff;
- }
- }
- else
- {
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ class spell_ichoron_merge_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ichoron_merge_SpellScript);
- events.Update(diff);
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHRINK))
+ return false;
+ return true;
+ }
- switch (events.ExecuteEvent())
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Creature* target = GetHitCreature())
{
- case EVENT_WATER_BLAST:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_WATER_BLAST);
- events.ScheduleEvent(EVENT_WATER_BLAST, urand(6000, 9000));
- break;
- case EVENT_WATER_BOLT_VOLLEY:
- DoCast(SPELL_WATER_BOLT_VOLLEY);
- events.ScheduleEvent(EVENT_WATER_BOLT_VOLLEY, urand(10000, 15000));
- break;
+ if (Aura* aura = target->GetAura(SPELL_SHRINK))
+ aura->ModStackAmount(-1);
+
+ target->AI()->DoAction(ACTION_WATER_GLOBULE_HIT);
}
+ }
- DoMeleeAttackIfReady();
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ichoron_merge_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
- }
+ };
- private:
- InstanceScript* instance;
- SummonList m_waterElements;
- EventMap events;
- bool bIsExploded;
- bool bIsFrenzy;
- bool bIsDrained;
- bool dehydration;
- int32 drainedTimer;
- int32 burstTimer;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<boss_ichoronAI>(creature);
- }
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ichoron_merge_SpellScript();
+ }
};
-class npc_ichor_globule : public CreatureScript
+// 54306 - Protective Bubble
+class spell_ichoron_protective_bubble : public SpellScriptLoader
{
-public:
- npc_ichor_globule() : CreatureScript("npc_ichor_globule") { }
+ public:
+ spell_ichoron_protective_bubble() : SpellScriptLoader("spell_ichoron_protective_bubble") { }
- struct npc_ichor_globuleAI : public ScriptedAI
- {
- npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature)
+ class spell_ichoron_protective_bubble_AuraScript : public AuraScript
{
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ PrepareAuraScript(spell_ichoron_protective_bubble_AuraScript);
- void Initialize()
- {
- pathId = 0;
- }
+ bool Load() override
+ {
+ return GetOwner()->GetEntry() == NPC_ICHORON || GetOwner()->GetEntry() == NPC_DUMMY_ICHORON;
+ }
- void Reset() override
- {
- Initialize();
- events.Reset();
- DoCast(SPELL_WATER_GLOBULE);
- me->SetReactState(REACT_PASSIVE);
- }
+ void HandleShatter(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ //if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_ENEMY_SPELL)
+ if (GetAura()->GetCharges() <= 1)
+ if (GetTarget()->IsAIEnabled)
+ GetTarget()->GetAI()->DoAction(ACTION_PROTECTIVE_BUBBLE_SHATTERED);
+ }
- void SetData(uint32 id, uint32 data) override
- {
- if (id == DATA_GLOBULE_PATH)
+ void Register() override
{
- pathId = data;
- me->GetMotionMaster()->MovePoint(0, globulePaths[pathId]);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_ichoron_protective_bubble_AuraScript::HandleShatter, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL);
}
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_ichoron_protective_bubble_AuraScript();
}
+};
- void MovementInform(uint32 type, uint32 id) override
+// 54259 - Splatter
+class spell_ichoron_splatter : public SpellScriptLoader
+{
+ public:
+ spell_ichoron_splatter() : SpellScriptLoader("spell_ichoron_splatter") { }
+
+ class spell_ichoron_splatter_AuraScript : public AuraScript
{
- if (type != POINT_MOTION_TYPE)
- return;
+ PrepareAuraScript(spell_ichoron_splatter_AuraScript);
- switch (id)
+ bool Validate(SpellInfo const* /*spellInfo*/) override
{
- case 0:
- me->GetMotionMaster()->Clear();
- events.ScheduleEvent(EVENT_GLOBULE_MOVE, 500);
- break;
- case 1:
- me->GetMotionMaster()->Clear();
- if (Creature* ichoron = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ICHORON)))
- me->GetMotionMaster()->MoveFollow(ichoron, 0.0f, 0.0f);
- break;
+ if (!sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_1)
+ || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_2)
+ || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_3)
+ || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_4)
+ || !sSpellMgr->GetSpellInfo(SPELL_WATER_GLOBULE_SUMMON_5)
+ || !sSpellMgr->GetSpellInfo(SPELL_SHRINK))
+ return false;
+ return true;
}
- }
- // on retail spell casted on a creature's death are not casted after death but keeping mob at 1 health, casting it and then letting the mob die.
- // this feature should be still implemented
- void DamageTaken(Unit* /*attacker*/, uint32 &damage) override
- {
- int32 actualHp = me->GetHealth();
- actualHp -= damage;
+ void PeriodicTick(AuraEffect const* /*aurEff*/)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(GetTarget(), RAND(SPELL_WATER_GLOBULE_SUMMON_1, SPELL_WATER_GLOBULE_SUMMON_2, SPELL_WATER_GLOBULE_SUMMON_3, SPELL_WATER_GLOBULE_SUMMON_4, SPELL_WATER_GLOBULE_SUMMON_5), true);
+ }
- if (actualHp <= 0)
- DoCast(SPELL_SPLASH);
- }
+ void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
+ if (Aura* aura = GetTarget()->GetAura(SPELL_SHRINK))
+ aura->ModStackAmount(10);
+ }
- void UpdateAI(uint32 diff) override
- {
- events.Update(diff);
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_ichoron_splatter_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_ichoron_splatter_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
- if (events.ExecuteEvent() == EVENT_GLOBULE_MOVE)
- me->GetMotionMaster()->MovePoint(1, globulePaths[pathId + 1]);
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_ichoron_splatter_AuraScript();
}
-
- private:
- InstanceScript* instance;
- EventMap events;
- uint8 pathId;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_ichor_globuleAI>(creature);
- }
};
class achievement_dehydration : public AchievementCriteriaScript
{
public:
- achievement_dehydration() : AchievementCriteriaScript("achievement_dehydration")
- {
- }
+ achievement_dehydration() : AchievementCriteriaScript("achievement_dehydration") { }
bool OnCheck(Player* /*player*/, Unit* target) override
{
@@ -542,5 +477,9 @@ void AddSC_boss_ichoron()
{
new boss_ichoron();
new npc_ichor_globule();
+ new spell_ichoron_drained();
+ new spell_ichoron_merge();
+ new spell_ichoron_protective_bubble();
+ new spell_ichoron_splatter();
new achievement_dehydration();
}
diff --git a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp
index 8b77b512ca4..c3b617f8199 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp
@@ -27,130 +27,82 @@ enum Spells
SPELL_LAVA_BURN = 54249
};
-enum LavanthorEvents
-{
- EVENT_CAUTERIZING_FLAMES = 1,
- EVENT_FIREBOLT,
- EVENT_FLAME_BREATH,
- EVENT_LAVA_BURN
-};
-
class boss_lavanthor : public CreatureScript
{
-public:
- boss_lavanthor() : CreatureScript("boss_lavanthor") { }
+ public:
+ boss_lavanthor() : CreatureScript("boss_lavanthor") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<boss_lavanthorAI>(creature);
- }
-
- struct boss_lavanthorAI : public ScriptedAI
- {
- boss_lavanthorAI(Creature* creature) : ScriptedAI(creature)
+ struct boss_lavanthorAI : public BossAI
{
- instance = creature->GetInstanceScript();
- }
+ boss_lavanthorAI(Creature* creature) : BossAI(creature, DATA_LAVANTHOR) { }
- void Reset() override
- {
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED);
-
- events.Reset();
- }
-
- void EnterCombat(Unit* /*who*/) override
- {
- if (GameObject* door = instance->GetGameObject(DATA_LAVANTHOR_CELL))
- if (door->GetGoState() == GO_STATE_READY)
- {
- EnterEvadeMode();
- return;
- }
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
-
- events.ScheduleEvent(EVENT_FIREBOLT, 1000);
- events.ScheduleEvent(EVENT_FLAME_BREATH, 5000);
- events.ScheduleEvent(EVENT_LAVA_BURN, 10000);
- if (IsHeroic())
- events.ScheduleEvent(EVENT_CAUTERIZING_FLAMES, 3000);
- }
+ void Reset() override
+ {
+ BossAI::Reset();
+ }
- void AttackStart(Unit* who) override
- {
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return;
+ void EnterCombat(Unit* who) override
+ {
+ BossAI::EnterCombat(who);
+ }
- if (me->Attack(who, true))
+ void JustReachedHome() override
{
- me->AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
- DoStartMovement(who);
+ BossAI::JustReachedHome();
+ instance->SetData(DATA_HANDLE_CELLS, DATA_LAVANTHOR);
}
- }
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
+ void JustDied(Unit* killer) override
+ {
+ BossAI::JustDied(killer);
+ }
- events.Update(diff);
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
+ }
- switch (events.ExecuteEvent())
+ void ScheduleTasks() override
{
- case EVENT_FIREBOLT:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ scheduler.Schedule(Seconds(1), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f, true))
DoCast(target, SPELL_FIREBOLT);
- events.ScheduleEvent(EVENT_FIREBOLT, urand(5000, 13000));
- break;
- case EVENT_FLAME_BREATH:
- DoCast(SPELL_FLAME_BREATH);
- events.ScheduleEvent(EVENT_FLAME_BREATH, urand(10000, 15000));
- break;
- case EVENT_LAVA_BURN:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ task.Repeat(Seconds(5), Seconds(13));
+ });
+
+ scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_FLAME_BREATH);
+ task.Repeat(Seconds(10), Seconds(15));
+ });
+
+ scheduler.Schedule(Seconds(10), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f))
DoCast(target, SPELL_LAVA_BURN);
- events.ScheduleEvent(EVENT_LAVA_BURN, urand(15000, 23000));
- break;
- case EVENT_CAUTERIZING_FLAMES:
- DoCast(SPELL_CAUTERIZING_FLAMES);
- events.ScheduleEvent(EVENT_CAUTERIZING_FLAMES, urand(10000, 16000));
- break;
- default:
- break;
- }
+ task.Repeat(Seconds(15), Seconds(23));
+ });
- DoMeleeAttackIfReady();
- }
+ if (IsHeroic())
+ {
+ scheduler.Schedule(Seconds(3), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_CAUTERIZING_FLAMES);
+ task.Repeat(Seconds(10), Seconds(16));
+ });
+ }
+ }
+ };
- void JustDied(Unit* /*killer*/) override
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- {
- instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 7);
- }
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- {
- instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 13);
- }
+ return GetVioletHoldAI<boss_lavanthorAI>(creature);
}
-
- private:
- EventMap events;
- InstanceScript* instance;
- };
};
void AddSC_boss_lavanthor()
diff --git a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp
index ee89faac3a4..4c5c0373b8c 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp
@@ -25,8 +25,10 @@ enum Spells
{
SPELL_CORROSIVE_SALIVA = 54527,
SPELL_OPTIC_LINK = 54396,
- SPELL_RAY_OF_PAIN = 54438, // NYI missing spelldifficulty
- SPELL_RAY_OF_SUFFERING = 54442, // NYI missing spelldifficulty
+ SPELL_RAY_OF_PAIN = 54438,
+ SPELL_RAY_OF_PAIN_H = 59523,
+ SPELL_RAY_OF_SUFFERING = 54442,
+ SPELL_RAY_OF_SUFFERING_H = 59524,
// Visual
SPELL_OPTIC_LINK_LEVEL_1 = 54393,
@@ -34,191 +36,107 @@ enum Spells
SPELL_OPTIC_LINK_LEVEL_3 = 54395
};
-enum MoraggEvents
-{
- EVENT_CORROSIVE_SALIVA = 1,
- EVENT_OPTIC_LINK
-};
-
class boss_moragg : public CreatureScript
{
-public:
- boss_moragg() : CreatureScript("boss_moragg") { }
-
- struct boss_moraggAI : public ScriptedAI
- {
- boss_moraggAI(Creature* creature) : ScriptedAI(creature)
- {
- instance = creature->GetInstanceScript();
- }
-
- void Reset() override
- {
- events.Reset();
-
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED);
- }
+ public:
+ boss_moragg() : CreatureScript("boss_moragg") { }
- void EnterCombat(Unit* /*who*/) override
+ struct boss_moraggAI : public BossAI
{
- if (GameObject* door = instance->GetGameObject(DATA_MORAGG_CELL))
- if (door->GetGoState() == GO_STATE_READY)
- {
- EnterEvadeMode();
- return;
- }
-
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
-
- me->SetInCombatWithZone();
-
- DoCast(SPELL_RAY_OF_PAIN);
- DoCast(SPELL_RAY_OF_SUFFERING);
- events.ScheduleEvent(EVENT_OPTIC_LINK, 15000);
- events.ScheduleEvent(EVENT_CORROSIVE_SALIVA, 5000);
- }
-
- void AttackStart(Unit* who) override
- {
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return;
+ boss_moraggAI(Creature* creature) : BossAI(creature, DATA_MORAGG) { }
- if (me->Attack(who, true))
+ void Reset() override
{
- me->AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
- DoStartMovement(who);
+ BossAI::Reset();
}
- }
-
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
- events.Update(diff);
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- switch (events.ExecuteEvent())
+ void EnterCombat(Unit* who) override
{
- case EVENT_OPTIC_LINK:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_OPTIC_LINK);
- events.ScheduleEvent(EVENT_OPTIC_LINK, 25000);
- break;
- case EVENT_CORROSIVE_SALIVA:
- DoCastVictim(SPELL_CORROSIVE_SALIVA);
- events.ScheduleEvent(EVENT_CORROSIVE_SALIVA, 10000);
- break;
- default:
- break;
+ BossAI::EnterCombat(who);
}
- DoMeleeAttackIfReady();
- }
-
- void JustDied(Unit* /*killer*/) override
- {
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
+ void JustReachedHome() override
{
- instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 7);
+ BossAI::JustReachedHome();
+ instance->SetData(DATA_HANDLE_CELLS, DATA_MORAGG);
}
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
+
+ void JustDied(Unit* killer) override
{
- instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 13);
+ BossAI::JustDied(killer);
}
- }
-
- private:
- EventMap events;
- InstanceScript* instance;
- };
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<boss_moraggAI>(creature);
- }
-};
-
-class spell_moragg_ray_of_suffering : public SpellScriptLoader
-{
-public:
- spell_moragg_ray_of_suffering() : SpellScriptLoader("spell_moragg_ray_of_suffering") { }
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- class spell_moragg_ray_of_suffering_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_moragg_ray_of_suffering_AuraScript);
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
+ }
- void OnPeriodic(AuraEffect const* aurEff)
- {
- PreventDefaultAction();
- std::list<HostileReference*> players = GetTarget()->getThreatManager().getThreatList();
- if (!players.empty())
+ void ScheduleTasks() override
{
- std::list<HostileReference*>::iterator itr = players.begin();
- std::advance(itr, urand(0, players.size() - 1));
+ scheduler.Async([this]
+ {
+ DoCast(me, DUNGEON_MODE(SPELL_RAY_OF_PAIN, SPELL_RAY_OF_PAIN_H));
+ DoCast(me, DUNGEON_MODE(SPELL_RAY_OF_SUFFERING, SPELL_RAY_OF_SUFFERING_H));
+ });
+
+ scheduler.Schedule(Seconds(15), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true))
+ DoCast(target, SPELL_OPTIC_LINK);
+ task.Repeat(Seconds(25));
+ });
- uint32 triggerSpell = GetSpellInfo()->GetEffect(aurEff->GetEffIndex())->TriggerSpell;
- GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff);
+ scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_CORROSIVE_SALIVA);
+ task.Repeat(Seconds(10));
+ });
}
- }
+ };
- void Register() override
+ CreatureAI* GetAI(Creature* creature) const override
{
- OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_suffering_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ return GetVioletHoldAI<boss_moraggAI>(creature);
}
- };
-
- AuraScript* GetAuraScript() const override
- {
- return new spell_moragg_ray_of_suffering_AuraScript();
- }
};
-class spell_moragg_ray_of_pain : public SpellScriptLoader
+class spell_moragg_ray : public SpellScriptLoader
{
-public:
- spell_moragg_ray_of_pain() : SpellScriptLoader("spell_moragg_ray_of_pain") { }
+ public:
+ spell_moragg_ray() : SpellScriptLoader("spell_moragg_ray") { }
- class spell_moragg_ray_of_pain_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_moragg_ray_of_pain_AuraScript);
-
- void OnPeriodic(AuraEffect const* aurEff)
+ class spell_moragg_ray_AuraScript : public AuraScript
{
- PreventDefaultAction();
- std::list<HostileReference*> players = GetTarget()->getThreatManager().getThreatList();
- if (!players.empty())
+ PrepareAuraScript(spell_moragg_ray_AuraScript);
+
+ void OnPeriodic(AuraEffect const* aurEff)
{
- std::list<HostileReference*>::iterator itr = players.begin();
- std::advance(itr, urand(0, players.size() - 1));
+ PreventDefaultAction();
+
+ if (!GetTarget()->IsAIEnabled)
+ return;
- uint32 triggerSpell = GetSpellInfo()->GetEffect(aurEff->GetEffIndex())->TriggerSpell;
- GetTarget()->CastCustomSpell(triggerSpell, SPELLVALUE_MAX_TARGETS, 1, (*itr)->getTarget(), TRIGGERED_FULL_MASK, NULL, aurEff);
+ if (Unit* target = GetTarget()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true))
+ {
+ uint32 triggerSpell = aurEff->GetSpellEffectInfo()->TriggerSpell;
+ GetTarget()->CastSpell(target, triggerSpell, TRIGGERED_FULL_MASK, nullptr, aurEff);
+ }
}
- }
- void Register() override
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
{
- OnEffectPeriodic += AuraEffectPeriodicFn(spell_moragg_ray_of_pain_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ return new spell_moragg_ray_AuraScript();
}
- };
-
- AuraScript* GetAuraScript() const override
- {
- return new spell_moragg_ray_of_pain_AuraScript();
- }
};
class spell_moragg_optic_link : public SpellScriptLoader
@@ -232,30 +150,15 @@ public:
void OnPeriodic(AuraEffect const* aurEff)
{
- switch (aurEff->GetTickNumber()) // Different visual based on tick
+ if (Unit* caster = GetCaster())
{
- case 1:
- case 2:
- case 3:
- GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff);
- break;
- case 4:
- case 5:
- case 6:
- case 7:
- GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff);
- GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_2, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff);
- break;
- case 8:
- case 9:
- case 10:
- case 11:
- GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_1, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff);
- GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_2, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff);
- GetTarget()->CastCustomSpell(SPELL_OPTIC_LINK_LEVEL_3, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, TRIGGERED_FULL_MASK, NULL, aurEff);
- break;
- default:
- break;
+ if (aurEff->GetTickNumber() >= 8)
+ caster->CastSpell(GetTarget(), SPELL_OPTIC_LINK_LEVEL_3, TRIGGERED_FULL_MASK, nullptr, aurEff);
+
+ if (aurEff->GetTickNumber() >= 4)
+ caster->CastSpell(GetTarget(), SPELL_OPTIC_LINK_LEVEL_2, TRIGGERED_FULL_MASK, nullptr, aurEff);
+
+ caster->CastSpell(GetTarget(), SPELL_OPTIC_LINK_LEVEL_1, TRIGGERED_FULL_MASK, nullptr, aurEff);
}
}
@@ -293,7 +196,6 @@ public:
void AddSC_boss_moragg()
{
new boss_moragg();
- new spell_moragg_ray_of_suffering();
- new spell_moragg_ray_of_pain();
+ new spell_moragg_ray();
new spell_moragg_optic_link();
}
diff --git a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
index fe0f161cc27..62fcda47c9b 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
@@ -22,50 +22,53 @@
#include "Player.h"
#include "violet_hold.h"
+/*
+ * TODO:
+ * - Implement Ethereal Summon Target
+ */
+
enum Spells
{
SPELL_ARCANE_BARRAGE_VOLLEY = 54202,
SPELL_ARCANE_BUFFET = 54226,
- SPELL_SUMMON_ETHEREAL_SPHERE_1 = 54102,
- SPELL_SUMMON_ETHEREAL_SPHERE_2 = 61337,
- SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138
+ SPELL_SUMMON_TARGET_VISUAL = 54111
};
+static uint32 const EtherealSphereCount = 3;
+static uint32 const EtherealSphereSummonSpells[EtherealSphereCount] = { 54102, 54137, 54138 };
+static uint32 const EtherealSphereHeroicSummonSpells[EtherealSphereCount] = { 54102, 54137, 54138 };
+
enum NPCs
{
NPC_ETHEREAL_SPHERE = 29271,
- NPC_ETHEREAL_SPHERE2 = 32582
+ NPC_ETHEREAL_SPHERE2 = 32582,
+ NPC_ETHEREAL_SUMMON_TARGET = 29276
};
enum CreatureSpells
{
SPELL_ARCANE_POWER = 54160,
H_SPELL_ARCANE_POWER = 59474,
+ SPELL_MAGIC_PULL = 50770,
SPELL_SUMMON_PLAYERS = 54164,
SPELL_POWER_BALL_VISUAL = 54141,
- SPELL_POWER_BALL_DAMAGE_TRIGGER = 54207
+ SPELL_POWER_BALL_DAMAGE_TRIGGER = 54207,
+ SPELL_POWER_BALL_DAMAGE_TRIGGER_H = 59476
};
enum Yells
{
+ // Xevozz
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_DEATH = 2,
SAY_SPAWN = 3,
SAY_CHARGED = 4,
SAY_REPEAT_SUMMON = 5,
- SAY_SUMMON_ENERGY = 6
-};
+ SAY_SUMMON_ENERGY = 6,
-enum XevozzEvents
-{
- EVENT_ARCANE_BARRAGE = 1,
- EVENT_ARCANE_BUFFET,
- EVENT_SUMMON_SPHERE,
- EVENT_SUMMON_SPHERE_2,
- EVENT_RANGE_CHECK,
- EVENT_SUMMON_PLAYERS,
- EVENT_DESPAWN_SPHERE
+ // Ethereal Sphere
+ SAY_ETHEREAL_SPHERE_SUMMON = 0
};
enum SphereActions
@@ -75,319 +78,210 @@ enum SphereActions
class boss_xevozz : public CreatureScript
{
-public:
- boss_xevozz() : CreatureScript("boss_xevozz") { }
-
- struct boss_xevozzAI : public ScriptedAI
- {
- boss_xevozzAI(Creature* creature) : ScriptedAI(creature)
- {
- instance = creature->GetInstanceScript();
- }
+ public:
+ boss_xevozz() : CreatureScript("boss_xevozz") { }
- void Reset() override
+ struct boss_xevozzAI : public BossAI
{
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, NOT_STARTED);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED);
+ boss_xevozzAI(Creature* creature) : BossAI(creature, DATA_XEVOZZ) { }
- DespawnSphere();
- events.Reset();
- }
+ void Reset() override
+ {
+ BossAI::Reset();
+ }
- void DespawnSphere()
- {
- std::list<Creature*> assistList;
- GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE, 150.0f);
- GetCreatureListWithEntryInGrid(assistList, me, NPC_ETHEREAL_SPHERE2, 150.0f);
+ void EnterCombat(Unit* who) override
+ {
+ BossAI::EnterCombat(who);
+ Talk(SAY_AGGRO);
+ }
- if (assistList.empty())
- return;
+ void JustReachedHome() override
+ {
+ BossAI::JustReachedHome();
+ instance->SetData(DATA_HANDLE_CELLS, DATA_XEVOZZ);
+ }
- for (std::list<Creature*>::const_iterator iter = assistList.begin(); iter != assistList.end(); ++iter)
+ void JustSummoned(Creature* summon) override
{
- if (Creature* pSphere = *iter)
- pSphere->Kill(pSphere, false);
+ BossAI::JustSummoned(summon);
+ summon->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f);
}
- }
- void JustSummoned(Creature* summoned) override
- {
- summoned->SetSpeed(MOVE_RUN, 0.5f);
- summoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f);
- }
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
- void AttackStart(Unit* who) override
- {
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return;
+ void JustDied(Unit* killer) override
+ {
+ BossAI::JustDied(killer);
+ Talk(SAY_DEATH);
+ }
- if (me->Attack(who, true))
+ void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
{
- me->AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
- DoStartMovement(who);
+ if (spell->Id == SPELL_ARCANE_POWER || spell->Id == H_SPELL_ARCANE_POWER)
+ Talk(SAY_SUMMON_ENERGY);
}
- }
- void EnterCombat(Unit* /*who*/) override
- {
- if (GameObject* door = instance->GetGameObject(DATA_XEVOZZ_CELL))
- if (door->GetGoState() == GO_STATE_READY)
- {
- EnterEvadeMode();
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
return;
- }
-
- Talk(SAY_AGGRO);
-
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetBossState(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
-
- events.ScheduleEvent(EVENT_SUMMON_SPHERE, 5000);
- events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(8000, 10000));
- events.ScheduleEvent(EVENT_ARCANE_BUFFET, urand(10000, 11000));
- }
- void JustDied(Unit* /*killer*/) override
- {
- Talk(SAY_DEATH);
-
- DespawnSphere();
-
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- {
- instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 7);
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
}
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
+
+ void ScheduleTasks() override
{
- instance->SetBossState(DATA_2ND_BOSS_EVENT, NOT_STARTED);
- instance->SetData(DATA_WAVE_COUNT, 13);
- }
- }
+ scheduler.Schedule(Seconds(8), Seconds(10), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_ARCANE_BARRAGE_VOLLEY);
+ task.Repeat(Seconds(8), Seconds(10));
+ });
- void KilledUnit(Unit* victim) override
- {
- if (victim->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
- }
+ scheduler.Schedule(Seconds(10), Seconds(11), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true))
+ DoCast(target, SPELL_ARCANE_BUFFET);
+ task.Repeat(Seconds(15), Seconds(20));
+ });
- void SpellHit(Unit* who, const SpellInfo* spell) override
- {
- if (!who->ToCreature())
- return;
+ scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ Talk(SAY_REPEAT_SUMMON);
- if ((spell->Id == SPELL_ARCANE_POWER) || (spell->Id == H_SPELL_ARCANE_POWER))
- Talk(SAY_SUMMON_ENERGY);
- }
+ std::list<uint8> summonSpells = { 0, 1, 2 };
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
+ uint8 spell = Trinity::Containers::SelectRandomContainerElement(summonSpells);
+ DoCast(me, EtherealSphereSummonSpells[spell]);
+ summonSpells.remove(spell);
- events.Update(diff);
+ if (IsHeroic())
+ {
+ spell = Trinity::Containers::SelectRandomContainerElement(summonSpells);
+ task.Schedule(Milliseconds(2500), [this, spell](TaskContext /*task*/)
+ {
+ DoCast(me, EtherealSphereHeroicSummonSpells[spell]);
+ });
+ }
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ task.Schedule(Seconds(33), Seconds(35), [this](TaskContext /*task*/)
+ {
+ DummyEntryCheckPredicate pred;
+ summons.DoAction(ACTION_SUMMON, pred);
+ });
- switch (events.ExecuteEvent())
- {
- case EVENT_ARCANE_BARRAGE:
- DoCast(SPELL_ARCANE_BARRAGE_VOLLEY);
- events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(8000, 10000));
- break;
- case EVENT_ARCANE_BUFFET:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_ARCANE_BUFFET);
- events.ScheduleEvent(EVENT_ARCANE_BUFFET, urand(15000, 20000));
- break;
- case EVENT_SUMMON_SPHERE:
- Talk(SAY_REPEAT_SUMMON);
- DoCast(SPELL_SUMMON_ETHEREAL_SPHERE_1);
- if (IsHeroic())
- events.ScheduleEvent(EVENT_SUMMON_SPHERE_2, 2500);
- events.ScheduleEvent(EVENT_SUMMON_PLAYERS, urand(33000, 35000));
- events.ScheduleEvent(EVENT_SUMMON_SPHERE, urand(45000, 47000));
- break;
- case EVENT_SUMMON_SPHERE_2:
- Talk(SAY_REPEAT_SUMMON);
- DoCast(SPELL_SUMMON_ETHEREAL_SPHERE_2);
- break;
- case EVENT_SUMMON_PLAYERS:
- {
- Creature* sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE, 150.0f);
- if (!sphere)
- sphere = me->FindNearestCreature(NPC_ETHEREAL_SPHERE2, 150.0f);
- if (sphere)
- sphere->GetAI()->DoAction(ACTION_SUMMON);
- break;
- }
- default:
- break;
+ task.Repeat(Seconds(45), Seconds(47));
+ });
}
+ };
- DoMeleeAttackIfReady();
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<boss_xevozzAI>(creature);
}
-
- private:
- InstanceScript* instance;
- EventMap events;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<boss_xevozzAI>(creature);
- }
};
class npc_ethereal_sphere : public CreatureScript
{
-public:
- npc_ethereal_sphere() : CreatureScript("npc_ethereal_sphere") { }
+ public:
+ npc_ethereal_sphere() : CreatureScript("npc_ethereal_sphere") { }
- struct npc_ethereal_sphereAI : public ScriptedAI
- {
- npc_ethereal_sphereAI(Creature* creature) : ScriptedAI(creature)
+ struct npc_ethereal_sphereAI : public ScriptedAI
{
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ npc_ethereal_sphereAI(Creature* creature) : ScriptedAI(creature)
+ {
+ instance = creature->GetInstanceScript();
+ }
- void Initialize()
- {
- arcanePower = false;
- }
+ void Reset() override
+ {
+ scheduler.CancelAll();
+ ScheduledTasks();
- void Reset() override
- {
- Initialize();
- events.Reset();
- DoCast(SPELL_POWER_BALL_VISUAL);
- DoCast(SPELL_POWER_BALL_DAMAGE_TRIGGER);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
- me->setFaction(16);
- events.ScheduleEvent(EVENT_DESPAWN_SPHERE, 40000);
- events.ScheduleEvent(EVENT_RANGE_CHECK, 1000);
- }
+ DoCast(me, SPELL_POWER_BALL_VISUAL);
+ DoCast(me, DUNGEON_MODE(SPELL_POWER_BALL_DAMAGE_TRIGGER, SPELL_POWER_BALL_DAMAGE_TRIGGER_H));
- void DoAction(int32 action) override
- {
- if (action == ACTION_SUMMON)
- DoCast(SPELL_SUMMON_PLAYERS);
- }
+ me->DespawnOrUnsummon(40000);
+ }
- void UpdateAI(uint32 diff) override
- {
- events.Update(diff);
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_SUMMON)
+ {
+ Talk(SAY_ETHEREAL_SPHERE_SUMMON);
+ DoCastAOE(SPELL_SUMMON_PLAYERS);
+ }
+ }
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ void UpdateAI(uint32 diff) override
+ {
+ scheduler.Update(diff);
+ }
- switch (events.ExecuteEvent())
+ void ScheduledTasks()
{
- case EVENT_RANGE_CHECK:
- if (Creature* xevozz = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_XEVOZZ)))
+ scheduler.Schedule(Seconds(1), [this](TaskContext task)
+ {
+ if (Creature* xevozz = instance->GetCreature(DATA_XEVOZZ))
{
- if (me->IsWithinDist(xevozz, 3.0f) && !arcanePower)
+ if (me->IsWithinDist(xevozz, 3.0f))
{
- DoCast(SPELL_ARCANE_POWER);
- arcanePower = true;
- events.ScheduleEvent(EVENT_DESPAWN_SPHERE, 8000);
+ DoCastAOE(SPELL_ARCANE_POWER);
+ me->DespawnOrUnsummon(8000);
+ return;
}
}
- events.ScheduleEvent(EVENT_RANGE_CHECK, 1000);
- break;
- case EVENT_DESPAWN_SPHERE:
- me->DespawnOrUnsummon();
- break;
+ task.Repeat();
+ });
}
- }
- private:
- InstanceScript* instance;
- EventMap events;
- bool arcanePower;
- };
+ private:
+ InstanceScript* instance;
+ TaskScheduler scheduler;
+ };
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_ethereal_sphereAI>(creature);
- }
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_ethereal_sphereAI>(creature);
+ }
};
class spell_xevozz_summon_players : public SpellScriptLoader
{
-public:
- spell_xevozz_summon_players() : SpellScriptLoader("spell_xevozz_summon_players") { }
-
- class spell_xevozz_summon_players_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_xevozz_summon_players_SpellScript);
+ public:
+ spell_xevozz_summon_players() : SpellScriptLoader("spell_xevozz_summon_players") { }
- void HandleScript(SpellEffIndex /*effIndex*/)
+ class spell_xevozz_summon_players_SpellScript : public SpellScript
{
- Unit* target = GetHitUnit();
+ PrepareSpellScript(spell_xevozz_summon_players_SpellScript);
- if (target)
+ bool Validate(SpellInfo const* /*spellInfo*/) override
{
- Position pos = GetOriginalCaster()->GetPosition();
-
- target->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGIC_PULL))
+ return false;
+ return true;
}
- }
-
- void Register() override
- {
- OnEffectHitTarget += SpellEffectFn(spell_xevozz_summon_players_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
- }
- };
-
- SpellScript* GetSpellScript() const override
- {
- return new spell_xevozz_summon_players_SpellScript();
- }
-};
-
-class spell_xevozz_summon_ethereal_sphere : public SpellScriptLoader
-{
-public:
- spell_xevozz_summon_ethereal_sphere() : SpellScriptLoader("spell_xevozz_summon_ethereal_sphere") { }
-
- class spell_xevozz_summon_ethereal_sphere_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_xevozz_summon_ethereal_sphere_SpellScript);
- void HandleScript(SpellDestination& target)
- {
- Unit* caster = GetOriginalCaster();
- Position pos;
- float distance = 0.0f;
-
- while (distance < 20.0f)
+ void HandleScript(SpellEffIndex /*effIndex*/)
{
- pos = caster->GetRandomNearPosition(60.0f);
- distance = caster->GetDistance(pos);
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_MAGIC_PULL, true);
}
- target.Relocate(pos);
- }
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_xevozz_summon_players_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
- void Register() override
+ SpellScript* GetSpellScript() const override
{
- OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_xevozz_summon_ethereal_sphere_SpellScript::HandleScript, EFFECT_0, TARGET_DEST_DB);
+ return new spell_xevozz_summon_players_SpellScript();
}
- };
-
- SpellScript* GetSpellScript() const override
- {
- return new spell_xevozz_summon_ethereal_sphere_SpellScript();
- }
};
void AddSC_boss_xevozz()
@@ -395,5 +289,4 @@ void AddSC_boss_xevozz()
new boss_xevozz();
new npc_ethereal_sphere();
new spell_xevozz_summon_players();
- new spell_xevozz_summon_ethereal_sphere();
}
diff --git a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp
index 5b3f06c9e40..14d7b5fcd95 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp
@@ -25,6 +25,10 @@ enum Spells
SPELL_SUMMON_VOID_SENTRY = 54369,
SPELL_VOID_SHIFT = 54361,
SPELL_VOID_SHIFTED = 54343,
+ SPELL_ZURAMAT_ADD = 54341,
+ SPELL_ZURAMAT_ADD_2 = 54342,
+ SPELL_ZURAMAT_ADD_DUMMY = 54351,
+ SPELL_SUMMON_VOID_SENTRY_BALL = 58650
};
enum Yells
@@ -39,188 +43,172 @@ enum Yells
enum Misc
{
+ ACTION_DESPAWN_VOID_SENTRY_BALL = 1,
DATA_VOID_DANCE = 2153
};
-enum ZuramatEvents
-{
- EVENT_VOID_SHIFT = 1,
- EVENT_SUMMON_VOID,
- EVENT_SHROUD_OF_DARKNESS
-};
-
class boss_zuramat : public CreatureScript
{
-public:
- boss_zuramat() : CreatureScript("boss_zuramat") { }
+ public:
+ boss_zuramat() : CreatureScript("boss_zuramat") { }
- struct boss_zuramatAI : public ScriptedAI
- {
- boss_zuramatAI(Creature* creature) : ScriptedAI(creature), sentries(me)
+ struct boss_zuramatAI : public BossAI
{
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ boss_zuramatAI(Creature* creature) : BossAI(creature, DATA_ZURAMAT)
+ {
+ Initialize();
+ }
- void Initialize()
- {
- voidDance = true;
- }
+ void Initialize()
+ {
+ _voidDance = true;
+ }
- void DespawnSentries()
- {
- sentries.DespawnAll();
- std::list<Creature*> sentrylist;
- GetCreatureListWithEntryInGrid(sentrylist, me, NPC_VOID_SENTRY_BALL, 200.0f);
- if (!sentrylist.empty())
- for (std::list<Creature*>::const_iterator itr = sentrylist.begin(); itr != sentrylist.end(); ++itr)
- (*itr)->DespawnOrUnsummon();
- }
+ void Reset() override
+ {
+ BossAI::Reset();
+ Initialize();
+ }
- void Reset() override
- {
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetData(DATA_1ST_BOSS_EVENT, NOT_STARTED);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED);
-
- Initialize();
- events.Reset();
- DespawnSentries();
- }
+ void EnterCombat(Unit* who) override
+ {
+ BossAI::EnterCombat(who);
+ Talk(SAY_AGGRO);
+ }
- void AttackStart(Unit* who) override
- {
- if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return;
+ void JustReachedHome() override
+ {
+ BossAI::JustReachedHome();
+ instance->SetData(DATA_HANDLE_CELLS, DATA_ZURAMAT);
+ }
- if (me->Attack(who, true))
+ void SummonedCreatureDies(Creature* summon, Unit* /*who*/) override
{
- me->AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
- DoStartMovement(who);
+ if (summon->GetEntry() == NPC_VOID_SENTRY)
+ _voidDance = false;
}
- }
- void EnterCombat(Unit* /*who*/) override
- {
- if (GameObject* door = instance->GetGameObject(DATA_ZURAMAT_CELL))
- if (door->GetGoState() == GO_STATE_READY)
- {
- EnterEvadeMode();
+ void SummonedCreatureDespawn(Creature* summon) override
+ {
+ if (summon->GetEntry() == NPC_VOID_SENTRY)
+ summon->AI()->DoAction(ACTION_DESPAWN_VOID_SENTRY_BALL);
+ BossAI::SummonedCreatureDespawn(summon);
+ }
+
+ uint32 GetData(uint32 type) const override
+ {
+ if (type == DATA_VOID_DANCE)
+ return _voidDance ? 1 : 0;
+
+ return 0;
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ BossAI::JustDied(killer);
+ Talk(SAY_DEATH);
+ }
+
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
return;
- }
- Talk(SAY_AGGRO);
+ scheduler.Update(diff,
+ std::bind(&BossAI::DoMeleeAttackIfReady, this));
+ }
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
- instance->SetBossState(DATA_1ST_BOSS_EVENT, IN_PROGRESS);
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
- instance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
+ void ScheduleTasks() override
+ {
+ scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ DoCast(me, SPELL_SUMMON_VOID_SENTRY);
+ task.Repeat(Seconds(7), Seconds(10));
+ });
- me->SetInCombatWithZone();
- events.ScheduleEvent(EVENT_SHROUD_OF_DARKNESS, urand(18000, 20000));
- events.ScheduleEvent(EVENT_VOID_SHIFT, 9000);
- events.ScheduleEvent(EVENT_SUMMON_VOID, 4000);
- }
+ scheduler.Schedule(Seconds(9), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true))
+ DoCast(target, SPELL_VOID_SHIFT);
+ task.Repeat(Seconds(15));
+ });
- void JustSummoned(Creature* summon) override
- {
- sentries.Summon(summon);
- }
+ scheduler.Schedule(Seconds(18), Seconds(20), [this](TaskContext task)
+ {
+ DoCast(me, SPELL_SHROUD_OF_DARKNESS);
+ task.Repeat(Seconds(18), Seconds(20));
+ });
+ }
- void SummonedCreatureDies(Creature* summoned, Unit* /*who*/) override
- {
- if (summoned->GetEntry() == NPC_VOID_SENTRY)
- voidDance = false;
- }
+ private:
+ bool _voidDance;
+ };
- uint32 GetData(uint32 type) const override
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (type == DATA_VOID_DANCE)
- return voidDance ? 1 : 0;
-
- return 0;
+ return GetVioletHoldAI<boss_zuramatAI>(creature);
}
+};
- void JustDied(Unit* /*killer*/) override
+class npc_void_sentry : public CreatureScript
+{
+ public:
+ npc_void_sentry() : CreatureScript("npc_void_sentry") { }
+
+ struct npc_void_sentryAI : public ScriptedAI
{
- instance->SetData(DATA_ZURAMAT, 1);
+ npc_void_sentryAI(Creature* creature) : ScriptedAI(creature), _summons(creature)
+ {
+ me->SetReactState(REACT_PASSIVE);
+ }
- Talk(SAY_DEATH);
+ void IsSummonedBy(Unit* /*summoner*/) override
+ {
+ me->CastSpell(me, SPELL_SUMMON_VOID_SENTRY_BALL, true);
+ }
- DespawnSentries();
+ void JustSummoned(Creature* summon) override
+ {
+ _summons.Summon(summon);
+ summon->SetReactState(REACT_PASSIVE);
+ }
- if (instance->GetData(DATA_WAVE_COUNT) == 6)
+ void SummonedCreatureDespawn(Creature* summon) override
{
- instance->SetBossState(DATA_1ST_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 7);
+ _summons.Despawn(summon);
}
- else if (instance->GetData(DATA_WAVE_COUNT) == 12)
+
+ void DoAction(int32 actionId) override
{
- instance->SetBossState(DATA_2ND_BOSS_EVENT, DONE);
- instance->SetData(DATA_WAVE_COUNT, 13);
+ if (actionId == ACTION_DESPAWN_VOID_SENTRY_BALL)
+ _summons.DespawnAll();
}
- }
- void KilledUnit(Unit* victim) override
- {
- if (victim->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
- }
+ void JustDied(Unit* /*killer*/) override
+ {
+ DoAction(ACTION_DESPAWN_VOID_SENTRY_BALL);
+ }
- void UpdateAI(uint32 diff) override
+ private:
+ SummonList _summons;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (!UpdateVictim())
- return;
-
- events.Update(diff);
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- switch (events.ExecuteEvent())
- {
- case EVENT_SUMMON_VOID:
- DoCast(SPELL_SUMMON_VOID_SENTRY);
- events.ScheduleEvent(EVENT_SUMMON_VOID, urand(7000, 10000));
- break;
- case EVENT_VOID_SHIFT:
- if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(unit, SPELL_VOID_SHIFT);
- events.ScheduleEvent(EVENT_VOID_SHIFT, 15000);
- break;
- case EVENT_SHROUD_OF_DARKNESS:
- DoCast(SPELL_SHROUD_OF_DARKNESS);
- events.ScheduleEvent(EVENT_SHROUD_OF_DARKNESS, urand(18000, 20000));
- break;
- default:
- break;
- }
-
- DoMeleeAttackIfReady();
+ return GetVioletHoldAI<npc_void_sentryAI>(creature);
}
-
- private:
- InstanceScript* instance;
- EventMap events;
- SummonList sentries;
- bool voidDance;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<boss_zuramatAI>(creature);
- }
};
class achievement_void_dance : public AchievementCriteriaScript
{
public:
- achievement_void_dance() : AchievementCriteriaScript("achievement_void_dance")
- {
- }
+ achievement_void_dance() : AchievementCriteriaScript("achievement_void_dance") { }
bool OnCheck(Player* /*player*/, Unit* target) override
{
@@ -238,5 +226,6 @@ class achievement_void_dance : public AchievementCriteriaScript
void AddSC_boss_zuramat()
{
new boss_zuramat();
+ new npc_void_sentry();
new achievement_void_dance();
}
diff --git a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
index e9c526df42e..9b51e5611ad 100644
--- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
+++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
@@ -18,82 +18,150 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "InstanceScript.h"
+#include "WorldStatePackets.h"
#include "violet_hold.h"
#include "Player.h"
-#include "TemporarySummon.h"
-
-/* Violet Hold encounters:
-0 - First boss
-1 - Second boss
-2 - Cyanigosa*/
-
-/* Violet hold bosses:
-1 - Moragg
-2 - Erekem
-3 - Ichoron
-4 - Lavanthor
-5 - Xevozz
-6 - Zuramat
-7 - Cyanigosa */
-
-enum AzureSaboteurSpells
+
+/*
+ * TODO:
+ * - replace bosses by dummy npcs also after grid unload
+ */
+
+Position const DefenseSystemLocation = { 1888.146f, 803.382f, 58.60389f, 3.071779f }; // sniff
+
+Position const CyanigosaSpawnLocation = { 1922.109f, 804.4493f, 52.49254f, 3.176499f }; // sniff
+Position const CyanigosaJumpLocation = { 1888.32f, 804.473f, 38.3578f, 0.0f }; // sniff
+
+Position const SaboteurSpawnLocation = { 1886.251f, 803.0743f, 38.42326f, 3.211406f }; // sniff
+
+uint32 const PortalPositionsSize = 5;
+Position const PortalPositions[PortalPositionsSize] = // sniff
+{
+ { 1877.523f, 850.1788f, 45.36822f, 4.34587f }, // 0
+ { 1890.679f, 753.4202f, 48.771f, 1.675516f }, // 1
+ { 1936.09f, 803.1875f, 54.09715f, 3.054326f }, // 2
+ { 1858.243f, 770.2379f, 40.42146f, 0.9075712f }, // 3
+ { 1907.288f, 831.1111f, 40.22015f, 3.560472f } // 4
+};
+
+uint32 const PortalElitePositionsSize = 3;
+Position const PortalElitePositions[PortalElitePositionsSize] = // sniff
+{
+ { 1911.281f, 800.9722f, 39.91673f, 3.01942f }, // 5
+ { 1926.516f, 763.6616f, 52.35725f, 2.251475f }, // 6
+ { 1922.464f, 847.0699f, 48.50161f, 3.961897f } // 7
+};
+
+uint32 const PortalIntroPositionsSize = 5;
+Position const PortalIntroPositions[PortalIntroPositionsSize] = // sniff
+{
+ { 1877.51f, 850.1042f, 44.65989f, 4.782202f }, // 0 - Intro
+ { 1890.637f, 753.4705f, 48.72239f, 1.710423f }, // 1 - Intro
+ { 1936.073f, 803.1979f, 53.37491f, 3.124139f }, // 2 - Intro
+ { 1886.545f, 803.2014f, 40.40931f, 3.159046f }, // 3 - Boss 1/2
+ { 1924.096f, 804.3707f, 54.29256f, 3.228859f } // 4 - Boss 3
+};
+
+uint32 const EncouterPortalsCount = PortalPositionsSize + PortalElitePositionsSize;
+
+uint32 const MoraggPathSize = 3;
+G3D::Vector3 const MoraggPath[MoraggPathSize] = // sniff
+{
+ { 1893.895f, 728.1261f, 47.75016f },
+ { 1892.997f, 738.4987f, 47.66684f },
+ { 1889.76f, 758.1089f, 47.66684f }
+};
+
+uint32 const ErekemPathSize = 3;
+G3D::Vector3 const ErekemPath[ErekemPathSize] = // sniff
+{
+ { 1871.456f, 871.0361f, 43.41524f },
+ { 1874.948f, 859.5452f, 43.33349f },
+ { 1877.245f, 851.967f, 43.3335f }
+};
+
+uint32 const ErekemGuardLeftPathSize = 3;
+G3D::Vector3 const ErekemGuardLeftPath[ErekemGuardLeftPathSize] = // sniff
+{
+ { 1853.752f, 862.4528f, 43.41614f },
+ { 1866.931f, 854.577f, 43.3335f },
+ { 1872.973f, 850.7875f, 43.3335f }
+};
+
+uint32 const ErekemGuardRightPathSize = 3;
+G3D::Vector3 const ErekemGuardRightPath[ErekemGuardRightPathSize] = // sniff
+{
+ { 1892.418f, 872.2831f, 43.41563f },
+ { 1885.639f, 859.0245f, 43.3335f },
+ { 1882.432f, 852.2423f, 43.3335f }
+};
+
+uint32 const IchoronPathSize = 5;
+G3D::Vector3 const IchoronPath[IchoronPathSize] = // sniff
{
- SABOTEUR_SHIELD_DISRUPTION = 58291,
- SABOTEUR_SHIELD_EFFECT = 45775
+ { 1942.041f, 749.5228f, 30.95229f },
+ { 1930.571f, 762.9065f, 31.98814f },
+ { 1923.657f, 770.6718f, 34.07256f },
+ { 1910.631f, 784.4096f, 37.09015f },
+ { 1906.595f, 788.3828f, 37.99429f }
};
-enum CrystalSpells
+uint32 const LavanthorPathSize = 3;
+G3D::Vector3 const LavanthorPath[LavanthorPathSize] = // sniff
{
- SPELL_ARCANE_LIGHTNING = 57930
+ { 1844.557f, 748.7083f, 38.74205f },
+ { 1854.618f, 761.5295f, 38.65631f },
+ { 1862.17f, 773.2255f, 38.74879f }
};
-Position const PortalLocation[] =
+uint32 const XevozzPathSize = 3;
+G3D::Vector3 const XevozzPath[XevozzPathSize] = // sniff
{
- {1877.51f, 850.104f, 44.6599f, 4.7822f }, // WP 1
- {1918.37f, 853.437f, 47.1624f, 4.12294f}, // WP 2
- {1936.07f, 803.198f, 53.3749f, 3.12414f}, // WP 3
- {1927.61f, 758.436f, 51.4533f, 2.20891f}, // WP 4
- {1890.64f, 753.471f, 48.7224f, 1.71042f}, // WP 5
- {1908.31f, 809.657f, 38.7037f, 3.08701f} // WP 6
+ { 1908.417f, 845.8502f, 38.71947f },
+ { 1905.557f, 841.3157f, 38.65529f },
+ { 1899.453f, 832.533f, 38.70752f }
+};
+
+uint32 const ZuramatPathSize = 3;
+G3D::Vector3 const ZuramatPath[ZuramatPathSize] = // sniff
+{
+ { 1934.151f, 860.9463f, 47.29499f },
+ { 1927.085f, 852.1342f, 47.19214f },
+ { 1923.226f, 847.3297f, 47.15541f }
};
-Position const ArcaneSphere = {1887.060059f, 806.151001f, 61.321602f, 0.0f};
-Position const BossStartMove1 = {1894.684448f, 739.390503f, 47.668003f, 0.0f};
-Position const BossStartMove2 = {1875.173950f, 860.832703f, 43.333565f, 0.0f};
-Position const BossStartMove21 = {1858.854614f, 855.071411f, 43.333565f, 0.0f};
-Position const BossStartMove22 = {1891.926636f, 863.388977f, 43.333565f, 0.0f};
-Position const BossStartMove3 = {1916.138062f, 778.152222f, 35.772308f, 0.0f};
-Position const BossStartMove4 = {1853.618286f, 758.557617f, 38.657505f, 0.0f};
-Position const BossStartMove5 = {1906.683960f, 842.348022f, 38.637459f, 0.0f};
-Position const BossStartMove6 = {1928.207031f, 852.864441f, 47.200813f, 0.0f};
-
-Position const CyanigosasSpawnLocation = {1930.281250f, 804.407715f, 52.410946f, 3.139621f};
-Position const MiddleRoomLocation = {1892.291260f, 805.696838f, 38.438862f, 3.139621f};
-Position const MiddleRoomPortalSaboLocation = {1896.622925f, 804.854126f, 38.504772f, 3.139621f};
-
-// Cyanigosa's prefight event data
enum Yells
{
- CYANIGOSA_SAY_SPAWN = 0
+ SAY_CYANIGOSA_SPAWN = 3,
+ SAY_XEVOZZ_SPAWN = 3,
+ SAY_EREKEM_SPAWN = 3,
+ SAY_ICHORON_SPAWN = 3,
+ SAY_ZURAMAT_SPAWN = 3,
+
+ SOUND_MORAGG_SPAWN = 10112
};
enum Spells
{
- CYANIGOSA_SPELL_TRANSFORM = 58668,
- CYANIGOSA_BLUE_AURA = 47759,
+ SPELL_CYANIGOSA_TRANSFORM = 58668,
+ SPELL_CYANIGOSA_ARCANE_POWER_STATE = 49411,
+ SPELL_MORAGG_EMOTE_ROAR = 48350,
+ SPELL_LAVANTHOR_SPECIAL_UNARMED = 33334,
+ SPELL_ZURAMAT_COSMETIC_CHANNEL_OMNI = 57552
};
ObjectData const creatureData[] =
{
- { NPC_XEVOZZ, DATA_XEVOZZ },
- { NPC_LAVANTHOR, DATA_LAVANTHOR },
- { NPC_ICHORON, DATA_ICHORON },
- { NPC_ZURAMAT, DATA_ZURAMAT },
- { NPC_EREKEM, DATA_EREKEM },
- { NPC_MORAGG, DATA_MORAGG },
- { NPC_CYANIGOSA, DATA_CYANIGOSA },
- { NPC_SINCLARI, DATA_SINCLARI },
- { 0, 0 } // END
+ { NPC_XEVOZZ, DATA_XEVOZZ },
+ { NPC_LAVANTHOR, DATA_LAVANTHOR },
+ { NPC_ICHORON, DATA_ICHORON },
+ { NPC_ZURAMAT, DATA_ZURAMAT },
+ { NPC_EREKEM, DATA_EREKEM },
+ { NPC_MORAGG, DATA_MORAGG },
+ { NPC_CYANIGOSA, DATA_CYANIGOSA },
+ { NPC_SINCLARI, DATA_SINCLARI },
+ { NPC_SINCLARI_TRIGGER, DATA_SINCLARI_TRIGGER },
+ { 0, 0 } // END
};
ObjectData const gameObjectData[] =
@@ -110,598 +178,770 @@ ObjectData const gameObjectData[] =
{ 0, 0 } // END
};
+MinionData const minionData[] =
+{
+ { NPC_EREKEM_GUARD, DATA_EREKEM },
+ { 0, 0, } // END
+};
+
class instance_violet_hold : public InstanceMapScript
{
-public:
- instance_violet_hold() : InstanceMapScript("instance_violet_hold", 608) { }
+ public:
+ instance_violet_hold() : InstanceMapScript(VioletHoldScriptName, 608) { }
- struct instance_violet_hold_InstanceMapScript : public InstanceScript
- {
- instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map)
+ struct instance_violet_hold_InstanceMapScript : public InstanceScript
{
- SetHeaders(DataHeader);
- SetBossNumber(EncounterCount);
- LoadObjectData(creatureData, gameObjectData);
-
- uiRemoveNpc = 0;
-
- uiDoorIntegrity = 100;
-
- uiWaveCount = 0;
- uiLocation = urand(0, 5);
- uiFirstBoss = 0;
- uiSecondBoss = 0;
- uiCountErekemGuards = 0;
- uiCountActivationCrystals = 0;
- uiCyanigosaEventPhase = 1;
-
- uiActivationTimer = 5000;
- uiDoorSpellTimer = 2000;
- uiCyanigosaEventTimer = 3 * IN_MILLISECONDS;
-
- bActive = false;
- bWiped = false;
- bIsDoorSpellCast = false;
- bCrystalActivated = false;
- defenseless = true;
- uiMainEventPhase = NOT_STARTED;
- zuramatDead = false;
- }
+ instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map)
+ {
+ SetHeaders(DataHeader);
+ SetBossNumber(EncounterCount);
+ LoadObjectData(creatureData, gameObjectData);
+ LoadMinionData(minionData);
- ObjectGuid uiErekemGuard[2];
+ FirstBossId = 0;
+ SecondBossId = 0;
- ObjectGuid uiTeleportationPortal;
- ObjectGuid uiSaboteurPortal;
+ DoorIntegrity = 100;
+ WaveCount = 0;
+ EventState = NOT_STARTED;
- ObjectGuid uiActivationCrystal[4];
+ LastPortalLocation = urand(0, EncouterPortalsCount - 1);
- uint32 uiActivationTimer;
- uint32 uiCyanigosaEventTimer;
- uint32 uiDoorSpellTimer;
+ Defenseless = true;
+ }
- GuidSet trashMobs; // to kill with crystal
+ void OnCreatureCreate(Creature* creature) override
+ {
+ InstanceScript::OnCreatureCreate(creature);
- uint8 uiWaveCount;
- uint8 uiLocation;
- uint8 uiFirstBoss;
- uint8 uiSecondBoss;
- uint8 uiRemoveNpc;
+ switch (creature->GetEntry())
+ {
+ case NPC_EREKEM_GUARD:
+ for (uint8 i = 0; i < ErekemGuardCount; ++i)
+ if (ErekemGuardGUIDs[i].IsEmpty())
+ {
+ ErekemGuardGUIDs[i] = creature->GetGUID();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
- uint8 uiDoorIntegrity;
+ void OnCreatureRemove(Creature* creature) override
+ {
+ InstanceScript::OnCreatureRemove(creature);
- uint8 uiCountErekemGuards;
- uint8 uiCountActivationCrystals;
- uint8 uiCyanigosaEventPhase;
- uint8 uiMainEventPhase; // SPECIAL: pre event animations, IN_PROGRESS: event itself
+ switch (creature->GetEntry())
+ {
+ case NPC_EREKEM_GUARD:
+ for (uint8 i = 0; i < ErekemGuardCount; ++i)
+ if (ErekemGuardGUIDs[i] == creature->GetGUID())
+ {
+ ErekemGuardGUIDs[i].Clear();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
- bool bActive;
- bool bWiped;
- bool bIsDoorSpellCast;
- bool bCrystalActivated;
- bool defenseless;
- bool zuramatDead;
+ void OnGameObjectCreate(GameObject* go) override
+ {
+ InstanceScript::OnGameObjectCreate(go);
- std::list<uint8> NpcAtDoorCastingList;
+ switch (go->GetEntry())
+ {
+ case GO_ACTIVATION_CRYSTAL:
+ for (uint8 i = 0; i < ActivationCrystalCount; ++i)
+ if (ActivationCrystalGUIDs[i].IsEmpty())
+ {
+ ActivationCrystalGUIDs[i] = go->GetGUID();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
- void OnCreatureCreate(Creature* creature) override
- {
- InstanceScript::OnCreatureCreate(creature);
+ void OnGameObjectRemove(GameObject* go) override
+ {
+ InstanceScript::OnGameObjectRemove(go);
+
+ switch (go->GetEntry())
+ {
+ case GO_ACTIVATION_CRYSTAL:
+ for (uint8 i = 0; i < ActivationCrystalCount; ++i)
+ if (ActivationCrystalGUIDs[i] == go->GetGUID())
+ {
+ ActivationCrystalGUIDs[i].Clear();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
- switch (creature->GetEntry())
+ void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& data) override
{
- case NPC_EREKEM_GUARD:
- if (uiCountErekemGuards < 2)
- {
- uiErekemGuard[uiCountErekemGuards++] = creature->GetGUID();
- creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE);
- }
- break;
- case NPC_CYANIGOSA:
- creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE);
- break;
- default:
- break;
- case NPC_VOID_SENTRY:
- if (zuramatDead)
- {
- creature->DespawnOrUnsummon();
- zuramatDead = false;
- }
- break;
+ data.Worldstates.emplace_back(uint32(WORLD_STATE_VH_SHOW), uint32(EventState == IN_PROGRESS ? 1 : 0));
+ data.Worldstates.emplace_back(uint32(WORLD_STATE_VH_PRISON_STATE), uint32(DoorIntegrity));
+ data.Worldstates.emplace_back(uint32(WORLD_STATE_VH_WAVE_COUNT), uint32(WaveCount));
}
- /*if (creature->GetGUID() == uiFirstBoss || creature->GetGUID() == uiSecondBoss)
+ bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override
{
- creature->AllLootRemovedFromCorpse();
- creature->RemoveLootMode(1);
- }*/
- }
+ if (_SkipCheckRequiredBosses(player))
+ return true;
- void OnGameObjectCreate(GameObject* go) override
- {
- InstanceScript::OnGameObjectCreate(go);
+ switch (bossId)
+ {
+ case DATA_MORAGG:
+ case DATA_EREKEM:
+ case DATA_ICHORON:
+ case DATA_LAVANTHOR:
+ case DATA_XEVOZZ:
+ case DATA_ZURAMAT:
+ /// old code used cell door state to check this
+ if (!(WaveCount == 6 && FirstBossId == bossId) && !(WaveCount == 12 && SecondBossId == bossId))
+ return false;
+ break;
+ case DATA_CYANIGOSA:
+ if (WaveCount < 18)
+ return false;
+ break;
+ default:
+ break;
+ }
- switch (go->GetEntry())
+ return true;
+ }
+
+ bool SetBossState(uint32 type, EncounterState state) override
{
- case GO_ACTIVATION_CRYSTAL:
- if (uiCountActivationCrystals < 4)
- uiActivationCrystal[uiCountActivationCrystals++] = go->GetGUID();
- break;
- default:
- break;
+ if (!InstanceScript::SetBossState(type, state))
+ return false;
+
+ switch (type)
+ {
+ case DATA_1ST_BOSS:
+ if (state == DONE)
+ UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_EREKEM, nullptr);
+ break;
+ case DATA_2ND_BOSS:
+ if (state == DONE)
+ UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_MORAGG, nullptr);
+ break;
+ case DATA_CYANIGOSA:
+ if (state == DONE)
+ SetData(DATA_MAIN_EVENT_STATE, DONE);
+ break;
+ case DATA_MORAGG:
+ case DATA_EREKEM:
+ case DATA_ICHORON:
+ case DATA_LAVANTHOR:
+ case DATA_XEVOZZ:
+ case DATA_ZURAMAT:
+ // this won't work correctly because bossstate was initializd with TO_BE_DECIDED
+ if (WaveCount == 6)
+ SetBossState(DATA_1ST_BOSS, state);
+ else if (WaveCount == 12)
+ SetBossState(DATA_2ND_BOSS, state);
+
+ if (state == DONE)
+ SetData(DATA_WAVE_COUNT, WaveCount + 1);
+ break;
+ default:
+ break;
+ }
+
+ return true;
}
- }
- bool SetBossState(uint32 type, EncounterState state) override
- {
- if (!InstanceScript::SetBossState(type, state))
- return false;
+ void SetData(uint32 type, uint32 data) override
+ {
+ switch (type)
+ {
+ case DATA_WAVE_COUNT:
+ WaveCount = data;
+ if (WaveCount)
+ {
+ Scheduler.Schedule(Seconds(IsBossWave(WaveCount - 1) ? 45 : 5), [this](TaskContext /*task*/)
+ {
+ AddWave();
+ });
+ }
+ break;
+ case DATA_DOOR_INTEGRITY:
+ DoorIntegrity = data;
+ Defenseless = false;
+ DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, DoorIntegrity);
+ break;
+ case DATA_START_BOSS_ENCOUNTER:
+ switch (WaveCount)
+ {
+ case 6:
+ StartBossEncounter(FirstBossId);
+ break;
+ case 12:
+ StartBossEncounter(SecondBossId);
+ break;
+ }
+ break;
+ case DATA_MAIN_EVENT_STATE:
+ EventState = data;
+ if (data == IN_PROGRESS) // Start event
+ {
+ DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, WaveCount);
+ DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, DoorIntegrity);
+ DoUpdateWorldState(WORLD_STATE_VH_SHOW, 1);
- switch (type)
+ WaveCount = 1;
+ Scheduler.Async(std::bind(&instance_violet_hold_InstanceMapScript::AddWave, this));
+
+ for (uint8 i = 0; i < ActivationCrystalCount; ++i)
+ if (GameObject* crystal = instance->GetGameObject(ActivationCrystalGUIDs[i]))
+ crystal->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+ }
+ else if (data == NOT_STARTED)
+ {
+ if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR))
+ {
+ mainDoor->SetGoState(GO_STATE_ACTIVE);
+ mainDoor->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
+ }
+
+ DoUpdateWorldState(WORLD_STATE_VH_SHOW, 0);
+ DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, WaveCount);
+ DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, DoorIntegrity);
+
+ for (uint8 i = 0; i < ActivationCrystalCount; ++i)
+ if (GameObject* crystal = instance->GetGameObject(ActivationCrystalGUIDs[i]))
+ crystal->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+ }
+ else if (data == DONE)
+ {
+ if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR))
+ {
+ mainDoor->SetGoState(GO_STATE_ACTIVE);
+ mainDoor->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
+ }
+
+ DoUpdateWorldState(WORLD_STATE_VH_SHOW, 0);
+
+ for (uint8 i = 0; i < ActivationCrystalCount; ++i)
+ if (GameObject* crystal = instance->GetGameObject(ActivationCrystalGUIDs[i]))
+ crystal->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+
+ if (Creature* sinclari = GetCreature(DATA_SINCLARI))
+ sinclari->AI()->DoAction(ACTION_SINCLARI_OUTRO);
+ }
+ break;
+ case DATA_HANDLE_CELLS:
+ HandleCells(data, false);
+ break;
+ }
+ }
+
+ uint32 GetData(uint32 type) const override
{
- case DATA_1ST_BOSS_EVENT:
- if (state == DONE)
- UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_EREKEM, nullptr);
- break;
- case DATA_2ND_BOSS_EVENT:
- if (state == DONE)
- UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_MORAGG, nullptr);
- break;
- case DATA_CYANIGOSA:
- if (state == DONE)
- {
- uiMainEventPhase = DONE;
- if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR))
- mainDoor->SetGoState(GO_STATE_ACTIVE);
- }
- break;
- default:
- break;
+ switch (type)
+ {
+ case DATA_1ST_BOSS:
+ return FirstBossId;
+ case DATA_2ND_BOSS:
+ return SecondBossId;
+ case DATA_MAIN_EVENT_STATE:
+ return EventState;
+ case DATA_WAVE_COUNT:
+ return WaveCount;
+ case DATA_DOOR_INTEGRITY:
+ return DoorIntegrity;
+ case DATA_DEFENSELESS:
+ return Defenseless ? 1 : 0;
+ default:
+ break;
+ }
+
+ return 0;
}
- return true;
- }
+ ObjectGuid GetGuidData(uint32 type) const override
+ {
+ switch (type)
+ {
+ case DATA_EREKEM_GUARD_1:
+ case DATA_EREKEM_GUARD_2:
+ return ErekemGuardGUIDs[type - DATA_EREKEM_GUARD_1];
+ default:
+ break;
+ }
- void SetData(uint32 type, uint32 data) override
- {
- switch (type)
+ return InstanceScript::GetGuidData(type);
+ }
+
+ void SpawnPortal()
{
- case DATA_WAVE_COUNT:
- uiWaveCount = data;
- bActive = true;
- break;
- case DATA_REMOVE_NPC:
- uiRemoveNpc = data;
- break;
- case DATA_PORTAL_LOCATION:
- uiLocation = (uint8)data;
- break;
- case DATA_DOOR_INTEGRITY:
- uiDoorIntegrity = data;
- defenseless = false;
- DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, uiDoorIntegrity);
- break;
- case DATA_NPC_PRESENCE_AT_DOOR_ADD:
- NpcAtDoorCastingList.push_back(data);
- break;
- case DATA_NPC_PRESENCE_AT_DOOR_REMOVE:
- if (!NpcAtDoorCastingList.empty())
- NpcAtDoorCastingList.pop_back();
- break;
- case DATA_MAIN_DOOR:
- if (GameObject* mainDoor = GetGameObject(type))
- mainDoor->SetGoState(GOState(data));
- break;
- case DATA_START_BOSS_ENCOUNTER:
- switch (uiWaveCount)
+ LastPortalLocation = (LastPortalLocation + urand(1, EncouterPortalsCount - 1)) % (EncouterPortalsCount);
+ if (Creature* sinclari = GetCreature(DATA_SINCLARI))
+ {
+ if (LastPortalLocation < PortalPositionsSize)
{
- case 6:
- StartBossEncounter(uiFirstBoss);
- break;
- case 12:
- StartBossEncounter(uiSecondBoss);
- break;
+ if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalPositions[LastPortalLocation], TEMPSUMMON_CORPSE_DESPAWN))
+ portal->AI()->SetData(DATA_PORTAL_LOCATION, LastPortalLocation);
}
- break;
- case DATA_ACTIVATE_CRYSTAL:
- ActivateCrystal();
- break;
- case DATA_MAIN_EVENT_PHASE:
- uiMainEventPhase = data;
- if (data == IN_PROGRESS) // Start event
+ else
{
- if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR))
- mainDoor->SetGoState(GO_STATE_READY);
- uiWaveCount = 1;
- bActive = true;
- for (int i = 0; i < 4; ++i)
- if (GameObject* crystal = instance->GetGameObject(uiActivationCrystal[i]))
- crystal->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
- uiRemoveNpc = 0; // might not have been reset after a wipe on a boss.
+ if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_ELITE, PortalElitePositions[LastPortalLocation - PortalPositionsSize], TEMPSUMMON_CORPSE_DESPAWN))
+ portal->AI()->SetData(DATA_PORTAL_LOCATION, LastPortalLocation);
}
- break;
- case DATA_ZURAMAT:
- zuramatDead = true;
- break;
+ }
}
- }
- void SetGuidData(uint32 type, ObjectGuid data) override
- {
- switch (type)
+ void HandleCells(uint8 bossId, bool open = true)
{
- case DATA_ADD_TRASH_MOB:
- trashMobs.insert(data);
- break;
- case DATA_DEL_TRASH_MOB:
- trashMobs.erase(data);
- break;
+ switch (bossId)
+ {
+ case DATA_MORAGG:
+ HandleGameObject(GetObjectGuid(DATA_MORAGG_CELL), open);
+ break;
+ case DATA_EREKEM:
+ HandleGameObject(GetObjectGuid(DATA_EREKEM_CELL), open);
+ HandleGameObject(GetObjectGuid(DATA_EREKEM_LEFT_GUARD_CELL), open);
+ HandleGameObject(GetObjectGuid(DATA_EREKEM_RIGHT_GUARD_CELL), open);
+ break;
+ case DATA_ICHORON:
+ HandleGameObject(GetObjectGuid(DATA_ICHORON_CELL), open);
+ break;
+ case DATA_LAVANTHOR:
+ HandleGameObject(GetObjectGuid(DATA_LAVANTHOR_CELL), open);
+ break;
+ case DATA_XEVOZZ:
+ HandleGameObject(GetObjectGuid(DATA_XEVOZZ_CELL), open);
+ break;
+ case DATA_ZURAMAT:
+ HandleGameObject(GetObjectGuid(DATA_ZURAMAT_CELL), open);
+ break;
+ default:
+ break;
+ }
}
- }
- uint32 GetData(uint32 type) const override
- {
- switch (type)
+ void StartBossEncounter(uint8 bossId)
{
- case DATA_WAVE_COUNT: return uiWaveCount;
- case DATA_REMOVE_NPC: return uiRemoveNpc;
- case DATA_PORTAL_LOCATION: return uiLocation;
- case DATA_DOOR_INTEGRITY: return uiDoorIntegrity;
- case DATA_NPC_PRESENCE_AT_DOOR: return NpcAtDoorCastingList.size();
- case DATA_FIRST_BOSS: return uiFirstBoss;
- case DATA_SECOND_BOSS: return uiSecondBoss;
- case DATA_MAIN_EVENT_PHASE: return uiMainEventPhase;
- case DATA_DEFENSELESS: return defenseless ? 1 : 0;
- }
+ switch (bossId)
+ {
+ case DATA_MORAGG:
+ Scheduler.Schedule(Seconds(2), [this](TaskContext task)
+ {
+ if (Creature* moragg = GetCreature(DATA_MORAGG))
+ {
+ moragg->PlayDirectSound(SOUND_MORAGG_SPAWN);
+ moragg->CastSpell(moragg, SPELL_MORAGG_EMOTE_ROAR);
+ }
+
+ task.Schedule(Seconds(3), [this](TaskContext task)
+ {
+ if (Creature* moragg = GetCreature(DATA_MORAGG))
+ moragg->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, MoraggPath, MoraggPathSize, true);
+
+ task.Schedule(Seconds(8), [this](TaskContext /*task*/)
+ {
+ if (Creature* moragg = GetCreature(DATA_MORAGG))
+ {
+ moragg->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ moragg->AI()->DoZoneInCombat(moragg, 200.0f);
+ }
+ });
+ });
+ });
+ break;
+ case DATA_EREKEM:
+ Scheduler.Schedule(Seconds(3), [this](TaskContext task)
+ {
+ if (Creature* erekem = GetCreature(DATA_EREKEM))
+ erekem->AI()->Talk(SAY_EREKEM_SPAWN);
+
+ task.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ if (Creature* erekem = GetCreature(DATA_EREKEM))
+ erekem->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ErekemPath, ErekemPathSize, true);
+
+ if (Creature* guard = instance->GetCreature(GetGuidData(DATA_EREKEM_GUARD_1)))
+ guard->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ErekemGuardLeftPath, ErekemGuardLeftPathSize, true);
+ if (Creature* guard = instance->GetCreature(GetGuidData(DATA_EREKEM_GUARD_2)))
+ guard->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ErekemGuardRightPath, ErekemGuardRightPathSize, true);
+
+ task.Schedule(Seconds(6), [this](TaskContext task)
+ {
+ if (Creature* erekem = GetCreature(DATA_EREKEM))
+ erekem->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
+
+ task.Schedule(Seconds(1), [this](TaskContext /*task*/)
+ {
+ for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i)
+ {
+ if (Creature* guard = instance->GetCreature(GetGuidData(i)))
+ guard->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ }
+
+ if (Creature* erekem = GetCreature(DATA_EREKEM))
+ {
+ erekem->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ erekem->AI()->DoZoneInCombat(erekem, 200.0f);
+ }
+ });
+ });
+ });
+ });
+ break;
+ case DATA_ICHORON:
+ Scheduler.Schedule(Seconds(2), [this](TaskContext task)
+ {
+ if (Creature* ichoron = GetCreature(DATA_ICHORON))
+ ichoron->AI()->Talk(SAY_ICHORON_SPAWN);
+
+ task.Schedule(Seconds(3), [this](TaskContext task)
+ {
+ if (Creature* ichoron = GetCreature(DATA_ICHORON))
+ ichoron->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, IchoronPath, IchoronPathSize, true);
+
+ task.Schedule(Seconds(14), [this](TaskContext /*task*/)
+ {
+ if (Creature* ichoron = GetCreature(DATA_ICHORON))
+ {
+ ichoron->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ ichoron->AI()->DoZoneInCombat(ichoron, 200.0f);
+ }
+ });
+ });
+ });
+ break;
+ case DATA_LAVANTHOR:
+ Scheduler.Schedule(Seconds(1), [this](TaskContext task)
+ {
+ if (Creature* lavanthor = GetCreature(DATA_LAVANTHOR))
+ lavanthor->CastSpell(lavanthor, SPELL_LAVANTHOR_SPECIAL_UNARMED);
+
+ task.Schedule(Seconds(3), [this](TaskContext task)
+ {
+ if (Creature* lavanthor = GetCreature(DATA_LAVANTHOR))
+ lavanthor->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, LavanthorPath, LavanthorPathSize, true);
+
+ task.Schedule(Seconds(8), [this](TaskContext /*task*/)
+ {
+ if (Creature* lavanthor = GetCreature(DATA_LAVANTHOR))
+ {
+ lavanthor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ lavanthor->AI()->DoZoneInCombat(lavanthor, 200.0f);
+ }
+ });
+ });
+ });
+ break;
+ case DATA_XEVOZZ:
+ Scheduler.Schedule(Seconds(2), [this](TaskContext task)
+ {
+ if (Creature* xevozz = GetCreature(DATA_XEVOZZ))
+ xevozz->AI()->Talk(SAY_XEVOZZ_SPAWN);
+
+ task.Schedule(Seconds(3), [this](TaskContext task)
+ {
+ if (Creature* xevozz = GetCreature(DATA_XEVOZZ))
+ xevozz->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NO_SHEATHE);
+
+ task.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ if (Creature* xevozz = GetCreature(DATA_XEVOZZ))
+ xevozz->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, XevozzPath, XevozzPathSize, true);
+
+ task.Schedule(Seconds(4), [this](TaskContext /*task*/)
+ {
+ if (Creature* xevozz = GetCreature(DATA_XEVOZZ))
+ {
+ xevozz->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ xevozz->AI()->DoZoneInCombat(xevozz, 200.0f);
+ }
+ });
+ });
+ });
+ });
+ break;
+ case DATA_ZURAMAT:
+ Scheduler.Schedule(Seconds(2), [this](TaskContext task)
+ {
+ if (Creature* zuramat = GetCreature(DATA_ZURAMAT))
+ {
+ zuramat->CastSpell(zuramat, SPELL_ZURAMAT_COSMETIC_CHANNEL_OMNI);
+ zuramat->AI()->Talk(SAY_ZURAMAT_SPAWN);
+ }
+
+ task.Schedule(Seconds(6), [this](TaskContext task)
+ {
+ if (Creature* zuramat = GetCreature(DATA_ZURAMAT))
+ zuramat->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, ZuramatPath, ZuramatPathSize, true);
+
+ task.Schedule(Seconds(4), [this](TaskContext /*task*/)
+ {
+ if (Creature* zuramat = GetCreature(DATA_ZURAMAT))
+ {
+ zuramat->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ zuramat->AI()->DoZoneInCombat(zuramat, 200.0f);
+ }
+ });
+ });
+ });
+ break;
+ default:
+ return;
+ }
- return 0;
- }
+ HandleCells(bossId);
+ }
- ObjectGuid GetGuidData(uint32 type) const override
- {
- switch (type)
+ void ResetBossEncounter(uint8 bossId)
{
- case DATA_EREKEM_GUARD_1: return uiErekemGuard[0];
- case DATA_EREKEM_GUARD_2: return uiErekemGuard[1];
- case DATA_TELEPORTATION_PORTAL: return uiTeleportationPortal;
- case DATA_SABOTEUR_PORTAL: return uiSaboteurPortal;
- }
+ if (bossId < DATA_CYANIGOSA || bossId > DATA_ZURAMAT)
+ return;
- return InstanceScript::GetGuidData(type);
- }
+ Creature* boss = GetCreature(bossId);
+ if (!boss)
+ return;
- void SpawnPortal()
- {
- SetData(DATA_PORTAL_LOCATION, (GetData(DATA_PORTAL_LOCATION) + urand(1, 5))%6);
- if (Creature* sinclari = GetCreature(DATA_SINCLARI))
- if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN))
- uiTeleportationPortal = portal->GetGUID();
- }
+ switch (bossId)
+ {
+ case DATA_CYANIGOSA:
+ boss->DespawnOrUnsummon();
+ break;
+ case DATA_EREKEM:
+ for (uint32 i = DATA_EREKEM_GUARD_1; i <= DATA_EREKEM_GUARD_2; ++i)
+ {
+ if (Creature* guard = instance->GetCreature(GetGuidData(i)))
+ {
+ if (guard->isDead())
+ guard->Respawn();
- void StartBossEncounter(uint8 uiBoss, bool bForceRespawn = true)
- {
- Creature* boss = nullptr;
+ if (GetBossState(bossId) == DONE)
+ UpdateKilledBoss(guard);
- switch (uiBoss)
- {
- case BOSS_MORAGG:
- HandleGameObject(GetObjectGuid(DATA_MORAGG_CELL), bForceRespawn);
- boss = GetCreature(DATA_MORAGG);
- if (boss)
- boss->GetMotionMaster()->MovePoint(0, BossStartMove1);
- break;
- case BOSS_EREKEM:
- HandleGameObject(GetObjectGuid(DATA_EREKEM_CELL), bForceRespawn);
- HandleGameObject(GetObjectGuid(DATA_EREKEM_LEFT_GUARD_CELL), bForceRespawn);
- HandleGameObject(GetObjectGuid(DATA_EREKEM_RIGHT_GUARD_CELL), bForceRespawn);
-
- boss = GetCreature(DATA_EREKEM);
- if (boss)
- boss->GetMotionMaster()->MovePoint(0, BossStartMove2);
-
- if (Creature* pGuard1 = instance->GetCreature(uiErekemGuard[0]))
- {
- if (bForceRespawn)
+ guard->GetMotionMaster()->MoveTargetedHome();
+ guard->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ }
+ }
+ // no break
+ default:
+ if (boss->isDead())
{
- pGuard1->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NON_ATTACKABLE);
- pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21);
+ // respawn and update to a placeholder npc to avoid be looted again
+ boss->Respawn();
+ UpdateKilledBoss(boss);
}
- else
- pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE);
- }
- if (Creature* pGuard2 = instance->GetCreature(uiErekemGuard[1]))
- {
- if (bForceRespawn)
+ boss->GetMotionMaster()->MoveTargetedHome();
+ boss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ break;
+ }
+ }
+
+ void AddWave()
+ {
+ DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, WaveCount);
+
+ switch (WaveCount)
+ {
+ case 6:
+ if (FirstBossId == 0)
+ FirstBossId = urand(DATA_MORAGG, DATA_ZURAMAT);
+ if (Creature* sinclari = GetCreature(DATA_SINCLARI))
{
- pGuard2->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE);
- pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22);
+ sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[3], TEMPSUMMON_TIMED_DESPAWN, 3000);
+ sinclari->SummonCreature(NPC_SABOTEOUR, SaboteurSpawnLocation, TEMPSUMMON_DEAD_DESPAWN);
}
- else
- pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE);
- }
- break;
- case BOSS_ICHORON:
- HandleGameObject(GetObjectGuid(DATA_ICHORON_CELL), bForceRespawn);
- boss = GetCreature(DATA_ICHORON);
- if (boss)
- boss->GetMotionMaster()->MovePoint(0, BossStartMove3);
- break;
- case BOSS_LAVANTHOR:
- HandleGameObject(GetObjectGuid(DATA_LAVANTHOR_CELL), bForceRespawn);
- boss = GetCreature(DATA_LAVANTHOR);
- if (boss)
- boss->GetMotionMaster()->MovePoint(0, BossStartMove4);
- break;
- case BOSS_XEVOZZ:
- HandleGameObject(GetObjectGuid(DATA_XEVOZZ_CELL), bForceRespawn);
- boss = GetCreature(DATA_XEVOZZ);
- if (boss)
- boss->GetMotionMaster()->MovePoint(0, BossStartMove5);
- break;
- case BOSS_ZURAMAT:
- HandleGameObject(GetObjectGuid(DATA_ZURAMAT_CELL), bForceRespawn);
- boss = GetCreature(DATA_ZURAMAT);
- if (boss)
- boss->GetMotionMaster()->MovePoint(0, BossStartMove6);
- break;
+ break;
+ case 12:
+ if (SecondBossId == 0)
+ do
+ {
+ SecondBossId = urand(DATA_MORAGG, DATA_ZURAMAT);
+ } while (SecondBossId == FirstBossId);
+ if (Creature* sinclari = GetCreature(DATA_SINCLARI))
+ {
+ sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[3], TEMPSUMMON_TIMED_DESPAWN, 3000);
+ sinclari->SummonCreature(NPC_SABOTEOUR, SaboteurSpawnLocation, TEMPSUMMON_DEAD_DESPAWN);
+ }
+ break;
+ case 18:
+ if (Creature* sinclari = GetCreature(DATA_SINCLARI))
+ {
+ sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[4], TEMPSUMMON_TIMED_DESPAWN, 6000);
+ if (Creature* cyanigosa = sinclari->SummonCreature(NPC_CYANIGOSA, CyanigosaSpawnLocation, TEMPSUMMON_DEAD_DESPAWN))
+ cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_ARCANE_POWER_STATE, true);
+ ScheduleCyanigosaIntro();
+ }
+ break;
+ default:
+ SpawnPortal();
+ break;
+ }
}
- // generic boss state changes
- if (boss)
+ void WriteSaveDataMore(std::ostringstream& data) override
{
- boss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE);
- boss->SetReactState(REACT_AGGRESSIVE);
+ data << FirstBossId << ' ' << SecondBossId;
+ }
+
+ void ReadSaveDataMore(std::istringstream& data) override
+ {
+ data >> FirstBossId;
+ data >> SecondBossId;
+ }
- if (!bForceRespawn)
+ bool CheckWipe() const
+ {
+ Map::PlayerList const& players = instance->GetPlayers();
+ for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
- if (boss->isDead())
- {
- // respawn but avoid to be looted again
- boss->Respawn();
- boss->RemoveLootMode(1);
- }
- else
- boss->GetMotionMaster()->MoveTargetedHome();
+ Player* player = itr->GetSource();
+ if (player->IsGameMaster())
+ continue;
- boss->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE);
- uiWaveCount = 0;
+ if (player->IsAlive())
+ return false;
}
- }
- }
- void AddWave()
- {
- DoUpdateWorldState(WORLD_STATE_VH, 1);
- DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, uiWaveCount);
+ return true;
+ }
- switch (uiWaveCount)
+ void UpdateKilledBoss(Creature* boss)
{
- case 6:
- if (uiFirstBoss == 0)
- uiFirstBoss = urand(1, 6);
- if (Creature* sinclari = GetCreature(DATA_SINCLARI))
- {
- if (Creature* portal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN))
- uiSaboteurPortal = portal->GetGUID();
- if (Creature* azureSaboteur = sinclari->SummonCreature(NPC_SABOTEOUR, MiddleRoomLocation, TEMPSUMMON_DEAD_DESPAWN))
- azureSaboteur->CastSpell(azureSaboteur, SABOTEUR_SHIELD_EFFECT, false);
- }
- break;
- case 12:
- if (uiSecondBoss == 0)
- do
- {
- uiSecondBoss = urand(1, 6);
- } while (uiSecondBoss == uiFirstBoss);
- if (Creature* sinclari = GetCreature(DATA_SINCLARI))
- {
- if (Creature* pPortal = sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN))
- uiSaboteurPortal = pPortal->GetGUID();
- if (Creature* pAzureSaboteur = sinclari->SummonCreature(NPC_SABOTEOUR, MiddleRoomLocation, TEMPSUMMON_DEAD_DESPAWN))
- pAzureSaboteur->CastSpell(pAzureSaboteur, SABOTEUR_SHIELD_EFFECT, false);
- }
- break;
- case 18:
- if (Creature* sinclari = GetCreature(DATA_SINCLARI))
- sinclari->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN);
- break;
- case 1:
+ switch (boss->GetEntry())
{
- if (GameObject* mainDoor = GetGameObject(DATA_MAIN_DOOR))
- mainDoor->SetGoState(GO_STATE_READY);
- DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, 100);
- // no break
+ case NPC_XEVOZZ:
+ boss->UpdateEntry(NPC_DUMMY_XEVOZZ);
+ break;
+ case NPC_LAVANTHOR:
+ boss->UpdateEntry(NPC_DUMMY_LAVANTHOR);
+ break;
+ case NPC_ICHORON:
+ boss->UpdateEntry(NPC_DUMMY_ICHORON);
+ break;
+ case NPC_ZURAMAT:
+ boss->UpdateEntry(NPC_DUMMY_ZURAMAT);
+ break;
+ case NPC_EREKEM:
+ boss->UpdateEntry(NPC_DUMMY_EREKEM);
+ break;
+ case NPC_MORAGG:
+ boss->UpdateEntry(NPC_DUMMY_MORAGG);
+ break;
+ case NPC_EREKEM_GUARD:
+ boss->UpdateEntry(NPC_DUMMY_EREKEM_GUARD);
+ break;
+ default:
+ break;
}
- default:
- SpawnPortal();
- break;
}
- }
- void WriteSaveDataMore(std::ostringstream& data) override
- {
- data << uiFirstBoss << ' ' << uiSecondBoss;
- }
+ void Update(uint32 diff) override
+ {
+ if (!instance->HavePlayers())
+ return;
- void ReadSaveDataMore(std::istringstream& data) override
- {
- data >> uiFirstBoss;
- data >> uiSecondBoss;
- }
+ // if main event is in progress and players have wiped then reset instance
+ if ((EventState == IN_PROGRESS && CheckWipe()) || EventState == FAIL)
+ {
+ ResetBossEncounter(FirstBossId);
+ ResetBossEncounter(SecondBossId);
+ ResetBossEncounter(DATA_CYANIGOSA);
- bool CheckWipe()
- {
- Map::PlayerList const &players = instance->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
- {
- Player* player = itr->GetSource();
- if (player->IsGameMaster())
- continue;
+ WaveCount = 0;
+ DoorIntegrity = 100;
+ Defenseless = true;
+ SetData(DATA_MAIN_EVENT_STATE, NOT_STARTED);
- if (player->IsAlive())
- return false;
- }
+ Scheduler.CancelAll();
- zuramatDead = false;
- return true;
- }
+ if (Creature* sinclari = GetCreature(DATA_SINCLARI))
+ sinclari->AI()->EnterEvadeMode();
+ }
- void Update(uint32 diff) override
- {
- if (!instance->HavePlayers())
- return;
+ Scheduler.Update(diff);
- // portals should spawn if other portal is dead and doors are closed
- if (bActive && uiMainEventPhase == IN_PROGRESS)
- {
- if (uiActivationTimer < diff)
+ if (EventState == IN_PROGRESS)
{
- AddWave();
- bActive = false;
- // 1 minute waiting time after each boss fight
- uiActivationTimer = (uiWaveCount == 6 || uiWaveCount == 12) ? 60000 : 5000;
- } else uiActivationTimer -= diff;
+ // if door is destroyed, event is failed
+ if (!GetData(DATA_DOOR_INTEGRITY))
+ EventState = FAIL;
+ }
}
- // if main event is in progress and players have wiped then reset instance
- if (uiMainEventPhase == IN_PROGRESS && CheckWipe())
+ void ScheduleCyanigosaIntro()
{
- SetData(DATA_REMOVE_NPC, 1);
- StartBossEncounter(uiFirstBoss, false);
- StartBossEncounter(uiSecondBoss, false);
-
- SetData(DATA_MAIN_DOOR, GO_STATE_ACTIVE);
- SetData(DATA_WAVE_COUNT, 0);
- uiMainEventPhase = NOT_STARTED;
- uiActivationTimer = 5000;
-
- for (int i = 0; i < 4; ++i)
- if (GameObject* crystal = instance->GetGameObject(uiActivationCrystal[i]))
- crystal->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
-
- if (Creature* sinclari = GetCreature(DATA_SINCLARI))
+ Scheduler.Schedule(Seconds(2), [this](TaskContext task)
{
- sinclari->SetVisible(true);
+ if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA))
+ cyanigosa->AI()->Talk(SAY_CYANIGOSA_SPAWN);
- std::list<Creature*> GuardList;
- sinclari->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f);
- if (!GuardList.empty())
+ task.Schedule(Seconds(6), [this](TaskContext task)
{
- for (Creature* guard : GuardList)
+ if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA))
+ cyanigosa->GetMotionMaster()->MoveJump(CyanigosaJumpLocation, 10.0f, 27.44744f);
+
+ task.Schedule(Seconds(7), [this](TaskContext /*task*/)
{
- guard->SetVisible(true);
- guard->SetReactState(REACT_AGGRESSIVE);
- guard->GetMotionMaster()->MovePoint(1, guard->GetHomePosition());
- }
- }
- sinclari->GetMotionMaster()->MovePoint(1, sinclari->GetHomePosition());
- sinclari->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- }
+ if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA))
+ {
+ cyanigosa->RemoveAurasDueToSpell(SPELL_CYANIGOSA_ARCANE_POWER_STATE);
+ cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_TRANSFORM, true);
+ cyanigosa->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
+ }
+ });
+ });
+ });
}
- // Cyanigosa is spawned but not tranformed, prefight event
- Creature* cyanigosa = GetCreature(DATA_CYANIGOSA);
- if (cyanigosa && !cyanigosa->HasAura(CYANIGOSA_SPELL_TRANSFORM))
+ void ProcessEvent(WorldObject* /*go*/, uint32 eventId) override
{
- if (uiCyanigosaEventTimer <= diff)
+ if (eventId == EVENT_ACTIVATE_CRYSTAL)
{
- switch (uiCyanigosaEventPhase)
- {
- case 1:
- cyanigosa->CastSpell(cyanigosa, CYANIGOSA_BLUE_AURA, false);
- cyanigosa->AI()->Talk(CYANIGOSA_SAY_SPAWN);
- uiCyanigosaEventTimer = 7*IN_MILLISECONDS;
- ++uiCyanigosaEventPhase;
- break;
- case 2:
- cyanigosa->GetMotionMaster()->MoveJump(MiddleRoomLocation.GetPositionX(), MiddleRoomLocation.GetPositionY(), MiddleRoomLocation.GetPositionZ(), 10.0f, 20.0f);
- cyanigosa->CastSpell(cyanigosa, CYANIGOSA_BLUE_AURA, false);
- uiCyanigosaEventTimer = 7*IN_MILLISECONDS;
- ++uiCyanigosaEventPhase;
- break;
- case 3:
- cyanigosa->RemoveAurasDueToSpell(CYANIGOSA_BLUE_AURA);
- cyanigosa->CastSpell(cyanigosa, CYANIGOSA_SPELL_TRANSFORM, 0);
- cyanigosa->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE);
- cyanigosa->SetReactState(REACT_AGGRESSIVE);
- uiCyanigosaEventTimer = 2*IN_MILLISECONDS;
- ++uiCyanigosaEventPhase;
- break;
- case 4:
- uiCyanigosaEventPhase = 0;
- break;
- }
- } else uiCyanigosaEventTimer -= diff;
+ instance->SummonCreature(NPC_DEFENSE_SYSTEM, DefenseSystemLocation);
+ Defenseless = false;
+ }
}
- // if there are NPCs in front of the prison door, which are casting the door seal spell and doors are active
- if (GetData(DATA_NPC_PRESENCE_AT_DOOR) && uiMainEventPhase == IN_PROGRESS)
+ static bool IsBossWave(uint8 wave)
{
- // if door integrity is > 0 then decrase it's integrity state
- if (GetData(DATA_DOOR_INTEGRITY))
- {
- if (uiDoorSpellTimer < diff)
- {
- SetData(DATA_DOOR_INTEGRITY, GetData(DATA_DOOR_INTEGRITY)-1);
- uiDoorSpellTimer =2000;
- } else uiDoorSpellTimer -= diff;
- }
- // else set door state to active (means door will open and group have failed to sustain mob invasion on the door)
- else
- {
- SetData(DATA_MAIN_DOOR, GO_STATE_ACTIVE);
- uiMainEventPhase = FAIL;
- }
+ return wave && ((wave % 6) == 0);
}
- }
- void ActivateCrystal()
- {
- // just to make things easier we'll get the gameobject from the map
- GameObject* invoker = instance->GetGameObject(uiActivationCrystal[0]);
- if (!invoker)
- return;
+ protected:
+ TaskScheduler Scheduler;
- SpellInfo const* spellInfoLightning = sSpellMgr->GetSpellInfo(SPELL_ARCANE_LIGHTNING);
- if (!spellInfoLightning)
- return;
+ static uint8 const ErekemGuardCount = 2;
+ ObjectGuid ErekemGuardGUIDs[ErekemGuardCount];
- // the orb
- TempSummon* trigger = invoker->SummonCreature(NPC_DEFENSE_SYSTEM, ArcaneSphere, TEMPSUMMON_MANUAL_DESPAWN, 0);
- if (!trigger)
- return;
+ static uint8 const ActivationCrystalCount = 5;
+ ObjectGuid ActivationCrystalGUIDs[ActivationCrystalCount];
- // visuals
- trigger->CastSpell(trigger, spellInfoLightning, true, 0, 0, trigger->GetGUID());
+ uint32 FirstBossId;
+ uint32 SecondBossId;
- // Kill all mobs registered with SetGuidData(ADD_TRASH_MOB)
- for (GuidSet::const_iterator itr = trashMobs.begin(); itr != trashMobs.end();)
- {
- Creature* creature = instance->GetCreature(*itr);
- // Increment the iterator before killing the creature because the kill will remove itr from trashMobs
- ++itr;
- if (creature && creature->IsAlive())
- trigger->Kill(creature);
- }
- }
+ uint8 DoorIntegrity;
+ uint8 WaveCount;
+ uint8 EventState;
+ uint8 LastPortalLocation;
+
+ bool Defenseless;
+ };
- void ProcessEvent(WorldObject* /*go*/, uint32 uiEventId) override
+ InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
- switch (uiEventId)
- {
- case EVENT_ACTIVATE_CRYSTAL:
- bCrystalActivated = true; // Activation by player's will throw event signal
- ActivateCrystal();
- break;
- }
+ return new instance_violet_hold_InstanceMapScript(map);
}
- };
-
- InstanceScript* GetInstanceScript(InstanceMap* map) const override
- {
- return new instance_violet_hold_InstanceMapScript(map);
- }
};
void AddSC_instance_violet_hold()
diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp
index b05da4b994c..fdb4c4dc3fc 100644
--- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp
+++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp
@@ -15,36 +15,41 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "ScriptedEscortAI.h"
-#include "violet_hold.h"
-#include "Player.h"
-#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
+#include "violet_hold.h"
-#define GOSSIP_START_EVENT "Get your people to safety, we'll keep the Blue Dragonflight's forces at bay."
-#define GOSSIP_ITEM_1 "Activate the crystals when we get in trouble, right"
-#define GOSSIP_I_WANT_IN "I'm not fighting, so send me in now!"
-#define SAY_EVENT_LOCK "I'm locking the door. Good luck, and thank you for doing this."
-#define SPAWN_TIME 20000
+/*
+ * TODO:
+ * - add missing trash emotes
+ */
-enum PortalCreatures
+enum PortalCreatureIds
{
NPC_AZURE_INVADER_1 = 30661,
- NPC_AZURE_INVADER_2 = 30961,
NPC_AZURE_SPELLBREAKER_1 = 30662,
- NPC_AZURE_SPELLBREAKER_2 = 30962,
NPC_AZURE_BINDER_1 = 30663,
- NPC_AZURE_BINDER_2 = 30918,
NPC_AZURE_MAGE_SLAYER_1 = 30664,
+ NPC_VETERAN_MAGE_HUNTER = 30665,
+ NPC_AZURE_CAPTAIN_1 = 30666,
+ NPC_AZURE_SORCEROR_1 = 30667,
+ NPC_AZURE_RAIDER_1 = 30668,
+
+ NPC_AZURE_BINDER_2 = 30918,
+ NPC_AZURE_INVADER_2 = 30961,
+ NPC_AZURE_SPELLBREAKER_2 = 30962,
NPC_AZURE_MAGE_SLAYER_2 = 30963,
- NPC_AZURE_CAPTAIN = 30666,
- NPC_AZURE_SORCEROR = 30667,
- NPC_AZURE_RAIDER = 30668,
- NPC_AZURE_STALKER = 32191
+ NPC_AZURE_BINDER_3 = 31007,
+ NPC_AZURE_INVADER_3 = 31008,
+ NPC_AZURE_SPELLBREAKER_3 = 31009,
+ NPC_AZURE_MAGE_SLAYER_3 = 31010,
+ NPC_AZURE_RAIDER_2 = 31118,
+ NPC_AZURE_STALKER_1 = 32191
};
enum AzureInvaderSpells
@@ -103,8 +108,8 @@ enum AzureStalkerSpells
enum AzureSaboteurSpells
{
- SABOTEUR_SHIELD_DISRUPTION = 58291,
- SABOTEUR_SHIELD_EFFECT = 45775
+ SPELL_SHIELD_DISRUPTION = 58291,
+ SPELL_TELEPORT_VISUAL = 51347
};
enum TrashDoorSpell
@@ -112,19 +117,45 @@ enum TrashDoorSpell
SPELL_DESTROY_DOOR_SEAL = 58040
};
-enum Spells
+enum DefenseSystemSpells
+{
+ SPELL_ARCANE_LIGHTNING_DAMAGE = 57912,
+ SPELL_ARCANE_LIGHTNING_INSTAKILL = 58152,
+ SPELL_ARCANE_LIGHTNING_DUMMY = 57930
+};
+
+enum MiscSpells
+{
+ SPELL_PORTAL_PERIODIC = 58008,
+ SPELL_PORTAL_CHANNEL = 58012,
+ SPELL_CRYSTAL_ACTIVATION = 57804,
+
+ SPELL_TELEPORT_PLAYER = 62138,
+ SPELL_TELEPORT_PLAYER_EFFECT = 62139
+};
+
+enum MiscData
{
- SPELL_PORTAL_CHANNEL = 58012,
- SPELL_CRYSTAL_ACTIVATION = 57804, // visual effect
- SPELL_ARCANE_SPHERE_PASSIVE = 44263
+ DATA_PORTAL_PERIODIC_TICK = 1
};
enum Sinclari
{
- SAY_SINCLARI_1 = 0
+ // Sinclari
+ SAY_SINCLARI_INTRO_1 = 0,
+ SAY_SINCLARI_INTRO_2 = 1,
+ SAY_SINCLARI_OUTRO = 2,
+
+ GOSSIP_MENU_START_ENCOUNTER = 9998,
+ GOSSIP_MENU_SEND_ME_IN = 10275,
+
+ // Sinclari Trigger
+ SAY_SINCLARI_ELITE_SQUAD = 0,
+ SAY_SINCLARI_PORTAL_GUARDIAN = 1,
+ SAY_SINCLARI_PORTAL_KEEPER = 2
};
-float FirstPortalWPs [6][3] =
+G3D::Vector3 const FirstPortalWPs[6] =
{
{1877.670288f, 842.280273f, 43.333591f},
{1877.338867f, 834.615356f, 38.762287f},
@@ -135,7 +166,7 @@ float FirstPortalWPs [6][3] =
//{1825.736084f, 807.305847f, 44.363785f}
};
-float SecondPortalFirstWPs [9][3] =
+G3D::Vector3 const SecondPortalFirstWPs[9] =
{
{1902.561401f, 853.334656f, 47.106117f},
{1895.486084f, 855.376404f, 44.334591f},
@@ -149,7 +180,7 @@ float SecondPortalFirstWPs [9][3] =
//{1825.736084f, 807.305847f, 44.363785f}
};
-float SecondPortalSecondWPs [8][3] =
+G3D::Vector3 const SecondPortalSecondWPs[8] =
{
{1929.392212f, 837.614990f, 47.136166f},
{1928.290649f, 824.750427f, 45.474411f},
@@ -162,7 +193,7 @@ float SecondPortalSecondWPs [8][3] =
//{1825.736084f, 807.305847f, 44.363785f}
};
-float ThirdPortalWPs [8][3] =
+G3D::Vector3 const ThirdPortalWPs[8] =
{
{1934.049438f, 815.778503f, 52.408699f},
{1928.290649f, 824.750427f, 45.474411f},
@@ -175,7 +206,7 @@ float ThirdPortalWPs [8][3] =
//{1825.736084f, 807.305847f, 44.363785f}
};
-float FourthPortalWPs [9][3] =
+G3D::Vector3 const FourthPortalWPs[9] =
{
{1921.658447f, 761.657043f, 50.866741f},
{1910.559814f, 755.780457f, 47.701447f},
@@ -189,7 +220,7 @@ float FourthPortalWPs [9][3] =
//{1827.100342f, 801.605957f, 44.363358f}
};
-float FifthPortalWPs [6][3] =
+G3D::Vector3 const FifthPortalWPs[6] =
{
{1887.398804f, 763.633240f, 47.666851f},
{1879.020386f, 775.396973f, 38.705990f},
@@ -200,7 +231,7 @@ float FifthPortalWPs [6][3] =
//{1827.100342f, 801.605957f, 44.363358f}
};
-float SixthPoralWPs [4][3] =
+G3D::Vector3 const SixthPoralWPs[4] =
{
{1888.861084f, 805.074768f, 38.375790f},
{1869.793823f, 804.135804f, 38.647018f},
@@ -209,1308 +240,1188 @@ float SixthPoralWPs [4][3] =
//{1826.889648f, 803.929993f, 44.363239f}
};
-const float SaboteurFinalPos1[3][3] =
+G3D::Vector3 const DefaultPortalWPs[1] =
{
- {1892.502319f, 777.410767f, 38.630402f},
- {1891.165161f, 762.969421f, 47.666920f},
- {1893.168091f, 740.919189f, 47.666920f}
+ { 1843.567017f, 804.288208f, 44.139091f }
};
-const float SaboteurFinalPos2[3][3] =
+
+uint32 const SaboteurMoraggPathSize = 5;
+G3D::Vector3 const SaboteurMoraggPath[SaboteurMoraggPathSize] = // sniff
{
- {1882.242676f, 834.818726f, 38.646786f},
- {1879.220825f, 842.224854f, 43.333641f},
- {1873.842896f, 863.892456f, 43.333641f}
+ { 1886.251f, 803.0743f, 38.42326f },
+ { 1885.71f, 799.8929f, 38.37241f },
+ { 1889.505f, 762.3288f, 47.66684f },
+ { 1894.542f, 742.1829f, 47.66684f },
+ { 1894.603f, 739.9231f, 47.66684f },
};
-const float SaboteurFinalPos3[2][3] =
+
+uint32 const SaboteurErekemPathSize = 5;
+G3D::Vector3 const SaboteurErekemPath[SaboteurErekemPathSize] = // sniff
{
- {1904.298340f, 792.400391f, 38.646782f},
- {1935.716919f, 758.437073f, 30.627895f}
+ { 1886.251f, 803.0743f, 38.42326f },
+ { 1881.047f, 829.6866f, 38.64856f },
+ { 1877.585f, 844.6685f, 38.49014f },
+ { 1876.085f, 851.6685f, 42.99014f },
+ { 1873.747f, 864.1373f, 43.33349f }
};
-const float SaboteurFinalPos4[3] =
+
+uint32 const SaboteurIchoronPathSize = 3;
+G3D::Vector3 const SaboteurIchoronPath[SaboteurIchoronPathSize] = // sniff
{
- 1855.006104f, 760.641724f, 38.655266f
+ { 1886.251f, 803.0743f, 38.42326f },
+ { 1888.672f, 801.2348f, 38.42305f },
+ { 1901.987f, 793.3254f, 38.65126f }
};
-const float SaboteurFinalPos5[3] =
+
+uint32 const SaboteurLavanthorPathSize = 3;
+G3D::Vector3 const SaboteurLavanthorPath[SaboteurLavanthorPathSize] = // sniff
{
- 1906.667358f, 841.705566f, 38.637894f
+ { 1886.251f, 803.0743f, 38.42326f },
+ { 1867.925f, 778.8035f, 38.64702f },
+ { 1853.304f, 759.0161f, 38.65761f }
};
-const float SaboteurFinalPos6[5][3] =
-{
- {1911.437012f, 821.289246f, 38.684128f},
- {1920.734009f, 822.978027f, 41.525414f},
- {1928.262939f, 830.836609f, 44.668266f},
- {1929.338989f, 837.593933f, 47.137596f},
- {1931.063354f, 848.468445f, 47.190434f}
- };
-
-const Position PortalLocation[] =
+
+uint32 const SaboteurXevozzPathSize = 4;
+G3D::Vector3 const SaboteurXevozzPath[SaboteurXevozzPathSize] = // sniff
{
- { 1877.51f, 850.104f, 44.6599f, 4.7822f }, // WP 1
- { 1936.07f, 803.198f, 53.3749f, 3.12414f }, // WP 3
- { 1890.64f, 753.471f, 48.7224f, 1.71042f }, // WP 5
+ { 1886.251f, 803.0743f, 38.42326f },
+ { 1889.096f, 810.0487f, 38.43871f },
+ { 1896.547f, 823.5473f, 38.72863f },
+ { 1906.666f, 842.3111f, 38.63351f }
};
-#define MAX_PRE_EVENT_PORTAL 3
+uint32 const SaboteurZuramatPathSize = 7;
+G3D::Vector3 const SaboteurZuramatPath[SaboteurZuramatPathSize] = // sniff
+{
+ { 1886.251f, 803.0743f, 38.42326f },
+ { 1889.69f, 807.0032f, 38.39914f },
+ { 1906.91f, 818.2574f, 38.86596f },
+ { 1929.03f, 824.2713f, 46.09165f },
+ { 1928.441f, 842.8891f, 47.15078f },
+ { 1927.454f, 851.6091f, 47.19094f },
+ { 1927.947f, 852.2986f, 47.19637f }
+};
-ObjectGuid preEventPortalGUID[MAX_PRE_EVENT_PORTAL] = { ObjectGuid::Empty };
+Position const SinclariPositions[] = // sniff
+{
+ { 1829.142f, 798.219f, 44.36212f, 0.122173f }, // 0 - Crystal
+ { 1820.12f, 803.916f, 44.36466f, 0.0f }, // 1 - Outside
+ { 1816.185f, 804.0629f, 44.44799f, 3.176499f }, // 2 - Second Spawn Point
+ { 1827.886f, 804.0555f, 44.36467f, 0.0f } // 3 - Outro
+};
-const Position MovePosition = { 1806.955566f, 803.851807f, 44.363323f, 0.0f };
-const Position playerTeleportPosition = { 1830.531006f, 803.939758f, 44.340508f, 6.281611f };
-const Position sinclariOutsidePosition = { 1820.429810f, 804.066040f, 44.363998f, 0.0f };
-const Position sinclariCrystalPosition = { 1828.868286f, 798.468811f, 44.363998f, 3.890467f };
+Position const GuardsMovePosition = { 1802.099f, 803.7724f, 44.36466f, 0.0f }; // sniff
class npc_sinclari_vh : public CreatureScript
{
-public:
- npc_sinclari_vh() : CreatureScript("npc_sinclari_vh") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF+1:
- player->CLOSE_GOSSIP_MENU();
- ENSURE_AI(npc_sinclari_vh::npc_sinclariAI, creature->AI())->uiPhase = 1;
- if (InstanceScript* instance = creature->GetInstanceScript())
- instance->SetData(DATA_MAIN_EVENT_PHASE, SPECIAL);
- break;
- case GOSSIP_ACTION_INFO_DEF+2:
- player->SEND_GOSSIP_MENU(13854, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF+3:
- player->NearTeleportTo(playerTeleportPosition.GetPositionX(), playerTeleportPosition.GetPositionY(), playerTeleportPosition.GetPositionZ(), playerTeleportPosition.GetOrientation(), true);
- player->CLOSE_GOSSIP_MENU();
- break;
- }
- return true;
- }
+ public:
+ npc_sinclari_vh() : CreatureScript("npc_sinclari_vh") { }
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (InstanceScript* instance = creature->GetInstanceScript())
+ bool OnGossipHello(Player* player, Creature* creature) override
{
- switch (instance->GetData(DATA_MAIN_EVENT_PHASE))
+ // override default gossip
+ if (InstanceScript* instance = creature->GetInstanceScript())
{
- case NOT_STARTED:
- case FAIL: // Allow to start event if not started or wiped
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_START_EVENT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
- player->SEND_GOSSIP_MENU(13853, creature->GetGUID());
- break;
- case IN_PROGRESS: // Allow to teleport inside if event is in progress
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_I_WANT_IN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3);
- player->SEND_GOSSIP_MENU(13853, creature->GetGUID());
- break;
- default:
- player->SEND_GOSSIP_MENU(13910, creature->GetGUID());
+ switch (instance->GetData(DATA_MAIN_EVENT_STATE))
+ {
+ case IN_PROGRESS:
+ player->PrepareGossipMenu(creature, GOSSIP_MENU_SEND_ME_IN, true);
+ player->SendPreparedGossip(creature);
+ return true;
+ case DONE:
+ return true; // NYI
+ case NOT_STARTED:
+ case FAIL:
+ default:
+ break;
+ }
}
- }
- return true;
- }
- struct npc_sinclariAI : public ScriptedAI
- {
- npc_sinclariAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
+ // load default gossip
+ return false;
}
- void Initialize()
+ struct npc_sinclariAI : public ScriptedAI
{
- uiPhase = 0;
- uiTimer = 0;
- }
+ npc_sinclariAI(Creature* creature) : ScriptedAI(creature), _summons(creature)
+ {
+ _instance = creature->GetInstanceScript();
+ }
- InstanceScript* instance;
+ void Reset() override
+ {
+ _summons.DespawnAll();
+ for (uint8 i = 0; i < PortalIntroCount; ++i)
+ if (Creature* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL_INTRO, PortalIntroPositions[i], TEMPSUMMON_MANUAL_DESPAWN))
+ summon->AI()->SetData(DATA_PORTAL_LOCATION, i);
- uint8 uiPhase;
- uint32 uiTimer;
+ me->SetVisible(true);
+ me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
- void Reset() override
- {
- Initialize();
+ std::list<Creature*> guardList;
+ me->GetCreatureListWithEntryInGrid(guardList, NPC_VIOLET_HOLD_GUARD, 100.0f);
+ for (Creature* guard : guardList)
+ {
+ guard->Respawn(true);
+ guard->SetVisible(true);
+ guard->SetReactState(REACT_AGGRESSIVE);
+ guard->AI()->EnterEvadeMode();
+ }
+ }
- me->SetReactState(REACT_AGGRESSIVE);
- for (uint8 i = 0; i < MAX_PRE_EVENT_PORTAL; i++)
- if (TempSummon* summon = me->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocation[i], TEMPSUMMON_MANUAL_DESPAWN))
- preEventPortalGUID[i] = summon->GetGUID();
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
+ {
+ if (menuId == GOSSIP_MENU_START_ENCOUNTER && gossipListId == 0)
+ {
+ me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ _instance->SetData(DATA_MAIN_EVENT_STATE, SPECIAL);
+ ScheduleIntro();
+ player->PlayerTalkClass->SendCloseGossip();
+ }
+ else if (menuId == GOSSIP_MENU_SEND_ME_IN && gossipListId == 0)
+ {
+ me->CastSpell(player, SPELL_TELEPORT_PLAYER, true);
+ player->PlayerTalkClass->SendCloseGossip();
+ }
+ }
- std::list<Creature*> GuardList;
- me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f);
- if (!GuardList.empty())
+ void DoAction(int32 actionId) override
{
- for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr)
+ if (actionId == ACTION_SINCLARI_OUTRO)
{
- if (Creature* pGuard = *itr)
- {
- pGuard->DisappearAndDie();
- pGuard->Respawn();
- pGuard->SetVisible(true);
- pGuard->SetReactState(REACT_AGGRESSIVE);
- }
+ me->SetVisible(true);
+ ScheduleOutro();
}
}
- }
- void UpdateAI(uint32 uiDiff) override
- {
- if (uiPhase)
+ void UpdateAI(uint32 diff) override
{
- if (uiTimer <= uiDiff)
+ _scheduler.Update(diff);
+
+ if (!UpdateVictim())
+ return;
+
+ DoMeleeAttackIfReady();
+ }
+
+ void ScheduleIntro()
+ {
+ _scheduler.Schedule(Seconds(1), [this](TaskContext task)
{
- switch (uiPhase)
+ switch (task.GetRepeatCounter())
{
- case 1:
+ case 0:
me->SetWalk(true);
- me->GetMotionMaster()->MovePoint(0, sinclariCrystalPosition);
- uiTimer = 1000;
- uiPhase = 6;
+ me->GetMotionMaster()->MovePoint(0, SinclariPositions[0]);
+ task.Repeat(Seconds(1));
break;
- case 2:
- {
- me->SetFacingTo(me->GetOrientation() - 3.14f);
- Talk(SAY_SINCLARI_1);
- uiTimer = 1500;
- uiPhase = 7;
+ case 1:
+ me->HandleEmoteCommand(EMOTE_ONESHOT_USE_STANDING);
+ me->GetMap()->SummonCreature(NPC_DEFENSE_SYSTEM, DefenseSystemLocation);
+ task.Repeat(Seconds(3));
break;
- }
- case 3:
- {
- std::list<Creature*> GuardList;
- me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f);
- if (!GuardList.empty())
- for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr)
+ case 2:
+ me->SetFacingTo(SinclariPositions[0].GetOrientation());
+ Talk(SAY_SINCLARI_INTRO_1);
+
+ task.Schedule(Seconds(1), [this](TaskContext /*task*/)
+ {
+ std::list<Creature*> guardList;
+ me->GetCreatureListWithEntryInGrid(guardList, NPC_VIOLET_HOLD_GUARD, 100.0f);
+ for (Creature* guard : guardList)
{
- if (Creature* pGuard = *itr)
- {
- pGuard->SetVisible(false);
- }
+ guard->SetReactState(REACT_PASSIVE);
+ guard->SetWalk(false);
+ guard->GetMotionMaster()->MovePoint(0, GuardsMovePosition);
}
- uiTimer = 2000;
- uiPhase = 4;
+ });
+
+ task.Repeat(Seconds(2));
+ break;
+ case 3:
+ me->GetMotionMaster()->MovePoint(0, SinclariPositions[1]);
+ _summons.DespawnAll();
+ task.Repeat(Seconds(5));
break;
- }
case 4:
- me->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition);
- uiTimer = 4000;
- uiPhase = 5;
+ me->SetFacingTo(SinclariPositions[1].GetOrientation());
+
+ task.Schedule(Seconds(1), [this](TaskContext /*task*/)
+ {
+ std::list<Creature*> guardList;
+ me->GetCreatureListWithEntryInGrid(guardList, NPC_VIOLET_HOLD_GUARD, 100.0f);
+ for (Creature* guard : guardList)
+ guard->SetVisible(false);
+ });
+
+ task.Repeat(Seconds(6));
break;
case 5:
- me->SetFacingTo(0.006673f);
- me->Say(SAY_EVENT_LOCK, LANG_UNIVERSAL, me); // need to change to db say
- me->SetReactState(REACT_PASSIVE);
- uiTimer = 3000;
- uiPhase = 8;
+ Talk(SAY_SINCLARI_INTRO_2);
+ task.Repeat(Seconds(4));
break;
case 6:
- me->GetMotionMaster()->MovementExpired();
- me->HandleEmoteCommand(EMOTE_STATE_USE_STANDING);
- uiTimer = 2000;
- uiPhase = 2;
+ me->HandleEmoteCommand(EMOTE_ONESHOT_TALK_NO_SHEATHE);
+ task.Repeat(Seconds(1));
break;
case 7:
- {
- std::list<Creature*> creatures;
- GetCreatureListWithEntryInGrid(creatures, me, NPC_TELEPORTATION_PORTAL, 200.0f);
- GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_BINDER_1, 200.0f);
- GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_MAGE_SLAYER_1, 200.0f);
- GetCreatureListWithEntryInGrid(creatures, me, NPC_AZURE_INVADER_1, 200.0f);
- DoCast(SPELL_CRYSTAL_ACTIVATION);
- if (!creatures.empty())
+ if (GameObject* mainDoor = _instance->GetGameObject(DATA_MAIN_DOOR))
{
- for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
- (*itr)->DisappearAndDie();
+ mainDoor->SetGoState(GO_STATE_READY);
+ mainDoor->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
}
- uiTimer = 500;
- uiPhase = 9;
- }
- break;
+ task.Repeat(Seconds(5));
+ break;
case 8:
- instance->SetData(DATA_MAIN_EVENT_PHASE, IN_PROGRESS);
- uiTimer = 0;
- uiPhase = 0;
+ me->SetVisible(false);
+ task.Repeat(Seconds(1));
break;
case 9:
- {
- std::list<Creature*> GuardList;
- me->GetCreatureListWithEntryInGrid(GuardList, NPC_VIOLET_HOLD_GUARD, 40.0f);
- if (!GuardList.empty())
- for (std::list<Creature*>::const_iterator itr = GuardList.begin(); itr != GuardList.end(); ++itr)
- {
- if (Creature* pGuard = *itr)
- {
- pGuard->SetReactState(REACT_PASSIVE);
- pGuard->SetWalk(false);
- pGuard->GetMotionMaster()->MovePoint(0, MovePosition);
- }
- }
- uiTimer = 4000;
- uiPhase = 3;
- }
- break;
+ _instance->SetData(DATA_MAIN_EVENT_STATE, IN_PROGRESS);
+ // [1] GUID: Full: 0xF1300077C202E6DD Type: Creature Entry: 30658 Low: 190173
+ break;
+ default:
+ break;
}
- }
- else uiTimer -= uiDiff;
+ });
}
- if (!UpdateVictim())
- return;
+ void ScheduleOutro()
+ {
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ Talk(SAY_SINCLARI_OUTRO);
+ me->GetMotionMaster()->MovePoint(0, SinclariPositions[3]);
- DoMeleeAttackIfReady();
- }
- };
+ task.Schedule(Seconds(10), [this](TaskContext /*task*/)
+ {
+ me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+ });
+ });
+ }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_sinclariAI>(creature);
- }
-};
+ void JustSummoned(Creature* summon) override
+ {
+ ScriptedAI::JustSummoned(summon);
+ _summons.Summon(summon);
+ }
-class npc_azure_saboteur : public CreatureScript
-{
-public:
- npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { }
+ void SummonedCreatureDespawn(Creature* summon) override
+ {
+ _summons.Despawn(summon);
+ ScriptedAI::SummonedCreatureDespawn(summon);
+ }
- struct npc_azure_saboteurAI : public npc_escortAI
- {
- npc_azure_saboteurAI(Creature* creature) : npc_escortAI(creature)
- {
- instance = creature->GetInstanceScript();
- bHasGotMovingPoints = false;
- uiBoss = 0;
- Reset();
- }
+ private:
+ InstanceScript* _instance;
+ TaskScheduler _scheduler;
- InstanceScript* instance;
- bool bHasGotMovingPoints;
- uint32 uiBoss;
+ SummonList _summons;
+ };
- void Reset() override
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (!uiBoss)
- uiBoss = instance->GetData(DATA_WAVE_COUNT) == 6 ? instance->GetData(DATA_FIRST_BOSS) : instance->GetData(DATA_SECOND_BOSS);
- me->SetReactState(REACT_PASSIVE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_NON_ATTACKABLE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ return GetVioletHoldAI<npc_sinclariAI>(creature);
}
+};
- void WaypointReached(uint32 waypointId) override
- {
- switch (uiBoss)
- {
- case 1:
- if (waypointId == 2)
- FinishPointReached();
- break;
- case 2:
- if (waypointId == 2)
- FinishPointReached();
- break;
- case 3:
- if (waypointId == 1)
- FinishPointReached();
- break;
- case 4:
- if (waypointId == 0)
- FinishPointReached();
- break;
- case 5:
- if (waypointId == 0)
- FinishPointReached();
- break;
- case 6:
- if (waypointId == 4)
- FinishPointReached();
- break;
- }
- }
+class npc_azure_saboteur : public CreatureScript
+{
+ public:
+ npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { }
- void UpdateAI(uint32 diff) override
+ struct npc_azure_saboteurAI : public ScriptedAI
{
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) != IN_PROGRESS)
- me->CastStop();
+ npc_azure_saboteurAI(Creature* creature) : ScriptedAI(creature)
+ {
+ _instance = creature->GetInstanceScript();
- npc_escortAI::UpdateAI(diff);
+ if (_instance->GetData(DATA_WAVE_COUNT) == 6)
+ _bossId = _instance->GetData(DATA_1ST_BOSS);
+ else
+ _bossId = _instance->GetData(DATA_2ND_BOSS);
+ }
- if (!bHasGotMovingPoints)
+ void StartMovement()
{
- bHasGotMovingPoints = true;
- switch (uiBoss)
+ uint32 pathSize = 0;
+ G3D::Vector3 const* path = nullptr;
+
+ switch (_bossId)
{
- 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);
+ case DATA_MORAGG:
+ pathSize = SaboteurMoraggPathSize;
+ path = SaboteurMoraggPath;
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);
+ case DATA_EREKEM:
+ pathSize = SaboteurErekemPathSize;
+ path = SaboteurErekemPath;
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);
+ case DATA_ICHORON:
+ pathSize = SaboteurIchoronPathSize;
+ path = SaboteurIchoronPath;
break;
- case 4:
- AddWaypoint(0, SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 0);
- me->SetHomePosition(SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 3.991108f);
+ case DATA_LAVANTHOR:
+ pathSize = SaboteurLavanthorPathSize;
+ path = SaboteurLavanthorPath;
break;
- case 5:
- AddWaypoint(0, SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 0);
- me->SetHomePosition(SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 1.100841f);
+ case DATA_XEVOZZ:
+ pathSize = SaboteurXevozzPathSize;
+ path = SaboteurXevozzPath;
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);
+ case DATA_ZURAMAT:
+ pathSize = SaboteurZuramatPathSize;
+ path = SaboteurZuramatPath;
break;
}
- SetDespawnAtEnd(false);
- Start(true, true);
+ if (path)
+ me->GetMotionMaster()->MoveSmoothPath(POINT_INTRO, path, pathSize, false);
+ }
+
+ void Reset() override
+ {
+ _scheduler.CancelAll();
+ _scheduler.Schedule(Seconds(2), [this](TaskContext /*task*/)
+ {
+ StartMovement();
+ });
+ }
+
+ void MovementInform(uint32 type, uint32 pointId) override
+ {
+ if (type == EFFECT_MOTION_TYPE && pointId == POINT_INTRO)
+ {
+ _scheduler.Schedule(Seconds(0), [this](TaskContext task)
+ {
+ me->CastSpell(me, SPELL_SHIELD_DISRUPTION, false);
+
+ if (task.GetRepeatCounter() < 2)
+ task.Repeat(Seconds(1));
+ else
+ {
+ task.Schedule(Seconds(2), [this](TaskContext /*task*/)
+ {
+ _instance->SetData(DATA_START_BOSS_ENCOUNTER, 1);
+ me->CastSpell(me, SPELL_TELEPORT_VISUAL, false);
+ me->DespawnOrUnsummon(1000);
+ });
+ }
+ });
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
}
- }
- void FinishPointReached()
+ private:
+ InstanceScript* _instance;
+ TaskScheduler _scheduler;
+
+ uint32 _bossId;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
{
- me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false);
- me->DisappearAndDie();
- if (Creature* pSaboPort = ObjectAccessor::GetCreature((*me), instance->GetGuidData(DATA_SABOTEUR_PORTAL)))
- pSaboPort->DisappearAndDie();
- instance->SetData(DATA_START_BOSS_ENCOUNTER, 1);
+ return GetVioletHoldAI<npc_azure_saboteurAI>(creature);
}
- };
+};
- CreatureAI* GetAI(Creature* creature) const override
+struct npc_violet_hold_teleportation_portal_commonAI : public ScriptedAI
+{
+ npc_violet_hold_teleportation_portal_commonAI(Creature* creature) : ScriptedAI(creature), _summons(me)
{
- return GetInstanceAI<npc_azure_saboteurAI>(creature);
+ _instance = creature->GetInstanceScript();
+ _portalLocation = 0;
}
-};
-class npc_teleportation_portal_vh : public CreatureScript
-{
-public:
- npc_teleportation_portal_vh() : CreatureScript("npc_teleportation_portal_vh") { }
+ void InitializeAI() override
+ {
+ ScriptedAI::InitializeAI();
+ ScheduleTasks();
+ }
- struct npc_teleportation_portalAI : public ScriptedAI
+ void SetData(uint32 type, uint32 data) override
{
- npc_teleportation_portalAI(Creature* creature) : ScriptedAI(creature), listOfMobs(me)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- uiTypeOfMobsPortal = urand(0, 1); // 0 - elite mobs 1 - portal guardian or portal keeper with regular mobs
+ if (type == DATA_PORTAL_LOCATION)
+ _portalLocation = uint8(data);
+ }
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) == NOT_STARTED)
- uiTypeOfMobsPortal = 2;
- }
+ void MoveInLineOfSight(Unit* /*who*/) override { }
- void Initialize()
- {
- uiSpawnTimer = 10000;
- bPortalGuardianOrKeeperOrEliteSpawn = false;
- }
+ void EnterCombat(Unit* /*who*/) override { }
- uint32 uiSpawnTimer;
- bool bPortalGuardianOrKeeperOrEliteSpawn;
- uint8 uiTypeOfMobsPortal;
+ void JustSummoned(Creature* summon) override
+ {
+ _summons.Summon(summon);
+ summon->AI()->SetData(DATA_PORTAL_LOCATION, _portalLocation);
+ }
- SummonList listOfMobs;
+ void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
+ {
+ _summons.Despawn(summon);
+ }
- InstanceScript* instance;
+ virtual void ScheduleTasks() { }
- void Reset() override
- {
- Initialize();
- }
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
+ }
- void EnterCombat(Unit* /*who*/) override { }
+protected:
+ InstanceScript* _instance;
+ SummonList _summons;
+ TaskScheduler _scheduler;
+ uint8 _portalLocation;
+};
- void MoveInLineOfSight(Unit* /*who*/) override { }
+class npc_violet_hold_teleportation_portal : public CreatureScript
+{
+ public:
+ npc_violet_hold_teleportation_portal() : CreatureScript("npc_violet_hold_teleportation_portal") { }
- void UpdateAI(uint32 diff) override
+ struct npc_violet_hold_teleportation_portalAI : public npc_violet_hold_teleportation_portal_commonAI
{
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS)
+ npc_violet_hold_teleportation_portalAI(Creature* creature) : npc_violet_hold_teleportation_portal_commonAI(creature)
{
- if (instance->GetData(DATA_REMOVE_NPC) == 1)
- {
- me->DespawnOrUnsummon();
- instance->SetData(DATA_REMOVE_NPC, 0);
- }
}
- uint8 uiWaveCount = instance->GetData(DATA_WAVE_COUNT);
- if ((uiWaveCount == 6) || (uiWaveCount == 12)) //Don't spawn mobs on boss encounters
- return;
+ void InitializeAI() override
+ {
+ npc_violet_hold_teleportation_portal_commonAI::InitializeAI();
+ me->CastSpell(me, SPELL_PORTAL_PERIODIC, true);
+ }
- switch (uiTypeOfMobsPortal)
+ void SetData(uint32 type, uint32 data) override
{
- // spawn elite mobs and then set portals visibility to make it look like it dissapeard
- case 0:
- if (!bPortalGuardianOrKeeperOrEliteSpawn)
+ npc_violet_hold_teleportation_portal_commonAI::SetData(type, data);
+
+ if (type == DATA_PORTAL_PERIODIC_TICK)
+ {
+ if (data == 1)
{
- if (uiSpawnTimer <= diff)
+ uint32 entry = RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER);
+ if (Creature* portalKeeper = DoSummon(entry, me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN))
+ me->CastSpell(portalKeeper, SPELL_PORTAL_CHANNEL, false);
+
+ if (Creature* sinclariTrigger = _instance->GetCreature(DATA_SINCLARI_TRIGGER))
{
- bPortalGuardianOrKeeperOrEliteSpawn = true;
- uint8 k = uiWaveCount < 12 ? 2 : 3;
- for (uint8 i = 0; i < k; ++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);
- } else uiSpawnTimer -= diff;
+ if (entry == NPC_PORTAL_GUARDIAN)
+ sinclariTrigger->AI()->Talk(SAY_SINCLARI_PORTAL_GUARDIAN);
+ else if (entry == NPC_PORTAL_KEEPER)
+ sinclariTrigger->AI()->Talk(SAY_SINCLARI_PORTAL_KEEPER);
+ }
}
else
{
- // if all spawned elites have died kill portal
- if (listOfMobs.empty())
+ uint8 k = _instance->GetData(DATA_WAVE_COUNT) < 12 ? 3 : 4;
+ while (k--)
{
- me->Kill(me, false);
- me->RemoveCorpse();
+ 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);
}
}
- break;
- // spawn portal guardian or portal keeper with regular mobs
- case 1:
- if (uiSpawnTimer <= diff)
- {
- if (bPortalGuardianOrKeeperOrEliteSpawn)
- {
- uint8 k = instance->GetData(DATA_WAVE_COUNT) < 12 ? 3 : 4;
- for (uint8 i = 0; i < k; ++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);
- }
- }
- else
- {
- bPortalGuardianOrKeeperOrEliteSpawn = true;
- uint32 entry = RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER);
- if (Creature* pPortalKeeper = DoSummon(entry, me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN))
- me->CastSpell(pPortalKeeper, SPELL_PORTAL_CHANNEL, false);
- }
- uiSpawnTimer = SPAWN_TIME;
- } else uiSpawnTimer -= diff;
+ }
+ }
- if (bPortalGuardianOrKeeperOrEliteSpawn && !me->IsNonMeleeSpellCast(false))
+ void SummonedCreatureDies(Creature* summon, Unit* killer) override
+ {
+ npc_violet_hold_teleportation_portal_commonAI::SummonedCreatureDies(summon, killer);
+
+ if (summon->GetEntry() == NPC_PORTAL_GUARDIAN || summon->GetEntry() == NPC_PORTAL_KEEPER)
+ {
+ _instance->SetData(DATA_WAVE_COUNT, _instance->GetData(DATA_WAVE_COUNT) + 1);
+ me->DespawnOrUnsummon();
+ }
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_violet_hold_teleportation_portalAI>(creature);
+ }
+};
+
+class npc_violet_hold_teleportation_portal_elite : public CreatureScript
+{
+ public:
+ npc_violet_hold_teleportation_portal_elite() : CreatureScript("npc_violet_hold_teleportation_portal_elite") { }
+
+ struct npc_violet_hold_teleportation_portal_eliteAI : public npc_violet_hold_teleportation_portal_commonAI
+ {
+ npc_violet_hold_teleportation_portal_eliteAI(Creature* creature) : npc_violet_hold_teleportation_portal_commonAI(creature)
+ {
+ }
+
+ void ScheduleTasks() override
+ {
+ _scheduler.Schedule(Seconds(15), [this](TaskContext task)
+ {
+ uint8 k = _instance->GetData(DATA_WAVE_COUNT) < 12 ? 3 : 4;
+ while (k--)
{
- me->Kill(me, false);
- me->RemoveCorpse();
+ uint32 entry = RAND(NPC_AZURE_CAPTAIN_1, NPC_AZURE_RAIDER_1, NPC_AZURE_STALKER_1, NPC_AZURE_SORCEROR_1);
+ DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN);
}
- break;
- case 2: // Pre-event
- if (uiSpawnTimer <= diff)
+
+ if (Creature* sinclariTrigger = _instance->GetCreature(DATA_SINCLARI_TRIGGER))
+ sinclariTrigger->AI()->Talk(SAY_SINCLARI_ELITE_SQUAD);
+
+ task.Schedule(Seconds(1), [this](TaskContext /*task*/)
{
- uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_BINDER_1);
- DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN);
- uiSpawnTimer = SPAWN_TIME;
- } else uiSpawnTimer -= diff;
- break;
+ me->SetVisible(false);
+ });
+ });
}
- }
- void JustDied(Unit* /*killer*/) override
+ void SummonedCreatureDies(Creature* summon, Unit* killer) override
+ {
+ npc_violet_hold_teleportation_portal_commonAI::SummonedCreatureDies(summon, killer);
+
+ if (_summons.empty())
+ {
+ _instance->SetData(DATA_WAVE_COUNT, _instance->GetData(DATA_WAVE_COUNT) + 1);
+ me->DespawnOrUnsummon();
+ }
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
{
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS)
- instance->SetData(DATA_WAVE_COUNT, instance->GetData(DATA_WAVE_COUNT) + 1);
+ return GetVioletHoldAI<npc_violet_hold_teleportation_portal_eliteAI>(creature);
}
+};
+
+class npc_violet_hold_teleportation_portal_intro : public CreatureScript
+{
+ public:
+ npc_violet_hold_teleportation_portal_intro() : CreatureScript("npc_violet_hold_teleportation_portal_intro") { }
- void JustSummoned(Creature* summoned) override
+ struct npc_violet_hold_teleportation_portal_introAI : public npc_violet_hold_teleportation_portal_commonAI
{
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS)
+ npc_violet_hold_teleportation_portal_introAI(Creature* creature) : npc_violet_hold_teleportation_portal_commonAI(creature)
{
- listOfMobs.Summon(summoned);
- instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID());
}
- }
- void SummonedCreatureDies(Creature* summoned, Unit* /*killer*/) override
- {
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) == IN_PROGRESS)
+ void ScheduleTasks() override
{
- listOfMobs.Despawn(summoned);
- instance->SetGuidData(DATA_DEL_TRASH_MOB, summoned->GetGUID());
+ if (_instance->GetData(DATA_MAIN_EVENT_STATE) != NOT_STARTED)
+ return;
+
+ _scheduler.Schedule(Seconds(15), [this](TaskContext task)
+ {
+ uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_BINDER_1);
+ DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN);
+
+ task.Repeat();
+ });
}
- }
- };
+ };
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_teleportation_portalAI>(creature);
- }
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_violet_hold_teleportation_portal_introAI>(creature);
+ }
};
struct violet_hold_trashAI : public npc_escortAI
{
violet_hold_trashAI(Creature* creature) : npc_escortAI(creature)
{
- instance = creature->GetInstanceScript();
- bHasGotMovingPoints = false;
+ _instance = creature->GetInstanceScript();
+ _lastWaypointId = 0;
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) == NOT_STARTED)
- {
- if (Creature* portal = me->FindNearestCreature(NPC_TELEPORTATION_PORTAL, 10.0f))
- {
- ObjectGuid portalGUID = portal->GetGUID();
- for (uint8 i = 0; i < MAX_PRE_EVENT_PORTAL; i++)
- if (portalGUID == preEventPortalGUID[i])
- portalLocationID = i * 2;
- }
- }
- else
+ SetDespawnAtEnd(false);
+
+ _scheduler.SetValidator([this]
{
- portalLocationID = instance->GetData(DATA_PORTAL_LOCATION);
- Reset();
- }
+ return !me->HasUnitState(UNIT_STATE_CASTING);
+ });
}
- public:
- InstanceScript* instance;
- bool bHasGotMovingPoints;
- uint32 portalLocationID;
- uint32 secondPortalRouteID;
-
- void WaypointReached(uint32 waypointId) override
+ void Reset() override
{
- switch (portalLocationID)
- {
- case 0:
- if (waypointId == 5)
- CreatureStartAttackDoor();
- break;
- case 1:
- if ((waypointId == 8 && secondPortalRouteID == 0) || (waypointId == 7 && secondPortalRouteID == 1))
- CreatureStartAttackDoor();
- break;
- case 2:
- if (waypointId == 7)
- CreatureStartAttackDoor();
- break;
- case 3:
- if (waypointId == 8)
- CreatureStartAttackDoor();
- break;
- case 4:
- if (waypointId == 5)
- CreatureStartAttackDoor();
- break;
- case 5:
- if (waypointId == 3)
- CreatureStartAttackDoor();
- break;
- }
+ _scheduler.CancelAll();
}
- void UpdateAI(uint32 diff) override
+ void SetData(uint32 type, uint32 data) override
{
- if (instance->GetData(DATA_MAIN_EVENT_PHASE) != IN_PROGRESS)
- me->CastStop();
-
- if (!bHasGotMovingPoints)
+ if (type == DATA_PORTAL_LOCATION)
{
- bHasGotMovingPoints = true;
- switch (portalLocationID)
+ G3D::Vector3 const* path = nullptr;
+
+ switch (data)
{
case 0:
- for (int i=0;i<6;i++)
- AddWaypoint(i, FirstPortalWPs[i][0]+irand(-1, 1), FirstPortalWPs[i][1]+irand(-1, 1), FirstPortalWPs[i][2]+irand(-1, 1), 0);
- me->SetHomePosition(FirstPortalWPs[5][0], FirstPortalWPs[5][1], FirstPortalWPs[5][2], 3.149439f);
+ _lastWaypointId = 5;
+ path = FirstPortalWPs;
break;
- case 1:
- secondPortalRouteID = urand(0, 1);
- switch (secondPortalRouteID)
+ case 7:
+ switch (urand(0, 1))
{
case 0:
- for (int i=0;i<9;i++)
- AddWaypoint(i, SecondPortalFirstWPs[i][0]+irand(-1, 1), SecondPortalFirstWPs[i][1]+irand(-1, 1), SecondPortalFirstWPs[i][2], 0);
- me->SetHomePosition(SecondPortalFirstWPs[8][0]+irand(-1, 1), SecondPortalFirstWPs[8][1]+irand(-1, 1), SecondPortalFirstWPs[8][2]+irand(-1, 1), 3.149439f);
+ _lastWaypointId = 8;
+ path = SecondPortalFirstWPs;
break;
case 1:
- for (int i=0;i<8;i++)
- AddWaypoint(i, SecondPortalSecondWPs[i][0]+irand(-1, 1), SecondPortalSecondWPs[i][1]+irand(-1, 1), SecondPortalSecondWPs[i][2], 0);
- me->SetHomePosition(SecondPortalSecondWPs[7][0], SecondPortalSecondWPs[7][1], SecondPortalSecondWPs[7][2], 3.149439f);
+ _lastWaypointId = 7;
+ path = SecondPortalSecondWPs;
break;
}
break;
case 2:
- for (int i=0;i<8;i++)
- AddWaypoint(i, ThirdPortalWPs[i][0]+irand(-1, 1), ThirdPortalWPs[i][1]+irand(-1, 1), ThirdPortalWPs[i][2], 0);
- me->SetHomePosition(ThirdPortalWPs[7][0], ThirdPortalWPs[7][1], ThirdPortalWPs[7][2], 3.149439f);
+ _lastWaypointId = 7;
+ path = ThirdPortalWPs;
break;
- case 3:
- for (int i=0;i<9;i++)
- AddWaypoint(i, FourthPortalWPs[i][0]+irand(-1, 1), FourthPortalWPs[i][1]+irand(-1, 1), FourthPortalWPs[i][2], 0);
- me->SetHomePosition(FourthPortalWPs[8][0], FourthPortalWPs[8][1], FourthPortalWPs[8][2], 3.149439f);
+ case 6:
+ _lastWaypointId = 8;
+ path = FourthPortalWPs;
break;
- case 4:
- for (int i=0;i<6;i++)
- AddWaypoint(i, FifthPortalWPs[i][0]+irand(-1, 1), FifthPortalWPs[i][1]+irand(-1, 1), FifthPortalWPs[i][2], 0);
- me->SetHomePosition(FifthPortalWPs[5][0], FifthPortalWPs[5][1], FifthPortalWPs[5][2], 3.149439f);
+ case 1:
+ _lastWaypointId = 5;
+ path = FifthPortalWPs;
break;
case 5:
- for (int i=0;i<4;i++)
- AddWaypoint(i, SixthPoralWPs[i][0]+irand(-1, 1), SixthPoralWPs[i][1]+irand(-1, 1), SixthPoralWPs[i][2], 0);
- me->SetHomePosition(SixthPoralWPs[3][0], SixthPoralWPs[3][1], SixthPoralWPs[3][2], 3.149439f);
+ _lastWaypointId = 3;
+ path = SixthPoralWPs;
break;
+ default:
+ _lastWaypointId = 0;
+ path = DefaultPortalWPs;
+ break;
+ }
+
+ if (path)
+ {
+ for (uint32 i = 0; i <= _lastWaypointId; i++)
+ AddWaypoint(i, path[i].x + irand(-1, 1), path[i].y + irand(-1, 1), path[i].z, 0);
+ me->SetHomePosition(path[_lastWaypointId].x, path[_lastWaypointId].y, path[_lastWaypointId].z, float(M_PI));
}
- SetDespawnAtEnd(false);
+
Start(true, true);
}
-
- npc_escortAI::UpdateAI(diff);
}
- void JustDied(Unit* /*killer*/) override
+ void WaypointReached(uint32 waypointId) override
{
- instance->SetData(DATA_NPC_PRESENCE_AT_DOOR_REMOVE, 1);
+ if (waypointId == _lastWaypointId)
+ CreatureStartAttackDoor();
}
- void CreatureStartAttackDoor()
+ void EnterCombat(Unit* who) override
{
- me->SetReactState(REACT_PASSIVE);
- DoCast(SPELL_DESTROY_DOOR_SEAL);
- instance->SetData(DATA_NPC_PRESENCE_AT_DOOR_ADD, 1);
+ npc_escortAI::EnterCombat(who);
+ ScheduledTasks();
}
-};
-
-class npc_azure_invader : public CreatureScript
-{
-public:
- npc_azure_invader() : CreatureScript("npc_azure_invader") { }
- struct npc_azure_invaderAI : public violet_hold_trashAI
+ void UpdateEscortAI(uint32 diff) override
{
- npc_azure_invaderAI(Creature* creature) : violet_hold_trashAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ if (_instance->GetData(DATA_MAIN_EVENT_STATE) != IN_PROGRESS)
+ me->CastStop();
- void Initialize()
- {
- uiCleaveTimer = 5000;
- uiImpaleTimer = 4000;
- uiBrutalStrikeTimer = 5000;
- uiSunderArmorTimer = 4000;
- }
+ if (!UpdateVictim())
+ return;
- uint32 uiCleaveTimer;
- uint32 uiImpaleTimer;
- uint32 uiBrutalStrikeTimer;
- uint32 uiSunderArmorTimer;
+ _scheduler.Update(diff,
+ std::bind(&npc_escortAI::DoMeleeAttackIfReady, this));
+ }
- void Reset() override
- {
- Initialize();
- }
+ virtual void ScheduledTasks() { }
- void UpdateAI(uint32 diff) override
- {
- violet_hold_trashAI::UpdateAI(diff);
+ void CreatureStartAttackDoor()
+ {
+ me->SetReactState(REACT_DEFENSIVE);
+ DoCastAOE(SPELL_DESTROY_DOOR_SEAL);
+ }
- if (!UpdateVictim())
- return;
+protected:
+ InstanceScript* _instance;
+ TaskScheduler _scheduler;
- if (me->GetEntry() == NPC_AZURE_INVADER_1)
- {
- if (uiCleaveTimer <= diff)
- {
- DoCastVictim(SPELL_CLEAVE);
- uiCleaveTimer = 5000;
- } else uiCleaveTimer -= diff;
+ uint32 _lastWaypointId;
+};
- if (uiImpaleTimer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_IMPALE);
- uiImpaleTimer = 4000;
- } else uiImpaleTimer -= diff;
- }
+class npc_azure_invader : public CreatureScript
+{
+ public:
+ npc_azure_invader() : CreatureScript("npc_azure_invader") { }
- if (me->GetEntry() == NPC_AZURE_INVADER_2)
+ struct npc_azure_invaderAI : public violet_hold_trashAI
+ {
+ npc_azure_invaderAI(Creature* creature) : violet_hold_trashAI(creature) { }
+
+ void ScheduledTasks() override
{
- if (uiBrutalStrikeTimer <= diff)
+ if (me->GetEntry() == NPC_AZURE_INVADER_1)
{
- DoCastVictim(SPELL_BRUTAL_STRIKE);
- uiBrutalStrikeTimer = 5000;
- } else uiBrutalStrikeTimer -= diff;
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_CLEAVE);
+ task.Repeat();
+ });
- if (uiSunderArmorTimer <= diff)
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_IMPALE);
+ task.Repeat();
+ });
+ }
+ else if (me->GetEntry() == NPC_AZURE_INVADER_2)
{
- DoCastVictim(SPELL_SUNDER_ARMOR);
- uiSunderArmorTimer = urand(8000, 10000);
- } else uiSunderArmorTimer -= diff;
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_BRUTAL_STRIKE);
+ task.Repeat();
+ });
- DoMeleeAttackIfReady();
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_SUNDER_ARMOR);
+ task.Repeat(Seconds(8), Seconds(10));
+ });
+ }
}
+ };
- DoMeleeAttackIfReady();
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_azure_invaderAI>(creature);
}
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_invaderAI>(creature);
- }
};
class npc_azure_binder : public CreatureScript
{
-public:
- npc_azure_binder() : CreatureScript("npc_azure_binder") { }
-
- struct npc_azure_binderAI : public violet_hold_trashAI
- {
- npc_azure_binderAI(Creature* creature) : violet_hold_trashAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- uiArcaneExplosionTimer = 5000;
- uiArcainBarrageTimer = 4000;
- uiFrostNovaTimer = 5000;
- uiFrostboltTimer = 4000;
- }
-
- uint32 uiArcaneExplosionTimer;
- uint32 uiArcainBarrageTimer;
- uint32 uiFrostNovaTimer;
- uint32 uiFrostboltTimer;
-
- void Reset() override
- {
- Initialize();
- }
+ public:
+ npc_azure_binder() : CreatureScript("npc_azure_binder") { }
- void UpdateAI(uint32 diff) override
+ struct npc_azure_binderAI : public violet_hold_trashAI
{
- violet_hold_trashAI::UpdateAI(diff);
+ npc_azure_binderAI(Creature* creature) : violet_hold_trashAI(creature) { }
- if (!UpdateVictim())
- return;
-
- if (me->GetEntry() == NPC_AZURE_BINDER_1)
+ void ScheduledTasks() override
{
- if (uiArcaneExplosionTimer <= diff)
- {
- DoCast(SPELL_ARCANE_EXPLOSION);
- uiArcaneExplosionTimer = 5000;
- } else uiArcaneExplosionTimer -= diff;
-
- if (uiArcainBarrageTimer <= diff)
+ if (me->GetEntry() == NPC_AZURE_BINDER_1)
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_ARCANE_BARRAGE);
- uiArcainBarrageTimer = 6000;
- } else uiArcainBarrageTimer -= diff;
- }
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_ARCANE_EXPLOSION);
+ task.Repeat();
+ });
- if (me->GetEntry() == NPC_AZURE_BINDER_2)
- {
- if (uiFrostNovaTimer <= diff)
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
+ DoCast(target, SPELL_ARCANE_BARRAGE);
+ task.Repeat(Seconds(6));
+ });
+ }
+ else if (me->GetEntry() == NPC_AZURE_BINDER_2)
{
- DoCast(SPELL_FROST_NOVA);
- uiFrostNovaTimer = 5000;
- } else uiFrostNovaTimer -= diff;
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_FROST_NOVA);
+ task.Repeat();
+ });
- if (uiFrostboltTimer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_FROSTBOLT);
- uiFrostboltTimer = 6000;
- } else uiFrostboltTimer -= diff;
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f))
+ DoCast(target, SPELL_FROSTBOLT);
+ task.Repeat(Seconds(6));
+ });
+ }
}
+ };
- DoMeleeAttackIfReady();
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_azure_binderAI>(creature);
}
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_binderAI>(creature);
- }
};
class npc_azure_mage_slayer : public CreatureScript
{
-public:
- npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { }
-
- struct npc_azure_mage_slayerAI : public violet_hold_trashAI
- {
- npc_azure_mage_slayerAI(Creature* creature) : violet_hold_trashAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- uiArcaneEmpowermentTimer = 5000;
- uiSpellLockTimer = 5000;
- }
-
- uint32 uiArcaneEmpowermentTimer;
- uint32 uiSpellLockTimer;
-
- void Reset() override
- {
- Initialize();
- }
+ public:
+ npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { }
- void UpdateAI(uint32 diff) override
+ struct npc_azure_mage_slayerAI : public violet_hold_trashAI
{
- violet_hold_trashAI::UpdateAI(diff);
-
- if (!UpdateVictim())
- return;
+ npc_azure_mage_slayerAI(Creature* creature) : violet_hold_trashAI(creature) { }
- if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1)
+ void ScheduledTasks() override
{
- if (uiArcaneEmpowermentTimer <= diff)
+ if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1)
{
- DoCast(me, SPELL_ARCANE_EMPOWERMENT);
- uiArcaneEmpowermentTimer = 14000;
- } else uiArcaneEmpowermentTimer -= diff;
- }
-
- if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2)
- {
- if (uiSpellLockTimer <= diff)
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCast(me, SPELL_ARCANE_EMPOWERMENT);
+ task.Repeat(Seconds(14));
+ });
+ }
+ else if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2)
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_SPELL_LOCK);
- uiSpellLockTimer = 9000;
- } else uiSpellLockTimer -= diff;
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ // wrong spellid?
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
+ DoCast(target, SPELL_SPELL_LOCK);
+ task.Repeat(Seconds(9));
+ });
+ }
}
+ };
- DoMeleeAttackIfReady();
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_azure_mage_slayerAI>(creature);
}
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_mage_slayerAI>(creature);
- }
};
class npc_azure_raider : public CreatureScript
{
-public:
- npc_azure_raider() : CreatureScript("npc_azure_raider") { }
-
- struct npc_azure_raiderAI : public violet_hold_trashAI
- {
- npc_azure_raiderAI(Creature* creature) : violet_hold_trashAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- uiConcussionBlowTimer = 5000;
- uiMagicReflectionTimer = 8000;
- }
-
- uint32 uiConcussionBlowTimer;
- uint32 uiMagicReflectionTimer;
-
- void Reset() override
- {
- Initialize();
- }
+ public:
+ npc_azure_raider() : CreatureScript("npc_azure_raider") { }
- void UpdateAI(uint32 diff) override
+ struct npc_azure_raiderAI : public violet_hold_trashAI
{
- violet_hold_trashAI::UpdateAI(diff);
+ npc_azure_raiderAI(Creature* creature) : violet_hold_trashAI(creature) { }
- if (!UpdateVictim())
- return;
-
- if (uiConcussionBlowTimer <= diff)
+ void ScheduledTasks() override
{
- DoCastVictim(SPELL_CONCUSSION_BLOW);
- uiConcussionBlowTimer = 5000;
- } else uiConcussionBlowTimer -= diff;
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ DoCastVictim(SPELL_CONCUSSION_BLOW);
+ task.Repeat();
+ });
- if (uiMagicReflectionTimer <= diff)
- {
- DoCast(SPELL_MAGIC_REFLECTION);
- uiMagicReflectionTimer = urand(10000, 15000);
- } else uiMagicReflectionTimer -= diff;
+ _scheduler.Schedule(Seconds(8), [this](TaskContext task)
+ {
+ DoCast(me, SPELL_MAGIC_REFLECTION);
+ task.Repeat(Seconds(10), Seconds(15));
+ });
+ }
+ };
- DoMeleeAttackIfReady();
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_azure_raiderAI>(creature);
}
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_raiderAI>(creature);
- }
};
class npc_azure_stalker : public CreatureScript
{
-public:
- npc_azure_stalker() : CreatureScript("npc_azure_stalker") { }
-
- struct npc_azure_stalkerAI : public violet_hold_trashAI
- {
- npc_azure_stalkerAI(Creature* creature) : violet_hold_trashAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- _backstabTimer = 1300;
- _tacticalBlinkTimer = 8000;
- _tacticalBlinkCast = false;
- }
-
- void Reset() override
- {
- Initialize();
- }
+ public:
+ npc_azure_stalker() : CreatureScript("npc_azure_stalker") { }
- void UpdateAI(uint32 diff) override
+ struct npc_azure_stalkerAI : public violet_hold_trashAI
{
- violet_hold_trashAI::UpdateAI(diff);
-
- if (!UpdateVictim())
- return;
+ npc_azure_stalkerAI(Creature* creature) : violet_hold_trashAI(creature) { }
- if (!_tacticalBlinkCast)
+ void ScheduledTasks() override
{
- if (_tacticalBlinkTimer <= diff)
+ _scheduler.Schedule(Seconds(8), [this](TaskContext task)
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40, true))
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 40.0f))
DoCast(target, SPELL_TACTICAL_BLINK);
- _tacticalBlinkTimer = 6000;
- _tacticalBlinkCast = true;
- } else _tacticalBlinkTimer -= diff;
- }
- else
- {
- if (_backstabTimer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 10, true))
- DoCast(target, SPELL_BACKSTAB);
- _tacticalBlinkCast = false;
- _backstabTimer =1300;
- } else _backstabTimer -= diff;
+ task.Schedule(Milliseconds(1300), [this](TaskContext /*task*/)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 5.0f))
+ DoCast(target, SPELL_BACKSTAB);
+ });
+
+ task.Repeat();
+ });
}
+ };
- DoMeleeAttackIfReady();
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_azure_stalkerAI>(creature);
}
-
- private:
- uint32 _backstabTimer;
- uint32 _tacticalBlinkTimer;
- bool _tacticalBlinkCast;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_stalkerAI>(creature);
- }
};
class npc_azure_spellbreaker : public CreatureScript
{
-public:
- npc_azure_spellbreaker() : CreatureScript("npc_azure_spellbreaker") { }
+ public:
+ npc_azure_spellbreaker() : CreatureScript("npc_azure_spellbreaker") { }
- struct npc_azure_spellbreakerAI : public violet_hold_trashAI
- {
- npc_azure_spellbreakerAI(Creature* creature) : violet_hold_trashAI(creature)
+ struct npc_azure_spellbreakerAI : public violet_hold_trashAI
{
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ npc_azure_spellbreakerAI(Creature* creature) : violet_hold_trashAI(creature) { }
- void Initialize()
- {
- uiArcaneBlastTimer = 5000;
- uiSlowTimer = 4000;
- uiChainsOfIceTimer = 5000;
- uiConeOfColdTimer = 4000;
- }
+ void ScheduledTasks() override
+ {
+ if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1)
+ {
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
+ DoCast(target, SPELL_ARCANE_BLAST);
+ task.Repeat(Seconds(6));
+ });
- uint32 uiArcaneBlastTimer;
- uint32 uiSlowTimer;
- uint32 uiChainsOfIceTimer;
- uint32 uiConeOfColdTimer;
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
+ DoCast(target, SPELL_SLOW);
+ task.Repeat(Seconds(5));
+ });
+ }
+ else if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2)
+ {
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
+ DoCast(target, SPELL_CHAINS_OF_ICE);
+ task.Repeat(Seconds(7));
+ });
- void Reset() override
- {
- Initialize();
- }
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ DoCast(me, SPELL_CONE_OF_COLD);
+ task.Repeat(Seconds(5));
+ });
+ }
+ }
+ };
- void UpdateAI(uint32 diff) override
+ CreatureAI* GetAI(Creature* creature) const override
{
- violet_hold_trashAI::UpdateAI(diff);
-
- if (!UpdateVictim())
- return;
+ return GetVioletHoldAI<npc_azure_spellbreakerAI>(creature);
+ }
+};
- if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1)
- {
- if (uiArcaneBlastTimer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_ARCANE_BLAST);
- uiArcaneBlastTimer = 6000;
- } else uiArcaneBlastTimer -= diff;
+class npc_azure_captain : public CreatureScript
+{
+ public:
+ npc_azure_captain() : CreatureScript("npc_azure_captain") { }
- if (uiSlowTimer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_SLOW);
- uiSlowTimer = 5000;
- } else uiSlowTimer -= diff;
- }
+ struct npc_azure_captainAI : public violet_hold_trashAI
+ {
+ npc_azure_captainAI(Creature* creature) : violet_hold_trashAI(creature) { }
- if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2)
+ void ScheduledTasks() override
{
- if (uiChainsOfIceTimer <= diff)
+ _scheduler.Schedule(Seconds(5), [this](TaskContext task)
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_CHAINS_OF_ICE);
- uiChainsOfIceTimer = 7000;
- } else uiChainsOfIceTimer -= diff;
+ DoCastVictim(SPELL_MORTAL_STRIKE);
+ task.Repeat();
+ });
- if (uiConeOfColdTimer <= diff)
+ _scheduler.Schedule(Seconds(8), [this](TaskContext task)
{
- DoCast(SPELL_CONE_OF_COLD);
- uiConeOfColdTimer = 5000;
- } else uiConeOfColdTimer -= diff;
+ DoCast(me, SPELL_WHIRLWIND_OF_STEEL);
+ task.Repeat();
+ });
}
+ };
- DoMeleeAttackIfReady();
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetVioletHoldAI<npc_azure_captainAI>(creature);
}
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_spellbreakerAI>(creature);
- }
};
-class npc_azure_captain : public CreatureScript
+class npc_azure_sorceror : public CreatureScript
{
-public:
- npc_azure_captain() : CreatureScript("npc_azure_captain") { }
+ public:
+ npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { }
- struct npc_azure_captainAI : public violet_hold_trashAI
- {
- npc_azure_captainAI(Creature* creature) : violet_hold_trashAI(creature)
+ struct npc_azure_sorcerorAI : public violet_hold_trashAI
{
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ npc_azure_sorcerorAI(Creature* creature) : violet_hold_trashAI(creature) { }
- void Initialize()
- {
- uiMortalStrikeTimer = 5000;
- uiWhirlwindTimer = 8000;
- }
+ void ScheduledTasks() override
+ {
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 35.0f))
+ DoCast(target, SPELL_ARCANE_STREAM);
+ task.Repeat(Seconds(5), Seconds(10));
+ });
- uint32 uiMortalStrikeTimer;
- uint32 uiWhirlwindTimer;
+ _scheduler.Schedule(Seconds(), Seconds(), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_MANA_DETONATION);
+ task.Repeat(Seconds(2), Seconds(6));
+ });
+ }
+ };
- void Reset() override
+ CreatureAI* GetAI(Creature* creature) const override
{
- Initialize();
+ return GetVioletHoldAI<npc_azure_sorcerorAI>(creature);
}
+};
+
+class npc_violet_hold_defense_system : public CreatureScript
+{
+ public:
+ npc_violet_hold_defense_system() : CreatureScript("npc_violet_hold_defense_system") { }
- void UpdateAI(uint32 diff) override
+ struct npc_violet_hold_defense_systemAI : public ScriptedAI
{
- violet_hold_trashAI::UpdateAI(diff);
+ npc_violet_hold_defense_systemAI(Creature* creature) : ScriptedAI(creature) { }
- if (!UpdateVictim())
- return;
+ void Reset() override
+ {
+ ScheduledTasks();
+ me->DespawnOrUnsummon(7000);
+ }
- if (uiMortalStrikeTimer <= diff)
+ void ScheduledTasks()
{
- DoCastVictim(SPELL_MORTAL_STRIKE);
- uiMortalStrikeTimer = 5000;
- } else uiMortalStrikeTimer -= diff;
+ _scheduler.Schedule(Seconds(4), [this](TaskContext task)
+ {
+ DoCastAOE(SPELL_ARCANE_LIGHTNING_DAMAGE);
+ DoCastAOE(SPELL_ARCANE_LIGHTNING_DUMMY);
+ if (task.GetRepeatCounter() == 2)
+ DoCastAOE(SPELL_ARCANE_LIGHTNING_INSTAKILL);
+ else
+ task.Repeat(Seconds(1));
+ });
+ }
- if (uiWhirlwindTimer <= diff)
+ void UpdateAI(uint32 diff) override
{
- DoCast(me, SPELL_WHIRLWIND_OF_STEEL);
- uiWhirlwindTimer = 8000;
- } else uiWhirlwindTimer -= diff;
+ _scheduler.Update(diff);
+ }
- DoMeleeAttackIfReady();
- }
- };
+ private:
+ TaskScheduler _scheduler;
+ };
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_captainAI>(creature);
- }
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_violet_hold_defense_systemAI(creature);
+ }
};
-class npc_azure_sorceror : public CreatureScript
+class go_activation_crystal : public GameObjectScript
{
-public:
- npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { }
-
- struct npc_azure_sorcerorAI : public violet_hold_trashAI
- {
- npc_azure_sorcerorAI(Creature* creature) : violet_hold_trashAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ public:
+ go_activation_crystal() : GameObjectScript("go_activation_crystal") { }
- void Initialize()
+ bool OnGossipHello(Player* player, GameObject* /*go*/) override
{
- uiArcaneStreamTimer = 4000;
- uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer;
- uiManaDetonationTimer = 5000;
+ player->CastSpell(player, SPELL_CRYSTAL_ACTIVATION, true);
+ return false;
}
+};
- uint32 uiArcaneStreamTimer;
- uint32 uiArcaneStreamTimerStartingValueHolder;
- uint32 uiManaDetonationTimer;
-
- void Reset() override
- {
- Initialize();
- }
+// 58040 - Destroy Door Seal
+class spell_violet_hold_destroy_door_seal : public SpellScriptLoader
+{
+ public:
+ spell_violet_hold_destroy_door_seal() : SpellScriptLoader("spell_violet_hold_destroy_door_seal") { }
- void UpdateAI(uint32 diff) override
+ class spell_violet_hold_destroy_door_seal_AuraScript : public AuraScript
{
- violet_hold_trashAI::UpdateAI(diff);
+ PrepareAuraScript(spell_violet_hold_destroy_door_seal_AuraScript);
- if (!UpdateVictim())
- return;
-
- if (uiArcaneStreamTimer <= diff)
+ bool Load() override
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_ARCANE_STREAM);
- uiArcaneStreamTimer = urand(0, 5000)+5000;
- uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer;
- } else uiArcaneStreamTimer -= diff;
+ _instance = GetUnitOwner()->GetInstanceScript();
+ return _instance != nullptr;
+ }
- if (uiManaDetonationTimer <= diff && uiArcaneStreamTimer >=1500 && uiArcaneStreamTimer <= uiArcaneStreamTimerStartingValueHolder/2)
+ void PeriodicTick(AuraEffect const* /*aurEff*/)
{
- DoCast(SPELL_MANA_DETONATION);
- uiManaDetonationTimer = urand(2000, 6000);
- } else uiManaDetonationTimer -= diff;
-
- DoMeleeAttackIfReady();
- }
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetInstanceAI<npc_azure_sorcerorAI>(creature);
- }
-};
+ PreventDefaultAction();
+ if (uint32 integrity = _instance->GetData(DATA_DOOR_INTEGRITY))
+ _instance->SetData(DATA_DOOR_INTEGRITY, integrity - 1);
+ }
-class npc_violet_hold_arcane_sphere : public CreatureScript
-{
-public:
- npc_violet_hold_arcane_sphere() : CreatureScript("npc_violet_hold_arcane_sphere") { }
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_violet_hold_destroy_door_seal_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
- struct npc_violet_hold_arcane_sphereAI : public ScriptedAI
- {
- npc_violet_hold_arcane_sphereAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- }
+ private:
+ InstanceScript* _instance = nullptr;
+ };
- void Initialize()
+ AuraScript* GetAuraScript() const override
{
- DespawnTimer = 3000;
+ return new spell_violet_hold_destroy_door_seal_AuraScript();
}
+};
- uint32 DespawnTimer;
+// 58008 - Portal Periodic
+class spell_violet_hold_portal_periodic : public SpellScriptLoader
+{
+ public:
+ spell_violet_hold_portal_periodic() : SpellScriptLoader("spell_violet_hold_portal_periodic") { }
- void Reset() override
+ class spell_violet_hold_portal_periodic_AuraScript : public AuraScript
{
- Initialize();
+ PrepareAuraScript(spell_violet_hold_portal_periodic_AuraScript);
- me->SetDisableGravity(true);
- DoCast(me, SPELL_ARCANE_SPHERE_PASSIVE, true);
- }
+ void PeriodicTick(AuraEffect const* aurEff)
+ {
+ PreventDefaultAction();
+ if (GetTarget()->IsAIEnabled)
+ GetTarget()->GetAI()->SetData(DATA_PORTAL_PERIODIC_TICK, aurEff->GetTickNumber());
+ }
- void EnterCombat(Unit * /*who*/) override { }
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_violet_hold_portal_periodic_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
- void UpdateAI(uint32 diff) override
+ AuraScript* GetAuraScript() const override
{
- if (DespawnTimer <= diff)
- me->Kill(me);
- else
- DespawnTimer -= diff;
+ return new spell_violet_hold_portal_periodic_AuraScript();
}
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_violet_hold_arcane_sphereAI(creature);
- }
};
-class go_activation_crystal : public GameObjectScript
+// 62138 - Teleport to Inside Violet Hold
+class spell_violet_hold_teleport_player : public SpellScriptLoader
{
-public:
- go_activation_crystal() : GameObjectScript("go_activation_crystal") { }
+ public:
+ spell_violet_hold_teleport_player() : SpellScriptLoader("spell_violet_hold_teleport_player") { }
- bool OnGossipHello(Player * /*player*/, GameObject* go) override
- {
- go->EventInform(EVENT_ACTIVATE_CRYSTAL);
- return false;
- }
-};
+ class spell_violet_hold_teleport_player_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_violet_hold_teleport_player_SpellScript);
-class spell_crystal_activation : public SpellScriptLoader
-{
-public:
- spell_crystal_activation() : SpellScriptLoader("spell_crystal_activation") { }
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_TELEPORT_PLAYER_EFFECT))
+ return false;
+ return true;
+ }
- class spell_crystal_activation_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_crystal_activation_SpellScript);
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ target->CastSpell(target, SPELL_TELEPORT_PLAYER_EFFECT, true);
+ }
- void HandleSendEvent(SpellEffIndex effIndex)
- {
- if (GetHitUnit()->GetEntry() == NPC_VIOLET_HOLD_GUARD)
- PreventHitDefaultEffect(effIndex);
- }
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_violet_hold_teleport_player_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
- void Register() override
+ SpellScript* GetSpellScript() const override
{
- OnEffectHitTarget += SpellEffectFn(spell_crystal_activation_SpellScript::HandleSendEvent, EFFECT_0, SPELL_EFFECT_SEND_EVENT);
+ return new spell_violet_hold_teleport_player_SpellScript();
}
- };
-
- SpellScript* GetSpellScript() const override
- {
- return new spell_crystal_activation_SpellScript();
- }
};
void AddSC_violet_hold()
{
new npc_sinclari_vh();
- new npc_teleportation_portal_vh();
+ new npc_violet_hold_teleportation_portal();
+ new npc_violet_hold_teleportation_portal_elite();
+ new npc_violet_hold_teleportation_portal_intro();
new npc_azure_invader();
new npc_azure_spellbreaker();
new npc_azure_binder();
@@ -1520,7 +1431,9 @@ void AddSC_violet_hold()
new npc_azure_raider();
new npc_azure_stalker();
new npc_azure_saboteur();
- new npc_violet_hold_arcane_sphere();
+ new npc_violet_hold_defense_system();
new go_activation_crystal();
- new spell_crystal_activation();
+ new spell_violet_hold_destroy_door_seal();
+ new spell_violet_hold_portal_periodic();
+ new spell_violet_hold_teleport_player();
}
diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.h b/src/server/scripts/Northrend/VioletHold/violet_hold.h
index 2bd90672024..113a3c46ea0 100644
--- a/src/server/scripts/Northrend/VioletHold/violet_hold.h
+++ b/src/server/scripts/Northrend/VioletHold/violet_hold.h
@@ -15,44 +15,56 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef DEF_VIOLET_HOLD_H
-#define DEF_VIOLET_HOLD_H
+#ifndef VIOLET_HOLD_H_
+#define VIOLET_HOLD_H_
+#define VioletHoldScriptName "instance_violet_hold"
#define DataHeader "VH"
-uint32 const EncounterCount = 3;
+uint32 const EncounterCount = 3 + 6;
+
+// Defined in instance_violet_hold.cpp
+extern Position const DefenseSystemLocation;
+uint8 const PortalIntroCount = 3;
+extern Position const PortalIntroPositions[];
+
+/*
+ * Violet hold bosses:
+ *
+ * 1 - Moragg
+ * 2 - Erekem
+ * 3 - Ichoron
+ * 4 - Lavanthor
+ * 5 - Xevozz
+ * 6 - Zuramat
+ * 7 - Cyanigosa
+ */
enum Data
{
// Main encounters
- DATA_1ST_BOSS_EVENT,
- DATA_2ND_BOSS_EVENT,
- DATA_CYANIGOSA,
+ DATA_1ST_BOSS = 0,
+ DATA_2ND_BOSS = 1,
+ DATA_CYANIGOSA = 2,
+ // Bosses
+ DATA_MORAGG = 3,
+ DATA_EREKEM = 4,
+ DATA_ICHORON = 5,
+ DATA_LAVANTHOR = 6,
+ DATA_XEVOZZ = 7,
+ DATA_ZURAMAT = 8,
// Misc
+ DATA_MAIN_EVENT_STATE,
DATA_WAVE_COUNT,
- DATA_REMOVE_NPC,
- DATA_PORTAL_LOCATION,
DATA_DOOR_INTEGRITY,
- DATA_NPC_PRESENCE_AT_DOOR,
- DATA_NPC_PRESENCE_AT_DOOR_ADD,
- DATA_NPC_PRESENCE_AT_DOOR_REMOVE,
+ DATA_PORTAL_LOCATION,
DATA_START_BOSS_ENCOUNTER,
- DATA_FIRST_BOSS,
- DATA_SECOND_BOSS,
- DATA_ACTIVATE_CRYSTAL,
- DATA_MAIN_EVENT_PHASE,
DATA_DEFENSELESS,
// Bosses
- DATA_MORAGG,
- DATA_EREKEM,
DATA_EREKEM_GUARD_1,
DATA_EREKEM_GUARD_2,
- DATA_ICHORON,
- DATA_LAVANTHOR,
- DATA_XEVOZZ,
- DATA_ZURAMAT,
// Cells
DATA_MORAGG_CELL,
@@ -67,43 +79,43 @@ enum Data
// Misc
DATA_MAIN_DOOR,
DATA_SINCLARI,
- DATA_TELEPORTATION_PORTAL,
- DATA_SABOTEUR_PORTAL,
- DATA_ADD_TRASH_MOB,
- DATA_DEL_TRASH_MOB
-};
-
-enum Bosses
-{
- BOSS_NONE, // 0 used as marker for not yet randomized
- BOSS_MORAGG,
- BOSS_EREKEM,
- BOSS_ICHORON,
- BOSS_LAVANTHOR,
- BOSS_XEVOZZ,
- BOSS_ZURAMAT,
- BOSS_CYANIGOSA
+ DATA_SINCLARI_TRIGGER,
+ DATA_HANDLE_CELLS
};
enum CreaturesIds
{
- NPC_TELEPORTATION_PORTAL = 31011,
+ NPC_TELEPORTATION_PORTAL = 30679,
+ NPC_TELEPORTATION_PORTAL_ELITE = 32174,
+ NPC_TELEPORTATION_PORTAL_INTRO = 31011,
NPC_PORTAL_GUARDIAN = 30660,
NPC_PORTAL_KEEPER = 30695,
NPC_XEVOZZ = 29266,
NPC_LAVANTHOR = 29312,
NPC_ICHORON = 29313,
+ NPC_ICHOR_GLOBULE = 29321,
+ NPC_ICHORON_SUMMON_TARGET = 29326,
NPC_ZURAMAT = 29314,
+ NPC_VOID_SENTRY = 29364,
+ NPC_VOID_SENTRY_BALL = 29365,
NPC_EREKEM = 29315,
NPC_EREKEM_GUARD = 29395,
NPC_MORAGG = 29316,
+
+ NPC_DUMMY_XEVOZZ = 32231,
+ NPC_DUMMY_LAVANTHOR = 32237,
+ NPC_DUMMY_ICHORON = 32234,
+ NPC_DUMMY_ZURAMAT = 32230,
+ NPC_DUMMY_EREKEM = 32226,
+ NPC_DUMMY_EREKEM_GUARD = 32228,
+ NPC_DUMMY_MORAGG = 32235,
+
NPC_CYANIGOSA = 31134,
NPC_SINCLARI = 30658,
+ NPC_SINCLARI_TRIGGER = 32204,
NPC_SABOTEOUR = 31079,
NPC_VIOLET_HOLD_GUARD = 30659,
- NPC_DEFENSE_SYSTEM = 30837,
- NPC_VOID_SENTRY = 29364,
- NPC_VOID_SENTRY_BALL = 29365
+ NPC_DEFENSE_SYSTEM = 30837
};
enum GameObjectIds
@@ -117,13 +129,13 @@ enum GameObjectIds
GO_EREKEM_GUARD_1_DOOR = 191563,
GO_EREKEM_GUARD_2_DOOR = 191562,
GO_MORAGG_DOOR = 191606,
- GO_INTRO_ACTIVATION_CRYSTAL = 193615,
- GO_ACTIVATION_CRYSTAL = 193611
+ GO_ACTIVATION_CRYSTAL = 193611,
+ GO_INTRO_ACTIVATION_CRYSTAL = 193615
};
enum WorldStateIds
{
- WORLD_STATE_VH = 3816,
+ WORLD_STATE_VH_SHOW = 3816,
WORLD_STATE_VH_PRISON_STATE = 3815,
WORLD_STATE_VH_WAVE_COUNT = 3810,
};
@@ -133,4 +145,16 @@ enum Events
EVENT_ACTIVATE_CRYSTAL = 20001
};
-#endif
+enum InstanceMisc
+{
+ ACTION_SINCLARI_OUTRO = 1,
+ POINT_INTRO = 1
+};
+
+template<class AI>
+inline AI* GetVioletHoldAI(Creature* creature)
+{
+ return GetInstanceAI<AI>(creature, VioletHoldScriptName);
+}
+
+#endif // VIOLET_HOLD_H_
diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp
index eef993f7a7d..511ddcff5fa 100644
--- a/src/server/scripts/Northrend/zone_borean_tundra.cpp
+++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp
@@ -1818,7 +1818,7 @@ public:
player->FailQuest(QUEST_GET_ME_OUTA_HERE);
}
- void UpdateEscortAI(const uint32 /*diff*/) override
+ void UpdateEscortAI(uint32 /*diff*/) override
{
if (GetAttack() && UpdateVictim())
{
diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp
index fe72a2cedf7..ba69a1385d5 100644
--- a/src/server/scripts/Northrend/zone_howling_fjord.cpp
+++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp
@@ -98,7 +98,7 @@ public:
player->FailQuest(QUEST_TRAIL_OF_FIRE);
}
- void UpdateEscortAI(const uint32 diff) override
+ void UpdateEscortAI(uint32 diff) override
{
if (HealthBelowPct(75))
{
diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp
index 9667b4e3bb0..80cc2028cb3 100644
--- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp
+++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp
@@ -136,7 +136,7 @@ class boss_ambassador_hellmaw : public CreatureScript
Talk(SAY_DEATH);
}
- void UpdateEscortAI(uint32 const diff) override
+ void UpdateEscortAI(uint32 diff) override
{
if (!UpdateVictim())
return;