aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h6
-rw-r--r--src/server/game/Spells/SpellMgr.cpp12
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h13
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp825
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp1175
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp974
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp28
7 files changed, 2393 insertions, 640 deletions
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 7639f45ead8..a0cd1e7daab 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -89,6 +89,12 @@ public:
return storage_.size();
}
+ // Clear the underlying storage. This does NOT despawn the creatures - use DespawnAll for that!
+ void clear()
+ {
+ storage_.clear();
+ }
+
void Summon(Creature const* summon) { storage_.push_back(summon->GetGUID()); }
void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); }
void DespawnEntry(uint32 entry);
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 858665453fa..1b5b481f458 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3031,14 +3031,18 @@ void SpellMgr::LoadSpellInfoCorrections()
case 45027: // Revitalize
case 45976: // Muru Portal Channel
case 52124: // Sky Darkener Assault
- case 53096: // Quetz'lun's Judgment
- case 70743: // AoD Special
- case 70614: // AoD Special - Vegard
- case 4020: // Safirdrang's Chill
case 52479: // Gift of the Harvester
case 61588: // Blazing Harpoon
case 55479: // Force Obedience
case 28560: // Summon Blizzard (Sapphiron)
+ case 53096: // Quetz'lun's Judgment
+ case 70743: // AoD Special
+ case 70614: // AoD Special - Vegard
+ case 4020: // Safirdrang's Chill
+ case 52438: // Summon Skittering Swarmer (Force Cast)
+ case 52449: // Summon Skittering Infector (Force Cast)
+ case 53609: // Summon Anub'ar Assassin (Force Cast)
+ case 53457: // Summon Impale Trigger (AoE)
spellInfo->MaxAffectedTargets = 1;
break;
case 36384: // Skartax Purple Beam
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h
index cc2e6a275f0..59429a4d6f6 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h
@@ -31,9 +31,10 @@ enum ANDataTypes
DATA_ANUBARAK = 2,
// Additional Data
- DATA_WATCHER_GASHRA = 3,
- DATA_WATCHER_SILTHIK = 4,
- DATA_WATCHER_NARJIL = 5
+ DATA_WATCHER_NARJIL,
+ DATA_WATCHER_GASHRA,
+ DATA_WATCHER_SILTHIK,
+ DATA_ANUBARAK_WALL
};
enum ANCreatureIds
@@ -55,6 +56,12 @@ enum ANGameObjectIds
GO_ANUBARAK_DOOR_3 = 192398
};
+// These are passed as -action to AI's DoAction to differentiate between them and boss scripts' own actions
+enum ANInstanceActions
+{
+ ACTION_GATEWATCHER_GREET = 1
+};
+
template<class AI>
AI* GetAzjolNerubAI(Creature* creature)
{
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
index 55bf1867797..74e89d66d4c 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp
@@ -17,65 +17,90 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellScript.h"
+#include "PassiveAI.h"
#include "azjol_nerub.h"
enum Spells
{
- SPELL_CARRION_BEETLES = 53520,
- SPELL_SUMMON_CARRION_BEETLES = 53521,
- SPELL_LEECHING_SWARM = 53467,
- SPELL_POUND = 53472,
- SPELL_SUBMERGE = 53421,
- SPELL_IMPALE_DMG = 53454,
- SPELL_IMPALE_SHAKEGROUND = 53455,
- SPELL_IMPALE_SPIKE = 53539, //this is not the correct visual effect
- //SPELL_IMPALE_TARGET = 53458,
+ SPELL_EMERGE = 53500,
+ SPELL_SUBMERGE = 53421,
+ SPELL_IMPALE_AURA = 53456,
+ SPELL_IMPALE_VISUAL = 53455,
+ SPELL_IMPALE_DAMAGE = 53454,
+ SPELL_LEECHING_SWARM = 53467,
+ SPELL_POUND = 59433,
+ SPELL_POUND_DAMAGE = 59432,
+ SPELL_CARRION_BEETLES = 53520,
+ SPELL_CARRION_BEETLE = 53521,
+
+ SPELL_SUMMON_DARTER = 53599,
+ SPELL_SUMMON_ASSASSIN = 53609,
+ SPELL_SUMMON_GUARDIAN = 53614,
+ SPELL_SUMMON_VENOMANCER = 53615,
+
+ SPELL_DART = 59349,
+ SPELL_BACKSTAB = 52540,
+ SPELL_ASSASSIN_VISUAL = 53611,
+ SPELL_SUNDER_ARMOR = 53618,
+ SPELL_POISON_BOLT = 53617
};
enum Creatures
{
- CREATURE_GUARDIAN = 29216,
- CREATURE_VENOMANCER = 29217,
- CREATURE_DATTER = 29213,
- CREATURE_IMPALE_TARGET = 89,
- DISPLAY_INVISIBLE = 11686
+ NPC_WORLD_TRIGGER = 22515,
};
-// not in db
enum Yells
{
- SAY_AGGRO = 0,
- SAY_SLAY = 1,
- SAY_DEATH = 2,
- SAY_LOCUST = 3,
- SAY_SUBMERGE = 4,
- SAY_INTRO = 5
+ SAY_AGGRO = 0,
+ SAY_SLAY = 1,
+ SAY_DEATH = 2,
+ SAY_LOCUST = 3,
+ SAY_SUBMERGE = 4,
+ SAY_INTRO = 5
+};
+
+enum Events
+{
+ EVENT_POUND = 1,
+ EVENT_IMPALE,
+ EVENT_LEECHING_SWARM,
+ EVENT_CARRION_BEETLES,
+ EVENT_SUBMERGE, // use event for this so we don't submerge mid-cast
+ EVENT_DARTER,
+ EVENT_ASSASSIN,
+ EVENT_GUARDIAN,
+ EVENT_VENOMANCER,
+ EVENT_CLOSE_DOOR
+};
+
+enum Actions
+{
+ ACTION_PET_DIED = 1,
+ ACTION_PET_EVADE
};
enum Misc
{
- ACHIEV_TIMED_START_EVENT = 20381,
+ ACHIEV_GOTTA_GO_START_EVENT = 20381,
};
enum Phases
{
- PHASE_MELEE = 0,
- PHASE_UNDERGROUND = 1,
- IMPALE_PHASE_TARGET = 0,
- IMPALE_PHASE_ATTACK = 1,
- IMPALE_PHASE_DMG = 2
+ PHASE_EMERGE = 1,
+ PHASE_SUBMERGE
};
-const Position SpawnPoint[2] =
+enum GUIDTypes
{
- { 550.7f, 282.8f, 224.3f, 0.0f },
- { 551.1f, 229.4f, 224.3f, 0.0f },
+ GUID_TYPE_PET = 0,
+ GUID_TYPE_IMPALE
};
-const Position SpawnPointGuardian[2] =
+enum SummonGroups
{
- { 550.348633f, 316.006805f, 234.2947f, 0.0f },
- { 550.188660f, 324.264557f, 237.7412f, 0.0f },
+ SUMMON_GROUP_WORLD_TRIGGER_GUARDIAN = 1
};
class boss_anub_arak : public CreatureScript
@@ -83,97 +108,58 @@ class boss_anub_arak : public CreatureScript
public:
boss_anub_arak() : CreatureScript("boss_anub_arak") { }
- struct boss_anub_arakAI : public ScriptedAI
+ struct boss_anub_arakAI : public BossAI
{
- boss_anub_arakAI(Creature* creature) : ScriptedAI(creature), Summons(me)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- GuardianSummoned = false;
- VenomancerSummoned = false;
- DatterSummoned = false;
- UndergroundTimer = 0;
- VenomancerTimer = 0;
- DatterTimer = 0;
- DelayTimer = 0;
- }
-
- void Initialize()
- {
- CarrionBeetlesTimer = 8 * IN_MILLISECONDS;
- LeechingSwarmTimer = 20 * IN_MILLISECONDS;
- ImpaleTimer = 9 * IN_MILLISECONDS;
- PoundTimer = 15 * IN_MILLISECONDS;
-
- Phase = PHASE_MELEE;
- UndergroundPhase = 0;
- Channeling = false;
- ImpalePhase = IMPALE_PHASE_TARGET;
- ImpaleTarget.Clear();
- }
-
- InstanceScript* instance;
-
- bool Channeling;
- bool GuardianSummoned;
- bool VenomancerSummoned;
- bool DatterSummoned;
- uint8 Phase;
- uint32 UndergroundPhase;
- uint32 CarrionBeetlesTimer;
- uint32 LeechingSwarmTimer;
- uint32 PoundTimer;
- uint32 UndergroundTimer;
- uint32 VenomancerTimer;
- uint32 DatterTimer;
- uint32 DelayTimer;
-
- uint32 ImpaleTimer;
- uint32 ImpalePhase;
- ObjectGuid ImpaleTarget;
-
- SummonList Summons;
+ boss_anub_arakAI(Creature* creature) : BossAI(creature, DATA_ANUBARAK), _nextSubmerge(0), _petCount(0), _assassinCount(0), _guardianCount(0), _venomancerCount(0) { }
void Reset() override
{
- Initialize();
-
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
- me->RemoveAura(SPELL_SUBMERGE);
-
- Summons.DespawnAll();
-
- instance->SetBossState(DATA_ANUBARAK, NOT_STARTED);
- instance->DoStopCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
+ BossAI::Reset();
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ instance->DoStopCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT);
+ _nextSubmerge = 75;
+ _petCount = 0;
}
- Creature* DoSummonImpaleTarget(Unit* target)
+ void EnterCombat(Unit* who) override
{
- Position targetPos = target->GetPosition();
+ BossAI::EnterCombat(who);
- if (TempSummon* impaleTarget = me->SummonCreature(CREATURE_IMPALE_TARGET, targetPos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 6*IN_MILLISECONDS))
- {
- ImpaleTarget = impaleTarget->GetGUID();
- impaleTarget->SetReactState(REACT_PASSIVE);
- impaleTarget->SetDisplayId(DISPLAY_INVISIBLE);
- impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
- impaleTarget->SetControlled(true, UNIT_STATE_ROOT);
- return impaleTarget;
- }
-
- return NULL;
- }
+ if (GameObject* door = instance->GetGameObject(DATA_ANUBARAK_WALL))
+ door->SetGoState(GO_STATE_ACTIVE); // open door for now
- void EnterCombat(Unit* /*who*/) override
- {
Talk(SAY_AGGRO);
- DelayTimer = 0;
- instance->DoStartCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
+ instance->DoStartCriteriaTimer(CRITERIA_TIMED_TYPE_EVENT, ACHIEV_GOTTA_GO_START_EVENT);
+
+ events.SetPhase(PHASE_EMERGE);
+ events.ScheduleEvent(EVENT_CLOSE_DOOR, Seconds(5));
+ events.ScheduleEvent(EVENT_POUND, randtime(Seconds(2), Seconds(4)), 0, PHASE_EMERGE);
+ events.ScheduleEvent(EVENT_LEECHING_SWARM, randtime(Seconds(5), Seconds(7)), 0, PHASE_EMERGE);
+ events.ScheduleEvent(EVENT_CARRION_BEETLES, randtime(Seconds(14), Seconds(17)), 0, PHASE_EMERGE);
+
+ // set up world triggers
+ std::list<TempSummon*> summoned;
+ me->SummonCreatureGroup(SUMMON_GROUP_WORLD_TRIGGER_GUARDIAN, &summoned);
+ if (summoned.empty()) // something went wrong
+ {
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ return;
+ }
+ _guardianTrigger = (*summoned.begin())->GetGUID();
+
+ if (Creature* trigger = DoSummon(NPC_WORLD_TRIGGER, me->GetPosition(), 0u, TEMPSUMMON_MANUAL_DESPAWN))
+ _assassinTrigger = trigger->GetGUID();
+ else
+ {
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ return;
+ }
}
- void DelayEventStart()
+ void EnterEvadeMode(EvadeReason /*why*/) override
{
- instance->SetBossState(DATA_ANUBARAK, IN_PROGRESS);
+ summons.DespawnAll();
+ _DespawnAtEvade();
}
void UpdateAI(uint32 diff) override
@@ -181,192 +167,549 @@ public:
if (!UpdateVictim())
return;
- if (DelayTimer && DelayTimer > 5000)
- DelayEventStart();
- else DelayTimer+=diff;
+ events.Update(diff);
- switch (Phase)
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
{
- case PHASE_UNDERGROUND:
- if (ImpaleTimer <= diff)
+ switch (eventId)
{
- switch (ImpalePhase)
+ case EVENT_CLOSE_DOOR:
+ if (GameObject* door = instance->GetGameObject(DATA_ANUBARAK_WALL))
+ door->SetGoState(GO_STATE_READY);
+ break;
+ case EVENT_POUND:
+ DoCastVictim(SPELL_POUND);
+ events.Repeat(randtime(Seconds(26), Seconds(32)));
+ break;
+ case EVENT_LEECHING_SWARM:
+ Talk(SAY_LOCUST);
+ DoCastAOE(SPELL_LEECHING_SWARM);
+ events.Repeat(randtime(Seconds(25), Seconds(28)));
+ break;
+ case EVENT_CARRION_BEETLES:
+ DoCastAOE(SPELL_CARRION_BEETLES);
+ events.Repeat(randtime(Seconds(24), Seconds(27)));
+ break;
+ case EVENT_IMPALE:
+ if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, _impaleTarget))
+ DoCast(impaleTarget, SPELL_IMPALE_DAMAGE, true);
+ break;
+ case EVENT_SUBMERGE:
+ Talk(SAY_SUBMERGE);
+ DoCastSelf(SPELL_SUBMERGE);
+ break;
+ case EVENT_DARTER:
{
- case IMPALE_PHASE_TARGET:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
+ std::list<Creature*> triggers;
+ me->GetCreatureListWithEntryInGrid(triggers, NPC_WORLD_TRIGGER);
+ if (!triggers.empty())
{
- if (Creature* impaleTarget = DoSummonImpaleTarget(target))
- impaleTarget->CastSpell(impaleTarget, SPELL_IMPALE_SHAKEGROUND, true);
- ImpaleTimer = 3*IN_MILLISECONDS;
- ImpalePhase = IMPALE_PHASE_ATTACK;
+ std::list<Creature*>::iterator it = triggers.begin();
+ std::advance(it, urand(0, triggers.size()-1));
+ (*it)->CastSpell(*it, SPELL_SUMMON_DARTER, true);
+ events.Repeat(Seconds(11));
}
+ else
+ EnterEvadeMode(EVADE_REASON_OTHER);
break;
- case IMPALE_PHASE_ATTACK:
- if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, ImpaleTarget))
+ }
+ case EVENT_ASSASSIN:
+ if (Creature* trigger = ObjectAccessor::GetCreature(*me, _assassinTrigger))
{
- impaleTarget->CastSpell(impaleTarget, SPELL_IMPALE_SPIKE, false);
- impaleTarget->RemoveAurasDueToSpell(SPELL_IMPALE_SHAKEGROUND);
+ trigger->CastSpell(trigger, SPELL_SUMMON_ASSASSIN, true);
+ trigger->CastSpell(trigger, SPELL_SUMMON_ASSASSIN, true);
+ if (_assassinCount > 2)
+ {
+ _assassinCount -= 2;
+ events.Repeat(Seconds(20));
+ }
+ else
+ _assassinCount = 0;
}
- ImpalePhase = IMPALE_PHASE_DMG;
- ImpaleTimer = 1*IN_MILLISECONDS;
- break;
- case IMPALE_PHASE_DMG:
- if (Creature* impaleTarget = ObjectAccessor::GetCreature(*me, ImpaleTarget))
- me->CastSpell(impaleTarget, SPELL_IMPALE_DMG, true);
- ImpalePhase = IMPALE_PHASE_TARGET;
- ImpaleTimer = 9*IN_MILLISECONDS;
+ else // something went wrong
+ EnterEvadeMode(EVADE_REASON_OTHER);
break;
- }
- } else ImpaleTimer -= diff;
-
- if (!GuardianSummoned)
- {
- for (uint8 i = 0; i < 2; ++i)
- {
- if (Creature* Guardian = me->SummonCreature(CREATURE_GUARDIAN, SpawnPointGuardian[i], TEMPSUMMON_CORPSE_DESPAWN, 0))
+ case EVENT_GUARDIAN:
+ if (Creature* trigger = ObjectAccessor::GetCreature(*me, _guardianTrigger))
{
- Guardian->AddThreat(me->GetVictim(), 0.0f);
- DoZoneInCombat(Guardian);
+ trigger->CastSpell(trigger, SPELL_SUMMON_GUARDIAN, true);
+ trigger->CastSpell(trigger, SPELL_SUMMON_GUARDIAN, true);
+ if (_guardianCount > 2)
+ {
+ _guardianCount -= 2;
+ events.Repeat(Seconds(20));
+ }
+ else
+ _guardianCount = 0;
}
- }
- GuardianSummoned = true;
- }
-
- if (!VenomancerSummoned)
- {
- if (VenomancerTimer <= diff)
- {
- if (UndergroundPhase > 1)
+ else
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ break;
+ case EVENT_VENOMANCER:
+ if (Creature* trigger = ObjectAccessor::GetCreature(*me, _guardianTrigger))
{
- for (uint8 i = 0; i < 2; ++i)
+ trigger->CastSpell(trigger, SPELL_SUMMON_VENOMANCER, true);
+ trigger->CastSpell(trigger, SPELL_SUMMON_VENOMANCER, true);
+ if (_venomancerCount > 2)
{
- if (Creature* Venomancer = me->SummonCreature(CREATURE_VENOMANCER, SpawnPoint[i], TEMPSUMMON_CORPSE_DESPAWN, 0))
- {
- Venomancer->AddThreat(me->GetVictim(), 0.0f);
- DoZoneInCombat(Venomancer);
- }
+ _venomancerCount -= 2;
+ events.Repeat(Seconds(20));
}
- VenomancerSummoned = true;
+ else
+ _venomancerCount = 0;
}
- } else VenomancerTimer -= diff;
+ else
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ break;
+ default:
+ break;
}
- if (!DatterSummoned)
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
+
+
+ DoMeleeAttackIfReady();
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _JustDied();
+ Talk(SAY_DEATH);
+ }
+
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
+
+ void SetGUID(ObjectGuid guid, int32 type) override
+ {
+ switch (type)
+ {
+ case GUID_TYPE_PET:
{
- if (DatterTimer <= diff)
+ if (Creature* creature = ObjectAccessor::GetCreature(*me, guid))
+ JustSummoned(creature);
+ else // something has gone horribly wrong
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ break;
+ }
+ case GUID_TYPE_IMPALE:
+ _impaleTarget = guid;
+ events.ScheduleEvent(EVENT_IMPALE, Seconds(4));
+ break;
+ }
+ }
+
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_PET_DIED:
+ if (!_petCount) // underflow check - something has gone horribly wrong
{
- if (UndergroundPhase > 2)
- {
- for (uint8 i = 0; i < 2; ++i)
- {
- if (Creature* Datter = me->SummonCreature(CREATURE_DATTER, SpawnPoint[i], TEMPSUMMON_CORPSE_DESPAWN, 0))
- {
- Datter->AddThreat(me->GetVictim(), 0.0f);
- DoZoneInCombat(Datter);
- }
- }
- DatterSummoned = true;
- }
- } else DatterTimer -= diff;
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ return;
+ }
+ if (!--_petCount) // last pet died, emerge
+ {
+ me->RemoveAurasDueToSpell(SPELL_SUBMERGE);
+ me->RemoveAurasDueToSpell(SPELL_IMPALE_AURA);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ DoCastSelf(SPELL_EMERGE);
+ events.SetPhase(PHASE_EMERGE);
+ events.ScheduleEvent(EVENT_POUND, randtime(Seconds(13), Seconds(18)), 0, PHASE_EMERGE);
+ events.ScheduleEvent(EVENT_LEECHING_SWARM, randtime(Seconds(3), Seconds(7)), 0, PHASE_EMERGE);
+ events.ScheduleEvent(EVENT_CARRION_BEETLES, randtime(Seconds(10), Seconds(15)), 0, PHASE_EMERGE);
+ }
+ break;
+ case ACTION_PET_EVADE:
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ break;
+ }
+ }
- if (me->HasAura(SPELL_LEECHING_SWARM))
- me->RemoveAurasDueToSpell(SPELL_LEECHING_SWARM);
+ void DamageTaken(Unit* /*source*/, uint32& damage) override
+ {
+ if (me->HasAura(SPELL_SUBMERGE))
+ damage = 0;
+ else
+ if (_nextSubmerge && me->HealthBelowPctDamaged(_nextSubmerge, damage))
+ {
+ events.CancelEvent(EVENT_SUBMERGE);
+ events.ScheduleEvent(EVENT_SUBMERGE, 0, 0, PHASE_EMERGE);
+ _nextSubmerge = _nextSubmerge-25;
}
+ }
- if (UndergroundTimer <= diff)
- {
- me->RemoveAura(SPELL_SUBMERGE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
- Phase = PHASE_MELEE;
- } else UndergroundTimer -= diff;
- break;
-
- case PHASE_MELEE:
- if (((UndergroundPhase == 0 && HealthBelowPct(75))
- || (UndergroundPhase == 1 && HealthBelowPct(50))
- || (UndergroundPhase == 2 && HealthBelowPct(25)))
- && !me->HasUnitState(UNIT_STATE_CASTING))
+ void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override
+ {
+ if (spell->Id == SPELL_SUBMERGE)
+ {
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveAurasDueToSpell(SPELL_LEECHING_SWARM);
+ DoCastSelf(SPELL_IMPALE_AURA, true);
+
+ events.SetPhase(PHASE_SUBMERGE);
+ switch (_nextSubmerge)
{
- GuardianSummoned = false;
- VenomancerSummoned = false;
- DatterSummoned = false;
+ case 50: // first submerge phase
+ _assassinCount = 4;
+ _guardianCount = 2;
+ _venomancerCount = 0;
+ break;
+ case 25: // second submerge phase
+ _assassinCount = 6;
+ _guardianCount = 2;
+ _venomancerCount = 2;
+ break;
+ case 0: // third submerge phase
+ _assassinCount = 6;
+ _guardianCount = 2;
+ _venomancerCount = 2;
+ events.ScheduleEvent(EVENT_DARTER, Seconds(0), 0, PHASE_SUBMERGE);
+ break;
+ }
+ _petCount = _guardianCount + _venomancerCount;
+ if (_assassinCount)
+ events.ScheduleEvent(EVENT_ASSASSIN, Seconds(0), 0, PHASE_SUBMERGE);
+ if (_guardianCount)
+ events.ScheduleEvent(EVENT_GUARDIAN, Seconds(4), 0, PHASE_SUBMERGE);
+ if (_venomancerCount)
+ events.ScheduleEvent(EVENT_VENOMANCER, Seconds(20), 0, PHASE_SUBMERGE);
+ }
+ }
+
+ private:
+ ObjectGuid _impaleTarget;
+ uint32 _nextSubmerge;
+ uint32 _petCount;
+ ObjectGuid _guardianTrigger;
+ ObjectGuid _assassinTrigger;
+ uint8 _assassinCount;
+ uint8 _guardianCount;
+ uint8 _venomancerCount;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<boss_anub_arakAI>(creature);
+ }
+};
- UndergroundTimer = 40*IN_MILLISECONDS;
- VenomancerTimer = 25*IN_MILLISECONDS;
- DatterTimer = 32*IN_MILLISECONDS;
+class npc_anubarak_pet_template : public ScriptedAI
+{
+ public:
+ npc_anubarak_pet_template(Creature* creature, bool isLarge) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _isLarge(isLarge) { }
+
+ void InitializeAI() override
+ {
+ ScriptedAI::InitializeAI();
+ if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK))
+ anubarak->AI()->SetGUID(me->GetGUID(), GUID_TYPE_PET);
+ else
+ me->DespawnOrUnsummon();
+ }
- ImpalePhase = 0;
- ImpaleTimer = 9*IN_MILLISECONDS;
+ void JustDied(Unit* killer) override
+ {
+ ScriptedAI::JustDied(killer);
+ if (_isLarge)
+ if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK))
+ anubarak->AI()->DoAction(ACTION_PET_DIED);
+ }
- DoCast(me, SPELL_SUBMERGE, false);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK))
+ anubarak->AI()->DoAction(ACTION_PET_EVADE);
+ else
+ me->DespawnOrUnsummon();
+ }
- Phase = PHASE_UNDERGROUND;
- ++UndergroundPhase;
- }
+ protected:
+ InstanceScript* _instance;
+ private:
+ bool const _isLarge;
+};
- if (Channeling == true)
+class npc_anubarak_anub_ar_darter : public CreatureScript
+{
+ public:
+ npc_anubarak_anub_ar_darter() : CreatureScript("npc_anubarak_anub_ar_darter") { }
+
+ struct npc_anubarak_anub_ar_darterAI : public npc_anubarak_pet_template
+ {
+ npc_anubarak_anub_ar_darterAI(Creature* creature) : npc_anubarak_pet_template(creature, false) { }
+
+ void InitializeAI() override
+ {
+ npc_anubarak_pet_template::InitializeAI();
+ DoCastAOE(SPELL_DART);
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anubarak_anub_ar_darterAI>(creature);
+ }
+};
+
+class npc_anubarak_anub_ar_assassin : public CreatureScript
+{
+ public:
+ npc_anubarak_anub_ar_assassin() : CreatureScript("npc_anubarak_anub_ar_assassin") { }
+
+ struct npc_anubarak_anub_ar_assassinAI : public npc_anubarak_pet_template
+ {
+ npc_anubarak_anub_ar_assassinAI(Creature* creature) : npc_anubarak_pet_template(creature, false), _backstabTimer(6 * IN_MILLISECONDS) { }
+
+ bool IsInBounds(Position const& jumpTo, CreatureBoundary const* boundary)
+ {
+ if (!boundary)
+ return true;
+ for (CreatureBoundary::const_iterator it = boundary->cbegin(); it != boundary->cend(); ++it)
+ if (!(*it)->IsWithinBoundary(&jumpTo))
+ return false;
+ return true;
+ }
+ Position GetRandomPositionAround(Creature* anubarak)
+ {
+ static float DISTANCE_MIN = 10.0f;
+ static float DISTANCE_MAX = 30.0f;
+ double angle = rand_norm() * 2.0 * M_PI;
+ return { anubarak->GetPositionX() + (float)(frand(DISTANCE_MIN, DISTANCE_MAX)*std::sin(angle)), anubarak->GetPositionY() + (float)(frand(DISTANCE_MIN, DISTANCE_MAX)*std::cos(angle)), anubarak->GetPositionZ() };
+ }
+ void InitializeAI() override
+ {
+ npc_anubarak_pet_template::InitializeAI();
+ CreatureBoundary const* boundary = _instance->GetBossBoundary(DATA_ANUBARAK);
+ if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK))
{
- for (uint8 i = 0; i < 8; ++i)
- DoCastVictim(SPELL_SUMMON_CARRION_BEETLES, true);
- Channeling = false;
+ Position jumpTo;
+ do
+ jumpTo = GetRandomPositionAround(anubarak);
+ while (!IsInBounds(jumpTo, boundary));
+ me->GetMotionMaster()->MoveJump(jumpTo, 40.0f, 40.0f);
+ DoCastSelf(SPELL_ASSASSIN_VISUAL, true);
}
- else if (CarrionBeetlesTimer <= diff)
- {
- Channeling = true;
- DoCastVictim(SPELL_CARRION_BEETLES);
- CarrionBeetlesTimer = 25*IN_MILLISECONDS;
- } else CarrionBeetlesTimer -= diff;
+ }
- if (LeechingSwarmTimer <= diff)
- {
- DoCast(me, SPELL_LEECHING_SWARM, true);
- LeechingSwarmTimer = 19*IN_MILLISECONDS;
- } else LeechingSwarmTimer -= diff;
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- if (PoundTimer <= diff)
+ if (diff >= _backstabTimer)
{
- if (Unit* target = me->GetVictim())
- {
- if (Creature* pImpaleTarget = DoSummonImpaleTarget(target))
- me->CastSpell(pImpaleTarget, SPELL_POUND, false);
- }
- PoundTimer = 16500;
- } else PoundTimer -= diff;
+ if (me->GetVictim() && me->GetVictim()->isInBack(me))
+ DoCastVictim(SPELL_BACKSTAB);
+ _backstabTimer = 6 * IN_MILLISECONDS;
+ }
+ else
+ _backstabTimer -= diff;
DoMeleeAttackIfReady();
- break;
}
- }
- void JustDied(Unit* /*killer*/) override
+ void MovementInform(uint32 /*type*/, uint32 id) override
+ {
+ if (id == EVENT_JUMP)
+ {
+ me->RemoveAurasDueToSpell(SPELL_ASSASSIN_VISUAL);
+ DoZoneInCombat();
+ }
+ }
+
+ private:
+ uint32 _backstabTimer;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
{
- Talk(SAY_DEATH);
- Summons.DespawnAll();
- instance->SetBossState(DATA_ANUBARAK, DONE);
+ return GetAzjolNerubAI<npc_anubarak_anub_ar_assassinAI>(creature);
}
+};
- void KilledUnit(Unit* victim) override
+class npc_anubarak_anub_ar_guardian : public CreatureScript
+{
+ public:
+ npc_anubarak_anub_ar_guardian() : CreatureScript("npc_anubarak_anub_ar_guardian") { }
+
+ struct npc_anubarak_anub_ar_guardianAI : public npc_anubarak_pet_template
+ {
+ npc_anubarak_anub_ar_guardianAI(Creature* creature) : npc_anubarak_pet_template(creature, true), _sunderTimer(6 * IN_MILLISECONDS) { }
+
+ void UpdateAI(uint32 diff) override
{
- if (victim->GetTypeId() != TYPEID_PLAYER)
+ if (!UpdateVictim())
return;
- Talk(SAY_SLAY);
+ if (diff >= _sunderTimer)
+ {
+ DoCastVictim(SPELL_SUNDER_ARMOR);
+ _sunderTimer = 12 * IN_MILLISECONDS;
+ }
+ else
+ _sunderTimer -= diff;
+
+ DoMeleeAttackIfReady();
}
- void JustSummoned(Creature* summon) override
+ private:
+ uint32 _sunderTimer;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anubarak_anub_ar_guardianAI>(creature);
+ }
+};
+
+class npc_anubarak_anub_ar_venomancer : public CreatureScript
+{
+ public:
+ npc_anubarak_anub_ar_venomancer() : CreatureScript("npc_anubarak_anub_ar_venomancer") { }
+
+ struct npc_anubarak_anub_ar_venomancerAI : public npc_anubarak_pet_template
+ {
+ npc_anubarak_anub_ar_venomancerAI(Creature* creature) : npc_anubarak_pet_template(creature, true), _boltTimer(5 * IN_MILLISECONDS) { }
+
+ void UpdateAI(uint32 diff) override
{
- Summons.Summon(summon);
+ if (!UpdateVictim())
+ return;
+
+ if (diff >= _boltTimer)
+ {
+ DoCastVictim(SPELL_POISON_BOLT);
+ _boltTimer = urandms(2, 3);
+ }
+ else
+ _boltTimer -= diff;
+
+ DoMeleeAttackIfReady();
}
+
+ private:
+ uint32 _boltTimer;
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetInstanceAI<boss_anub_arakAI>(creature);
+ return GetAzjolNerubAI<npc_anubarak_anub_ar_venomancerAI>(creature);
}
};
+class npc_anubarak_impale_target : public CreatureScript
+{
+ public:
+ npc_anubarak_impale_target() : CreatureScript("npc_anubarak_impale_target") { }
+
+ struct npc_anubarak_impale_targetAI : public NullCreatureAI
+ {
+ npc_anubarak_impale_targetAI(Creature* creature) : NullCreatureAI(creature) { }
+
+ void InitializeAI() override
+ {
+ if (Creature* anubarak = me->GetInstanceScript()->GetCreature(DATA_ANUBARAK))
+ {
+ DoCastSelf(SPELL_IMPALE_VISUAL);
+ me->DespawnOrUnsummon(Seconds(6));
+ anubarak->AI()->SetGUID(me->GetGUID(), GUID_TYPE_IMPALE);
+ }
+ else
+ me->DespawnOrUnsummon();
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anubarak_impale_targetAI>(creature);
+ }
+};
+
+class spell_anubarak_pound : public SpellScriptLoader
+{
+ public:
+ spell_anubarak_pound() : SpellScriptLoader("spell_anubarak_pound") { }
+
+ class spell_anubarak_pound_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_anubarak_pound_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return sSpellMgr->GetSpellInfo(SPELL_POUND_DAMAGE) != nullptr;
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ GetCaster()->CastSpell(target, SPELL_POUND_DAMAGE, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_anubarak_pound_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_anubarak_pound_SpellScript();
+ }
+};
+
+class spell_anubarak_carrion_beetles : public SpellScriptLoader
+{
+ public:
+ spell_anubarak_carrion_beetles() : SpellScriptLoader("spell_anubarak_carrion_beetles") { }
+
+ class spell_anubarak_carrion_beetles_AuraScript : public AuraScript
+ {
+ public:
+ PrepareAuraScript(spell_anubarak_carrion_beetles_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return (sSpellMgr->GetSpellInfo(SPELL_CARRION_BEETLE) != nullptr);
+ }
+
+ void HandlePeriodic(AuraEffect const* /*eff*/)
+ {
+ GetCaster()->CastSpell(GetCaster(), SPELL_CARRION_BEETLE, true);
+ GetCaster()->CastSpell(GetCaster(), SPELL_CARRION_BEETLE, true);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_anubarak_carrion_beetles_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_anubarak_carrion_beetles_AuraScript();
+ }
+};
+
void AddSC_boss_anub_arak()
{
new boss_anub_arak();
+
+ new npc_anubarak_anub_ar_darter();
+ new npc_anubarak_anub_ar_assassin();
+ new npc_anubarak_anub_ar_guardian();
+ new npc_anubarak_anub_ar_venomancer();
+ new npc_anubarak_impale_target();
+
+ new spell_anubarak_pound();
+ new spell_anubarak_carrion_beetles();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp
index 45e984f8936..ac840c58f55 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp
@@ -15,34 +15,136 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
-* Comment: No Waves atm and the doors spells are crazy...
-*
-* When your group enters the main room (the one after the bridge), you will notice a group of 3 Nerubians.
-* When you engage them, 2 more groups like this one spawn behind the first one - it is important to pull the first group back,
-* so you don't aggro all 3. Hadronox will be under you, fighting Nerubians.
-*
-* This is the timed gauntlet - waves of non-elite spiders
-* will spawn from the 3 doors located a little above the main room, and will then head down to fight Hadronox. After clearing the
-* main room, it is recommended to just stay in it, kill the occasional non-elites that will attack you instead of the boss, and wait for
-* Hadronox to make his way to you. When Hadronox enters the main room, she will web the doors, and no more non-elites will spawn.
-*/
-
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellScript.h"
+#include "SpellAuras.h"
+#include "SpellAuraEffects.h"
#include "azjol_nerub.h"
+enum Events
+{
+ // Hadronox
+ EVENT_LEECH_POISON = 1,
+ EVENT_ACID_CLOUD,
+ EVENT_WEB_GRAB,
+ EVENT_PIERCE_ARMOR,
+ EVENT_PLAYER_CHECK,
+
+ // Anub'ar Crusher
+ EVENT_SMASH,
+
+ // Anub'ar foes - Shared
+ EVENT_TAUNT,
+
+ // Anub'ar Champion
+ EVENT_REND,
+ EVENT_PUMMEL,
+
+ // Anub'ar Crypt Guard
+ EVENT_CRUSHING_WEBS,
+ EVENT_INFECTED_WOUND,
+
+ // Anub'ar Necromancer
+ EVENT_SHADOW_BOLT,
+ EVENT_ANIMATE_BONES
+};
+
enum Spells
{
- SPELL_ACID_CLOUD = 53400, // Victim
- SPELL_LEECH_POISON = 53030, // Victim
- SPELL_PIERCE_ARMOR = 53418, // Victim
- SPELL_WEB_GRAB = 57731, // Victim
- SPELL_WEB_FRONT_DOORS = 53177, // Self
- SPELL_WEB_SIDE_DOORS = 53185, // Self
- H_SPELL_ACID_CLOUD = 59419,
- H_SPELL_LEECH_POISON = 59417,
- H_SPELL_WEB_GRAB = 59421
+ // Hadronox
+ SPELL_WEB_FRONT_DOORS = 53177,
+ SPELL_WEB_SIDE_DOORS = 53185,
+ SPELL_LEECH_POISON = 53030,
+ SPELL_LEECH_POISON_HEAL = 53800,
+ SPELL_ACID_CLOUD = 53400,
+ SPELL_WEB_GRAB = 57731,
+ SPELL_PIERCE_ARMOR = 53418,
+
+ // Anub'ar opponent summoning spells
+ SPELL_SUMMON_CHAMPION_PERIODIC = 53035,
+ SPELL_SUMMON_CRYPT_FIEND_PERIODIC = 53037,
+ SPELL_SUMMON_NECROMANCER_PERIODIC = 53036,
+ SPELL_SUMMON_CHAMPION_TOP = 53064,
+ SPELL_SUMMON_CRYPT_FIEND_TOP = 53065,
+ SPELL_SUMMON_NECROMANCER_TOP = 53066,
+ SPELL_SUMMON_CHAMPION_BOTTOM = 53090,
+ SPELL_SUMMON_CRYPT_FIEND_BOTTOM = 53091,
+ SPELL_SUMMON_NECROMANCER_BOTTOM = 53092,
+
+ // Anub'ar Crusher
+ SPELL_SMASH = 53318,
+ SPELL_FRENZY = 53801,
+
+ // Anub'ar foes - Shared
+ SPELL_TAUNT = 53798,
+
+ // Anub'ar Champion
+ SPELL_REND = 59343,
+ SPELL_PUMMEL = 59344,
+
+ // Anub'ar Crypt Guard
+ SPELL_CRUSHING_WEBS = 59347,
+ SPELL_INFECTED_WOUND = 59348,
+
+ // Anub'ar Necromancer
+ SPELL_SHADOW_BOLT = 53333,
+ SPELL_ANIMATE_BONES_1 = 53334,
+ SPELL_ANIMATE_BONES_2 = 53336,
+};
+
+enum SummonGroups
+{
+ SUMMON_GROUP_CRUSHER_1 = 1,
+ SUMMON_GROUP_CRUSHER_2 = 2,
+ SUMMON_GROUP_CRUSHER_3 = 3
+};
+
+enum Actions
+{
+ ACTION_HADRONOX_MOVE = 1,
+ ACTION_CRUSHER_ENGAGED,
+ ACTION_PACK_WALK
+};
+
+enum Data
+{
+ DATA_CRUSHER_PACK_ID = 1,
+ DATA_HADRONOX_ENTERED_COMBAT,
+ DATA_HADRONOX_WEBBED_DOORS
+};
+
+enum Creatures
+{
+ NPC_CRUSHER = 28922,
+ NPC_WORLDTRIGGER_LARGE = 23472
+};
+
+enum Talk
+{
+ CRUSHER_SAY_AGGRO = 1,
+ CRUSHER_EMOTE_FRENZY = 2,
+ HADRONOX_EMOTE_MOVE = 1
+};
+
+// Movement IDs used by the permanently spawning Anub'ar opponents - they are done in sequence, as one finishes, the next one starts
+enum Movements
+{
+ MOVE_NONE = 0,
+ MOVE_OUTSIDE,
+ MOVE_DOWNSTAIRS,
+ MOVE_DOWNSTAIRS_2,
+ MOVE_HADRONOX, // this one might have us take a detour to avoid pathfinding "through" the floor...
+ MOVE_HADRONOX_REAL // while this one will always make us movechase
+};
+
+static const uint8 NUM_STEPS = 4;
+static const Position hadronoxStep[NUM_STEPS] =
+{
+ { 515.5848f, 544.2007f, 673.6272f },
+ { 562.191f , 514.068f , 696.4448f },
+ { 610.3828f, 518.6407f, 695.9385f },
+ { 530.42f , 560.003f, 733.0308f }
};
class boss_hadronox : public CreatureScript
@@ -50,157 +152,1014 @@ class boss_hadronox : public CreatureScript
public:
boss_hadronox() : CreatureScript("boss_hadronox") { }
- struct boss_hadronoxAI : public ScriptedAI
+ struct boss_hadronoxAI : public BossAI
{
- boss_hadronoxAI(Creature* creature) : ScriptedAI(creature)
+ boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX), _enteredCombat(false), _doorsWebbed(false), _lastPlayerCombatState(false), _step(0) { }
+
+ bool IsInCombatWithPlayer() const
{
- Initialize();
- instance = creature->GetInstanceScript();
- fMaxDistance = 50.0f;
- bFirstTime = true;
+ std::list<HostileReference*> const& refs = me->getThreatManager().getThreatList();
+ for (HostileReference const* hostileRef : refs)
+ {
+ if (Unit const* target = hostileRef->getTarget())
+ if (target->IsControlledByPlayer())
+ return true;
+ }
+ return false;
}
- void Initialize()
+ void SetStep(uint8 step)
{
- uiAcidTimer = urand(10 * IN_MILLISECONDS, 14 * IN_MILLISECONDS);
- uiLeechTimer = urand(3 * IN_MILLISECONDS, 9 * IN_MILLISECONDS);
- uiPierceTimer = urand(1 * IN_MILLISECONDS, 3 * IN_MILLISECONDS);
- uiGrabTimer = urand(15 * IN_MILLISECONDS, 19 * IN_MILLISECONDS);
- uiDoorsTimer = urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS);
- uiCheckDistanceTimer = 2 * IN_MILLISECONDS;
+ if (_lastPlayerCombatState)
+ return;
+
+ _step = step;
+ me->SetHomePosition(hadronoxStep[step]);
+ me->GetMotionMaster()->Clear();
+ me->AttackStop();
+ SetCombatMovement(false);
+ me->GetMotionMaster()->MovePoint(0, hadronoxStep[step]);
}
- InstanceScript* instance;
+ void SummonCrusherPack(SummonGroups group)
+ {
+ std::list<TempSummon*> summoned;
+ me->SummonCreatureGroup(group, &summoned);
+ for (TempSummon* summon : summoned)
+ {
+ summon->AI()->SetData(DATA_CRUSHER_PACK_ID, group);
+ summon->AI()->DoAction(ACTION_PACK_WALK);
+ }
+ }
+
+ void MovementInform(uint32 type, uint32 /*id*/) override
+ {
+ if (type != POINT_MOTION_TYPE)
+ return;
+ SetCombatMovement(true);
+ AttackStart(me->GetVictim());
+ if (_step < NUM_STEPS-1)
+ return;
+ DoCastAOE(SPELL_WEB_FRONT_DOORS);
+ DoCastAOE(SPELL_WEB_SIDE_DOORS);
+ _doorsWebbed = true;
+ DoZoneInCombat();
+ }
- uint32 uiAcidTimer;
- uint32 uiLeechTimer;
- uint32 uiPierceTimer;
- uint32 uiGrabTimer;
- uint32 uiDoorsTimer;
- uint32 uiCheckDistanceTimer;
+ uint32 GetData(uint32 data) const override
+ {
+ if (data == DATA_HADRONOX_ENTERED_COMBAT)
+ return _enteredCombat ? 1 : 0;
+ if (data == DATA_HADRONOX_WEBBED_DOORS)
+ return _doorsWebbed ? 1 : 0;
+ return 0;
+ }
- bool bFirstTime;
+ bool CanAIAttack(Unit const* target) const override
+ {
+ // Prevent Hadronox from going too far from her current home position
+ if (!target->IsControlledByPlayer() && target->GetDistance(me->GetHomePosition()) > 20.0f)
+ return false;
+ return BossAI::CanAIAttack(target);
+ }
- float fMaxDistance;
+ void EnterCombat(Unit* /*who*/) override
+ {
+ events.ScheduleEvent(EVENT_LEECH_POISON, randtime(Seconds(5), Seconds(7)));
+ events.ScheduleEvent(EVENT_ACID_CLOUD, randtime(Seconds(7), Seconds(13)));
+ events.ScheduleEvent(EVENT_WEB_GRAB, randtime(Seconds(13), Seconds(19)));
+ events.ScheduleEvent(EVENT_PIERCE_ARMOR, randtime(Seconds(4), Seconds(7)));
+ events.ScheduleEvent(EVENT_PLAYER_CHECK, Seconds(1));
+ me->setActive(true);
+ }
+
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_CRUSHER_ENGAGED:
+ if (_enteredCombat)
+ break;
+ instance->SetBossState(DATA_HADRONOX, IN_PROGRESS);
+ _enteredCombat = true;
+ SummonCrusherPack(SUMMON_GROUP_CRUSHER_2);
+ SummonCrusherPack(SUMMON_GROUP_CRUSHER_3);
+ break;
+ case ACTION_HADRONOX_MOVE:
+ if (_step < NUM_STEPS-1)
+ {
+ SetStep(_step + 1);
+ Talk(HADRONOX_EMOTE_MOVE);
+ }
+ break;
+ }
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ std::list<Creature*> triggers;
+ me->GetCreatureListWithEntryInGrid(triggers, NPC_WORLDTRIGGER_LARGE);
+ for (Creature* trigger : triggers)
+ if (trigger->HasAura(SPELL_SUMMON_CHAMPION_PERIODIC) || trigger->HasAura(SPELL_WEB_FRONT_DOORS) || trigger->HasAura(SPELL_WEB_SIDE_DOORS))
+ _DespawnAtEvade(25, trigger);
+ _DespawnAtEvade(25);
+ summons.DespawnAll();
+ for (ObjectGuid gNerubian : _anubar)
+ if (Creature* nerubian = ObjectAccessor::GetCreature(*me, gNerubian))
+ nerubian->DespawnOrUnsummon();
+ }
+
+ void SetGUID(ObjectGuid guid, int32 /*what*/) override
+ {
+ _anubar.push_back(guid);
+ }
- void Reset() override
+ void Initialize()
{
me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 9.0f);
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 9.0f);
+ _enteredCombat = false;
+ _doorsWebbed = false;
+ _lastPlayerCombatState = false;
+ SetStep(0);
+ SetCombatMovement(true);
+ SummonCrusherPack(SUMMON_GROUP_CRUSHER_1);
+ }
+
+ void InitializeAI() override
+ {
+ BossAI::InitializeAI();
+ if (me->IsAlive())
+ Initialize();
+ }
+ void JustRespawned() override
+ {
+ BossAI::JustRespawned();
Initialize();
+ }
- if (instance->GetBossState(DATA_HADRONOX) != DONE && !bFirstTime)
- instance->SetBossState(DATA_HADRONOX, FAIL);
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- bFirstTime = false;
+ events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_LEECH_POISON:
+ DoCastAOE(SPELL_LEECH_POISON);
+ events.Repeat(randtime(Seconds(7), Seconds(9)));
+ break;
+ case EVENT_ACID_CLOUD:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f))
+ DoCast(target, SPELL_ACID_CLOUD);
+ events.Repeat(randtime(Seconds(16), Seconds(23)));
+ break;
+ case EVENT_WEB_GRAB:
+ DoCastAOE(SPELL_WEB_GRAB);
+ events.Repeat(randtime(Seconds(20), Seconds(25)));
+ break;
+ case EVENT_PIERCE_ARMOR:
+ DoCastVictim(SPELL_PIERCE_ARMOR);
+ events.Repeat(randtime(Seconds(10), Seconds(15)));
+ break;
+ case EVENT_PLAYER_CHECK:
+ if (IsInCombatWithPlayer() != _lastPlayerCombatState)
+ {
+ _lastPlayerCombatState = !_lastPlayerCombatState;
+ if (_lastPlayerCombatState) // we are now in combat with players
+ {
+ if (!instance->CheckRequiredBosses(DATA_HADRONOX))
+ {
+ EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK);
+ return;
+ }
+ // cancel current point movement if engaged by players
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
+ {
+ me->GetMotionMaster()->Clear();
+ SetCombatMovement(true);
+ AttackStart(me->GetVictim());
+ }
+ }
+ else // we are no longer in combat with players - reset the encounter
+ EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
+ }
+ events.Repeat(Seconds(1));
+ break;
+ }
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
+
+ DoMeleeAttackIfReady();
}
- //when Hadronox kills any enemy (that includes a party member) she will regain 10% of her HP if the target had Leech Poison on
- void KilledUnit(Unit* Victim) override
+ // Safeguard to prevent Hadronox dying to NPCs
+ void DamageTaken(Unit* who, uint32& damage) override
{
- // not sure if this aura check is correct, I think it is though
- if (!Victim || !Victim->HasAura(DUNGEON_MODE(SPELL_LEECH_POISON, H_SPELL_LEECH_POISON)) || !me->IsAlive())
- return;
+ if (!who->IsControlledByPlayer() && me->HealthBelowPct(70))
+ {
+ if (me->HealthBelowPctDamaged(5, damage))
+ damage = 0;
+ else
+ damage *= (me->GetHealthPct()-5.0f)/ 65.0f;
+ }
+ }
- me->ModifyHealth(int32(me->CountPctFromMaxHealth(10)));
+ void JustSummoned(Creature* summon) override
+ {
+ summons.Summon(summon);
+ // Do not enter combat with zone
}
+
+ private:
+ bool _enteredCombat; // has a player entered combat with the first crusher pack? (talk and spawn two more packs)
+ bool _doorsWebbed; // obvious - have we reached the top and webbed the doors shut? (trigger for hadronox denied achievement)
+ bool _lastPlayerCombatState; // was there a player in our threat list the last time we checked (we check every second)
+ uint8 _step;
+ std::list<ObjectGuid> _anubar;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<boss_hadronoxAI>(creature);
+ }
+};
+
+struct npc_hadronox_crusherPackAI : public ScriptedAI
+{
+ npc_hadronox_crusherPackAI(Creature* creature, Position const* positions) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _positions(positions), _myPack(SummonGroups(0)), _doFacing(false) { }
- void JustDied(Unit* /*killer*/) override
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_PACK_WALK)
{
- instance->SetBossState(DATA_HADRONOX, DONE);
+ switch (_myPack)
+ {
+ case SUMMON_GROUP_CRUSHER_1:
+ case SUMMON_GROUP_CRUSHER_2:
+ case SUMMON_GROUP_CRUSHER_3:
+ me->GetMotionMaster()->MovePoint(ACTION_PACK_WALK, _positions[_myPack - SUMMON_GROUP_CRUSHER_1]);
+ break;
+ default:
+ break;
+ }
}
+ }
- void EnterCombat(Unit* /*who*/) override
+ void MovementInform(uint32 type, uint32 id) override
+ {
+ if (type == POINT_MOTION_TYPE && id == ACTION_PACK_WALK)
+ _doFacing = true;
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX))
+ hadronox->AI()->EnterEvadeMode(EVADE_REASON_OTHER);
+ }
+
+ uint32 GetData(uint32 data) const override
+ {
+ if (data == DATA_CRUSHER_PACK_ID)
+ return _myPack;
+ return 0;
+ }
+
+ void SetData(uint32 data, uint32 value) override
+ {
+ if (data == DATA_CRUSHER_PACK_ID)
{
- instance->SetBossState(DATA_HADRONOX, IN_PROGRESS);
- me->SetInCombatWithZone();
+ _myPack = SummonGroups(value);
+ me->SetReactState(_myPack ? REACT_PASSIVE : REACT_AGGRESSIVE);
}
+ }
- void CheckDistance(float dist, const uint32 uiDiff)
+ void EnterCombat(Unit* who) override
+ {
+ if (me->HasReactState(REACT_PASSIVE))
{
- if (!me->IsInCombat())
- return;
+ std::list<Creature*> others;
+ me->GetCreatureListWithEntryInGrid(others, 0, 40.0f);
+ for (Creature* other : others)
+ if (other->AI()->GetData(DATA_CRUSHER_PACK_ID) == _myPack)
+ {
+ other->SetReactState(REACT_AGGRESSIVE);
+ other->AI()->AttackStart(who);
+ }
+ }
+ _EnterCombat();
+ ScriptedAI::EnterCombat(who);
+ }
- float x=0.0f, y=0.0f, z=0.0f;
- me->GetRespawnPosition(x, y, z);
+ virtual void _EnterCombat() = 0;
+ virtual void DoEvent(uint32 /*eventId*/) = 0;
- if (uiCheckDistanceTimer <= uiDiff)
- uiCheckDistanceTimer = 5*IN_MILLISECONDS;
- else
+ void MoveInLineOfSight(Unit* who) override
+ {
+ if (!me->HasReactState(REACT_PASSIVE))
+ {
+ ScriptedAI::MoveInLineOfSight(who);
+ return;
+ }
+
+ if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance))
+ EnterCombat(who);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (_doFacing)
+ {
+ _doFacing = false;
+ me->SetFacingTo(_positions[_myPack - SUMMON_GROUP_CRUSHER_1].GetOrientation());
+ }
+
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ DoEvent(eventId);
+
+ DoMeleeAttackIfReady();
+ }
+
+ protected:
+ InstanceScript* const _instance;
+ EventMap _events;
+ Position const* const _positions;
+ SummonGroups _myPack;
+ bool _doFacing;
+
+};
+
+static const Position crusherWaypoints[] =
+{
+ { 529.6913f, 547.1257f, 731.9155f, 4.799650f },
+ { 517.51f , 561.439f , 734.0306f, 4.520403f },
+ { 543.414f , 551.728f , 732.0522f, 3.996804f }
+};
+class npc_anub_ar_crusher : public CreatureScript
+{
+ public:
+ npc_anub_ar_crusher() : CreatureScript("npc_anub_ar_crusher") { }
+
+ struct npc_anub_ar_crusherAI : public npc_hadronox_crusherPackAI
+ {
+ npc_anub_ar_crusherAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, crusherWaypoints), _hadFrenzy(false) { }
+
+ void _EnterCombat() override
{
- uiCheckDistanceTimer -= uiDiff;
- return;
+ _events.ScheduleEvent(EVENT_SMASH, randtime(Seconds(8), Seconds(12)));
+
+ if (_myPack != SUMMON_GROUP_CRUSHER_1)
+ return;
+
+ if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX))
+ {
+ if (hadronox->AI()->GetData(DATA_HADRONOX_ENTERED_COMBAT))
+ return;
+ hadronox->AI()->DoAction(ACTION_CRUSHER_ENGAGED);
+ }
+
+ Talk(CRUSHER_SAY_AGGRO);
}
- if (me->IsInEvadeMode() || !me->GetVictim())
- return;
- if (me->GetDistance(x, y, z) > dist)
- EnterEvadeMode();
+
+ void DamageTaken(Unit* /*source*/, uint32& damage) override
+ {
+ if (_hadFrenzy || !me->HealthBelowPctDamaged(25, damage))
+ return;
+ _hadFrenzy = true;
+ Talk(CRUSHER_EMOTE_FRENZY);
+ DoCastSelf(SPELL_FRENZY);
+ }
+
+ void DoEvent(uint32 eventId) override
+ {
+ switch (eventId)
+ {
+ case EVENT_SMASH:
+ DoCastVictim(SPELL_SMASH);
+ _events.Repeat(randtime(Seconds(13), Seconds(21)));
+ break;
+ }
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX))
+ hadronox->AI()->DoAction(ACTION_HADRONOX_MOVE);
+ ScriptedAI::JustDied(killer);
+ }
+
+ private:
+ bool _hadFrenzy;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anub_ar_crusherAI>(creature);
}
+};
- void UpdateAI(uint32 diff) override
+static const Position championWaypoints[] =
+{
+ { 539.2076f, 549.7539f, 732.8668f, 4.55531f },
+ { 527.3098f, 559.5197f, 732.9407f, 4.742493f },
+ { }
+};
+class npc_anub_ar_crusher_champion : public CreatureScript
+{
+ public:
+ npc_anub_ar_crusher_champion() : CreatureScript("npc_anub_ar_crusher_champion") { }
+
+ struct npc_anub_ar_crusher_championAI : public npc_hadronox_crusherPackAI
{
- //Return since we have no target
- if (!UpdateVictim())
- return;
+ npc_anub_ar_crusher_championAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, championWaypoints) { }
+
+ void DoEvent(uint32 eventId) override
+ {
+ switch (eventId)
+ {
+ case EVENT_REND:
+ DoCastVictim(SPELL_REND);
+ _events.Repeat(randtime(Seconds(12), Seconds(16)));
+ break;
+ case EVENT_PUMMEL:
+ DoCastVictim(SPELL_PUMMEL);
+ _events.Repeat(randtime(Seconds(12), Seconds(17)));
+ break;
+ }
+ }
+
+ void _EnterCombat() override
+ {
+ _events.ScheduleEvent(EVENT_REND, randtime(Seconds(4), Seconds(8)));
+ _events.ScheduleEvent(EVENT_PUMMEL, randtime(Seconds(15), Seconds(19)));
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anub_ar_crusher_championAI>(creature);
+ }
+};
+
+static const Position cryptFiendWaypoints[] =
+{
+ { 520.3911f, 548.7895f, 732.0118f, 5.0091f },
+ { },
+ { 550.9611f, 545.1674f, 731.9031f, 3.996804f }
+};
+class npc_anub_ar_crusher_crypt_fiend : public CreatureScript
+{
+ public:
+ npc_anub_ar_crusher_crypt_fiend() : CreatureScript("npc_anub_ar_crusher_crypt_fiend") { }
+
+ struct npc_anub_ar_crusher_crypt_fiendAI : public npc_hadronox_crusherPackAI
+ {
+ npc_anub_ar_crusher_crypt_fiendAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, cryptFiendWaypoints) { }
- // Without he comes up through the air to players on the bridge after krikthir if players crossing this bridge!
- CheckDistance(fMaxDistance, diff);
+ void DoEvent(uint32 eventId) override
+ {
+ switch (eventId)
+ {
+ case EVENT_CRUSHING_WEBS:
+ DoCastVictim(SPELL_CRUSHING_WEBS);
+ _events.Repeat(randtime(Seconds(12), Seconds(16)));
+ break;
+ case EVENT_INFECTED_WOUND:
+ DoCastVictim(SPELL_INFECTED_WOUND);
+ _events.Repeat(randtime(Seconds(16), Seconds(25)));
+ break;
+ }
+ }
- if (me->HasAura(SPELL_WEB_FRONT_DOORS) || me->HasAura(SPELL_WEB_SIDE_DOORS))
+ void _EnterCombat() override
{
- if (IsCombatMovementAllowed())
- SetCombatMovement(false);
+ _events.ScheduleEvent(EVENT_CRUSHING_WEBS, randtime(Seconds(4), Seconds(8)));
+ _events.ScheduleEvent(EVENT_INFECTED_WOUND, randtime(Seconds(15), Seconds(19)));
}
- else if (!IsCombatMovementAllowed())
- SetCombatMovement(true);
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anub_ar_crusher_crypt_fiendAI>(creature);
+ }
+};
+
+static const Position necromancerWaypoints[] =
+{
+ { },
+ { 507.6937f, 563.3471f, 734.8986f, 4.520403f },
+ { 535.1049f, 552.8961f, 732.8441f, 3.996804f },
+};
+class npc_anub_ar_crusher_necromancer : public CreatureScript
+{
+ public:
+ npc_anub_ar_crusher_necromancer() : CreatureScript("npc_anub_ar_crusher_necromancer") { }
+
+ struct npc_anub_ar_crusher_necromancerAI : public npc_hadronox_crusherPackAI
+ {
+ npc_anub_ar_crusher_necromancerAI(Creature* creature) : npc_hadronox_crusherPackAI(creature, necromancerWaypoints) { }
- if (uiPierceTimer <= diff)
+ void DoEvent(uint32 eventId) override
{
- DoCastVictim(SPELL_PIERCE_ARMOR);
- uiPierceTimer = 8*IN_MILLISECONDS;
- } else uiPierceTimer -= diff;
+ switch (eventId)
+ {
+ case EVENT_SHADOW_BOLT:
+ DoCastVictim(SPELL_SHADOW_BOLT);
+ _events.Repeat(randtime(Seconds(2), Seconds(5)));
+ break;
+ case EVENT_ANIMATE_BONES:
+ DoCastVictim(urand(0, 1) ? SPELL_ANIMATE_BONES_2 : SPELL_ANIMATE_BONES_1);
+ _events.Repeat(randtime(Seconds(35), Seconds(50)));
+ break;
+ }
+ }
- if (uiAcidTimer <= diff)
+ void _EnterCombat() override
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_ACID_CLOUD);
+ _events.ScheduleEvent(EVENT_SHADOW_BOLT, randtime(Seconds(2), Seconds(4)));
+ _events.ScheduleEvent(EVENT_ANIMATE_BONES, randtime(Seconds(37), Seconds(45)));
+ }
+ };
- uiAcidTimer = urand(20*IN_MILLISECONDS, 30*IN_MILLISECONDS);
- } else uiAcidTimer -= diff;
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anub_ar_crusher_necromancerAI>(creature);
+ }
+};
- if (uiLeechTimer <= diff)
+static const uint8 NUM_SPAWNS = 3;
+static const Position initialMoves[NUM_SPAWNS] =
+{
+ { 485.314606f, 611.418640f, 771.428406f },
+ { 575.760437f, 611.516418f, 771.427368f },
+ { 588.930725f, 598.233276f, 739.142151f }
+};
+static const Position downstairsMoves[NUM_SPAWNS] =
+{
+ { 513.574341f, 587.022156f, 736.229065f },
+ { 537.920410f, 580.436157f, 732.796692f },
+ { 601.289246f, 583.259644f, 725.443054f },
+};
+static const Position downstairsMoves2[NUM_SPAWNS] =
+{
+ { 571.498718f, 576.978333f, 727.582947f },
+ { 571.498718f, 576.978333f, 727.582947f },
+ { }
+};
+struct npc_hadronox_foeAI : public ScriptedAI
+{
+ npc_hadronox_foeAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _nextMovement(MOVE_OUTSIDE), _mySpawn(0) { }
+
+ void InitializeAI() override
+ {
+ ScriptedAI::InitializeAI();
+ if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX))
+ hadronox->AI()->SetGUID(me->GetGUID());
+ }
+
+ void MovementInform(uint32 type, uint32 id) override
+ {
+ if (type == POINT_MOTION_TYPE)
+ _nextMovement = Movements(id+1);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ me->DespawnOrUnsummon();
+ }
+
+ virtual void DoEvent(uint32 /*eventId*/) = 0;
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (_nextMovement)
+ {
+ switch (_nextMovement)
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_LEECH_POISON);
+ case MOVE_OUTSIDE:
+ {
+ float dist = HUGE_VALF;
+ for (uint8 spawn = 0; spawn < NUM_SPAWNS; ++spawn)
+ {
+ float thisDist = initialMoves[spawn].GetExactDistSq(me);
+ if (thisDist < dist)
+ {
+ _mySpawn = spawn;
+ dist = thisDist;
+ }
+ }
+ me->GetMotionMaster()->MovePoint(MOVE_OUTSIDE, initialMoves[_mySpawn], false); // do not pathfind here, we have to pass through a "wall" of webbing
+ break;
+ }
+ case MOVE_DOWNSTAIRS:
+ me->GetMotionMaster()->MovePoint(MOVE_DOWNSTAIRS, downstairsMoves[_mySpawn]);
+ break;
+ case MOVE_DOWNSTAIRS_2:
+ if (downstairsMoves2[_mySpawn].GetPositionX() > 0.0f) // might be unset for this spawn - if yes, skip
+ {
+ me->GetMotionMaster()->MovePoint(MOVE_DOWNSTAIRS_2, downstairsMoves2[_mySpawn]);
+ break;
+ }
+ // intentional missing break
+ case MOVE_HADRONOX:
+ case MOVE_HADRONOX_REAL:
+ {
+ static const float zCutoff = 702.0f;
+ Creature* hadronox = _instance->GetCreature(DATA_HADRONOX);
+ if (hadronox && hadronox->IsAlive())
+ {
+ if (_nextMovement != MOVE_HADRONOX_REAL)
+ if (hadronox->GetPositionZ() < zCutoff)
+ {
+ me->GetMotionMaster()->MovePoint(MOVE_HADRONOX, hadronoxStep[2]);
+ break;
+ }
+ me->GetMotionMaster()->MoveChase(hadronox);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ _nextMovement = MOVE_NONE;
+ }
+
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ DoEvent(eventId);
+
+ DoMeleeAttackIfReady();
+ }
- uiLeechTimer = urand(11*IN_MILLISECONDS, 14*IN_MILLISECONDS);
- } else uiLeechTimer -= diff;
+ protected:
+ EventMap _events;
+ InstanceScript* const _instance;
- if (uiGrabTimer <= diff)
+ private:
+ Movements _nextMovement;
+ uint8 _mySpawn;
+};
+
+class npc_anub_ar_champion : public CreatureScript
+{
+ public:
+ npc_anub_ar_champion() : CreatureScript("npc_anub_ar_champion") { }
+
+ struct npc_anub_ar_championAI : public npc_hadronox_foeAI
+ {
+ npc_anub_ar_championAI(Creature* creature) : npc_hadronox_foeAI(creature) { }
+
+ void DoEvent(uint32 eventId) override
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) // Draws all players (and attacking Mobs) to itself.
- DoCast(target, SPELL_WEB_GRAB);
+ switch (eventId)
+ {
+ case EVENT_REND:
+ DoCastVictim(SPELL_REND);
+ _events.Repeat(randtime(Seconds(12), Seconds(16)));
+ break;
+ case EVENT_PUMMEL:
+ DoCastVictim(SPELL_PUMMEL);
+ _events.Repeat(randtime(Seconds(12), Seconds(17)));
+ break;
+ case EVENT_TAUNT:
+ DoCastVictim(SPELL_TAUNT);
+ _events.Repeat(randtime(Seconds(15), Seconds(50)));
+ break;
+ }
+ }
- uiGrabTimer = urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS);
- } else uiGrabTimer -= diff;
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _events.ScheduleEvent(EVENT_REND, randtime(Seconds(4), Seconds(8)));
+ _events.ScheduleEvent(EVENT_PUMMEL, randtime(Seconds(15), Seconds(19)));
+ _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50)));
+ }
+ };
- if (uiDoorsTimer <= diff)
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anub_ar_championAI>(creature);
+ }
+};
+
+class npc_anub_ar_crypt_fiend : public CreatureScript
+{
+ public:
+ npc_anub_ar_crypt_fiend() : CreatureScript("npc_anub_ar_crypt_fiend") { }
+
+ struct npc_anub_ar_crypt_fiendAI : public npc_hadronox_foeAI
+ {
+ npc_anub_ar_crypt_fiendAI(Creature* creature) : npc_hadronox_foeAI(creature) { }
+
+ void DoEvent(uint32 eventId) override
+ {
+ switch (eventId)
{
- uiDoorsTimer = urand(30*IN_MILLISECONDS, 60*IN_MILLISECONDS);
- } else uiDoorsTimer -= diff;
+ case EVENT_CRUSHING_WEBS:
+ DoCastVictim(SPELL_CRUSHING_WEBS);
+ _events.Repeat(randtime(Seconds(12), Seconds(16)));
+ break;
+ case EVENT_INFECTED_WOUND:
+ DoCastVictim(SPELL_INFECTED_WOUND);
+ _events.Repeat(randtime(Seconds(16), Seconds(25)));
+ break;
+ case EVENT_TAUNT:
+ DoCastVictim(SPELL_TAUNT);
+ _events.Repeat(randtime(Seconds(15), Seconds(50)));
+ break;
+ }
+ }
- DoMeleeAttackIfReady();
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _events.ScheduleEvent(EVENT_CRUSHING_WEBS, randtime(Seconds(4), Seconds(8)));
+ _events.ScheduleEvent(EVENT_INFECTED_WOUND, randtime(Seconds(15), Seconds(19)));
+ _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50)));
}
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetInstanceAI<boss_hadronoxAI>(creature);
+ return GetAzjolNerubAI<npc_anub_ar_crypt_fiendAI>(creature);
+ }
+};
+
+class npc_anub_ar_necromancer : public CreatureScript
+{
+ public:
+ npc_anub_ar_necromancer() : CreatureScript("npc_anub_ar_necromancer") { }
+
+ struct npc_anub_ar_necromancerAI : public npc_hadronox_foeAI
+ {
+ npc_anub_ar_necromancerAI(Creature* creature) : npc_hadronox_foeAI(creature) { }
+
+ void DoEvent(uint32 eventId) override
+ {
+ switch (eventId)
+ {
+ case EVENT_SHADOW_BOLT:
+ DoCastVictim(SPELL_SHADOW_BOLT);
+ _events.Repeat(randtime(Seconds(2), Seconds(5)));
+ break;
+ case EVENT_ANIMATE_BONES:
+ DoCastVictim(urand(0,1) ? SPELL_ANIMATE_BONES_2 : SPELL_ANIMATE_BONES_1);
+ _events.Repeat(randtime(Seconds(35), Seconds(50)));
+ break;
+ case EVENT_TAUNT:
+ DoCastVictim(SPELL_TAUNT);
+ _events.Repeat(randtime(Seconds(15), Seconds(50)));
+ break;
+ }
+ }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _events.ScheduleEvent(EVENT_SHADOW_BOLT, randtime(Seconds(2), Seconds(4)));
+ _events.ScheduleEvent(EVENT_ANIMATE_BONES, randtime(Seconds(37), Seconds(45)));
+ _events.ScheduleEvent(EVENT_TAUNT, randtime(Seconds(15), Seconds(50)));
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anub_ar_necromancerAI>(creature);
+ }
+};
+
+class spell_hadronox_periodic_summon_template_AuraScript : public AuraScript
+{
+ public:
+ spell_hadronox_periodic_summon_template_AuraScript(uint32 topSpellId, uint32 bottomSpellId) : AuraScript(), _topSpellId(topSpellId), _bottomSpellId(bottomSpellId) { }
+ PrepareAuraScript(spell_hadronox_periodic_summon_template_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return
+ (sSpellMgr->GetSpellInfo(_topSpellId) != nullptr) &&
+ (sSpellMgr->GetSpellInfo(_bottomSpellId) != nullptr);
+ }
+
+ void HandleApply(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (AuraEffect* effect = GetAura()->GetEffect(EFFECT_0))
+ effect->SetPeriodicTimer(urandms(2, 17));
+ }
+
+ void HandlePeriodic(AuraEffect const* /*eff*/)
+ {
+ Unit* caster = GetCaster();
+ if (!caster)
+ return;
+ InstanceScript* instance = caster->GetInstanceScript();
+ if (!instance)
+ return;
+ if (instance->GetBossState(DATA_HADRONOX) == DONE)
+ GetAura()->Remove();
+ else
+ {
+ if (caster->GetPositionZ() >= 750.0f)
+ caster->CastSpell(caster, _topSpellId, true);
+ else
+ caster->CastSpell(caster, _bottomSpellId, true);
+ }
+ }
+
+ void Register() override
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_hadronox_periodic_summon_template_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_hadronox_periodic_summon_template_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+
+ private:
+ uint32 _topSpellId;
+ uint32 _bottomSpellId;
+};
+
+class spell_hadronox_periodic_summon_champion : public SpellScriptLoader
+{
+ public:
+ spell_hadronox_periodic_summon_champion() : SpellScriptLoader("spell_hadronox_periodic_summon_champion") { }
+
+ class spell_hadronox_periodic_summon_champion_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript
+ {
+ public:
+ spell_hadronox_periodic_summon_champion_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_CHAMPION_TOP, SPELL_SUMMON_CHAMPION_BOTTOM) { }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_hadronox_periodic_summon_champion_AuraScript();
+ }
+};
+
+class spell_hadronox_periodic_summon_crypt_fiend : public SpellScriptLoader
+{
+ public:
+ spell_hadronox_periodic_summon_crypt_fiend() : SpellScriptLoader("spell_hadronox_periodic_summon_crypt_fiend") { }
+
+ class spell_hadronox_periodic_summon_crypt_fiend_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript
+ {
+ public:
+ spell_hadronox_periodic_summon_crypt_fiend_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_CRYPT_FIEND_TOP, SPELL_SUMMON_CRYPT_FIEND_BOTTOM) { }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_hadronox_periodic_summon_crypt_fiend_AuraScript();
+ }
+};
+
+class spell_hadronox_periodic_summon_necromancer : public SpellScriptLoader
+{
+ public:
+ spell_hadronox_periodic_summon_necromancer() : SpellScriptLoader("spell_hadronox_periodic_summon_necromancer") { }
+
+ class spell_hadronox_periodic_summon_necromancer_AuraScript : public spell_hadronox_periodic_summon_template_AuraScript
+ {
+ public:
+ spell_hadronox_periodic_summon_necromancer_AuraScript() : spell_hadronox_periodic_summon_template_AuraScript(SPELL_SUMMON_NECROMANCER_TOP, SPELL_SUMMON_NECROMANCER_BOTTOM) { }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_hadronox_periodic_summon_necromancer_AuraScript();
+ }
+};
+
+class spell_hadronox_leeching_poison : public SpellScriptLoader
+{
+ public:
+ spell_hadronox_leeching_poison() : SpellScriptLoader("spell_hadronox_leeching_poison") { }
+
+ class spell_hadronox_leeching_poison_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_hadronox_leeching_poison_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return sSpellMgr->GetSpellInfo(SPELL_LEECH_POISON_HEAL) != nullptr;
+ }
+
+ void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH)
+ return;
+
+ if (GetTarget()->IsGuardian())
+ return;
+
+ if (Unit* caster = GetCaster())
+ caster->CastSpell(caster, SPELL_LEECH_POISON_HEAL, true);
+ }
+
+ void Register() override
+ {
+ OnEffectRemove += AuraEffectRemoveFn(spell_hadronox_leeching_poison_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_LEECH, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_hadronox_leeching_poison_AuraScript();
}
};
+class spell_hadronox_web_doors : public SpellScriptLoader
+{
+ public:
+ spell_hadronox_web_doors() : SpellScriptLoader("spell_hadronox_web_doors") { }
+
+ class spell_hadronox_web_doors_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_hadronox_web_doors_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return (
+ sSpellMgr->GetSpellInfo(SPELL_SUMMON_CHAMPION_PERIODIC) &&
+ sSpellMgr->GetSpellInfo(SPELL_SUMMON_CRYPT_FIEND_PERIODIC) &&
+ sSpellMgr->GetSpellInfo(SPELL_SUMMON_NECROMANCER_PERIODIC)
+ );
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ {
+ target->RemoveAurasDueToSpell(SPELL_SUMMON_CHAMPION_PERIODIC);
+ target->RemoveAurasDueToSpell(SPELL_SUMMON_CRYPT_FIEND_PERIODIC);
+ target->RemoveAurasDueToSpell(SPELL_SUMMON_NECROMANCER_PERIODIC);
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_hadronox_web_doors_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_hadronox_web_doors_SpellScript();
+ }
+};
+
+class achievement_hadronox_denied : public AchievementCriteriaScript
+{
+ public:
+ achievement_hadronox_denied() : AchievementCriteriaScript("achievement_hadronox_denied") { }
+
+ bool OnCheck(Player* /*player*/, Unit* target) override
+ {
+ if (!target)
+ return false;
+
+ if (Creature* cTarget = target->ToCreature())
+ if (!cTarget->AI()->GetData(DATA_HADRONOX_WEBBED_DOORS))
+ return true;
+
+ return false;
+ }
+};
+
void AddSC_boss_hadronox()
{
new boss_hadronox();
+
+ new npc_anub_ar_crusher();
+ new npc_anub_ar_crusher_champion();
+ new npc_anub_ar_crusher_crypt_fiend();
+ new npc_anub_ar_crusher_necromancer();
+
+ new npc_anub_ar_champion();
+ new npc_anub_ar_crypt_fiend();
+ new npc_anub_ar_necromancer();
+
+ new spell_hadronox_periodic_summon_champion();
+ new spell_hadronox_periodic_summon_crypt_fiend();
+ new spell_hadronox_periodic_summon_necromancer();
+
+ new spell_hadronox_leeching_poison();
+ new spell_hadronox_web_doors();
+
+ new achievement_hadronox_denied();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp
index 5a6d8be1ec2..a06d915e594 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp
@@ -21,63 +21,105 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellScript.h"
+#include "PassiveAI.h"
+#include "SpellAuras.h"
#include "azjol_nerub.h"
-enum Spells
+enum Events
{
- SPELL_MIND_FLAY = 52586,
- SPELL_CURSE_OF_FATIGUE = 52592,
- SPELL_FRENZY = 28747, //maybe 53361
- SPELL_SUMMON_SKITTERING_SWARMER = 52438, //AOE Effect 140, maybe 52439
- SPELL_SUMMON_SKITTERING_SWARMER_1 = 52439, //Summon 3x 28735
- SPELL_ACID_SPLASH = 52446,
- SPELL_CHARGE = 16979, //maybe is another spell
- SPELL_BACKSTAB = 52540,
- SPELL_SHADOW_BOLT = 52534,
- SPELL_SHADOW_NOVA = 52535,
- SPELL_STRIKE = 52532,
- SPELL_CLEAVE = 49806,
- SPELL_ENRAGE = 52470,
- SPELL_INFECTED_BITE = 52469,
- SPELL_WEB_WRAP = 52086, //the spell is not working properly
- SPELL_BLINDING_WEBS = 52524,
- SPELL_POSION_SPRAY = 52493
+ // Krik'thir the Gatewatcher
+ EVENT_SEND_GROUP = 1,
+ EVENT_SWARM,
+ EVENT_MIND_FLAY,
+ EVENT_FRENZY,
+
+ // Watchers - Shared
+ EVENT_WEB_WRAP,
+ EVENT_INFECTED_BITE,
+
+ // Watcher Gashra
+ EVENT_ENRAGE,
+ // Watcher Narjil
+ EVENT_BLINDING_WEBS,
+ // Watcher Silthik
+ EVENT_POISON_SPRAY,
+
+ // Anubar Skirmisher
+ EVENT_ANUBAR_CHARGE,
+ EVENT_BACKSTAB,
+
+ // Anubar Shadowcaster
+ EVENT_SHADOW_BOLT,
+ EVENT_SHADOW_NOVA,
+
+ // Anubar Warrior
+ EVENT_STRIKE,
+ EVENT_CLEAVE
};
-enum Mobs
+enum Spells
{
- NPC_SKITTERING_SWARMER = 28735,
- NPC_SKITTERING_SWARMER_CONTROLLER = 32593,
- NPC_SKITTERING_INFECTIOR = 28736
+ // Krik'thir the Gatewatcher
+ SPELL_SUBBOSS_AGGRO_TRIGGER = 52343,
+ SPELL_SWARM = 52440,
+ SPELL_MIND_FLAY = 52586,
+ SPELL_CURSE_OF_FATIGUE = 52592,
+ SPELL_FRENZY = 28747,
+
+ // Watchers - Shared
+ SPELL_WEB_WRAP = 52086,
+ SPELL_WEB_WRAP_WRAPPED = 52087,
+ SPELL_INFECTED_BITE = 52469,
+
+ // Watcher Gashra
+ SPELL_ENRAGE = 52470,
+ // Watcher Narjil
+ SPELL_BLINDING_WEBS = 52524,
+ // Watcher Silthik
+ SPELL_POISON_SPRAY = 52493,
+
+ // Anub'ar Warrior
+ SPELL_CLEAVE = 49806,
+ SPELL_STRIKE = 52532,
+
+ // Anub'ar Skirmisher
+ SPELL_CHARGE = 52538,
+ SPELL_BACKSTAB = 52540,
+ SPELL_FIXTATE_TRIGGER = 52536,
+ SPELL_FIXTATE_TRIGGERED = 52537,
+
+ // Anub'ar Shadowcaster
+ SPELL_SHADOW_BOLT = 52534,
+ SPELL_SHADOW_NOVA = 52535,
+
+ // Skittering Infector
+ SPELL_ACID_SPLASH = 52446
};
-enum Yells
+enum Data
{
- SAY_AGGRO = 0,
- SAY_SLAY = 1,
- SAY_DEATH = 2,
- SAY_SWARM = 3,
- SAY_PREFIGHT = 4,
- SAY_SEND_GROUP = 5
+ DATA_PET_GROUP
};
-Position const SpawnPoint[] =
+enum Actions
{
- { 566.164f, 682.087f, 769.079f, 2.21657f },
- { 529.042f, 706.941f, 777.298f, 1.0821f },
- { 489.975f, 671.239f, 772.131f, 0.261799f },
- { 488.556f, 692.95f, 771.764f, 4.88692f },
- { 553.34f, 640.387f, 777.419f, 1.20428f },
- { 517.486f, 706.398f, 777.335f, 5.35816f },
- { 504.01f, 637.693f, 777.479f, 0.506145f },
- { 552.625f, 706.408f, 777.177f, 3.4383f }
+ ACTION_GASHRA_DIED,
+ ACTION_NARJIL_DIED,
+ ACTION_SILTHIK_DIED,
+ ACTION_WATCHER_ENGAGED,
+ ACTION_PET_ENGAGED,
+ ACTION_PET_EVADE
};
-enum Events
+enum Yells
{
- EVENT_SUMMON = 1,
- EVENT_MIND_FLAY,
- EVENT_CURSE_FATIGUE
+ SAY_AGGRO = 0,
+ SAY_SLAY = 1,
+ SAY_DEATH = 2,
+ SAY_SWARM = 3,
+ SAY_PREFIGHT = 4,
+ SAY_SEND_GROUP = 5
};
class boss_krik_thir : public CreatureScript
@@ -87,37 +129,128 @@ class boss_krik_thir : public CreatureScript
struct boss_krik_thirAI : public BossAI
{
- boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR_THE_GATEWATCHER) { }
+ boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR_THE_GATEWATCHER), _hadGreet(false), _hadFrenzy(false), _petsInCombat(false), _watchersActive(0) { }
+
+ void SummonAdds()
+ {
+ if (instance->GetBossState(DATA_KRIKTHIR_THE_GATEWATCHER) == DONE)
+ return;
+
+ for (uint8 i = 1; i <= 3; ++i)
+ {
+ std::list<TempSummon*> summons;
+ me->SummonCreatureGroup(i, &summons);
+ for (TempSummon* summon : summons)
+ summon->AI()->SetData(DATA_PET_GROUP, i);
+ }
+ }
void Reset() override
{
- _Reset();
+ BossAI::Reset();
+ _hadFrenzy = false;
+ _petsInCombat = false;
+ _watchersActive = 0;
+ me->SetReactState(REACT_PASSIVE);
}
- void EnterCombat(Unit* /*who*/) override
+ void InitializeAI() override
{
- Talk(SAY_AGGRO);
- _EnterCombat();
- Summon();
+ BossAI::InitializeAI();
+ SummonAdds();
+ }
- events.ScheduleEvent(EVENT_SUMMON, 15000);
- events.ScheduleEvent(EVENT_MIND_FLAY, 15000);
- events.ScheduleEvent(EVENT_CURSE_FATIGUE, 12000);
+ void JustRespawned() override
+ {
+ BossAI::JustRespawned();
+ SummonAdds();
}
- void Summon()
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ summons.clear();
+ BossAI::JustDied(killer);
+ Talk(SAY_DEATH);
+ }
+
+ void EnterCombat(Unit* who) override
{
- for (uint8 i = 0; i < 8; i++)
+ _petsInCombat = false;
+ me->SetReactState(REACT_AGGRESSIVE);
+ summons.DoZoneInCombat();
+
+ events.CancelEvent(EVENT_SEND_GROUP);
+ events.ScheduleEvent(EVENT_SWARM, Seconds(5));
+ events.ScheduleEvent(EVENT_MIND_FLAY, randtime(Seconds(1), Seconds(3)));
+
+ BossAI::EnterCombat(who);
+ }
+
+ void MoveInLineOfSight(Unit* who) override
+ {
+ if (!me->HasReactState(REACT_PASSIVE))
{
- me->SummonCreature(NPC_SKITTERING_SWARMER, SpawnPoint[i], TEMPSUMMON_TIMED_DESPAWN, 25000);
- uint32 summon_entry = (i == 4 || i == 5 || i == 6) ? NPC_SKITTERING_INFECTIOR : NPC_SKITTERING_SWARMER;
- me->SummonCreature(summon_entry, SpawnPoint[i], TEMPSUMMON_TIMED_DESPAWN, 25000);
+ ScriptedAI::MoveInLineOfSight(who);
+ return;
+ }
+
+ if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance))
+ EnterCombat(who);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ summons.DespawnAll();
+ _DespawnAtEvade();
+ }
+
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case -ACTION_GATEWATCHER_GREET:
+ if (!_hadGreet && me->IsAlive() && !me->IsInCombat() && !_petsInCombat)
+ {
+ _hadGreet = true;
+ Talk(SAY_PREFIGHT);
+ }
+ break;
+ case ACTION_GASHRA_DIED:
+ case ACTION_NARJIL_DIED:
+ case ACTION_SILTHIK_DIED:
+ if (!_watchersActive) // something is wrong
+ {
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ return;
+ }
+ if (!--_watchersActive) // if there are no watchers currently in combat...
+ events.RescheduleEvent(EVENT_SEND_GROUP, Seconds(5)); // ...send the next watcher after the targets sooner
+ break;
+ case ACTION_WATCHER_ENGAGED:
+ ++_watchersActive;
+ break;
+ case ACTION_PET_ENGAGED:
+ if (_petsInCombat || me->IsInCombat())
+ break;
+ _petsInCombat = true;
+ Talk(SAY_AGGRO);
+ events.ScheduleEvent(EVENT_SEND_GROUP, Seconds(70));
+ break;
+ case ACTION_PET_EVADE:
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ break;
}
}
void UpdateAI(uint32 diff) override
{
- if (!UpdateVictim())
+ if (!UpdateVictim() && !_petsInCombat)
return;
events.Update(diff);
@@ -125,52 +258,62 @@ class boss_krik_thir : public CreatureScript
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
+ if (me->HealthBelowPct(10) && !_hadFrenzy)
+ {
+ _hadFrenzy = true;
+ events.ScheduleEvent(EVENT_FRENZY, Seconds(1));
+ }
+
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
- case EVENT_SUMMON:
- Summon();
- events.ScheduleEvent(EVENT_SUMMON, 15000);
+ case EVENT_SEND_GROUP:
+ DoCastAOE(SPELL_SUBBOSS_AGGRO_TRIGGER, true);
+ events.Repeat(Seconds(70));
break;
+
+ case EVENT_SWARM:
+ DoCastAOE(SPELL_SWARM);
+ Talk(SAY_SWARM);
+ break;
+
case EVENT_MIND_FLAY:
DoCastVictim(SPELL_MIND_FLAY);
- events.ScheduleEvent(EVENT_MIND_FLAY, 15000);
+ events.Repeat(randtime(Seconds(9), Seconds(11)));
break;
- case EVENT_CURSE_FATIGUE:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_CURSE_OF_FATIGUE);
- events.ScheduleEvent(EVENT_CURSE_FATIGUE, 10000);
- break;
- default:
+
+ case EVENT_FRENZY:
+ DoCastSelf(SPELL_FRENZY);
+ DoCastAOE(SPELL_CURSE_OF_FATIGUE);
+ events.Repeat(Seconds(15));
break;
}
- }
- if (!me->HasAura(SPELL_FRENZY) && HealthBelowPct(10))
- DoCast(me, SPELL_FRENZY, true);
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
DoMeleeAttackIfReady();
}
- void JustDied(Unit* /*killer*/) override
+ void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override
{
- Talk(SAY_DEATH);
- _JustDied();
+ if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER)
+ DoZoneInCombat();
}
- void KilledUnit(Unit* victim) override
+ void SpellHitTarget(Unit* /*who*/, SpellInfo const* spell) override
{
- if (victim->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Talk(SAY_SLAY);
+ if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER)
+ Talk(SAY_SEND_GROUP);
}
- void JustSummoned(Creature* summoned) override
- {
- summoned->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
- }
+ private:
+ bool _hadGreet;
+ bool _hadFrenzy;
+ bool _petsInCombat;
+ uint8 _watchersActive;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -179,74 +322,123 @@ class boss_krik_thir : public CreatureScript
}
};
-class npc_skittering_infector : public CreatureScript
+struct npc_gatewatcher_petAI : public ScriptedAI
{
- public:
- npc_skittering_infector() : CreatureScript("npc_skittering_infector") { }
+ npc_gatewatcher_petAI(Creature* creature, bool isWatcher) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _petGroup(0), _isWatcher(isWatcher) { }
- struct npc_skittering_infectorAI : public ScriptedAI
+ virtual void _EnterCombat() = 0;
+ void EnterCombat(Unit* who) override
+ {
+ if (_isWatcher)
{
- npc_skittering_infectorAI(Creature* creature) : ScriptedAI(creature) { }
-
- void JustDied(Unit* /*killer*/) override
- {
- DoCastAOE(SPELL_ACID_SPLASH);
- }
- };
+ _isWatcher = false;
+ if (TempSummon* meSummon = me->ToTempSummon())
+ if (Creature* summoner = meSummon->GetSummonerCreatureBase())
+ summoner->AI()->DoAction(ACTION_WATCHER_ENGAGED);
+ }
- CreatureAI* GetAI(Creature* creature) const override
+ if (me->HasReactState(REACT_PASSIVE))
{
- return GetAzjolNerubAI<npc_skittering_infectorAI>(creature);
- }
-};
+ std::list<Creature*> others;
+ me->GetCreatureListWithEntryInGrid(others, 0, 40.0f);
+ for (Creature* other : others)
+ if (other->AI()->GetData(DATA_PET_GROUP) == _petGroup)
+ {
+ other->SetReactState(REACT_AGGRESSIVE);
+ other->AI()->AttackStart(who);
+ }
-enum TrashEvents
-{
- // Anubar Skrimisher
- EVENT_ANUBAR_CHARGE = 1,
- EVENT_BACKSTAB,
+ if (TempSummon* meSummon = me->ToTempSummon())
+ if (Creature* summoner = meSummon->GetSummonerCreatureBase())
+ summoner->AI()->DoAction(ACTION_PET_ENGAGED);
+ }
+ _EnterCombat();
+ ScriptedAI::EnterCombat(who);
+ }
- // Anubar Shadowcaster
- EVENT_SHADOW_BOLT,
- EVENT_SHADOW_NOVA,
+ void SetData(uint32 data, uint32 value) override
+ {
+ if (data == DATA_PET_GROUP)
+ {
+ _petGroup = value;
+ me->SetReactState(_petGroup ? REACT_PASSIVE : REACT_AGGRESSIVE);
+ }
+ }
+
+ uint32 GetData(uint32 data) const override
+ {
+ if (data == DATA_PET_GROUP)
+ return _petGroup;
+ return 0;
+ }
+
+ void MoveInLineOfSight(Unit* who) override
+ {
+ if (!me->HasReactState(REACT_PASSIVE))
+ {
+ ScriptedAI::MoveInLineOfSight(who);
+ return;
+ }
- // Anubar Warrior
- EVENT_STRIKE,
- EVENT_CLEAVE,
+ if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance))
+ EnterCombat(who);
+ }
- // Watcher Gashra
- EVENT_WEB_WRAP_GASHRA,
- EVENT_INFECTED_BITE_GASHRA,
+ void SpellHit(Unit* /*whose*/, SpellInfo const* spell) override
+ {
+ if (spell->Id == SPELL_SUBBOSS_AGGRO_TRIGGER)
+ DoZoneInCombat();
+ }
- // Watcher Narjil
- EVENT_WEB_WRAP_NARJIL,
- EVENT_INFECTED_BITE_NARJIL,
- EVENT_BINDING_WEBS,
+ void EnterEvadeMode(EvadeReason why) override
+ {
+ if (TempSummon* meSummon = me->ToTempSummon())
+ {
+ if (Creature* summoner = meSummon->GetSummonerCreatureBase())
+ summoner->AI()->DoAction(ACTION_PET_EVADE);
+ else
+ me->DespawnOrUnsummon();
+ return;
+ }
+ ScriptedAI::EnterEvadeMode(why);
+ }
- // Watcher Silthik
- EVENT_WEB_WRAP_SILTHIK,
- EVENT_INFECTED_BITE_SILTHIK,
- EVENT_POISON_SPRAY
+ EventMap _events;
+ InstanceScript* _instance;
+ uint32 _petGroup;
+ bool _isWatcher;
};
-class npc_anub_ar_skirmisher : public CreatureScript
+class npc_watcher_gashra : public CreatureScript
{
public:
- npc_anub_ar_skirmisher() : CreatureScript("npc_anub_ar_skirmisher") { }
+ npc_watcher_gashra() : CreatureScript("npc_watcher_gashra") { }
- struct npc_anub_ar_skirmisherAI : public ScriptedAI
+ struct npc_watcher_gashraAI : public npc_gatewatcher_petAI
{
- npc_anub_ar_skirmisherAI(Creature* creature) : ScriptedAI(creature) { }
+ npc_watcher_gashraAI(Creature* creature) : npc_gatewatcher_petAI(creature, true)
+ {
+ _instance = creature->GetInstanceScript();
+ me->SetReactState(REACT_PASSIVE);
+ }
void Reset() override
{
- events.Reset();
+ _events.Reset();
}
- void EnterCombat(Unit* /*who*/) override
+ void _EnterCombat() override
{
- events.ScheduleEvent(EVENT_ANUBAR_CHARGE, 11000);
- events.ScheduleEvent(EVENT_BACKSTAB, 7000);
+ _events.ScheduleEvent(EVENT_ENRAGE, randtime(Seconds(3), Seconds(5)));
+ _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(16), Seconds(19)));
+ _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(7),Seconds(11)));
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER);
+ if (krikthir && krikthir->IsAlive())
+ krikthir->AI()->DoAction(ACTION_GASHRA_DIED);
}
void UpdateAI(uint32 diff) override
@@ -254,64 +446,79 @@ class npc_anub_ar_skirmisher : public CreatureScript
if (!UpdateVictim())
return;
- events.Update(diff);
+ _events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
- case EVENT_ANUBAR_CHARGE:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- {
- DoResetThreat();
- me->AddThreat(target, 1.0f);
- DoCast(target, SPELL_CHARGE, true);
- }
- events.ScheduleEvent(EVENT_ANUBAR_CHARGE, 15000);
+ case EVENT_ENRAGE:
+ DoCastSelf(SPELL_ENRAGE);
+ _events.Repeat(randtime(Seconds(12), Seconds(20)));
break;
- case EVENT_BACKSTAB:
- DoCastVictim(SPELL_BACKSTAB);
- events.ScheduleEvent(EVENT_BACKSTAB, 12000);
+ case EVENT_WEB_WRAP:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f))
+ DoCast(target, SPELL_WEB_WRAP);
+ _events.Repeat(randtime(Seconds(13), Seconds(19)));
+ break;
+ case EVENT_INFECTED_BITE:
+ DoCastVictim(SPELL_INFECTED_BITE);
+ _events.Repeat(randtime(Seconds(23), Seconds(27)));
break;
default:
break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
DoMeleeAttackIfReady();
}
private:
- EventMap events;
+ EventMap _events;
+ InstanceScript* _instance;
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetAzjolNerubAI<npc_anub_ar_skirmisherAI>(creature);
+ return GetAzjolNerubAI<npc_watcher_gashraAI>(creature);
}
};
-class npc_anub_ar_shadowcaster : public CreatureScript
+class npc_watcher_narjil : public CreatureScript
{
public:
- npc_anub_ar_shadowcaster() : CreatureScript("npc_anub_ar_shadowcaster") { }
+ npc_watcher_narjil() : CreatureScript("npc_watcher_narjil") { }
- struct npc_anub_ar_shadowcasterAI : public ScriptedAI
+ struct npc_watcher_narjilAI : public npc_gatewatcher_petAI
{
- npc_anub_ar_shadowcasterAI(Creature* creature) : ScriptedAI(creature) { }
+ npc_watcher_narjilAI(Creature* creature) : npc_gatewatcher_petAI(creature, true)
+ {
+ _instance = creature->GetInstanceScript();
+ }
void Reset() override
{
- events.Reset();
+ _events.Reset();
}
- void EnterCombat(Unit* /*who*/) override
+ void _EnterCombat() override
{
- events.ScheduleEvent(EVENT_SHADOW_BOLT, 6000);
- events.ScheduleEvent(EVENT_SHADOW_NOVA, 15000);
+ _events.ScheduleEvent(EVENT_BLINDING_WEBS, randtime(Seconds(13), Seconds(18)));
+ _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(3), Seconds(5)));
+ _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(7), Seconds(11)));
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER);
+ if (krikthir && krikthir->IsAlive())
+ krikthir->AI()->DoAction(ACTION_NARJIL_DIED);
}
void UpdateAI(uint32 diff) override
@@ -319,60 +526,79 @@ class npc_anub_ar_shadowcaster : public CreatureScript
if (!UpdateVictim())
return;
- events.Update(diff);
+ _events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
- case EVENT_SHADOW_BOLT:
+ case EVENT_BLINDING_WEBS:
+ DoCastVictim(SPELL_BLINDING_WEBS);
+ _events.Repeat(randtime(Seconds(23), Seconds(27)));
+ break;
+ case EVENT_WEB_WRAP:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_SHADOW_BOLT, true);
- events.ScheduleEvent(EVENT_SHADOW_BOLT, 15000);
+ DoCast(target, SPELL_WEB_WRAP);
+ _events.Repeat(randtime(Seconds(13), Seconds(19)));
break;
- case EVENT_SHADOW_NOVA:
- DoCastVictim(SPELL_SHADOW_NOVA, true);
- events.ScheduleEvent(EVENT_SHADOW_NOVA, 17000);
+ case EVENT_INFECTED_BITE:
+ DoCastVictim(SPELL_INFECTED_BITE);
+ _events.Repeat(randtime(Seconds(20), Seconds(25)));
break;
default:
break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
DoMeleeAttackIfReady();
}
private:
- EventMap events;
+ EventMap _events;
+ InstanceScript* _instance;
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetAzjolNerubAI<npc_anub_ar_shadowcasterAI>(creature);
+ return GetAzjolNerubAI<npc_watcher_narjilAI>(creature);
}
};
-class npc_anub_ar_warrior : public CreatureScript
+class npc_watcher_silthik : public CreatureScript
{
public:
- npc_anub_ar_warrior() : CreatureScript("npc_anub_ar_warrior") { }
+ npc_watcher_silthik() : CreatureScript("npc_watcher_silthik") { }
- struct npc_anub_ar_warriorAI : public ScriptedAI
+ struct npc_watcher_silthikAI : public npc_gatewatcher_petAI
{
- npc_anub_ar_warriorAI(Creature* creature) : ScriptedAI(creature) { }
+ npc_watcher_silthikAI(Creature* creature) : npc_gatewatcher_petAI(creature, true)
+ {
+ _instance = creature->GetInstanceScript();
+ }
void Reset() override
{
- events.Reset();
+ _events.Reset();
}
- void EnterCombat(Unit* /*who*/) override
+ void _EnterCombat() override
{
- events.ScheduleEvent(EVENT_CLEAVE, 11000);
- events.ScheduleEvent(EVENT_STRIKE, 6000);
+ _events.ScheduleEvent(EVENT_POISON_SPRAY, randtime(Seconds(16), Seconds(19)));
+ _events.ScheduleEvent(EVENT_WEB_WRAP, randtime(Seconds(7), Seconds(11)));
+ _events.ScheduleEvent(EVENT_INFECTED_BITE, randtime(Seconds(3), Seconds(5)));
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER);
+ if (krikthir && krikthir->IsAlive())
+ krikthir->AI()->DoAction(ACTION_SILTHIK_DIED);
}
void UpdateAI(uint32 diff) override
@@ -380,70 +606,68 @@ class npc_anub_ar_warrior : public CreatureScript
if (!UpdateVictim())
return;
- events.Update(diff);
+ _events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
- case EVENT_CLEAVE:
- DoCastVictim(SPELL_STRIKE, true);
- events.ScheduleEvent(EVENT_CLEAVE, 15000);
+ case EVENT_POISON_SPRAY:
+ DoCastVictim(SPELL_POISON_SPRAY);
+ _events.Repeat(randtime(Seconds(13), Seconds(19)));
break;
- case EVENT_STRIKE:
- DoCastVictim(SPELL_CLEAVE, true);
- events.ScheduleEvent(EVENT_STRIKE, 17000);
+ case EVENT_WEB_WRAP:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
+ DoCast(target, SPELL_WEB_WRAP);
+ _events.Repeat(randtime(Seconds(13), Seconds(17)));
+ break;
+ case EVENT_INFECTED_BITE:
+ DoCastVictim(SPELL_INFECTED_BITE);
+ _events.Repeat(randtime(Seconds(20), Seconds(24)));
break;
default:
break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
DoMeleeAttackIfReady();
}
private:
- EventMap events;
+ EventMap _events;
+ InstanceScript* _instance;
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetAzjolNerubAI<npc_anub_ar_warriorAI>(creature);
+ return GetAzjolNerubAI<npc_watcher_silthikAI>(creature);
}
};
-class npc_watcher_gashra : public CreatureScript
+class npc_anub_ar_warrior : public CreatureScript
{
public:
- npc_watcher_gashra() : CreatureScript("npc_watcher_gashra") { }
+ npc_anub_ar_warrior() : CreatureScript("npc_anub_ar_warrior") { }
- struct npc_watcher_gashraAI : public ScriptedAI
+ struct npc_anub_ar_warriorAI : public npc_gatewatcher_petAI
{
- npc_watcher_gashraAI(Creature* creature) : ScriptedAI(creature)
- {
- _instance = creature->GetInstanceScript();
- }
+ npc_anub_ar_warriorAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { }
void Reset() override
{
_events.Reset();
}
- void EnterCombat(Unit* /*who*/) override
+ void _EnterCombat() override
{
- DoCast(me, SPELL_ENRAGE, true);
- _events.ScheduleEvent(EVENT_WEB_WRAP_GASHRA, 11000);
- _events.ScheduleEvent(EVENT_INFECTED_BITE_GASHRA, 4000);
- }
-
- void JustDied(Unit* /*killer*/) override
- {
- Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER);
- if (krikthir && krikthir->IsAlive())
- krikthir->AI()->Talk(SAY_PREFIGHT);
+ _events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(7), Seconds(9)));
+ _events.ScheduleEvent(EVENT_STRIKE, randtime(Seconds(5), Seconds(10)));
}
void UpdateAI(uint32 diff) override
@@ -460,63 +684,50 @@ class npc_watcher_gashra : public CreatureScript
{
switch (eventId)
{
- case EVENT_WEB_WRAP_GASHRA:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_WEB_WRAP, true);
- _events.ScheduleEvent(EVENT_WEB_WRAP_GASHRA, 17000);
+ case EVENT_CLEAVE:
+ DoCastVictim(SPELL_CLEAVE);
+ _events.Repeat(randtime(Seconds(10), Seconds(16)));
break;
- case EVENT_INFECTED_BITE_GASHRA:
- DoCastVictim(SPELL_INFECTED_BITE, true);
- _events.ScheduleEvent(EVENT_INFECTED_BITE_GASHRA, 15000);
+ case EVENT_STRIKE:
+ DoCastVictim(SPELL_STRIKE);
+ _events.Repeat(randtime(Seconds(15), Seconds(19)));
break;
default:
break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
DoMeleeAttackIfReady();
}
-
- private:
- EventMap _events;
- InstanceScript* _instance;
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetAzjolNerubAI<npc_watcher_gashraAI>(creature);
+ return GetAzjolNerubAI<npc_anub_ar_warriorAI>(creature);
}
};
-class npc_watcher_narjil : public CreatureScript
+class npc_anub_ar_skirmisher : public CreatureScript
{
public:
- npc_watcher_narjil() : CreatureScript("npc_watcher_narjil") { }
+ npc_anub_ar_skirmisher() : CreatureScript("npc_anub_ar_skirmisher") { }
- struct npc_watcher_narjilAI : public ScriptedAI
+ struct npc_anub_ar_skirmisherAI : public npc_gatewatcher_petAI
{
- npc_watcher_narjilAI(Creature* creature) : ScriptedAI(creature)
- {
- _instance = creature->GetInstanceScript();
- }
+ npc_anub_ar_skirmisherAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { }
void Reset() override
{
_events.Reset();
}
- void EnterCombat(Unit* /*who*/) override
+ void _EnterCombat() override
{
- _events.ScheduleEvent(EVENT_WEB_WRAP_NARJIL, 11000);
- _events.ScheduleEvent(EVENT_INFECTED_BITE_NARJIL, 4000);
- _events.ScheduleEvent(EVENT_BINDING_WEBS, 17000);
- }
-
- void JustDied(Unit* /*killer*/) override
- {
- Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER);
- if (krikthir && krikthir->IsAlive())
- krikthir->AI()->Talk(SAY_PREFIGHT);
+ _events.ScheduleEvent(EVENT_ANUBAR_CHARGE, randtime(Seconds(6), Seconds(8)));
+ _events.ScheduleEvent(EVENT_BACKSTAB, randtime(Seconds(7), Seconds(9)));
}
void UpdateAI(uint32 diff) override
@@ -533,67 +744,58 @@ class npc_watcher_narjil : public CreatureScript
{
switch (eventId)
{
- case EVENT_WEB_WRAP_NARJIL:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_WEB_WRAP, true);
- _events.ScheduleEvent(EVENT_WEB_WRAP_NARJIL, 15000);
- break;
- case EVENT_INFECTED_BITE_NARJIL:
- DoCastVictim(SPELL_INFECTED_BITE, true);
- _events.ScheduleEvent(EVENT_INFECTED_BITE_NARJIL, 11000);
+ case EVENT_ANUBAR_CHARGE:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
+ DoCast(target, SPELL_CHARGE);
+ _events.Repeat(randtime(Seconds(20), Seconds(25)));
break;
- case EVENT_BINDING_WEBS:
- DoCastVictim(SPELL_BLINDING_WEBS, true);
- _events.ScheduleEvent(EVENT_BINDING_WEBS, 17000);
+ case EVENT_BACKSTAB:
+ if (me->GetVictim() && me->GetVictim()->isInBack(me))
+ DoCastVictim(SPELL_BACKSTAB);
+ _events.Repeat(randtime(Seconds(10), Seconds(13)));
break;
default:
break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
DoMeleeAttackIfReady();
}
- private:
- EventMap _events;
- InstanceScript* _instance;
+ void SpellHitTarget(Unit* target, SpellInfo const* spell) override
+ {
+ if (spell->Id == SPELL_CHARGE && target)
+ DoCast(target, SPELL_FIXTATE_TRIGGER);
+ }
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetAzjolNerubAI<npc_watcher_narjilAI>(creature);
+ return GetAzjolNerubAI<npc_anub_ar_skirmisherAI>(creature);
}
};
-class npc_watcher_silthik : public CreatureScript
+class npc_anub_ar_shadowcaster : public CreatureScript
{
public:
- npc_watcher_silthik() : CreatureScript("npc_watcher_silthik") { }
+ npc_anub_ar_shadowcaster() : CreatureScript("npc_anub_ar_shadowcaster") { }
- struct npc_watcher_silthikAI : public ScriptedAI
+ struct npc_anub_ar_shadowcasterAI : public npc_gatewatcher_petAI
{
- npc_watcher_silthikAI(Creature* creature) : ScriptedAI(creature)
- {
- _instance = creature->GetInstanceScript();
- }
+ npc_anub_ar_shadowcasterAI(Creature* creature) : npc_gatewatcher_petAI(creature, false) { }
void Reset() override
{
_events.Reset();
}
- void EnterCombat(Unit* /*who*/) override
+ void _EnterCombat() override
{
- _events.ScheduleEvent(EVENT_WEB_WRAP_SILTHIK, 11000);
- _events.ScheduleEvent(EVENT_INFECTED_BITE_SILTHIK, 4000);
- _events.ScheduleEvent(EVENT_POISON_SPRAY, 15000);
- }
-
- void JustDied(Unit* /*killer*/) override
- {
- Creature* krikthir = _instance->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER);
- if (krikthir && krikthir->IsAlive())
- krikthir->AI()->Talk(SAY_PREFIGHT);
+ _events.ScheduleEvent(EVENT_SHADOW_BOLT, Seconds(4));
+ _events.ScheduleEvent(EVENT_SHADOW_NOVA, randtime(Seconds(10), Seconds(14)));
}
void UpdateAI(uint32 diff) override
@@ -610,35 +812,230 @@ class npc_watcher_silthik : public CreatureScript
{
switch (eventId)
{
- case EVENT_WEB_WRAP_SILTHIK:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
- DoCast(target, SPELL_WEB_WRAP, true);
- _events.ScheduleEvent(EVENT_WEB_WRAP_SILTHIK, 15000);
- break;
- case EVENT_INFECTED_BITE_SILTHIK:
- DoCastVictim(SPELL_INFECTED_BITE, true);
- _events.ScheduleEvent(EVENT_INFECTED_BITE_SILTHIK, 11000);
+ case EVENT_SHADOW_BOLT:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
+ DoCast(target, SPELL_SHADOW_BOLT);
+ _events.Repeat(randtime(Seconds(2), Seconds(4)));
break;
- case EVENT_POISON_SPRAY:
- DoCastVictim(SPELL_POSION_SPRAY, true);
- _events.ScheduleEvent(EVENT_POISON_SPRAY, 17000);
+ case EVENT_SHADOW_NOVA:
+ DoCastVictim(SPELL_SHADOW_NOVA);
+ _events.Repeat(randtime(Seconds(10), Seconds(16)));
break;
default:
break;
}
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
DoMeleeAttackIfReady();
}
+ };
- private:
- EventMap _events;
- InstanceScript* _instance;
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_anub_ar_shadowcasterAI>(creature);
+ }
+};
+
+class npc_skittering_swarmer : public CreatureScript
+{
+ public:
+ npc_skittering_swarmer() : CreatureScript("npc_skittering_swarmer") { }
+
+ struct npc_skittering_swarmerAI : public ScriptedAI
+ {
+ npc_skittering_swarmerAI(Creature* creature) : ScriptedAI(creature) { }
+
+ void InitializeAI() override
+ {
+ ScriptedAI::InitializeAI();
+ if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER))
+ {
+ if (Unit* target = gatewatcher->getAttackerForHelper())
+ AttackStart(target);
+ gatewatcher->AI()->JustSummoned(me);
+ }
+ }
};
CreatureAI* GetAI(Creature* creature) const override
{
- return GetAzjolNerubAI<npc_watcher_silthikAI>(creature);
+ return GetAzjolNerubAI<npc_skittering_swarmerAI>(creature);
+ }
+};
+
+class npc_skittering_infector : public CreatureScript
+{
+ public:
+ npc_skittering_infector() : CreatureScript("npc_skittering_infector") { }
+
+ struct npc_skittering_infectorAI : public ScriptedAI
+ {
+ npc_skittering_infectorAI(Creature* creature) : ScriptedAI(creature) { }
+
+ void InitializeAI() override
+ {
+ ScriptedAI::InitializeAI();
+ if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER))
+ {
+ if (Unit* target = gatewatcher->getAttackerForHelper())
+ AttackStart(target);
+ gatewatcher->AI()->JustSummoned(me);
+ }
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ DoCastAOE(SPELL_ACID_SPLASH);
+ ScriptedAI::JustDied(killer);
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_skittering_infectorAI>(creature);
+ }
+};
+
+class npc_gatewatcher_web_wrap : public CreatureScript
+{
+ public:
+ npc_gatewatcher_web_wrap() : CreatureScript("npc_gatewatcher_web_wrap") { }
+
+ struct npc_gatewatcher_web_wrapAI : public NullCreatureAI
+ {
+ npc_gatewatcher_web_wrapAI(Creature* creature) : NullCreatureAI(creature) { }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ if (TempSummon* meSummon = me->ToTempSummon())
+ if (Unit* summoner = meSummon->GetSummoner())
+ summoner->RemoveAurasDueToSpell(SPELL_WEB_WRAP_WRAPPED);
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetAzjolNerubAI<npc_gatewatcher_web_wrapAI>(creature);
+ }
+};
+
+class spell_gatewatcher_subboss_trigger : public SpellScriptLoader
+{
+ public:
+ spell_gatewatcher_subboss_trigger() : SpellScriptLoader("spell_gatewatcher_subboss_trigger") { }
+
+ class spell_gatewatcher_subboss_trigger_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gatewatcher_subboss_trigger_SpellScript);
+
+ void HandleTargets(std::list<WorldObject*>& targetList)
+ {
+ // Remove any Watchers that are already in combat
+ for (std::list<WorldObject*>::iterator it = targetList.begin(); it != targetList.end(); ++it)
+ {
+ if (Creature* creature = (*it)->ToCreature())
+ if (creature->IsAlive() && !creature->IsInCombat())
+ continue;
+ it = targetList.erase(it);
+ }
+
+ // Default to Krik'thir himself if he isn't engaged
+ WorldObject* target = nullptr;
+ if (GetCaster() && !GetCaster()->IsInCombat())
+ target = GetCaster();
+ // Unless there are Watchers that aren't engaged yet
+ if (!targetList.empty())
+ {
+ // If there are, pick one of them at random
+ std::list<WorldObject*>::iterator it = targetList.begin();
+ std::advance(it, urand(0, targetList.size() - 1));
+ target = *it;
+ }
+ // And hit only that one
+ targetList.clear();
+ if (target)
+ targetList.push_back(target);
+ }
+
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gatewatcher_subboss_trigger_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_gatewatcher_subboss_trigger_SpellScript();
+ }
+};
+
+class spell_anub_ar_skirmisher_fixtate : public SpellScriptLoader
+{
+ public:
+ spell_anub_ar_skirmisher_fixtate() : SpellScriptLoader("spell_anub_ar_skirmisher_fixtate") { }
+
+ class spell_anub_ar_skirmisher_fixtate_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_anub_ar_skirmisher_fixtate_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return sSpellMgr->GetSpellInfo(SPELL_FIXTATE_TRIGGERED) != nullptr;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ target->CastSpell(GetCaster(), SPELL_FIXTATE_TRIGGERED, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_anub_ar_skirmisher_fixtate_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_anub_ar_skirmisher_fixtate_SpellScript();
+ }
+};
+
+class spell_gatewatcher_web_wrap : public SpellScriptLoader
+{
+ public:
+ spell_gatewatcher_web_wrap() : SpellScriptLoader("spell_gatewatcher_web_wrap") { }
+
+ class spell_gatewatcher_web_wrap_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_gatewatcher_web_wrap_AuraScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return sSpellMgr->GetSpellInfo(SPELL_WEB_WRAP_WRAPPED) != nullptr;
+ }
+
+ void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE)
+ return;
+
+ if (Unit* target = GetTarget())
+ target->CastSpell(target, SPELL_WEB_WRAP_WRAPPED, true);
+ }
+
+ void Register() override
+ {
+ OnEffectRemove += AuraEffectRemoveFn(spell_gatewatcher_web_wrap_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_ROOT, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_gatewatcher_web_wrap_AuraScript();
}
};
@@ -656,11 +1053,12 @@ class achievement_watch_him_die : public AchievementCriteriaScript
if (!instance)
return false;
- for (uint8 n = 0; n < 3; ++n)
+ for (ANDataTypes watcherData : {DATA_WATCHER_GASHRA, DATA_WATCHER_NARJIL, DATA_WATCHER_SILTHIK})
{
- if (Creature* watcher = instance->GetCreature(DATA_WATCHER_GASHRA + n))
- if (!watcher->IsAlive())
- return false;
+ if (Creature* watcher = instance->GetCreature(watcherData))
+ if (watcher->IsAlive())
+ continue;
+ return false;
}
return true;
@@ -670,12 +1068,22 @@ class achievement_watch_him_die : public AchievementCriteriaScript
void AddSC_boss_krik_thir()
{
new boss_krik_thir();
- new npc_skittering_infector();
- new npc_anub_ar_skirmisher();
- new npc_anub_ar_shadowcaster();
+
new npc_watcher_gashra();
- new npc_anub_ar_warrior();
- new npc_watcher_silthik();
new npc_watcher_narjil();
+ new npc_watcher_silthik();
+
+ new npc_anub_ar_warrior();
+ new npc_anub_ar_skirmisher();
+ new npc_anub_ar_shadowcaster();
+
+ new npc_skittering_swarmer();
+ new npc_skittering_infector();
+ new npc_gatewatcher_web_wrap();
+
+ new spell_gatewatcher_subboss_trigger();
+ new spell_anub_ar_skirmisher_fixtate();
+ new spell_gatewatcher_web_wrap();
+
new achievement_watch_him_die();
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp
index bba3a87efcf..7ebf309c74f 100644
--- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp
@@ -31,12 +31,27 @@ DoorData const doorData[] =
ObjectData const creatureData[] =
{
{ NPC_KRIKTHIR, DATA_KRIKTHIR_THE_GATEWATCHER },
+ { NPC_HADRONOX, DATA_HADRONOX },
+ { NPC_ANUBARAK, DATA_ANUBARAK },
{ NPC_WATCHER_NARJIL, DATA_WATCHER_GASHRA },
{ NPC_WATCHER_GASHRA, DATA_WATCHER_SILTHIK },
{ NPC_WATCHER_SILTHIK, DATA_WATCHER_NARJIL },
{ 0, 0 } // END
};
+ObjectData const gameobjectData[] =
+{
+ { GO_ANUBARAK_DOOR_3, DATA_ANUBARAK_WALL },
+ { 0, 0 } // END
+};
+
+BossBoundaryData const boundaries =
+{
+ { DATA_KRIKTHIR_THE_GATEWATCHER, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) },
+ { DATA_HADRONOX, new ZRangeBoundary(666.0f, 776.0f) },
+ { DATA_ANUBARAK, new CircleBoundary(Position(550.6178f, 253.5917f), 26.0f) }
+};
+
class instance_azjol_nerub : public InstanceMapScript
{
public:
@@ -48,8 +63,19 @@ class instance_azjol_nerub : public InstanceMapScript
{
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
+ LoadBossBoundaries(boundaries);
LoadDoorData(doorData);
- LoadObjectData(creatureData, nullptr);
+ LoadObjectData(creatureData, gameobjectData);
+ }
+
+ void OnUnitDeath(Unit* who) override
+ {
+ InstanceScript::OnUnitDeath(who);
+ Creature* creature = who->ToCreature();
+ if (!creature || creature->IsCritter() || creature->IsControlledByPlayer())
+ return;
+ if (Creature* gatewatcher = GetCreature(DATA_KRIKTHIR_THE_GATEWATCHER))
+ gatewatcher->AI()->DoAction(-ACTION_GATEWATCHER_GREET);
}
};