aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp16
-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/EasternKingdoms/zone_hinterlands.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.cpp184
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_erekem.cpp476
-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.cpp254
-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.cpp1339
-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
32 files changed, 2890 insertions, 3061 deletions
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index aa7ec3b847f..f1ba985458e 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -457,7 +457,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()
{
@@ -467,6 +473,7 @@ void BossAI::_Reset()
me->ResetLootMode();
events.Reset();
summons.DespawnAll();
+ scheduler.CancelAll();
if (instance)
instance->SetBossState(_bossId, NOT_STARTED);
}
@@ -475,14 +482,13 @@ void BossAI::_JustDied()
{
events.Reset();
summons.DespawnAll();
+ scheduler.CancelAll();
if (instance)
instance->SetBossState(_bossId, DONE);
}
void BossAI::_EnterCombat()
{
- me->setActive(true);
- DoZoneInCombat();
if (instance)
{
// bosses do not respawn, check only on enter combat
@@ -493,6 +499,10 @@ void BossAI::_EnterCombat()
}
instance->SetBossState(_bossId, IN_PROGRESS);
}
+
+ 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 1a4c3064a59..7d7811d9e75 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))
@@ -359,6 +360,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(); }
@@ -384,6 +387,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 362c40a0982..5e1d1d37950 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -9590,6 +9590,17 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
data << uint32(4131) << uint32(0); // 10 WORLDSTATE_ALGALON_DESPAWN_TIMER
}
break;
+ // Violet Hold
+ case 4415:
+ if (instance && mapid == 608)
+ instance->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(3816) << uint32(0); // 9 WORLD_STATE_VH_SHOW
+ data << uint32(3815) << uint32(100); // 10 WORLD_STATE_VH_PRISON_STATE
+ data << uint32(3810) << uint32(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 da806e5b038..5d44f3ec696 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 be05a9f4495..3e90e430590 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -273,6 +273,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 2b323e196a7..b69322f5720 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -423,6 +423,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 d9f24a9010a..f27f9220aaa 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3355,6 +3355,19 @@ void SpellMgr::LoadSpellInfoCorrections()
//! HACK: This spell break quest complete for alliance and on retail not used °_O
spellInfo->Effects[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
+ spellInfo->Effects[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 5ad5a3782ae..64513fece8b 100644
--- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
+++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp
@@ -365,7 +365,7 @@ public:
}
}
- void UpdateEscortAI(const uint32 uiDiff) override
+ void UpdateEscortAI(uint32 uiDiff) override
{
if (uiPhase)
{
diff --git a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp
index de2bcc5561e..e0612a5ec78 100644
--- a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp
+++ b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp
@@ -272,7 +272,7 @@ public:
}
}
- void UpdateEscortAI(const uint32 diff) override
+ void UpdateEscortAI(uint32 diff) override
{
//Check if we have a current target
if (!UpdateVictim())
diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
index eab104d8df2..bb54ae08d77 100644
--- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp
+++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
@@ -612,7 +612,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 5cb6e78b399..53eadd4a38b 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 8c1befe72ff..ab1450a87ea 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 1b823b4a452..992ca0b4d74 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
@@ -1121,7 +1119,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..26e55c46def 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp
@@ -21,6 +21,7 @@
enum Spells
{
+ SPELL_SUMMON_PLAYER = 21150,
SPELL_ARCANE_VACUUM = 58694,
SPELL_BLIZZARD = 58693,
SPELL_MANA_DESTRUCTION = 59374,
@@ -42,119 +43,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 +138,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 +179,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..a7de2cab9d5 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp
@@ -41,269 +41,207 @@ 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);
+
+ task.Repeat(!CheckGuardAlive() ? Seconds(3) : (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 +253,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 e9ee996b65b..41542416468 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp
@@ -25,8 +25,8 @@ 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_SUFFERING = 54442,
// Visual
SPELL_OPTIC_LINK_LEVEL_1 = 54393,
@@ -34,191 +34,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, SPELL_RAY_OF_PAIN);
+ DoCast(me, SPELL_RAY_OF_SUFFERING);
+ });
+
+ 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()->Effects[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()->Effects[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 = GetSpellInfo()->Effects[aurEff->GetEffIndex()].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 +148,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 +194,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..5e3c7014239 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
@@ -22,25 +22,34 @@
#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
@@ -48,24 +57,17 @@ enum CreatureSpells
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 +77,213 @@ 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;
+ auto it = summonSpells.begin();
+ std::advance(it, urand(0, summonSpells.size() - 1));
+ DoCast(me, EtherealSphereSummonSpells[*it]);
+ it = summonSpells.erase(it);
- events.Update(diff);
+ if (IsHeroic())
+ {
+ task.Schedule(Milliseconds(2500), [this, &it, &summonSpells](TaskContext /*task*/)
+ {
+ it = summonSpells.begin();
+ std::advance(it, urand(0, summonSpells.size() - 1));
+ DoCast(me, EtherealSphereHeroicSummonSpells[*it]);
+ it = summonSpells.erase(it);
+ });
+ }
- 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, SPELL_POWER_BALL_DAMAGE_TRIGGER);
- 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 +291,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 652b4815be0..2294c51e59f 100644
--- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
+++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp
@@ -20,80 +20,147 @@
#include "InstanceScript.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 +177,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;
- }
-
- ObjectGuid uiErekemGuard[2];
+ instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map)
+ {
+ SetHeaders(DataHeader);
+ SetBossNumber(EncounterCount);
+ LoadObjectData(creatureData, gameObjectData);
+ LoadMinionData(minionData);
- ObjectGuid uiTeleportationPortal;
- ObjectGuid uiSaboteurPortal;
+ FirstBossId = 0;
+ SecondBossId = 0;
- ObjectGuid uiActivationCrystal[4];
+ DoorIntegrity = 100;
+ WaveCount = 0;
+ EventState = NOT_STARTED;
- uint32 uiActivationTimer;
- uint32 uiCyanigosaEventTimer;
- uint32 uiDoorSpellTimer;
+ LastPortalLocation = urand(0, EncouterPortalsCount - 1);
- GuidSet trashMobs; // to kill with crystal
+ Defenseless = true;
+ }
- uint8 uiWaveCount;
- uint8 uiLocation;
- uint8 uiFirstBoss;
- uint8 uiSecondBoss;
- uint8 uiRemoveNpc;
+ void OnCreatureCreate(Creature* creature) override
+ {
+ InstanceScript::OnCreatureCreate(creature);
- uint8 uiDoorIntegrity;
+ 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 uiCountErekemGuards;
- uint8 uiCountActivationCrystals;
- uint8 uiCyanigosaEventPhase;
- uint8 uiMainEventPhase; // SPECIAL: pre event animations, IN_PROGRESS: event itself
+ void OnCreatureRemove(Creature* creature) override
+ {
+ InstanceScript::OnCreatureRemove(creature);
- bool bActive;
- bool bWiped;
- bool bIsDoorSpellCast;
- bool bCrystalActivated;
- bool defenseless;
- bool zuramatDead;
+ 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;
+ }
+ }
- std::list<uint8> NpcAtDoorCastingList;
+ void OnGameObjectCreate(GameObject* go) override
+ {
+ InstanceScript::OnGameObjectCreate(go);
- void OnCreatureCreate(Creature* creature) override
- {
- InstanceScript::OnCreatureCreate(creature);
+ 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;
+ }
+ }
- switch (creature->GetEntry())
+ void OnGameObjectRemove(GameObject* go) 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;
+ 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;
+ }
}
- if (creature->GetGUID() == uiFirstBoss || creature->GetGUID() == uiSecondBoss)
+ void FillInitialWorldStates(WorldPacket& data) override
{
- creature->AllLootRemovedFromCorpse();
- creature->RemoveLootMode(1);
+ data << uint32(WORLD_STATE_VH_SHOW) << uint32(EventState == IN_PROGRESS ? 1 : 0);
+ data << uint32(WORLD_STATE_VH_PRISON_STATE) << uint32(DoorIntegrity);
+ data << uint32(WORLD_STATE_VH_WAVE_COUNT) << uint32(WaveCount);
}
- }
- void OnGameObjectCreate(GameObject* go) override
- {
- InstanceScript::OnGameObjectCreate(go);
+ bool CheckRequiredBosses(uint32 bossId, Player const* player = nullptr) const override
+ {
+ if (_SkipCheckRequiredBosses(player))
+ return true;
+
+ 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;
+ }
+
+ return true;
+ }
- switch (go->GetEntry())
+ 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);
+
+ 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);
+ }
- switch (type)
+ 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;
+ }
- if (!bForceRespawn)
+ void ReadSaveDataMore(std::istringstream& data) override
+ {
+ data >> FirstBossId;
+ data >> SecondBossId;
+ }
+
+ 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 280a94aa21f..6069c0d8be7 100644
--- a/src/server/scripts/Northrend/zone_borean_tundra.cpp
+++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp
@@ -1819,7 +1819,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;