aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp1566
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp1
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h2
3 files changed, 732 insertions, 837 deletions
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
index 9f85d6ccec7..ec4418e6e51 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp
@@ -15,119 +15,94 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
- @todo
- If the boss is to close to a scrap pile -> no summon -- Needs retail confirmation
- Codestyle
-*/
-
#include "ScriptMgr.h"
#include "CombatLogPackets.h"
#include "InstanceScript.h"
+#include "Map.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "PassiveAI.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
+#include "SpellMgr.h"
#include "SpellScript.h"
#include "ulduar.h"
#include "Vehicle.h"
enum Spells
{
- SPELL_TYMPANIC_TANTRUM = 62776,
- SPELL_SEARING_LIGHT = 63018,
-
- SPELL_SUMMON_LIFE_SPARK = 64210,
- SPELL_SUMMON_VOID_ZONE = 64203,
-
- SPELL_GRAVITY_BOMB = 63024,
-
- SPELL_HEARTBREAK = 65737,
-
- // Cast by 33337 at Heartbreak:
- SPELL_RECHARGE_PUMMELER = 62831, // Summons 33344
- SPELL_RECHARGE_SCRAPBOT = 62828, // Summons 33343
- SPELL_RECHARGE_BOOMBOT = 62835, // Summons 33346
-
- // Cast by 33329 on 33337 (visual?)
- SPELL_ENERGY_ORB = 62790, // Triggers 62826 - needs spellscript for periodic tick to cast one of the random spells above
-
- SPELL_HEART_HEAL_TO_FULL = 17683,
- SPELL_HEART_OVERLOAD = 62789,
-
- SPELL_HEART_LIGHTNING_TETHER = 64799, // Cast on self?
- SPELL_ENRAGE = 26662,
- SPELL_STAND = 37752,
- SPELL_SUBMERGE = 37751,
-
- //------------------VOID ZONE--------------------
- SPELL_VOID_ZONE = 64203,
- SPELL_CONSUMPTION = 64208,
+ SPELL_TYMPANIC_TANTRUM = 62776,
+ SPELL_SEARING_LIGHT = 63018,
+ SPELL_SUMMON_LIFE_SPARK = 64210,
+ SPELL_SUMMON_VOID_ZONE = 64203,
+ SPELL_GRAVITY_BOMB = 63024,
+ SPELL_HEARTBREAK = 65737,
+ SPELL_STAND = 37752,
+ SPELL_SUBMERGE = 37751,
+ SPELL_ENRAGE = 26662,
+ SPELL_COOLDOWN_CREATURE_SPECIAL_2 = 64404,
+ SPELL_SCRAP_REPAIR = 62832,
+
+ // XT-Toy Pile
+ SPELL_RECHARGE_PUMMELER = 62831,
+ SPELL_RECHARGE_SCRAPBOT = 62828,
+ SPELL_RECHARGE_BOOMBOT = 62835,
+
+ // Heart of the Deconstructor
+ SPELL_ENERGY_ORB = 62790,
+ SPELL_RIDE_VEHICLE_EXPOSED = 63313,
+ SPELL_EXPOSED_HEART = 63849,
+ SPELL_HEART_RIDE_VEHICLE = 63852,
+ SPELL_SCRAPBOT_RIDE_VEHICLE = 47020,
+ SPELL_FULL_HEAL = 17683,
+ SPELL_HEART_OVERLOAD = 62789,
+ SPELL_HEART_LIGHTNING_TETHER = 64799,
+
+ // Void Zone
+ SPELL_CONSUMPTION = 64208,
// Life Spark
- SPELL_ARCANE_POWER_STATE = 49411,
- SPELL_STATIC_CHARGED = 64227,
- SPELL_SHOCK = 64230,
-
- //----------------XT-002 HEART-------------------
- SPELL_EXPOSED_HEART = 63849,
- SPELL_HEART_RIDE_VEHICLE = 63852,
- SPELL_RIDE_VEHICLE_EXPOSED = 63313, //Heart Exposed
+ SPELL_ARCANE_POWER_STATE = 49411,
+ SPELL_STATIC_CHARGED = 64227,
+ SPELL_SHOCK = 64230,
- //---------------XM-024 PUMMELLER----------------
- SPELL_ARCING_SMASH = 8374,
- SPELL_TRAMPLE = 5568,
- SPELL_UPPERCUT = 10966,
+ // XM-024 Pummeller
+ SPELL_ARCING_SMASH = 8374,
+ SPELL_TRAMPLE = 5568,
+ SPELL_UPPERCUT = 10966,
- // Scrabot:
- SPELL_SCRAPBOT_RIDE_VEHICLE = 47020,
- SPELL_SCRAP_REPAIR = 62832,
- SPELL_SUICIDE = 7,
-
- //------------------BOOMBOT-----------------------
- SPELL_AURA_BOOMBOT = 65032,
- SPELL_BOOM = 62834,
+ //Boombot
+ SPELL_321_BOOMBOT_AURA = 65032,
+ SPELL_BOOM = 62834,
// Achievement-related spells
- SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS = 65037
+ SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS = 65037
};
enum Events
{
EVENT_TYMPANIC_TANTRUM = 1,
+ EVENT_PHASE_CHECK,
EVENT_SEARING_LIGHT,
EVENT_GRAVITY_BOMB,
- EVENT_HEART_PHASE,
- EVENT_ENERGY_ORB,
+ EVENT_SUBMERGE,
EVENT_DISPOSE_HEART,
EVENT_ENRAGE,
EVENT_ENTER_HARD_MODE,
+ EVENT_RESUME_ATTACK
};
-enum Timers
+enum XT002Phases
{
- TIMER_TYMPANIC_TANTRUM_MIN = 32000,
- TIMER_TYMPANIC_TANTRUM_MAX = 36000,
- TIMER_SEARING_LIGHT = 20000,
- TIMER_GRAVITY_BOMB = 20000,
- TIMER_HEART_PHASE = 30000,
- TIMER_ENERGY_ORB_MIN = 9000,
- TIMER_ENERGY_ORB_MAX = 10000,
- TIMER_ENRAGE = 600000,
-
- // Pummeller
- // Timers may be off
- TIMER_ARCING_SMASH = 27000,
- TIMER_TRAMPLE = 22000,
- TIMER_UPPERCUT = 17000,
-
- TIMER_SPAWN_ADD = 12000,
+ PHASE_1 = 1,
+ PHASE_HEART
};
enum Actions
{
ACTION_ENTER_HARD_MODE,
+ ACTION_START_PHASE_HEART,
+ ACTION_DISPOSE_HEART
};
enum XT002Data
@@ -135,935 +110,851 @@ enum XT002Data
DATA_TRANSFERED_HEALTH,
DATA_HARD_MODE,
DATA_HEALTH_RECOVERED,
- DATA_GRAVITY_BOMB_CASUALTY,
+ DATA_GRAVITY_BOMB_CASUALTY
};
enum Yells
{
- SAY_AGGRO = 0,
- SAY_HEART_OPENED = 1,
- SAY_HEART_CLOSED = 2,
- SAY_TYMPANIC_TANTRUM = 3,
- SAY_SLAY = 4,
- SAY_BERSERK = 5,
- SAY_DEATH = 6,
- SAY_SUMMON = 7,
- EMOTE_HEART_OPENED = 8,
- EMOTE_HEART_CLOSED = 9,
- EMOTE_TYMPANIC_TANTRUM = 10,
- EMOTE_SCRAPBOT = 11
+ SAY_AGGRO = 0,
+ SAY_HEART_OPENED = 1,
+ SAY_HEART_CLOSED = 2,
+ SAY_TYMPANIC_TANTRUM = 3,
+ SAY_SLAY = 4,
+ SAY_BERSERK = 5,
+ SAY_DEATH = 6,
+ SAY_SUMMON = 7,
+ EMOTE_HEART_OPENED = 8,
+ EMOTE_HEART_CLOSED = 9,
+ EMOTE_TYMPANIC_TANTRUM = 10,
+ EMOTE_SCRAPBOT = 11
};
-enum AchievementCredits
+enum Misc
{
- ACHIEV_MUST_DECONSTRUCT_FASTER = 21027,
+ ACHIEV_MUST_DECONSTRUCT_FASTER = 21027,
+ HEART_VEHICLE_SEAT_EXPOSED = 1,
+ GROUP_SEARING_GRAVITY = 1
};
-enum VehicleSeats
+struct boss_xt002 : public BossAI
{
- HEART_VEHICLE_SEAT_NORMAL = 0,
- HEART_VEHICLE_SEAT_EXPOSED = 1,
-};
+ boss_xt002(Creature* creature) : BossAI(creature, BOSS_XT002)
+ {
+ Initialize();
+ }
-/*-------------------------------------------------------
- *
- * XT-002 DECONSTRUCTOR
- *
- *///----------------------------------------------------
-class boss_xt002 : public CreatureScript
-{
- public:
- boss_xt002() : CreatureScript("boss_xt002") { }
+ void Initialize()
+ {
+ _healthRecovered = false;
+ _gravityBombCasualty = false;
+ _hardMode = false;
+ _exposeHeartPercent = 75;
+ }
- struct boss_xt002_AI : public BossAI
+ void ChangeNextExpose()
+ {
+ switch (_exposeHeartPercent)
{
- boss_xt002_AI(Creature* creature) : BossAI(creature, BOSS_XT002)
- {
- Initialize();
- _transferHealth = 0;
- }
-
- void Initialize()
- {
- _healthRecovered = false;
- _gravityBombCasualty = false;
- _hardMode = false;
-
- _phase = 1;
- _heartExposed = 0;
- }
-
- void Reset() override
- {
- _Reset();
-
- me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->SetReactState(REACT_AGGRESSIVE);
- DoCastSelf(SPELL_STAND);
-
- Initialize();
-
- instance->DoStopCriteriaTimer(CriteriaStartEvent::SendEvent, ACHIEV_MUST_DECONSTRUCT_FASTER);
- }
+ case 75:
+ _exposeHeartPercent = 50;
+ break;
+ case 50:
+ _exposeHeartPercent = 25;
+ break;
+ default:
+ _exposeHeartPercent = 0;
+ break;
+ }
+ }
- void EnterEvadeMode(EvadeReason /*why*/) override
- {
- summons.DespawnAll();
- _DespawnAtEvade();
- }
+ void Reset() override
+ {
+ _Reset();
+ events.SetPhase(PHASE_1);
+ me->SetReactState(REACT_DEFENSIVE);
+ Initialize();
+ instance->DoStopCriteriaTimer(CriteriaStartEvent::SendEvent, ACHIEV_MUST_DECONSTRUCT_FASTER);
+ }
- void JustEngagedWith(Unit* /*who*/) override
- {
- Talk(SAY_AGGRO);
- _JustEngagedWith();
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ summons.DespawnAll();
+ _DespawnAtEvade();
+ }
- events.ScheduleEvent(EVENT_ENRAGE, TIMER_ENRAGE);
- events.ScheduleEvent(EVENT_GRAVITY_BOMB, TIMER_GRAVITY_BOMB);
- events.ScheduleEvent(EVENT_SEARING_LIGHT, TIMER_SEARING_LIGHT);
- //Tantrum is cast a bit slower the first time.
- events.ScheduleEvent(EVENT_TYMPANIC_TANTRUM, urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX) * 2);
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ Talk(SAY_AGGRO);
+ _JustEngagedWith();
+ events.ScheduleEvent(EVENT_SEARING_LIGHT, Is25ManRaid() ? 9s : 11s, GROUP_SEARING_GRAVITY, PHASE_1);
+ events.ScheduleEvent(EVENT_GRAVITY_BOMB, Is25ManRaid() ? 18s : 21s, GROUP_SEARING_GRAVITY, PHASE_1);
+ events.ScheduleEvent(EVENT_ENRAGE, 10min);
+ events.ScheduleEvent(EVENT_TYMPANIC_TANTRUM, 60s, 0, PHASE_1);
+ events.ScheduleEvent(EVENT_PHASE_CHECK, 1s, 0, PHASE_1);
+ instance->DoStartCriteriaTimer(CriteriaStartEvent::SendEvent, ACHIEV_MUST_DECONSTRUCT_FASTER);
+ }
- instance->DoStartCriteriaTimer(CriteriaStartEvent::SendEvent, ACHIEV_MUST_DECONSTRUCT_FASTER);
- }
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_ENTER_HARD_MODE)
+ events.ScheduleEvent(EVENT_ENTER_HARD_MODE, 1);
+ }
- void DoAction(int32 action) override
- {
- switch (action)
- {
- case ACTION_ENTER_HARD_MODE:
- events.ScheduleEvent(EVENT_ENTER_HARD_MODE, 1);
- break;
- }
- }
+ void KilledUnit(Unit* who) override
+ {
+ if (who->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
- void KilledUnit(Unit* who) override
- {
- if (who->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
- }
+ void JustDied(Unit* /*killer*/) override
+ {
+ Talk(SAY_DEATH);
+ _JustDied();
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ }
- void JustDied(Unit* /*killer*/) override
- {
- Talk(SAY_DEATH);
- _JustDied();
- me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- }
+ void ExposeHeart()
+ {
+ events.SetPhase(PHASE_HEART);
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ Talk(SAY_HEART_OPENED);
+ events.CancelEvent(EVENT_TYMPANIC_TANTRUM);
+ events.ScheduleEvent(EVENT_SUBMERGE, 6s, 0, PHASE_HEART);
+ ChangeNextExpose();
+ }
- void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) override
- {
- if (!_hardMode && _phase == 1 && !HealthAbovePct(100 - 25 * (_heartExposed+1)))
- ExposeHeart();
- }
+ void DisposeHeart(bool isHardMode = false)
+ {
+ Talk(SAY_HEART_CLOSED);
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
+ if (isHardMode)
+ {
+ me->SetReactState(REACT_AGGRESSIVE);
+ RescheduleEvents();
+ }
+ else
+ {
+ Talk(EMOTE_HEART_CLOSED);
+ events.ScheduleEvent(EVENT_RESUME_ATTACK, 1s, 0, PHASE_HEART);
+ }
- events.Update(diff);
+ DoCastSelf(SPELL_STAND);
+ DoCastSelf(SPELL_COOLDOWN_CREATURE_SPECIAL_2);
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ if (Creature* heart = instance->GetCreature(DATA_XT002_HEART))
+ {
+ if (heart->IsAlive())
+ heart->AI()->DoAction(ACTION_DISPOSE_HEART);
+ else
+ heart->DespawnOrUnsummon();
+ }
+ }
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ void RescheduleEvents()
+ {
+ events.SetPhase(PHASE_1);
+ events.ScheduleEvent(EVENT_SEARING_LIGHT, 25s, GROUP_SEARING_GRAVITY, PHASE_1);
+ events.ScheduleEvent(EVENT_GRAVITY_BOMB, Is25ManRaid() ? 33s : 15s, GROUP_SEARING_GRAVITY, PHASE_1);
+ events.ScheduleEvent(EVENT_TYMPANIC_TANTRUM, 25s, 0, PHASE_1);
+ if (!_hardMode)
+ events.ScheduleEvent(EVENT_PHASE_CHECK, 1s, 0, PHASE_1);
+ }
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_SEARING_LIGHT:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_SEARING_LIGHT);
-
- events.ScheduleEvent(EVENT_SEARING_LIGHT, TIMER_SEARING_LIGHT);
- break;
- case EVENT_GRAVITY_BOMB:
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_GRAVITY_BOMB);
-
- events.ScheduleEvent(EVENT_GRAVITY_BOMB, TIMER_GRAVITY_BOMB);
- break;
- case EVENT_TYMPANIC_TANTRUM:
- Talk(SAY_TYMPANIC_TANTRUM);
- Talk(EMOTE_TYMPANIC_TANTRUM);
- DoCast(SPELL_TYMPANIC_TANTRUM);
- events.ScheduleEvent(EVENT_TYMPANIC_TANTRUM, urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX));
- break;
- case EVENT_DISPOSE_HEART:
- SetPhaseOne();
- break;
- case EVENT_ENRAGE:
- Talk(SAY_BERSERK);
- DoCastSelf(SPELL_ENRAGE);
- break;
- case EVENT_ENTER_HARD_MODE:
- me->SetFullHealth();
- DoCastSelf(SPELL_HEARTBREAK, true);
- me->AddLootMode(LOOT_MODE_HARD_MODE_1);
- _hardMode = true;
- SetPhaseOne();
- break;
- }
+ void PassengerBoarded(Unit* who, int8 seatId, bool apply) override
+ {
+ if (!apply)
+ return;
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
- }
+ if (who->GetEntry() == NPC_XS013_SCRAPBOT)
+ {
+ Talk(EMOTE_SCRAPBOT);
+ _healthRecovered = true;
+ }
+ else if (seatId == HEART_VEHICLE_SEAT_EXPOSED)
+ who->CastSpell(who, SPELL_EXPOSED_HEART); // Channeled
+ }
- if (_phase == 1)
- DoMeleeAttackIfReady();
- }
+ uint32 GetData(uint32 type) const override
+ {
+ switch (type)
+ {
+ case DATA_HARD_MODE:
+ return _hardMode ? 1 : 0;
+ case DATA_HEALTH_RECOVERED:
+ return _healthRecovered ? 1 : 0;
+ case DATA_GRAVITY_BOMB_CASUALTY:
+ return _gravityBombCasualty ? 1 : 0;
+ default:
+ return 0;
+ }
+ }
- void PassengerBoarded(Unit* who, int8 seatId, bool apply) override
- {
- if (apply && who->GetEntry() == NPC_XS013_SCRAPBOT)
+ void SetData(uint32 type, uint32 data) override
+ {
+ switch (type)
+ {
+ case DATA_TRANSFERED_HEALTH:
+ if (!_hardMode)
{
- // Need this so we can properly determine when to expose heart again in damagetaken hook
- if (me->GetHealthPct() > (25 * (4 - _heartExposed)))
- ++_heartExposed;
+ uint32 transferHealth = data;
+ if (transferHealth >= me->GetHealth())
+ transferHealth = me->GetHealth() - 1;
- Talk(EMOTE_SCRAPBOT);
- DoCast(who, SPELL_SCRAP_REPAIR, true);
- _healthRecovered = true;
+ me->ModifyHealth(-static_cast<int32>(transferHealth));
+ me->LowerPlayerDamageReq(transferHealth);
}
+ break;
+ case DATA_GRAVITY_BOMB_CASUALTY:
+ _gravityBombCasualty = (data > 0) ? true : false;
+ break;
+ default:
+ break;
+ }
+ }
- if (apply && seatId == HEART_VEHICLE_SEAT_EXPOSED)
- who->CastSpell(who, SPELL_EXPOSED_HEART); // Channeled
- }
-
- uint32 GetData(uint32 type) const override
- {
- switch (type)
- {
- case DATA_HARD_MODE:
- return _hardMode ? 1 : 0;
- case DATA_HEALTH_RECOVERED:
- return _healthRecovered ? 1 : 0;
- case DATA_GRAVITY_BOMB_CASUALTY:
- return _gravityBombCasualty ? 1 : 0;
- }
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- return 0;
- }
+ events.Update(diff);
- void SetData(uint32 type, uint32 data) override
- {
- switch (type)
- {
- case DATA_TRANSFERED_HEALTH:
- _transferHealth = data;
- break;
- case DATA_GRAVITY_BOMB_CASUALTY:
- _gravityBombCasualty = (data > 0) ? true : false;
- break;
- }
- }
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- void ExposeHeart()
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- Talk(SAY_HEART_OPENED);
- Talk(EMOTE_HEART_OPENED);
-
- DoCastSelf(SPELL_SUBMERGE); // Will make creature untargetable
- me->AttackStop();
- me->SetReactState(REACT_PASSIVE);
-
- Unit* heart = me->GetVehicleKit() ? me->GetVehicleKit()->GetPassenger(HEART_VEHICLE_SEAT_NORMAL) : nullptr;
- if (heart)
- {
- heart->CastSpell(heart, SPELL_HEART_OVERLOAD);
- heart->CastSpell(me, SPELL_HEART_LIGHTNING_TETHER);
- heart->CastSpell(heart, SPELL_HEART_HEAL_TO_FULL, true);
- heart->CastSpell(me, SPELL_RIDE_VEHICLE_EXPOSED, true);
- heart->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- heart->AddUnitFlag(UNIT_FLAG_UNK_29);
- }
-
- events.CancelEvent(EVENT_SEARING_LIGHT);
- events.CancelEvent(EVENT_GRAVITY_BOMB);
- events.CancelEvent(EVENT_TYMPANIC_TANTRUM);
-
- // Start "end of phase 2 timer"
- events.ScheduleEvent(EVENT_DISPOSE_HEART, TIMER_HEART_PHASE);
-
- // Phase 2 has officially started
- _phase = 2;
- _heartExposed++;
+ case EVENT_SEARING_LIGHT:
+ DoCastSelf(SPELL_SEARING_LIGHT);
+ events.Repeat(Is25ManRaid() ? 16s : 20s);
+ break;
+ case EVENT_GRAVITY_BOMB:
+ DoCastSelf(SPELL_GRAVITY_BOMB);
+ events.Repeat(Is25ManRaid() ? 16s : 20s);
+ break;
+ case EVENT_TYMPANIC_TANTRUM:
+ Talk(SAY_TYMPANIC_TANTRUM);
+ Talk(EMOTE_TYMPANIC_TANTRUM);
+ events.DelayEvents(10s, GROUP_SEARING_GRAVITY);
+ DoCastSelf(SPELL_TYMPANIC_TANTRUM);
+ events.Repeat(60s);
+ break;
+ case EVENT_PHASE_CHECK:
+ if (me->HealthBelowPct(_exposeHeartPercent))
+ ExposeHeart();
+ events.Repeat(1s);
+ break;
+ case EVENT_SUBMERGE:
+ DoCastSelf(SPELL_SUBMERGE);
+ Talk(EMOTE_HEART_OPENED);
+ if (Creature* heart = instance->GetCreature(DATA_XT002_HEART))
+ heart->AI()->DoAction(ACTION_START_PHASE_HEART);
+ events.ScheduleEvent(EVENT_DISPOSE_HEART, 30s, PHASE_HEART);
+ break;
+ case EVENT_DISPOSE_HEART:
+ DisposeHeart();
+ break;
+ case EVENT_ENRAGE:
+ Talk(SAY_BERSERK);
+ DoCastSelf(SPELL_ENRAGE);
+ break;
+ case EVENT_ENTER_HARD_MODE:
+ me->SetFullHealth();
+ DoCastSelf(SPELL_HEARTBREAK, true);
+ me->AddLootMode(LOOT_MODE_HARD_MODE_1);
+ _hardMode = true;
+ DisposeHeart(_hardMode);
+ break;
+ case EVENT_RESUME_ATTACK:
+ me->SetReactState(REACT_AGGRESSIVE);
+ RescheduleEvents();
+ break;
+ default:
+ break;
}
- void SetPhaseOne()
- {
- Talk(SAY_HEART_CLOSED);
- Talk(EMOTE_HEART_CLOSED);
-
- me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->SetReactState(REACT_AGGRESSIVE);
- DoCastSelf(SPELL_STAND);
-
- _phase = 1;
-
- events.RescheduleEvent(EVENT_SEARING_LIGHT, TIMER_SEARING_LIGHT / 2);
- events.RescheduleEvent(EVENT_GRAVITY_BOMB, TIMER_GRAVITY_BOMB);
- events.RescheduleEvent(EVENT_TYMPANIC_TANTRUM, urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX));
-
- Unit* heart = me->GetVehicleKit() ? me->GetVehicleKit()->GetPassenger(HEART_VEHICLE_SEAT_EXPOSED) : nullptr;
- if (!heart)
- return;
-
- heart->CastSpell(me, SPELL_HEART_RIDE_VEHICLE, true);
- heart->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- heart->RemoveUnitFlag(UNIT_FLAG_UNK_29);
- heart->RemoveAurasDueToSpell(SPELL_EXPOSED_HEART);
-
- if (!_hardMode)
- {
- if (!_transferHealth)
- _transferHealth = (heart->GetMaxHealth() - heart->GetHealth());
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+ }
- if (_transferHealth >= me->GetHealth())
- _transferHealth = me->GetHealth() - 1;
+ if (events.IsInPhase(PHASE_1))
+ DoMeleeAttackIfReady();
+ }
- me->ModifyHealth(-((int32)_transferHealth));
- me->LowerPlayerDamageReq(_transferHealth);
- }
- }
+private:
+ bool _healthRecovered; // Did a scrapbot recover XT-002's health during the encounter?
+ bool _hardMode; // Are we in hard mode? Or: was the heart killed during phase 2?
+ bool _gravityBombCasualty; // Did someone die because of Gravity Bomb damage?
+ uint8 _exposeHeartPercent;
+};
- private:
- // Achievement related
- bool _healthRecovered; // Did a scrapbot recover XT-002's health during the encounter?
- bool _hardMode; // Are we in hard mode? Or: was the heart killed during phase 2?
- bool _gravityBombCasualty; // Did someone die because of Gravity Bomb damage?
+struct npc_xt002_heart : public NullCreatureAI
+{
+ npc_xt002_heart(Creature* creature) : NullCreatureAI(creature), _instance(creature->GetInstanceScript()) { }
- uint8 _phase;
- uint8 _heartExposed;
- uint32 _transferHealth;
- };
+ void DoAction(int32 action) override
+ {
+ Creature* xt002 = _instance->GetCreature(BOSS_XT002);
+ if (!xt002)
+ return;
- CreatureAI* GetAI(Creature* creature) const override
+ if (action == ACTION_START_PHASE_HEART)
{
- return GetUlduarAI<boss_xt002_AI>(creature);
+ DoCastSelf(SPELL_FULL_HEAL);
+ DoCast(xt002, SPELL_RIDE_VEHICLE_EXPOSED, true);
+ DoCastSelf(SPELL_HEART_OVERLOAD);
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->AddUnitFlag(UNIT_FLAG_UNK_29);
}
+ else if (action == ACTION_DISPOSE_HEART)
+ {
+ DoCast(xt002, SPELL_HEART_RIDE_VEHICLE, true);
+ me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveUnitFlag(UNIT_FLAG_UNK_29);
+ }
+ }
-};
+ void JustDied(Unit* /*killer*/) override
+ {
+ if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
+ xt002->AI()->DoAction(ACTION_ENTER_HARD_MODE);
+ }
-/*-------------------------------------------------------
- *
- * XT-002 HEART
- *
- *///----------------------------------------------------
+private:
+ InstanceScript* _instance;
+};
-class npc_xt002_heart : public CreatureScript
+struct npc_scrapbot : public ScriptedAI
{
- public:
- npc_xt002_heart() : CreatureScript("npc_xt002_heart") { }
-
- struct npc_xt002_heartAI : public NullCreatureAI
- {
- npc_xt002_heartAI(Creature* creature) : NullCreatureAI(creature), _instance(creature->GetInstanceScript()) { }
-
- void JustDied(Unit* /*killer*/) override
- {
- if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
- {
- xt002->AI()->SetData(DATA_TRANSFERED_HEALTH, me->GetHealth());
- xt002->AI()->DoAction(ACTION_ENTER_HARD_MODE);
- }
- }
+ npc_scrapbot(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }
- private:
- InstanceScript* _instance;
- };
+ void Reset() override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ _scheduler.CancelAll();
- CreatureAI* GetAI(Creature* creature) const override
+ if (_instance->GetBossState(BOSS_XT002) != IN_PROGRESS)
{
- return GetUlduarAI<npc_xt002_heartAI>(creature);
+ me->DespawnOrUnsummon();
+ return;
}
-};
-/*-------------------------------------------------------
- *
- * XS-013 SCRAPBOT
- *
- *///----------------------------------------------------
-class npc_scrapbot : public CreatureScript
-{
- public:
- npc_scrapbot() : CreatureScript("npc_scrapbot") { }
-
- struct npc_scrapbotAI : public ScriptedAI
- {
- npc_scrapbotAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- _instance = me->GetInstanceScript();
- }
+ if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
+ xt002->AI()->JustSummoned(me);
- void Initialize()
+ _scheduler.
+ Schedule(2s, [this](TaskContext /*StartMove*/)
{
- _rangeCheckTimer = 500;
- }
-
- void Reset() override
- {
- me->SetReactState(REACT_PASSIVE);
-
- Initialize();
-
if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
me->GetMotionMaster()->MoveFollow(xt002, 0.0f, 0.0f);
- }
-
- void UpdateAI(uint32 diff) override
+ })
+ .Schedule(1s, [this](TaskContext checkXt002)
{
- if (_rangeCheckTimer <= diff)
+ if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
{
- if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
+ if (me->IsWithinMeleeRange(xt002))
{
- if (me->IsWithinMeleeRange(xt002))
+ DoCast(xt002, SPELL_SCRAPBOT_RIDE_VEHICLE);
+ _scheduler.Schedule(1s, [this](TaskContext /*ScrapRepair*/)
{
- DoCast(xt002, SPELL_SCRAPBOT_RIDE_VEHICLE);
- // Unapply vehicle aura again
- xt002->RemoveAurasDueToSpell(SPELL_SCRAPBOT_RIDE_VEHICLE);
- me->DespawnOrUnsummon();
- }
+ if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
+ xt002->CastSpell(me, SPELL_SCRAP_REPAIR, true);
+ me->DespawnOrUnsummon(1s);
+ });
}
+ else
+ checkXt002.Repeat();
}
else
- _rangeCheckTimer -= diff;
- }
+ me->DespawnOrUnsummon();
+ });
+ }
- private:
- InstanceScript* _instance;
- uint32 _rangeCheckTimer;
- };
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
+ }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetUlduarAI<npc_scrapbotAI>(creature);
- }
+private:
+ InstanceScript* _instance;
+ TaskScheduler _scheduler;
};
-/*-------------------------------------------------------
- *
- * XM-024 PUMMELLER
- *
- *///----------------------------------------------------
-class npc_pummeller : public CreatureScript
+struct npc_pummeller : public ScriptedAI
{
- public:
- npc_pummeller() : CreatureScript("npc_pummeller") { }
+ npc_pummeller(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }
- struct npc_pummellerAI : public ScriptedAI
- {
- npc_pummellerAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- _instance = creature->GetInstanceScript();
- }
+ void Reset() override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ _scheduler.CancelAll();
- void Initialize()
- {
- _arcingSmashTimer = TIMER_ARCING_SMASH;
- _trampleTimer = TIMER_TRAMPLE;
- _uppercutTimer = TIMER_UPPERCUT;
- }
+ if (_instance->GetBossState(BOSS_XT002) != IN_PROGRESS)
+ {
+ me->DespawnOrUnsummon();
+ return;
+ }
- void Reset() override
- {
- Initialize();
- if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
- {
- Position pos = xt002->GetPosition();
- me->GetMotionMaster()->MovePoint(0, pos);
- }
- }
+ if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
+ xt002->AI()->JustSummoned(me);
- void UpdateAI(uint32 diff) override
+ _scheduler.
+ Schedule(1s, [this](TaskContext /*StartMove*/)
{
- if (!UpdateVictim())
- return;
-
- if (me->IsWithinMeleeRange(me->GetVictim()))
- {
- if (_arcingSmashTimer <= diff)
- {
- DoCastVictim(SPELL_ARCING_SMASH);
- _arcingSmashTimer = TIMER_ARCING_SMASH;
- }
- else
- _arcingSmashTimer -= diff;
-
- if (_trampleTimer <= diff)
- {
- DoCastVictim(SPELL_TRAMPLE);
- _trampleTimer = TIMER_TRAMPLE;
- }
- else
- _trampleTimer -= diff;
-
- if (_uppercutTimer <= diff)
- {
- DoCastVictim(SPELL_UPPERCUT);
- _uppercutTimer = TIMER_UPPERCUT;
- }
- else
- _uppercutTimer -= diff;
- }
+ me->SetReactState(REACT_AGGRESSIVE);
+ DoZoneInCombat();
+ })
+ .Schedule(17s, [this](TaskContext trample)
+ {
+ DoCastSelf(SPELL_TRAMPLE);
+ trample.Repeat(11s);
+ })
+ .Schedule(19s, [this](TaskContext arcingSmash)
+ {
+ DoCastSelf(SPELL_ARCING_SMASH);
+ arcingSmash.Repeat(8s);
+ })
+ .Schedule(19s, [this](TaskContext upperCut)
+ {
+ DoCastVictim(SPELL_UPPERCUT);
+ upperCut.Repeat(14s);
+ });
- DoMeleeAttackIfReady();
- }
+ }
- private:
- InstanceScript* _instance;
- uint32 _arcingSmashTimer;
- uint32 _trampleTimer;
- uint32 _uppercutTimer;
- };
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- CreatureAI* GetAI(Creature* creature) const override
+ _scheduler.Update(diff, [this]
{
- return GetUlduarAI<npc_pummellerAI>(creature);
- }
+ DoMeleeAttackIfReady();
+ });
+ }
+private:
+ InstanceScript* _instance;
+ TaskScheduler _scheduler;
};
-/*-------------------------------------------------------
- *
- * XE-321 BOOMBOT
- *
- *///----------------------------------------------------
-class npc_boombot : public CreatureScript
+struct npc_boombot : public ScriptedAI
{
- public:
- npc_boombot() : CreatureScript("npc_boombot") { }
+ npc_boombot(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _boomed(false) { }
+
+ void Reset() override
+ {
+ DoCastSelf(SPELL_321_BOOMBOT_AURA);
+ me->SetReactState(REACT_PASSIVE);
+ _scheduler.CancelAll();
- struct npc_boombotAI : public ScriptedAI
+ if (_instance->GetBossState(BOSS_XT002) != IN_PROGRESS)
{
- npc_boombotAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- _instance = creature->GetInstanceScript();
- }
+ me->DespawnOrUnsummon();
+ return;
+ }
- void Initialize()
- {
- _boomed = false;
- }
+ if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
+ xt002->AI()->JustSummoned(me);
- void Reset() override
+ _scheduler.
+ Schedule(4s, [this](TaskContext /*StartMove*/)
{
- Initialize();
-
- DoCast(SPELL_AURA_BOOMBOT); // For achievement
-
- /// @todo proper waypoints?
if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
me->GetMotionMaster()->MoveFollow(xt002, 0.0f, 0.0f);
- }
- void DamageTaken(Unit* /*who*/, uint32& damage) override
+ })
+ .Schedule(1s, [this](TaskContext checkXt002)
{
- if (damage >= (me->GetHealth() - me->GetMaxHealth() * 0.5f) && !_boomed)
+ if (Creature* xt002 = _instance->GetCreature(BOSS_XT002))
{
- _boomed = true; // Prevent recursive calls
-
- WorldPackets::CombatLog::SpellInstakillLog instakill;
- instakill.Caster = me->GetGUID();
- instakill.Target = me->GetGUID();
- instakill.SpellID = SPELL_BOOM;
- me->SendMessageToSet(instakill.Write(), false);
-
- me->KillSelf();
-
- damage = 0;
-
- DoCastAOE(SPELL_BOOM);
+ if (me->IsWithinMeleeRange(xt002))
+ DoCastAOE(SPELL_BOOM);
+ else
+ checkXt002.Repeat();
}
- }
-
- void UpdateAI(uint32 /*diff*/) override
- {
- if (!UpdateVictim())
- return;
-
- // No melee attack
- }
-
- private:
- InstanceScript* _instance;
- bool _boomed;
- };
+ else
+ me->DespawnOrUnsummon();
+ });
+ }
- CreatureAI* GetAI(Creature* creature) const override
+ void DamageTaken(Unit* /*who*/, uint32& damage) override
+ {
+ if (damage >= (me->GetHealth() - me->GetMaxHealth() * 0.5f) && !_boomed)
{
- return GetUlduarAI<npc_boombotAI>(creature);
+ _boomed = true; // Prevent recursive call
+ damage = 0;
+ DoCastAOE(SPELL_BOOM);
}
+ }
-};
+ void UpdateAI(uint32 diff) override
+ {
+ _scheduler.Update(diff);
+ }
+private:
+ InstanceScript* _instance;
+ bool _boomed;
+ TaskScheduler _scheduler;
+};
-class npc_life_spark : public CreatureScript
+struct npc_life_spark : public ScriptedAI
{
- public:
- npc_life_spark() : CreatureScript("npc_life_spark") { }
+ npc_life_spark(Creature* creature) : ScriptedAI(creature) { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetUlduarAI<npc_life_sparkAI>(creature);
- }
+ void Reset() override
+ {
+ DoCastSelf(SPELL_ARCANE_POWER_STATE);
+ _scheduler.CancelAll();
+ }
- struct npc_life_sparkAI : public ScriptedAI
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ DoCastSelf(SPELL_STATIC_CHARGED);
+ _scheduler.Schedule(12s, [this](TaskContext spellShock)
{
- npc_life_sparkAI(Creature* creature) : ScriptedAI(creature){ }
-
- void Reset() override
- {
- DoCastSelf(SPELL_ARCANE_POWER_STATE);
- _scheduler.CancelAll();
- }
-
- void JustEngagedWith(Unit* /*who*/) override
- {
- DoCastSelf(SPELL_STATIC_CHARGED);
- _scheduler.Schedule(Seconds(12), [this](TaskContext spellShock)
- {
- DoCastVictim(SPELL_SHOCK);
- spellShock.Repeat();
- });
- }
+ DoCastVictim(SPELL_SHOCK);
+ spellShock.Repeat();
+ });
+ }
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- _scheduler.Update(diff, [this]
- {
- DoMeleeAttackIfReady();
- });
- }
+ _scheduler.Update(diff, [this]
+ {
+ DoMeleeAttackIfReady();
+ });
+ }
- private:
- TaskScheduler _scheduler;
- };
+private:
+ TaskScheduler _scheduler;
};
-class npc_xt_void_zone : public CreatureScript
+struct npc_xt_void_zone : public PassiveAI
{
-public:
- npc_xt_void_zone() : CreatureScript("npc_xt_void_zone") { }
+ npc_xt_void_zone(Creature* creature) : PassiveAI(creature) { }
- struct npc_xt_void_zoneAI : public PassiveAI
+ void Reset() override
{
- npc_xt_void_zoneAI(Creature* creature) : PassiveAI(creature) { }
+ int32 bp = 0;
+ if (SpellInfo const* createdBySpell = sSpellMgr->GetSpellInfo(me->m_unitData->CreatedBySpell, me->GetMap()->GetDifficultyID()))
+ if (createdBySpell->GetEffects().size() > EFFECT_1)
+ bp = createdBySpell->GetEffect(EFFECT_1).CalcValue();
- void Reset() override
+ _scheduler.Schedule(1s, [this, bp](TaskContext consumption)
{
- _scheduler.Schedule(Seconds(1), [this](TaskContext consumption)
- {
- DoCastSelf(SPELL_CONSUMPTION);
- consumption.Repeat();
- });
- }
-
- void UpdateAI(uint32 diff) override
- {
- _scheduler.Update(diff);
- }
-
- private:
- TaskScheduler _scheduler;
- };
+ CastSpellExtraArgs args(false);
+ if (bp)
+ args.AddSpellBP0(bp);
+ DoCastSelf(SPELL_CONSUMPTION, args);
+ consumption.Repeat();
+ });
+ }
- CreatureAI* GetAI(Creature* creature) const override
+ void UpdateAI(uint32 diff) override
{
- return GetUlduarAI<npc_xt_void_zoneAI>(creature);
+ _scheduler.Update(diff);
}
+private:
+ TaskScheduler _scheduler;
};
-class spell_xt002_searing_light_spawn_life_spark : public SpellScriptLoader
+/* 63018 - Searing Light
+ 65121 - Searing Light */
+class spell_xt002_searing_light_spawn_life_spark : public AuraScript
{
- public:
- spell_xt002_searing_light_spawn_life_spark() : SpellScriptLoader("spell_xt002_searing_light_spawn_life_spark") { }
+ PrepareAuraScript(spell_xt002_searing_light_spawn_life_spark);
- class spell_xt002_searing_light_spawn_life_spark_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_xt002_searing_light_spawn_life_spark_AuraScript);
-
- bool Validate(SpellInfo const* /*spell*/) override
- {
- return ValidateSpellInfo({ SPELL_SUMMON_LIFE_SPARK });
- }
-
- void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
- {
- if (GetOwner()->GetTypeId() == TYPEID_PLAYER)
- if (Unit* xt002 = GetCaster())
- if (xt002->HasAura(aurEff->GetAmount())) // Heartbreak aura indicating hard mode
- xt002->CastSpell(GetUnitOwner(), SPELL_SUMMON_LIFE_SPARK, true);
- }
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return ValidateSpellInfo({ SPELL_SUMMON_LIFE_SPARK });
+ }
- void Register() override
- {
- AfterEffectRemove += AuraEffectRemoveFn(spell_xt002_searing_light_spawn_life_spark_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
- }
- };
+ void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ if (Unit* xt002 = GetCaster())
+ if (xt002->HasAura(aurEff->GetAmount())) // Heartbreak aura indicating hard mode
+ xt002->CastSpell(GetOwner(), SPELL_SUMMON_LIFE_SPARK, true);
+ }
- AuraScript* GetAuraScript() const override
- {
- return new spell_xt002_searing_light_spawn_life_spark_AuraScript();
- }
+ void Register() override
+ {
+ AfterEffectRemove += AuraEffectRemoveFn(spell_xt002_searing_light_spawn_life_spark::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
+ }
};
-class spell_xt002_gravity_bomb_aura : public SpellScriptLoader
+/* 63024 - Gravity Bomb
+ 64234 - Gravity Bomb */
+class spell_xt002_gravity_bomb_aura : public AuraScript
{
- public:
- spell_xt002_gravity_bomb_aura() : SpellScriptLoader("spell_xt002_gravity_bomb_aura") { }
+ PrepareAuraScript(spell_xt002_gravity_bomb_aura);
- class spell_xt002_gravity_bomb_aura_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_xt002_gravity_bomb_aura_AuraScript);
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return ValidateSpellInfo({ SPELL_SUMMON_VOID_ZONE });
+ }
- bool Validate(SpellInfo const* /*spell*/) override
- {
- return ValidateSpellInfo({ SPELL_SUMMON_VOID_ZONE });
- }
+ void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ if (Unit* xt002 = GetCaster())
+ if (xt002->HasAura(aurEff->GetAmount())) // Heartbreak aura indicating hard mode
+ xt002->CastSpell(GetOwner(), SPELL_SUMMON_VOID_ZONE, true);
+ }
- void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
- {
- if (GetOwner()->GetTypeId() == TYPEID_PLAYER)
- if (Unit* xt002 = GetCaster())
- if (xt002->HasAura(aurEff->GetAmount())) // Heartbreak aura indicating hard mode
- xt002->CastSpell(GetUnitOwner(), SPELL_SUMMON_VOID_ZONE, true);
- }
+ void OnPeriodic(AuraEffect const* aurEff)
+ {
+ Unit* xt002 = GetCaster();
+ Unit* owner = GetTarget();
+ if (!xt002)
+ return;
- void OnPeriodic(AuraEffect const* aurEff)
- {
- Unit* xt002 = GetCaster();
- if (!xt002)
- return;
+ if (aurEff->GetAmount() >= int32(owner->GetHealth()))
+ xt002->GetAI()->SetData(DATA_GRAVITY_BOMB_CASUALTY, 1);
+ }
- Unit* owner = GetOwner()->ToUnit();
- if (!owner)
- return;
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_xt002_gravity_bomb_aura::OnPeriodic, EFFECT_2, SPELL_AURA_PERIODIC_DAMAGE);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_xt002_gravity_bomb_aura::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
+ }
+};
- if (aurEff->GetAmount() >= int32(owner->GetHealth()))
- if (xt002->GetAI())
- xt002->GetAI()->SetData(DATA_GRAVITY_BOMB_CASUALTY, 1);
- }
+/* 63025 - Gravity Bomb
+ 64233 - Gravity Bomb */
+class spell_xt002_gravity_bomb_damage : public SpellScript
+{
+ PrepareSpellScript(spell_xt002_gravity_bomb_damage);
- void Register() override
- {
- OnEffectPeriodic += AuraEffectPeriodicFn(spell_xt002_gravity_bomb_aura_AuraScript::OnPeriodic, EFFECT_2, SPELL_AURA_PERIODIC_DAMAGE);
- AfterEffectRemove += AuraEffectRemoveFn(spell_xt002_gravity_bomb_aura_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
- }
- };
+ void HandleScript(SpellEffIndex /*eff*/)
+ {
+ Unit* caster = GetCaster();
+ if (GetHitDamage() >= int32(GetHitUnit()->GetHealth()))
+ caster->GetAI()->SetData(DATA_GRAVITY_BOMB_CASUALTY, 1);
+ }
- AuraScript* GetAuraScript() const override
- {
- return new spell_xt002_gravity_bomb_aura_AuraScript();
- }
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_xt002_gravity_bomb_damage::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
+ }
};
-class spell_xt002_gravity_bomb_damage : public SpellScriptLoader
+// 62791 - XT-002 Heart Overload Trigger Spell (SERVERSIDE)
+class spell_xt002_heart_overload_periodic : public SpellScript
{
- public:
- spell_xt002_gravity_bomb_damage() : SpellScriptLoader("spell_xt002_gravity_bomb_damage") { }
+ PrepareSpellScript(spell_xt002_heart_overload_periodic);
- class spell_xt002_gravity_bomb_damage_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_xt002_gravity_bomb_damage_SpellScript);
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return ValidateSpellInfo
+ ({
+ SPELL_ENERGY_ORB,
+ SPELL_HEART_LIGHTNING_TETHER
+ });
+ }
- void HandleScript(SpellEffIndex /*eff*/)
- {
- Unit* caster = GetCaster();
- if (!caster)
- return;
+ Creature* GetRandomToyPile()
+ {
+ std::list<Creature*> possibleCreatures;
+ Unit* caster = GetCaster();
+ caster->GetCreatureListWithEntryInGrid(possibleCreatures, NPC_XT_TOY_PILE);
+ possibleCreatures.remove_if([caster](Creature* creature)
+ {
+ return caster->GetDistance2d(creature) < 60.0f;
+ });
- if (GetHitDamage() >= int32(GetHitUnit()->GetHealth()))
- if (caster->GetAI())
- caster->GetAI()->SetData(DATA_GRAVITY_BOMB_CASUALTY, 1);
- }
+ if (possibleCreatures.empty())
+ return nullptr;
- void Register() override
- {
- OnEffectHitTarget += SpellEffectFn(spell_xt002_gravity_bomb_damage_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
- }
- };
+ return Trinity::Containers::SelectRandomContainerElement(possibleCreatures);
+ }
- SpellScript* GetSpellScript() const override
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ Unit* caster = GetCaster();
+ if (Creature* toyPile = GetRandomToyPile())
{
- return new spell_xt002_gravity_bomb_damage_SpellScript();
+ caster->CastSpell(toyPile, SPELL_ENERGY_ORB, true);
+ caster->CastSpell(nullptr, SPELL_HEART_LIGHTNING_TETHER, true);
}
+ }
+
+ void Register() override
+ {
+ OnEffectHit += SpellEffectFn(spell_xt002_heart_overload_periodic::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
};
-class spell_xt002_heart_overload_periodic : public SpellScriptLoader
+// 62826 Energy Orb
+class spell_xt002_energy_orb : public SpellScript
{
- public:
- spell_xt002_heart_overload_periodic() : SpellScriptLoader("spell_xt002_heart_overload_periodic") { }
+ PrepareSpellScript(spell_xt002_energy_orb);
- class spell_xt002_heart_overload_periodic_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_xt002_heart_overload_periodic_SpellScript);
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return ValidateSpellInfo
+ ({
+ SPELL_RECHARGE_BOOMBOT,
+ SPELL_RECHARGE_PUMMELER,
+ SPELL_RECHARGE_SCRAPBOT
+ });
+ }
- bool Validate(SpellInfo const* /*spell*/) override
- {
- return ValidateSpellInfo({ SPELL_ENERGY_ORB, SPELL_RECHARGE_BOOMBOT, SPELL_RECHARGE_PUMMELER, SPELL_RECHARGE_SCRAPBOT });
- }
+ void HandleSummons(SpellEffIndex /*effIndex*/)
+ {
+ Unit* target = GetHitUnit();
+ if (target->GetEntry() != NPC_XT_TOY_PILE)
+ return;
- void HandleScript(SpellEffIndex /*effIndex*/)
- {
- if (Unit* caster = GetCaster())
- {
- if (InstanceScript* instance = caster->GetInstanceScript())
- {
- if (Unit* toyPile = ObjectAccessor::GetUnit(*caster, instance->GetGuidData(DATA_TOY_PILE_0 + urand(0, 3))))
- {
- caster->CastSpell(toyPile, SPELL_ENERGY_ORB, true);
-
- // This should probably be incorporated in a dummy effect handler, but I've had trouble getting the correct target
- // Weighed randomization (approximation)
- uint32 const spells[] = { SPELL_RECHARGE_SCRAPBOT, SPELL_RECHARGE_SCRAPBOT, SPELL_RECHARGE_SCRAPBOT,
- SPELL_RECHARGE_PUMMELER, SPELL_RECHARGE_BOOMBOT };
-
- for (uint8 i = 0; i < 5; ++i)
- {
- uint8 a = urand(0, 4);
- uint32 spellId = spells[a];
- toyPile->CastSpell(toyPile, spellId, CastSpellExtraArgs(TRIGGERED_FULL_MASK)
- .SetOriginalCaster(instance->GetGuidData(BOSS_XT002)));
- }
- }
- }
+ target->CastSpell(target, SPELL_RECHARGE_BOOMBOT, true);
- if (Creature* base = caster->GetVehicleCreatureBase())
- base->AI()->Talk(SAY_SUMMON);
- }
- }
+ if (roll_chance_i(30))
+ target->CastSpell(target, SPELL_RECHARGE_PUMMELER, true);
- void Register() override
- {
- OnEffectHit += SpellEffectFn(spell_xt002_heart_overload_periodic_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
- }
- };
+ for (uint8 i = 0; i < urand(5, 7); ++i)
+ target->CastSpell(target, SPELL_RECHARGE_SCRAPBOT, true);
- SpellScript* GetSpellScript() const override
- {
- return new spell_xt002_heart_overload_periodic_SpellScript();
- }
+ if (Creature* base = GetCaster()->GetVehicleCreatureBase())
+ base->AI()->Talk(SAY_SUMMON);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_xt002_energy_orb::HandleSummons, EFFECT_2, SPELL_EFFECT_DUMMY);
+ }
};
-class spell_xt002_tympanic_tantrum : public SpellScriptLoader
+// 62775 - Tympanic Tantrum
+class spell_xt002_tympanic_tantrum : public SpellScript
{
- public:
- spell_xt002_tympanic_tantrum() : SpellScriptLoader("spell_xt002_tympanic_tantrum") { }
+ PrepareSpellScript(spell_xt002_tympanic_tantrum);
- class spell_xt002_tympanic_tantrum_SpellScript : public SpellScript
+ void FilterTargets(std::list<WorldObject*>& targets)
+ {
+ targets.remove_if([](WorldObject* object) -> bool
{
- PrepareSpellScript(spell_xt002_tympanic_tantrum_SpellScript);
+ if (object->GetTypeId() == TYPEID_PLAYER)
+ return false;
- void FilterTargets(std::list<WorldObject*>& targets)
- {
- targets.remove_if([](WorldObject* target)
- {
- return target->GetTypeId() != TYPEID_PLAYER && (target->GetTypeId() != TYPEID_UNIT || !target->ToUnit()->IsPet());
- });
- }
+ if (Creature* creature = object->ToCreature())
+ return !creature->IsPet();
- void RecalculateDamage()
- {
- SetHitDamage(GetHitUnit()->CountPctFromMaxHealth(GetHitDamage()));
- }
+ return true;
+ });
+ }
- void Register() override
- {
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_xt002_tympanic_tantrum_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_xt002_tympanic_tantrum_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
- OnHit += SpellHitFn(spell_xt002_tympanic_tantrum_SpellScript::RecalculateDamage);
- }
- };
+ void RecalculateDamage()
+ {
+ SetHitDamage(GetHitUnit()->CountPctFromMaxHealth(GetHitDamage()));
+ }
- SpellScript* GetSpellScript() const override
- {
- return new spell_xt002_tympanic_tantrum_SpellScript();
- }
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_xt002_tympanic_tantrum::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_xt002_tympanic_tantrum::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnHit += SpellHitFn(spell_xt002_tympanic_tantrum::RecalculateDamage);
+ }
};
-class spell_xt002_submerged : public SpellScriptLoader
+// 37751 - Submerged
+class spell_xt002_submerged : public SpellScript
{
- public:
- spell_xt002_submerged() : SpellScriptLoader("spell_xt002_submerged") { }
+ PrepareSpellScript(spell_xt002_submerged);
- class spell_xt002_submerged_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_xt002_submerged_SpellScript);
+ void HandleScript(SpellEffIndex /*eff*/)
+ {
+ Creature* target = GetHitCreature();
+ if (!target)
+ return;
- void HandleScript(SpellEffIndex /*eff*/)
- {
- Creature* target = GetHitCreature();
- if (!target)
- return;
+ target->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ target->SetStandState(UNIT_STAND_STATE_SUBMERGED);
+ }
- target->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- target->SetStandState(UNIT_STAND_STATE_SUBMERGED);
- }
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_xt002_submerged::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+};
- void Register() override
- {
- OnEffectHitTarget += SpellEffectFn(spell_xt002_submerged_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
- }
- };
+// 65032 - 321-Boombot Aura
+class spell_xt002_321_boombot_aura : public AuraScript
+{
+ PrepareAuraScript(spell_xt002_321_boombot_aura);
- SpellScript* GetSpellScript() const override
- {
- return new spell_xt002_submerged_SpellScript();
- }
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS });
+ }
+
+ bool CheckProc(ProcEventInfo& eventInfo)
+ {
+ if (eventInfo.GetActionTarget()->GetEntry() != NPC_XS013_SCRAPBOT)
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect* /*aurEff*/, ProcEventInfo& eventInfo)
+ {
+ if (InstanceScript* instance = eventInfo.GetActor()->GetInstanceScript())
+ instance->DoCastSpellOnPlayers(SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS);
+ }
+
+ void Register() override
+ {
+ DoCheckProc += AuraCheckProcFn(spell_xt002_321_boombot_aura::CheckProc);
+ OnEffectProc += AuraEffectProcFn(spell_xt002_321_boombot_aura::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
+ }
};
-class spell_xt002_321_boombot_aura : public SpellScriptLoader
+// 63849 - Exposed Heart
+class spell_xt002_exposed_heart : public AuraScript
{
- public:
- spell_xt002_321_boombot_aura() : SpellScriptLoader("spell_xt002_321_boombot_aura") { }
+ PrepareAuraScript(spell_xt002_exposed_heart);
- class spell_xt002_321_boombot_aura_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_xt002_321_boombot_aura_AuraScript);
-
- bool Validate(SpellInfo const* /*spellInfo*/) override
- {
- return ValidateSpellInfo({ SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS });
- }
+ bool Load() override
+ {
+ _damageAmount = 0;
+ return true;
+ }
- bool CheckProc(ProcEventInfo& eventInfo)
- {
- if (eventInfo.GetActionTarget()->GetEntry() != NPC_XS013_SCRAPBOT)
- return false;
- return true;
- }
+ void OnProc(AuraEffect* /*aurEff*/, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ DamageInfo* damageInfo = eventInfo.GetDamageInfo();
+ if (!damageInfo || !damageInfo->GetDamage())
+ return;
- void HandleProc(AuraEffect* /*aurEff*/, ProcEventInfo& eventInfo)
- {
- InstanceScript* instance = eventInfo.GetActor()->GetInstanceScript();
- if (!instance)
- return;
+ _damageAmount += damageInfo->GetDamage();
+ }
- instance->DoCastSpellOnPlayers(SPELL_ACHIEVEMENT_CREDIT_NERF_SCRAPBOTS);
- }
+ void HandleLifeTransfer(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (InstanceScript* instance = GetTarget()->GetInstanceScript())
+ if (Creature* xt002 = instance->GetCreature(BOSS_XT002))
+ xt002->AI()->SetData(DATA_TRANSFERED_HEALTH, _damageAmount);
+ }
- void Register() override
- {
- DoCheckProc += AuraCheckProcFn(spell_xt002_321_boombot_aura_AuraScript::CheckProc);
- OnEffectProc += AuraEffectProcFn(spell_xt002_321_boombot_aura_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY);
- }
- };
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_xt002_exposed_heart::OnProc, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_xt002_exposed_heart::HandleLifeTransfer, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL);
+ }
- AuraScript* GetAuraScript() const override
- {
- return new spell_xt002_321_boombot_aura_AuraScript();
- }
+private:
+ uint32 _damageAmount;
};
class achievement_nerf_engineering : public AchievementCriteriaScript
@@ -1110,22 +1001,23 @@ class achievement_nerf_gravity_bombs : public AchievementCriteriaScript
void AddSC_boss_xt002()
{
- new npc_xt002_heart();
- new npc_scrapbot();
- new npc_pummeller();
- new npc_boombot();
-
- new npc_life_spark();
- new npc_xt_void_zone();
- new boss_xt002();
-
- new spell_xt002_searing_light_spawn_life_spark();
- new spell_xt002_gravity_bomb_aura();
- new spell_xt002_gravity_bomb_damage();
- new spell_xt002_heart_overload_periodic();
- new spell_xt002_tympanic_tantrum();
- new spell_xt002_submerged();
- new spell_xt002_321_boombot_aura();
+ RegisterUlduarCreatureAI(boss_xt002);
+ RegisterUlduarCreatureAI(npc_xt002_heart);
+ RegisterUlduarCreatureAI(npc_scrapbot);
+ RegisterUlduarCreatureAI(npc_pummeller);
+ RegisterUlduarCreatureAI(npc_boombot);
+ RegisterUlduarCreatureAI(npc_life_spark);
+ RegisterUlduarCreatureAI(npc_xt_void_zone);
+
+ RegisterAuraScript(spell_xt002_searing_light_spawn_life_spark);
+ RegisterAuraScript(spell_xt002_gravity_bomb_aura);
+ RegisterSpellScript(spell_xt002_gravity_bomb_damage);
+ RegisterSpellScript(spell_xt002_heart_overload_periodic);
+ RegisterSpellScript(spell_xt002_energy_orb);
+ RegisterSpellScript(spell_xt002_tympanic_tantrum);
+ RegisterSpellScript(spell_xt002_submerged);
+ RegisterAuraScript(spell_xt002_321_boombot_aura);
+ RegisterAuraScript(spell_xt002_exposed_heart);
new achievement_nerf_engineering();
new achievement_heartbreaker();
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
index 18456688ede..c34a889f1d4 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
@@ -115,6 +115,7 @@ ObjectData const creatureData[] =
{ NPC_LORE_KEEPER_OF_NORGANNON, DATA_LORE_KEEPER_OF_NORGANNON },
{ NPC_HIGH_EXPLORER_DELLORAH, DATA_DELLORAH },
{ NPC_BRONZEBEARD_RADIO, DATA_BRONZEBEARD_RADIO },
+ { NPC_HEART_OF_DECONSTRUCTOR, DATA_XT002_HEART },
{ 0, 0, }
};
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h
index e845a503619..1e6e77b6c71 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h
@@ -96,6 +96,7 @@ enum UlduarNPCs
//XT002
NPC_XS013_SCRAPBOT = 33343,
+ NPC_HEART_OF_DECONSTRUCTOR = 33329,
// Flame Leviathan
NPC_ULDUAR_COLOSSUS = 33237,
@@ -409,6 +410,7 @@ enum UlduarData
DATA_TOY_PILE_1,
DATA_TOY_PILE_2,
DATA_TOY_PILE_3,
+ DATA_XT002_HEART,
// Assembly of Iron
DATA_STEELBREAKER,