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
@@ -0,0 +1,74 @@
--
-- HARD_RESET
UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 2147483648 WHERE (`entry` IN (28921, 31611));
DELETE FROM `spelldifficulty_dbc` WHERE `ID` IN (53406, 53317, 53394, 53330, 53322);
INSERT INTO `spelldifficulty_dbc` (`ID`, `DifficultySpellID_1`, `DifficultySpellID_2`, `DifficultySpellID_3`, `DifficultySpellID_4`) VALUES
(53406, 53406, 59420, 0, 0),
(53317, 53317, 59343, 0, 0),
(53394, 53394, 59344, 0, 0),
(53330, 53330, 59348, 0, 0),
(53322, 53322, 59347, 0, 0);
DELETE FROM `spell_script_names` WHERE `spell_id` IN (53406, 59420) AND `ScriptName` = 'spell_hadronox_web_grab';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(53406, 'spell_hadronox_web_grab'),
(59420, 'spell_hadronox_web_grab');
DELETE FROM `creature_summon_groups` WHERE `summonerId` = 28921;
INSERT INTO `creature_summon_groups` (`summonerId`, `summonerType`, `groupId`, `entry`, `position_x`, `position_y`, `position_z`, `orientation`, `summonType`, `summonTime`, `Comment`) VALUES
(28921, 0, 1, 28922, 529.691, 547.126, 731.916, 4.79965, 6, 45000, 'Hadronox - Group 1 - Anub''ar Crusher'),
(28921, 0, 1, 29117, 539.208, 549.754, 732.867, 4.55531, 6, 30000, 'Hadronox - Group 1 - Anub''ar Champion'),
(28921, 0, 1, 29118, 520.391, 548.789, 732.012, 5.0091, 6, 30000, 'Hadronox - Group 1 - Anub''ar Crypt Fiend'),
(28921, 0, 2, 28922, 493.477, 603.344, 760.563, 5.44024, 6, 45000, 'Hadronox - Group 2 - Anub''ar Crusher'),
(28921, 0, 2, 29117, 490.442, 604.335, 763.182, 5.6256, 6, 30000, 'Hadronox - Group 2 - Anub''ar Champion'),
(28921, 0, 2, 29119, 488.825, 609.282, 767.588, 5.59029, 6, 30000, 'Hadronox - Group 2 - Anub''ar Necromancer'),
(28921, 0, 3, 28922, 566.979, 602.571, 759.642, 3.88597, 6, 45000, 'Hadronox - Group 3 - Anub''ar Crusher'),
(28921, 0, 3, 29118, 569.348, 604.999, 763.214, 4.17983, 6, 30000, 'Hadronox - Group 3 - Anub''ar Crypt Fiend'),
(28921, 0, 3, 29119, 572.474, 607.411, 767.178, 3.94417, 6, 30000, 'Hadronox - Group 3 - Anub''ar Necromancer'),
(28921, 0, 4, 23472, 581.448, 608.841, 739.405, 1.72788, 6, 3600000, 'Hadronox - Group 4 - World Trigger'),
(28921, 0, 4, 23472, 477.016, 618.4, 771.515, 2.35619, 6, 3600000, 'Hadronox - Group 4 - World Trigger'),
(28921, 0, 4, 23472, 583.091, 617.371, 771.551, 0.645772, 6, 3600000, 'Hadronox - Group 4 - World Trigger');
DELETE FROM `creature` WHERE (`id1` = 23472) AND (`guid` IN (127376, 127377, 127378));
DELETE FROM `creature_addon` WHERE (`guid` IN (127376, 127377, 127378));
DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` IN (29117, 29118, 29119));
UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_anub_ar_crusher_champion' WHERE (`entry` = 29117);
UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_anub_ar_crusher_crypt_fiend' WHERE (`entry` = 29118);
UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_anub_ar_crusher_necromancer' WHERE (`entry` = 29119);
SET @SPAWN_X := 522.53107;
SET @SPAWN_Y := 544.91125;
SET @SPAWN_Z := 674.6791;
SET @SPAWN_O := 5.633617;
DELETE FROM `creature` WHERE (`id1` = 28921) AND (`guid` = 127401);
INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `Comment`, `VerifiedBuild`) VALUES
(127401, 28921, 0, 0, 601, 0, 0, 3, 1, 0, @SPAWN_X, @SPAWN_Y, @SPAWN_Z, @SPAWN_O, 86400, 0, 0, 154230, 0, 0, 0, 0, 0, '', NULL, 0);
DELETE FROM `waypoint_data` WHERE `id` IN (3000012, 3000013) AND `point` IN (13, 14);
DELETE FROM `waypoint_data` WHERE `id` = 3000014 AND `point` IN (9, 10);
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
(3000012, 13, 538.0253, 529.9829, 686.0557, 0, 1, 0, 100, 0),
(3000012, 14, 532.7753, 535.2329, 681.0557, 0, 1, 0, 100, 0),
(3000013, 13, 538.0253, 529.9829, 686.0557, 0, 1, 0, 100, 0),
(3000013, 14, 532.7753, 535.2329, 681.0557, 0, 1, 0, 100, 0),
(3000014, 9, 538.0253, 529.9829, 686.0557, 0, 1, 0, 100, 0),
(3000014, 10, 532.7753, 535.2329, 681.0557, 0, 1, 0, 100, 0);
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` IN (29062, 29063, 29064));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(29062, 0, 0, 0, 0, 0, 100, 0, 4000, 7000, 12000, 18000, 0, 0, 11, 53317, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Champion - In Combat - Cast \'Rend\''),
(29062, 0, 1, 0, 105, 0, 100, 0, 9000, 13000, 9000, 13000, 0, 5, 11, 53394, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Champion - On Hostile Casting in Range - Cast \'Pummel\''),
(29062, 0, 2, 0, 6, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 10000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Champion - On Just Died - Despawn In 10000 ms'),
(29062, 0, 3, 0, 1, 0, 100, 0, 10000, 10000, 1000, 1000, 0, 0, 49, 0, 0, 0, 0, 0, 0, 11, 28921, 50, 1, 0, 0, 0, 0, 0, 'Anub\'ar Champion Out of Combat - Start Attacking'),
(29062, 0, 4, 0, 0, 0, 100, 0, 15000, 50000, 15000, 50000, 0, 0, 11, 53798, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Champion - In Combat - Cast \'Taunt\''),
(29063, 0, 0, 0, 0, 0, 100, 0, 4000, 7000, 9000, 12000, 0, 0, 11, 53330, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Crypt Fiend - In Combat - Cast \'Infected Wound\''),
(29063, 0, 1, 0, 0, 0, 100, 0, 9000, 12000, 13000, 17000, 0, 0, 11, 53322, 0, 0, 0, 0, 0, 5, 30, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Crypt Fiend - In Combat - Cast \'Crushing Webs\''),
(29063, 0, 2, 0, 6, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 10000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Crypt Fiend - On Just Died - Despawn In 10000 ms'),
(29063, 0, 3, 0, 1, 0, 100, 0, 10000, 10000, 1000, 1000, 0, 0, 49, 0, 0, 0, 0, 0, 0, 11, 28921, 50, 1, 0, 0, 0, 0, 0, 'Anub\'ar Crypt Fiend - Out of Combat - Start Attacking'),
(29063, 0, 4, 0, 0, 0, 100, 0, 15000, 50000, 15000, 50000, 0, 0, 11, 53798, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Crypt Fiend - In Combat - Cast \'Taunt\''),
(29064, 0, 0, 0, 1, 0, 100, 0, 0, 1000, 2000, 3000, 0, 0, 11, 53333, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Necromancer - Out of Combat - Cast \'Shadow Bolt\''),
(29064, 0, 2, 0, 6, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 10000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Necromancer - On Just Died - Despawn In 10000 ms'),
(29064, 0, 3, 0, 1, 0, 100, 0, 10000, 10000, 1000, 1000, 0, 0, 49, 0, 0, 0, 0, 0, 0, 11, 28921, 50, 1, 0, 0, 0, 0, 0, 'Anub\'ar Necromancer Out of Combat - Start Attacking'),
(29064, 0, 4, 0, 0, 0, 100, 0, 15000, 50000, 15000, 50000, 0, 0, 11, 53798, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Anub\'ar Necromancer - In Combat - Cast \'Taunt\'');
@@ -47,6 +47,7 @@ enum ANIds
NPC_HADRONOX = 28921, NPC_HADRONOX = 28921,
NPC_ANUBARAK = 29120, NPC_ANUBARAK = 29120,
NPC_WORLD_TRIGGER_LAOI = 23472,
NPC_ANUB_AR_CHAMPION = 29062, NPC_ANUB_AR_CHAMPION = 29062,
NPC_ANUB_AR_NECROMANCER = 29063, NPC_ANUB_AR_NECROMANCER = 29063,
NPC_ANUB_AR_CRYPTFIEND = 29064, NPC_ANUB_AR_CRYPTFIEND = 29064,
@@ -75,7 +75,6 @@ enum Events
enum CreatureIds enum CreatureIds
{ {
NPC_WORLD_TRIGGER = 22515,
NPC_ANUBAR_GUARDIAN = 29216, NPC_ANUBAR_GUARDIAN = 29216,
NPC_ANUBAR_VENOMANCER = 29217, NPC_ANUBAR_VENOMANCER = 29217,
}; };
@@ -26,19 +26,39 @@
enum Spells enum Spells
{ {
// World Trigger
SPELL_SUMMON_ANUBAR_CHAMPION = 53064, SPELL_SUMMON_ANUBAR_CHAMPION = 53064,
SPELL_SUMMON_ANUBAR_CRYPT_FIEND = 53065, SPELL_SUMMON_ANUBAR_CRYPT_FIEND = 53065,
SPELL_SUMMON_ANUBAR_NECROMANCER = 53066, 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_FRONT_DOORS = 53177,
SPELL_WEB_SIDE_DOORS = 53185, SPELL_WEB_SIDE_DOORS = 53185,
SPELL_ACID_CLOUD = 53400, SPELL_ACID_CLOUD = 53400,
SPELL_LEECH_POISON = 53030, SPELL_LEECH_POISON = 53030,
SPELL_LEECH_POISON_HEAL = 53800, SPELL_LEECH_POISON_HEAL = 53800,
SPELL_WEB_GRAB = 57731, SPELL_WEB_GRAB = 53406,
SPELL_PIERCE_ARMOR = 53418, SPELL_PIERCE_ARMOR = 53418,
// Anub'ar Crusher
SPELL_SMASH = 53318, 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 enum Events
@@ -54,71 +74,134 @@ enum Events
EVENT_HADRONOX_SUMMON = 9, EVENT_HADRONOX_SUMMON = 9,
EVENT_CRUSHER_SMASH = 20, 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 enum Misc
{ {
NPC_ANUB_AR_CRUSHER = 28922, SAY_CRUSHER_AGGRO = 1,
SAY_CRUSHER_EMOTE = 2,
SAY_CRUSHER_AGGRO = 0,
SAY_CRUSHER_EMOTE = 1,
SAY_HADRONOX_EMOTE = 0, SAY_HADRONOX_EMOTE = 0,
ACTION_DESPAWN_ADDS = 1, ACTION_CRUSHER_ENGAGED = 1,
ACTION_START_EVENT = 2 ACTION_CRUSHER_DIED = 2,
ACTION_PACK_WALK = 3,
}; };
const Position hadronoxSteps[4] = static const std::array<Position, 3> hadronoxSteps =
{ {{
{607.9f, 512.8f, 695.3f, 0.0f}, { 562.191f, 514.068f, 696.50710f },
{611.67f, 564.11f, 720.0f, 0.0f}, { 615.802f, 517.418f, 695.68066f },
{576.1f, 580.0f, 727.5f, 0.0f}, { 530.420f, 560.003f, 733.22473f },
{534.87f, 554.0f, 733.0f, 0.0f} }};
};
class boss_hadronox : public CreatureScript struct boss_hadronox : public BossAI
{ {
public: explicit boss_hadronox(Creature* creature) : BossAI(creature, DATA_HADRONOX), _crushersLeft(0), _doorsWebbed(false), _lastPlayerCombatState(false) { }
boss_hadronox() : CreatureScript("boss_hadronox") { }
struct boss_hadronoxAI : public BossAI
{
boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX)
{
}
void Reset() override void Reset() override
{ {
summons.DoAction(ACTION_DESPAWN_ADDS);
BossAI::Reset(); BossAI::Reset();
me->SummonCreature(NPC_ANUB_AR_CRUSHER, 542.9f, 519.5f, 741.24f, 2.14f); 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 void DoAction(int32 param) override
{ {
if (param == ACTION_START_EVENT) 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); instance->SetBossState(DATA_HADRONOX, IN_PROGRESS);
me->setActive(true); events.ScheduleEvent(EVENT_CHECK_EVADE, 5s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE1, 20s); SummonCrusherPack(SUMMON_GROUP_CRUSHER_2);
events.ScheduleEvent(EVENT_HADRONOX_MOVE2, 40s); SummonCrusherPack(SUMMON_GROUP_CRUSHER_3);
events.ScheduleEvent(EVENT_HADRONOX_MOVE3, 60s); events.ScheduleEvent(EVENT_HADRONOX_MOVE1, 0s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE4, 80s); events.ScheduleEvent(EVENT_HADRONOX_MOVE2, 45s);
events.ScheduleEvent(EVENT_HADRONOX_MOVE3, 90s);
} }
} }
uint32 GetData(uint32 data) const override void SummonCrusherPack(const SummonGroups group)
{ {
if (data == me->GetEntry()) std::list<TempSummon*> summoned;
return !me->isActiveObject() || events.HasTimeUntilEvent(EVENT_HADRONOX_MOVE4) ? 1 : 0; me->SummonCreatureGroup(group, &summoned);
return 0; _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 void JustSummoned(Creature* summon) override
{ {
summons.Summon(summon); 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... // Xinef: cannot use pathfinding...
if (summon->GetDistance(477.0f, 618.0f, 771.0f) < 5.0f) if (summon->GetDistance(477.0f, 618.0f, 771.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000012, false); summon->GetMotionMaster()->MoveWaypoint(3000012, false);
@@ -126,6 +209,10 @@ public:
summon->GetMotionMaster()->MoveWaypoint(3000013, false); summon->GetMotionMaster()->MoveWaypoint(3000013, false);
else if (summon->GetDistance(581.0f, 608.5f, 739.0f) < 5.0f) else if (summon->GetDistance(581.0f, 608.5f, 739.0f) < 5.0f)
summon->GetMotionMaster()->MoveWaypoint(3000014, false); summon->GetMotionMaster()->MoveWaypoint(3000014, false);
break;
default:
break;
}
} }
void KilledUnit(Unit* victim) override void KilledUnit(Unit* victim) override
@@ -133,30 +220,24 @@ public:
if (!me->IsAlive() || !victim->HasAura(SPELL_LEECH_POISON)) if (!me->IsAlive() || !victim->HasAura(SPELL_LEECH_POISON))
return; return;
me->ModifyHealth(int32(me->CountPctFromMaxHealth(10))); me->ModifyHealth(static_cast<int32>(me->CountPctFromMaxHealth(10)));
} }
void JustDied(Unit* killer) override void JustEngagedWith(Unit* /*who*/) override
{
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit*) override
{ {
me->setActive(true);
events.RescheduleEvent(EVENT_HADRONOX_ACID, 10s); events.RescheduleEvent(EVENT_HADRONOX_ACID, 10s);
events.RescheduleEvent(EVENT_HADRONOX_LEECH, 4s); events.RescheduleEvent(EVENT_HADRONOX_LEECH, 4s);
events.RescheduleEvent(EVENT_HADRONOX_PIERCE, 1s); events.RescheduleEvent(EVENT_HADRONOX_PIERCE, 1s);
events.RescheduleEvent(EVENT_HADRONOX_GRAB, 15s); events.RescheduleEvent(EVENT_HADRONOX_GRAB, 15s);
events.RescheduleEvent(EVENT_CHECK_EVADE, 1s);
} }
bool AnyPlayerValid() const bool IsInCombatWithPlayer() const
{ {
Map::PlayerList const& playerList = me->GetMap()->GetPlayers(); return std::ranges::any_of(me->GetThreatMgr().GetThreatList(), [](auto const& ref) {
for(Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) return ref->getTarget()->IsControlledByPlayer();
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 void DamageTaken(Unit* who, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override
@@ -164,164 +245,362 @@ public:
if ((!who || !who->IsControlledByPlayer()) && me->HealthBelowPct(70)) if ((!who || !who->IsControlledByPlayer()) && me->HealthBelowPct(70))
{ {
if (me->HealthBelowPctDamaged(5, damage)) if (me->HealthBelowPctDamaged(5, damage))
{
damage = 0; damage = 0;
}
else else
{
damage *= (me->GetHealthPct() - 5.0f) / 65.0f; damage *= (me->GetHealthPct() - 5.0f) / 65.0f;
} }
} }
}
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
{ {
events.Update(diff);
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING)) if (me->HasUnitState(UNIT_STATE_CASTING))
return; return;
switch (uint32 eventId = events.ExecuteEvent()) switch (uint32 eventId = events.ExecuteEvent())
{ {
case EVENT_HADRONOX_PIERCE: case EVENT_HADRONOX_PIERCE:
me->CastSpell(me->GetVictim(), SPELL_PIERCE_ARMOR, false); DoCastVictim(SPELL_PIERCE_ARMOR);
events.ScheduleEvent(EVENT_HADRONOX_PIERCE, 8s); events.Repeat(8s);
break; break;
case EVENT_HADRONOX_ACID: case EVENT_HADRONOX_ACID:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, false)) if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, false))
me->CastSpell(target, SPELL_ACID_CLOUD, false); DoCast(target, SPELL_ACID_CLOUD);
events.ScheduleEvent(EVENT_HADRONOX_ACID, 25s); events.Repeat(25s);
break; break;
case EVENT_HADRONOX_LEECH: case EVENT_HADRONOX_LEECH:
me->CastSpell(me, SPELL_LEECH_POISON, false); DoCastSelf(SPELL_LEECH_POISON);
events.ScheduleEvent(EVENT_HADRONOX_LEECH, 12s); events.Repeat(12s);
break; break;
case EVENT_HADRONOX_GRAB: case EVENT_HADRONOX_GRAB:
me->CastSpell(me, SPELL_WEB_GRAB, false); DoCastSelf(SPELL_WEB_GRAB);
events.ScheduleEvent(EVENT_HADRONOX_GRAB, 25s); events.Repeat(25s);
break; break;
case EVENT_HADRONOX_MOVE4: case EVENT_HADRONOX_MOVE3:
me->CastSpell(me, SPELL_WEB_FRONT_DOORS, true); if (_crushersLeft > 0)
[[fallthrough]]; /// @todo: Not sure whether the fallthrough was a mistake (forgetting a break) or intended. This should be double-checked. {
events.Repeat(2s);
break;
}
[[fallthrough]];
case EVENT_HADRONOX_MOVE1: case EVENT_HADRONOX_MOVE1:
case EVENT_HADRONOX_MOVE2: case EVENT_HADRONOX_MOVE2:
case EVENT_HADRONOX_MOVE3:
Talk(SAY_HADRONOX_EMOTE); Talk(SAY_HADRONOX_EMOTE);
me->GetMotionMaster()->MoveCharge(hadronoxSteps[eventId - 1].GetPositionX(), hadronoxSteps[eventId - 1].GetPositionY(), hadronoxSteps[eventId - 1].GetPositionZ(), 10.0f, 0, nullptr, true); 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; break;
} }
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
bool CheckEvadeIfOutOfCombatArea() const override void MovementInform(uint32 movementType, uint32 pointId) override
{ {
return me->isActiveObject() && !AnyPlayerValid(); 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;
}; };
CreatureAI* GetAI(Creature* creature) const override struct npc_hadronox_crusherPackAI : public ScriptedAI
{ {
return GetAzjolNerubAI<boss_hadronoxAI>(creature); npc_hadronox_crusherPackAI(Creature* creature, Position const* positions) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _positions(positions), _myPack(SUMMON_GROUP_CRUSHER_NONE), _doFacing(false) { }
virtual void DoEngagedWith() = 0;
virtual void DoEvent(uint32 /*eventId*/) = 0;
void DoAction(int32 action) override
{
if (action == ACTION_PACK_WALK)
{
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;
} }
};
class npc_anub_ar_crusher : public CreatureScript
{
public:
npc_anub_ar_crusher() : CreatureScript("npc_anub_ar_crusher") { }
struct npc_anub_ar_crusherAI : public ScriptedAI
{
npc_anub_ar_crusherAI(Creature* c) : ScriptedAI(c), summons(me) {}
EventMap events;
SummonList summons;
void Reset() override
{
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);
} }
} }
void JustSummoned(Creature* summon) override void MovementInform(uint32 type, uint32 id) override
{ {
if (summon->GetEntry() != me->GetEntry()) if (type == POINT_MOTION_TYPE && id == ACTION_PACK_WALK)
{ _doFacing = true;
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 uint32 GetData(uint32 data) const override
{ {
if (param == ACTION_DESPAWN_ADDS) if (data == DATA_CRUSHER_PACK_ID)
return _myPack;
return 0;
}
void SetData(uint32 data, uint32 value) override
{ {
summons.DoAction(ACTION_DESPAWN_ADDS); if (data == DATA_CRUSHER_PACK_ID)
summons.DespawnAll(); {
_myPack = SummonGroups(value);
me->SetReactState(_myPack ? REACT_PASSIVE : REACT_AGGRESSIVE);
} }
} }
void JustEngagedWith(Unit*) override void JustEngagedWith(Unit* who) override
{ {
if (me->ToTempSummon()) if (me->HasReactState(REACT_PASSIVE))
if (Unit* summoner = me->ToTempSummon()->GetSummonerUnit())
if (summoner->GetEntry() != me->GetEntry())
{ {
summoner->GetAI()->DoAction(ACTION_START_EVENT); std::list<Creature*> creatures;
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); 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);
Talk(SAY_CRUSHER_AGGRO); 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);
} }
events.ScheduleEvent(EVENT_CRUSHER_SMASH, 8s, 0, 0); void MoveInLineOfSight(Unit* who) override
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1s); {
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 void UpdateAI(uint32 diff) override
{ {
if (_doFacing)
{
_doFacing = false;
me->SetFacingTo(_positions[_myPack - SUMMON_GROUP_CRUSHER_1].GetOrientation());
}
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
events.Update(diff); events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent()) while (uint32 eventId = events.ExecuteEvent())
{ DoEvent(eventId);
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(); DoMeleeAttackIfReady();
} }
protected:
InstanceScript* const _instance;
Position const* const _positions;
SummonGroups _myPack;
bool _doFacing;
}; };
CreatureAI* GetAI(Creature* creature) const override static const Position crusherWaypoints[] =
{ {
return GetAzjolNerubAI<npc_anub_ar_crusherAI>(creature); { 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);
}
};
static const Position cryptFiendWaypoints[] =
{
{ 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) { }
void DoEvent(uint32 eventId) override
{
switch (eventId)
{
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 DoEngagedWith() override
{
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); PrepareAuraScript(spell_hadronox_summon_periodic_aura);
public: 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 bool Validate(SpellInfo const* /*spellInfo*/) override
{ {
@@ -342,7 +621,7 @@ public:
PreventDefaultAction(); PreventDefaultAction();
Unit* owner = GetUnitOwner(); Unit* owner = GetUnitOwner();
if (InstanceScript* instance = owner->GetInstanceScript()) if (InstanceScript* instance = owner->GetInstanceScript())
if (!instance->IsBossDone(DATA_HADRONOX)) if (!instance->IsBossDone(DATA_HADRONOX) != NOT_STARTED)
{ {
if (!owner->HasAura(SPELL_WEB_FRONT_DOORS)) if (!owner->HasAura(SPELL_WEB_FRONT_DOORS))
owner->CastSpell(owner, _spellEntry, true); owner->CastSpell(owner, _spellEntry, true);
@@ -363,7 +642,7 @@ public:
} }
private: private:
uint32 _delay; int32 _delay;
uint32 _spellEntry; 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 class achievement_hadronox_denied : public AchievementCriteriaScript
{ {
public: 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 bool OnCheck(Player* /*player*/, Unit* target, uint32 /*criteria_id*/) override
{ {
@@ -407,11 +703,15 @@ public:
void AddSC_boss_hadronox() void AddSC_boss_hadronox()
{ {
new boss_hadronox(); RegisterAzjolNerubCreatureAI(boss_hadronox);
new npc_anub_ar_crusher(); RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_champion_aura", 15000, SPELL_SUMMON_ANUBAR_CHAMPION); RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_champion);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_necromancer_aura", 10000, SPELL_SUMMON_ANUBAR_NECROMANCER); RegisterAzjolNerubCreatureAI(npc_anub_ar_crusher_crypt_fiend);
RegisterSpellScriptWithArgs(spell_hadronox_summon_periodic_aura, "spell_hadronox_summon_periodic_crypt_fiend_aura", 5000, SPELL_SUMMON_ANUBAR_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_leech_poison_aura);
RegisterSpellScript(spell_hadronox_web_grab);
new achievement_hadronox_denied(); new achievement_hadronox_denied();
} }
@@ -50,6 +50,7 @@ ObjectData const summonData[] =
{ NPC_ANUB_AR_CHAMPION, DATA_HADRONOX }, { NPC_ANUB_AR_CHAMPION, DATA_HADRONOX },
{ NPC_ANUB_AR_NECROMANCER, DATA_HADRONOX }, { NPC_ANUB_AR_NECROMANCER, DATA_HADRONOX },
{ NPC_ANUB_AR_CRYPTFIEND, DATA_HADRONOX }, { NPC_ANUB_AR_CRYPTFIEND, DATA_HADRONOX },
{ NPC_WORLD_TRIGGER_LAOI, DATA_HADRONOX },
{ 0, 0 } { 0, 0 }
}; };