fix(Scripts/Azjol'Nerub): Hadronox (#23246)

Co-authored-by: Treeston <treeston.mmoc@gmail.com>
This commit is contained in:
sogladev
2025-12-29 05:45:43 +01:00
committed by GitHub
parent 2d09f2e29a
commit 380d8000e5
5 changed files with 627 additions and 252 deletions

View File

@@ -47,6 +47,7 @@ enum ANIds
NPC_HADRONOX = 28921,
NPC_ANUBARAK = 29120,
NPC_WORLD_TRIGGER_LAOI = 23472,
NPC_ANUB_AR_CHAMPION = 29062,
NPC_ANUB_AR_NECROMANCER = 29063,
NPC_ANUB_AR_CRYPTFIEND = 29064,

View File

@@ -75,7 +75,6 @@ enum Events
enum CreatureIds
{
NPC_WORLD_TRIGGER = 22515,
NPC_ANUBAR_GUARDIAN = 29216,
NPC_ANUBAR_VENOMANCER = 29217,
};

View File

@@ -26,19 +26,39 @@
enum Spells
{
// World Trigger
SPELL_SUMMON_ANUBAR_CHAMPION = 53064,
SPELL_SUMMON_ANUBAR_CRYPT_FIEND = 53065,
SPELL_SUMMON_ANUBAR_NECROMANCER = 53066,
SPELL_SUMMON_ANUBAR_CHAMPION_PERIODIC = 53035,
SPELL_SUMMON_ANUBAR_NECROMANCER_PERIODIC = 53036,
SPELL_SUMMON_ANUBAR_CRYPT_FIEND_PERIODIC = 53037,
// Hadronox
SPELL_WEB_FRONT_DOORS = 53177,
SPELL_WEB_SIDE_DOORS = 53185,
SPELL_ACID_CLOUD = 53400,
SPELL_LEECH_POISON = 53030,
SPELL_LEECH_POISON_HEAL = 53800,
SPELL_WEB_GRAB = 57731,
SPELL_WEB_GRAB = 53406,
SPELL_PIERCE_ARMOR = 53418,
// Anub'ar Crusher
SPELL_SMASH = 53318,
SPELL_FRENZY = 53801
SPELL_FRENZY = 53801,
// 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 Events
@@ -54,274 +74,533 @@ enum Events
EVENT_HADRONOX_SUMMON = 9,
EVENT_CRUSHER_SMASH = 20,
EVENT_CHECK_HEALTH = 21
EVENT_CHECK_HEALTH = 21,
EVENT_CHECK_EVADE = 22,
// 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 NPCs
{
NPC_ANUB_AR_CRUSHER = 28922,
NPC_ANUB_AR_CHAMPION_PACK = 29117,
NPC_ANUB_AR_CRYPT_FIEND_PACK = 29118,
NPC_ANUB_AR_NECROMANCER_PACK = 29119,
};
enum SummonGroups : uint32
{
SUMMON_GROUP_CRUSHER_NONE = 0,
SUMMON_GROUP_CRUSHER_1 = 1,
SUMMON_GROUP_CRUSHER_2 = 2,
SUMMON_GROUP_CRUSHER_3 = 3,
SUMMON_GROUP_WORLD_TRIGGERS = 4,
};
enum Data
{
DATA_CRUSHER_PACK_ID = 1,
};
enum Misc
{
NPC_ANUB_AR_CRUSHER = 28922,
SAY_CRUSHER_AGGRO = 0,
SAY_CRUSHER_EMOTE = 1,
SAY_CRUSHER_AGGRO = 1,
SAY_CRUSHER_EMOTE = 2,
SAY_HADRONOX_EMOTE = 0,
ACTION_DESPAWN_ADDS = 1,
ACTION_START_EVENT = 2
ACTION_CRUSHER_ENGAGED = 1,
ACTION_CRUSHER_DIED = 2,
ACTION_PACK_WALK = 3,
};
const Position hadronoxSteps[4] =
static const std::array<Position, 3> hadronoxSteps =
{{
{ 562.191f, 514.068f, 696.50710f },
{ 615.802f, 517.418f, 695.68066f },
{ 530.420f, 560.003f, 733.22473f },
}};
struct boss_hadronox : public BossAI
{
{607.9f, 512.8f, 695.3f, 0.0f},
{611.67f, 564.11f, 720.0f, 0.0f},
{576.1f, 580.0f, 727.5f, 0.0f},
{534.87f, 554.0f, 733.0f, 0.0f}
explicit boss_hadronox(Creature* creature) : BossAI(creature, DATA_HADRONOX), _crushersLeft(0), _doorsWebbed(false), _lastPlayerCombatState(false) { }
void Reset() override
{
BossAI::Reset();
SummonCrusherPack(SUMMON_GROUP_CRUSHER_1);
me->SummonCreatureGroup(SUMMON_GROUP_WORLD_TRIGGERS);
_doorsWebbed = false;
_lastPlayerCombatState = false;
}
void SummonedCreatureEvade(Creature* summon) override
{
switch (summon->GetEntry())
{
case NPC_ANUB_AR_CRUSHER:
case NPC_ANUB_AR_CHAMPION_PACK:
case NPC_ANUB_AR_CRYPT_FIEND_PACK:
case NPC_ANUB_AR_NECROMANCER_PACK:
EnterEvadeMode(EVADE_REASON_OTHER);
break;
default:
break;
}
}
void DoAction(int32 param) override
{
if (param == ACTION_CRUSHER_DIED)
--_crushersLeft;
else if (param == ACTION_CRUSHER_ENGAGED)
{
if (instance->GetBossState(DATA_HADRONOX) == IN_PROGRESS)
return;
instance->SetBossState(DATA_HADRONOX, IN_PROGRESS);
events.ScheduleEvent(EVENT_CHECK_EVADE, 5s);
SummonCrusherPack(SUMMON_GROUP_CRUSHER_2);
SummonCrusherPack(SUMMON_GROUP_CRUSHER_3);
events.ScheduleEvent(EVENT_HADRONOX_MOVE1, 0s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE2, 45s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE3, 90s);
}
}
void SummonCrusherPack(const SummonGroups group)
{
std::list<TempSummon*> summoned;
me->SummonCreatureGroup(group, &summoned);
_crushersLeft = summoned.size();
for (TempSummon* summon : summoned)
{
summon->AI()->SetData(DATA_CRUSHER_PACK_ID, group);
summon->AI()->DoAction(ACTION_PACK_WALK);
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
switch (summon->GetEntry())
{
case NPC_WORLD_TRIGGER_LAOI:
summon->AddAura(SPELL_SUMMON_ANUBAR_CHAMPION_PERIODIC, summon);
summon->AddAura(SPELL_SUMMON_ANUBAR_NECROMANCER_PERIODIC, summon);
summon->AddAura(SPELL_SUMMON_ANUBAR_CRYPT_FIEND_PERIODIC, summon);
break;
case NPC_ANUB_AR_CHAMPION:
case NPC_ANUB_AR_NECROMANCER:
case NPC_ANUB_AR_CRYPTFIEND:
// Xinef: cannot use pathfinding...
if (summon->GetDistance(477.0f, 618.0f, 771.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000012, false);
else if (summon->GetDistance(583.0f, 617.0f, 771.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000013, false);
else if (summon->GetDistance(581.0f, 608.5f, 739.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000014, false);
break;
default:
break;
}
}
void KilledUnit(Unit* victim) override
{
if (!me->IsAlive() || !victim->HasAura(SPELL_LEECH_POISON))
return;
me->ModifyHealth(static_cast<int32>(me->CountPctFromMaxHealth(10)));
}
void JustEngagedWith(Unit* /*who*/) override
{
me->setActive(true);
events.RescheduleEvent(EVENT_HADRONOX_ACID, 10s);
events.RescheduleEvent(EVENT_HADRONOX_LEECH, 4s);
events.RescheduleEvent(EVENT_HADRONOX_PIERCE, 1s);
events.RescheduleEvent(EVENT_HADRONOX_GRAB, 15s);
events.RescheduleEvent(EVENT_CHECK_EVADE, 1s);
}
bool IsInCombatWithPlayer() const
{
return std::ranges::any_of(me->GetThreatMgr().GetThreatList(), [](auto const& ref) {
return ref->getTarget()->IsControlledByPlayer();
});
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if ((!who || !who->IsControlledByPlayer()) && me->HealthBelowPct(70))
{
if (me->HealthBelowPctDamaged(5, damage))
damage = 0;
else
damage *= (me->GetHealthPct() - 5.0f) / 65.0f;
}
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (uint32 eventId = events.ExecuteEvent())
{
case EVENT_HADRONOX_PIERCE:
DoCastVictim(SPELL_PIERCE_ARMOR);
events.Repeat(8s);
break;
case EVENT_HADRONOX_ACID:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, false))
DoCast(target, SPELL_ACID_CLOUD);
events.Repeat(25s);
break;
case EVENT_HADRONOX_LEECH:
DoCastSelf(SPELL_LEECH_POISON);
events.Repeat(12s);
break;
case EVENT_HADRONOX_GRAB:
DoCastSelf(SPELL_WEB_GRAB);
events.Repeat(25s);
break;
case EVENT_HADRONOX_MOVE3:
if (_crushersLeft > 0)
{
events.Repeat(2s);
break;
}
[[fallthrough]];
case EVENT_HADRONOX_MOVE1:
case EVENT_HADRONOX_MOVE2:
Talk(SAY_HADRONOX_EMOTE);
me->SetReactState(REACT_PASSIVE);
me->GetMotionMaster()->Clear();
me->AttackStop();
me->GetMotionMaster()->MovePoint(eventId, hadronoxSteps[eventId - 1]);
break;
case EVENT_CHECK_EVADE:
if (IsInCombatWithPlayer() != _lastPlayerCombatState)
{
_lastPlayerCombatState = !_lastPlayerCombatState;
if (_lastPlayerCombatState)
{
me->SetReactState(REACT_AGGRESSIVE);
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
me->GetMotionMaster()->Clear();
}
else
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
}
events.Repeat(1s);
break;
default:
break;
}
DoMeleeAttackIfReady();
}
void MovementInform(uint32 movementType, uint32 pointId) override
{
if (movementType != POINT_MOTION_TYPE)
return;
me->SetReactState(REACT_AGGRESSIVE);
if (pointId == EVENT_HADRONOX_MOVE3)
{
DoCastSelf(SPELL_WEB_FRONT_DOORS, true);
_doorsWebbed = true;
}
}
uint32 GetData(uint32 data) const override
{
if (data == me->GetEntry()) // 'Hadronox Denied' achievement
return _doorsWebbed ? 0 : 1;
return 0;
}
private:
uint8 _crushersLeft;
bool _doorsWebbed;
bool _lastPlayerCombatState;
};
class boss_hadronox : public CreatureScript
struct npc_hadronox_crusherPackAI : public ScriptedAI
{
public:
boss_hadronox() : CreatureScript("boss_hadronox") { }
npc_hadronox_crusherPackAI(Creature* creature, Position const* positions) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _positions(positions), _myPack(SUMMON_GROUP_CRUSHER_NONE), _doFacing(false) { }
struct boss_hadronoxAI : public BossAI
virtual void DoEngagedWith() = 0;
virtual void DoEvent(uint32 /*eventId*/) = 0;
void DoAction(int32 action) override
{
boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX)
if (action == ACTION_PACK_WALK)
{
}
void Reset() override
{
summons.DoAction(ACTION_DESPAWN_ADDS);
BossAI::Reset();
me->SummonCreature(NPC_ANUB_AR_CRUSHER, 542.9f, 519.5f, 741.24f, 2.14f);
}
void DoAction(int32 param) override
{
if (param == ACTION_START_EVENT)
switch (_myPack)
{
instance->SetBossState(DATA_HADRONOX, IN_PROGRESS);
me->setActive(true);
events.ScheduleEvent(EVENT_HADRONOX_MOVE1, 20s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE2, 40s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE3, 60s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE4, 80s);
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;
}
}
}
uint32 GetData(uint32 data) const override
{
if (data == me->GetEntry())
return !me->isActiveObject() || events.HasTimeUntilEvent(EVENT_HADRONOX_MOVE4) ? 1 : 0;
return 0;
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
// Xinef: cannot use pathfinding...
if (summon->GetDistance(477.0f, 618.0f, 771.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000012, false);
else if (summon->GetDistance(583.0f, 617.0f, 771.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000013, false);
else if (summon->GetDistance(581.0f, 608.5f, 739.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000014, false);
}
void KilledUnit(Unit* victim) override
{
if (!me->IsAlive() || !victim->HasAura(SPELL_LEECH_POISON))
return;
me->ModifyHealth(int32(me->CountPctFromMaxHealth(10)));
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit*) override
{
events.RescheduleEvent(EVENT_HADRONOX_ACID, 10s);
events.RescheduleEvent(EVENT_HADRONOX_LEECH, 4s);
events.RescheduleEvent(EVENT_HADRONOX_PIERCE, 1s);
events.RescheduleEvent(EVENT_HADRONOX_GRAB, 15s);
}
bool AnyPlayerValid() const
{
Map::PlayerList const& playerList = me->GetMap()->GetPlayers();
for(Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
if (me->GetDistance(itr->GetSource()) < 130.0f && itr->GetSource()->IsAlive() && !itr->GetSource()->IsGameMaster() && me->CanCreatureAttack(itr->GetSource()))
return true;
return false;
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if ((!who || !who->IsControlledByPlayer()) && me->HealthBelowPct(70))
{
if (me->HealthBelowPctDamaged(5, damage))
{
damage = 0;
}
else
{
damage *= (me->GetHealthPct() - 5.0f) / 65.0f;
}
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (uint32 eventId = events.ExecuteEvent())
{
case EVENT_HADRONOX_PIERCE:
me->CastSpell(me->GetVictim(), SPELL_PIERCE_ARMOR, false);
events.ScheduleEvent(EVENT_HADRONOX_PIERCE, 8s);
break;
case EVENT_HADRONOX_ACID:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, false))
me->CastSpell(target, SPELL_ACID_CLOUD, false);
events.ScheduleEvent(EVENT_HADRONOX_ACID, 25s);
break;
case EVENT_HADRONOX_LEECH:
me->CastSpell(me, SPELL_LEECH_POISON, false);
events.ScheduleEvent(EVENT_HADRONOX_LEECH, 12s);
break;
case EVENT_HADRONOX_GRAB:
me->CastSpell(me, SPELL_WEB_GRAB, false);
events.ScheduleEvent(EVENT_HADRONOX_GRAB, 25s);
break;
case EVENT_HADRONOX_MOVE4:
me->CastSpell(me, SPELL_WEB_FRONT_DOORS, true);
[[fallthrough]]; /// @todo: Not sure whether the fallthrough was a mistake (forgetting a break) or intended. This should be double-checked.
case EVENT_HADRONOX_MOVE1:
case EVENT_HADRONOX_MOVE2:
case EVENT_HADRONOX_MOVE3:
Talk(SAY_HADRONOX_EMOTE);
me->GetMotionMaster()->MoveCharge(hadronoxSteps[eventId - 1].GetPositionX(), hadronoxSteps[eventId - 1].GetPositionY(), hadronoxSteps[eventId - 1].GetPositionZ(), 10.0f, 0, nullptr, true);
break;
}
DoMeleeAttackIfReady();
}
bool CheckEvadeIfOutOfCombatArea() const override
{
return me->isActiveObject() && !AnyPlayerValid();
}
};
CreatureAI* GetAI(Creature* creature) const override
void MovementInform(uint32 type, uint32 id) override
{
return GetAzjolNerubAI<boss_hadronoxAI>(creature);
if (type == POINT_MOTION_TYPE && id == ACTION_PACK_WALK)
_doFacing = true;
}
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)
{
_myPack = SummonGroups(value);
me->SetReactState(_myPack ? REACT_PASSIVE : REACT_AGGRESSIVE);
}
}
void JustEngagedWith(Unit* who) override
{
if (me->HasReactState(REACT_PASSIVE))
{
std::list<Creature*> creatures;
me->GetCreatureListWithEntryInGrid(creatures, { NPC_ANUB_AR_CRUSHER, NPC_ANUB_AR_CHAMPION_PACK, NPC_ANUB_AR_NECROMANCER_PACK, NPC_ANUB_AR_CRYPT_FIEND_PACK }, 40.0f);
for (Creature* creature : creatures)
if (creature->AI()->GetData(DATA_CRUSHER_PACK_ID) == _myPack)
{
creature->SetReactState(REACT_AGGRESSIVE);
creature->AI()->AttackStart(who);
}
}
DoEngagedWith();
ScriptedAI::JustEngagedWith(who);
}
void MoveInLineOfSight(Unit* who) override
{
if (!me->HasReactState(REACT_PASSIVE))
{
ScriptedAI::MoveInLineOfSight(who);
return;
}
if (me->CanStartAttack(who) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance))
JustEngagedWith(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;
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 }
};
struct npc_anub_ar_crusher : public npc_hadronox_crusherPackAI
{
explicit npc_anub_ar_crusher(Creature* creature) : npc_hadronox_crusherPackAI(creature, crusherWaypoints), _hadFrenzy(false) { }
void DoEngagedWith() override
{
events.ScheduleEvent(EVENT_CRUSHER_SMASH, 8s, 12s);
if (_myPack != SUMMON_GROUP_CRUSHER_1)
return;
if (_instance->GetBossState(DATA_HADRONOX) == IN_PROGRESS)
return;
if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX))
hadronox->AI()->DoAction(ACTION_CRUSHER_ENGAGED);
Talk(SAY_CRUSHER_AGGRO);
}
void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if (_hadFrenzy || !me->HealthBelowPctDamaged(25, damage))
return;
_hadFrenzy = true;
Talk(SAY_CRUSHER_EMOTE);
DoCastSelf(SPELL_FRENZY);
}
void DoEvent(uint32 eventId) override
{
if (eventId == EVENT_CRUSHER_SMASH)
{
DoCastVictim(SPELL_SMASH);
events.Repeat(13s, 21s);
}
}
void JustDied(Unit* killer) override
{
if (Creature* hadronox = _instance->GetCreature(DATA_HADRONOX))
hadronox->AI()->DoAction(ACTION_CRUSHER_DIED);
ScriptedAI::JustDied(killer);
}
private:
bool _hadFrenzy;
};
static const Position championWaypoints[] =
{
{ 539.2076f, 549.7539f, 732.8668f, 4.55531f },
{ 527.3098f, 559.5197f, 732.9407f, 4.742493f },
{ }
};
struct npc_anub_ar_crusher_champion : public npc_hadronox_crusherPackAI
{
explicit npc_anub_ar_crusher_champion(Creature* creature) : npc_hadronox_crusherPackAI(creature, championWaypoints) { }
void DoEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_REND:
DoCastVictim(SPELL_REND);
events.Repeat(12s, 16s);
break;
case EVENT_PUMMEL:
DoCastVictim(SPELL_PUMMEL);
events.Repeat(12s, 17s);
break;
default:
break;
}
}
void DoEngagedWith() override
{
events.ScheduleEvent(EVENT_REND, 4s, 8s);
events.ScheduleEvent(EVENT_PUMMEL, 15s, 19s);
}
};
class npc_anub_ar_crusher : public CreatureScript
static const Position cryptFiendWaypoints[] =
{
public:
npc_anub_ar_crusher() : CreatureScript("npc_anub_ar_crusher") { }
{ 520.3911f, 548.7895f, 732.0118f, 5.0091f },
{ },
{ 550.9611f, 545.1674f, 731.9031f, 3.996804f }
};
struct npc_anub_ar_crusher_crypt_fiend : public npc_hadronox_crusherPackAI
{
explicit npc_anub_ar_crusher_crypt_fiend(Creature* creature) : npc_hadronox_crusherPackAI(creature, cryptFiendWaypoints) { }
struct npc_anub_ar_crusherAI : public ScriptedAI
void DoEvent(uint32 eventId) override
{
npc_anub_ar_crusherAI(Creature* c) : ScriptedAI(c), summons(me) {}
EventMap events;
SummonList summons;
void Reset() override
switch (eventId)
{
summons.DespawnAll();
events.Reset();
if (me->ToTempSummon())
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
if (summoner->GetEntry() == me->GetEntry())
{
me->CastSpell(me, RAND(SPELL_SUMMON_ANUBAR_CHAMPION, SPELL_SUMMON_ANUBAR_CRYPT_FIEND, SPELL_SUMMON_ANUBAR_NECROMANCER), true);
me->CastSpell(me, RAND(SPELL_SUMMON_ANUBAR_CHAMPION, SPELL_SUMMON_ANUBAR_CRYPT_FIEND, SPELL_SUMMON_ANUBAR_NECROMANCER), true);
}
case EVENT_CRUSHING_WEBS:
DoCastVictim(SPELL_CRUSHING_WEBS);
events.Repeat(12s, 16s);
break;
case EVENT_INFECTED_WOUND:
DoCastVictim(SPELL_INFECTED_WOUND);
events.Repeat(16s, 25s);
break;
default:
break;
}
}
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() != me->GetEntry())
{
summon->GetMotionMaster()->MovePoint(0, *me, FORCED_MOVEMENT_NONE, 0.f, false);
summon->GetMotionMaster()->MoveFollow(me, 0.1f, 0.0f + M_PI * 0.3f * summons.size());
}
summons.Summon(summon);
}
void DoAction(int32 param) override
{
if (param == ACTION_DESPAWN_ADDS)
{
summons.DoAction(ACTION_DESPAWN_ADDS);
summons.DespawnAll();
}
}
void JustEngagedWith(Unit*) override
{
if (me->ToTempSummon())
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
if (summoner->GetEntry() != me->GetEntry())
{
summoner->GetAI()->DoAction(ACTION_START_EVENT);
me->SummonCreature(NPC_ANUB_AR_CRUSHER, 519.58f, 573.73f, 734.30f, 4.50f);
me->SummonCreature(NPC_ANUB_AR_CRUSHER, 539.38f, 573.25f, 732.20f, 4.738f);
Talk(SAY_CRUSHER_AGGRO);
}
events.ScheduleEvent(EVENT_CRUSHER_SMASH, 8s, 0, 0);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1s);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_CRUSHER_SMASH:
me->CastSpell(me->GetVictim(), SPELL_SMASH, false);
events.ScheduleEvent(EVENT_CRUSHER_SMASH, 15s);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(30))
{
Talk(SAY_CRUSHER_EMOTE);
me->CastSpell(me, SPELL_FRENZY, false);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1s);
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
void DoEngagedWith() override
{
return GetAzjolNerubAI<npc_anub_ar_crusherAI>(creature);
events.ScheduleEvent(EVENT_CRUSHING_WEBS, 4s, 8s);
events.ScheduleEvent(EVENT_INFECTED_WOUND, 15s, 19s);
}
};
static const Position necromancerWaypoints[] =
{
{ },
{ 507.6937f, 563.3471f, 734.8986f, 4.520403f },
{ 535.1049f, 552.8961f, 732.8441f, 3.996804f },
};
struct npc_anub_ar_crusher_necromancer : public npc_hadronox_crusherPackAI
{
explicit npc_anub_ar_crusher_necromancer(Creature* creature) : npc_hadronox_crusherPackAI(creature, necromancerWaypoints) { }
void DoEvent(uint32 eventId) override
{
switch (eventId)
{
case EVENT_SHADOW_BOLT:
DoCastVictim(SPELL_SHADOW_BOLT);
events.Repeat(2s, 5s);
break;
case EVENT_ANIMATE_BONES:
DoCastVictim(RAND(SPELL_ANIMATE_BONES_2, SPELL_ANIMATE_BONES_1));
events.Repeat(35s, 50s);
break;
default:
break;
}
}
void DoEngagedWith() override
{
events.ScheduleEvent(EVENT_SHADOW_BOLT, 2s, 4s);
events.ScheduleEvent(EVENT_ANIMATE_BONES, 37s, 45s);
}
};
@@ -330,7 +609,7 @@ class spell_hadronox_summon_periodic_aura : public AuraScript
PrepareAuraScript(spell_hadronox_summon_periodic_aura);
public:
spell_hadronox_summon_periodic_aura(uint32 delay, uint32 spellEntry) : _delay(delay), _spellEntry(spellEntry) { }
spell_hadronox_summon_periodic_aura(int32 delay, uint32 spellEntry) : _delay(delay), _spellEntry(spellEntry) { }
bool Validate(SpellInfo const* /*spellInfo*/) override
{
@@ -342,7 +621,7 @@ public:
PreventDefaultAction();
Unit* owner = GetUnitOwner();
if (InstanceScript* instance = owner->GetInstanceScript())
if (!instance->IsBossDone(DATA_HADRONOX))
if (!instance->IsBossDone(DATA_HADRONOX) != NOT_STARTED)
{
if (!owner->HasAura(SPELL_WEB_FRONT_DOORS))
owner->CastSpell(owner, _spellEntry, true);
@@ -363,7 +642,7 @@ public:
}
private:
uint32 _delay;
int32 _delay;
uint32 _spellEntry;
};
@@ -389,12 +668,29 @@ class spell_hadronox_leech_poison_aura : public AuraScript
}
};
class spell_hadronox_web_grab : public SpellScript
{
PrepareSpellScript(spell_hadronox_web_grab);
// hack to avoid pulling Anub'ar Crusher through the floor and causing Hadronox to evade
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if([&](WorldObject* target) -> bool
{
return target->GetEntry() == NPC_ANUB_AR_CRUSHER;
});
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hadronox_web_grab::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
class achievement_hadronox_denied : public AchievementCriteriaScript
{
public:
achievement_hadronox_denied() : AchievementCriteriaScript("achievement_hadronox_denied")
{
}
achievement_hadronox_denied() : AchievementCriteriaScript("achievement_hadronox_denied") { }
bool OnCheck(Player* /*player*/, Unit* target, uint32 /*criteria_id*/) override
{
@@ -407,11 +703,15 @@ public:
void AddSC_boss_hadronox()
{
new boss_hadronox();
new npc_anub_ar_crusher();
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_champion_aura", 15000, SPELL_SUMMON_ANUBAR_CHAMPION);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_necromancer_aura", 10000, SPELL_SUMMON_ANUBAR_NECROMANCER);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_crypt_fiend_aura", 5000, SPELL_SUMMON_ANUBAR_CRYPT_FIEND);
RegisterAzjolNerubCreatureAI(boss_hadronox);
RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher);
RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_champion);
RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_crypt_fiend);
RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_necromancer);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_champion_aura", 15'000, SPELL_SUMMON_ANUBAR_CHAMPION);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_necromancer_aura", 10'000, SPELL_SUMMON_ANUBAR_NECROMANCER);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_crypt_fiend_aura", 5'000, SPELL_SUMMON_ANUBAR_CRYPT_FIEND);
RegisterSpellScript(spell_hadronox_leech_poison_aura);
RegisterSpellScript(spell_hadronox_web_grab);
new achievement_hadronox_denied();
}

View File

@@ -50,6 +50,7 @@ ObjectData const summonData[] =
{ NPC_ANUB_AR_CHAMPION, DATA_HADRONOX },
{ NPC_ANUB_AR_NECROMANCER, DATA_HADRONOX },
{ NPC_ANUB_AR_CRYPTFIEND, DATA_HADRONOX },
{ NPC_WORLD_TRIGGER_LAOI, DATA_HADRONOX },
{ 0, 0 }
};