diff options
Diffstat (limited to 'src')
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, |