diff options
Diffstat (limited to 'src')
3 files changed, 400 insertions, 419 deletions
diff --git a/src/server/scripts/Northrend/Ulduar/ulduar/boss_xt002.cpp b/src/server/scripts/Northrend/Ulduar/ulduar/boss_xt002.cpp index de46497d2b2..aa958610c93 100644 --- a/src/server/scripts/Northrend/Ulduar/ulduar/boss_xt002.cpp +++ b/src/server/scripts/Northrend/Ulduar/ulduar/boss_xt002.cpp @@ -20,11 +20,14 @@ Add achievments Boombot explosion only hurt allies to the npc at the moment Boombot explosion visual - Test the enrage timer - Fix gravity bomb - tractor beam. - Fix void zone spell - If the boss is to close to a scrap pile -> no summon - make the life sparks visible... + Fix gravity bomb - tractor beam. /? Need test + Fix void zone spell /? Need test + If the boss is to close to a scrap pile -> no summon + make the life sparks visible... /? Need test + Phase transition kneel/stand up animation + Tympanic Tantrum needs its range reduced + Proper scripts for adds (scrapbots should enter vehicle) + Codestyle */ #include "ScriptMgr.h" @@ -37,6 +40,9 @@ enum Spells SPELL_SEARING_LIGHT_10 = 63018, SPELL_SEARING_LIGHT_25 = 65121, + SPELL_SUMMON_LIFE_SPARK = 64210, + SPELL_SUMMON_VOID_ZONE = 64203, + SPELL_GRAVITY_BOMB_10 = 63024, SPELL_GRAVITY_BOMB_25 = 64234, SPELL_GRAVITY_BOMB_AURA_10 = 63025, @@ -45,7 +51,22 @@ enum Spells SPELL_HEARTBREAK_10 = 65737, SPELL_HEARTBREAK_25 = 64193, + // 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_HEART_RIDE_VEHICLE = 63313, SPELL_ENRAGE = 26662, + SPELL_STAND = 37752, + SPELL_SUBMERGE = 37751, //------------------VOID ZONE-------------------- SPELL_VOID_ZONE_10 = 64203, @@ -58,26 +79,42 @@ enum Spells //----------------XT-002 HEART------------------- SPELL_EXPOSED_HEART = 63849, + // Channeled //---------------XM-024 PUMMELLER---------------- SPELL_ARCING_SMASH = 8374, SPELL_TRAMPLE = 5568, SPELL_UPPERCUT = 10966, + // Scrabot: + SPELL_SCRAPBOT_RIDE_VEHICLE = 47020, + SPELL_SUICIDE = 7, + //------------------BOOMBOT----------------------- SPELL_BOOM = 62834, }; +enum Events +{ + EVENT_TYMPANIC_TANTRUM = 1, + EVENT_SEARING_LIGHT, + EVENT_GRAVITY_BOMB, + EVENT_HEART_PHASE, + EVENT_ENERGY_ORB, + EVENT_DISPOSE_HEART, + EVENT_ENRAGE, +}; + enum Timers { TIMER_TYMPANIC_TANTRUM_MIN = 32000, TIMER_TYMPANIC_TANTRUM_MAX = 36000, TIMER_SEARING_LIGHT = 20000, - TIMER_SPAWN_LIFE_SPARK = 9000, TIMER_GRAVITY_BOMB = 20000, TIMER_HEART_PHASE = 30000, + TIMER_ENERGY_ORB_MIN = 9000, + TIMER_ENERGY_ORB_MAX = 10000, TIMER_ENRAGE = 600000, - TIMER_GRAVITY_BOMB_AURA = 8900, TIMER_VOID_ZONE = 3000, @@ -131,33 +168,15 @@ enum ACHIEV_TIMED_START_EVENT = 21027, }; -//#define GRAVITY_BOMB_DMG_MIN_10 11700 -//#define GRAVITY_BOMB_DMG_MAX_10 12300 -//#define GRAVITY_BOMB_DMG_MIN_25 14625 -//#define GRAVITY_BOMB_DMG_MAX_25 15375 -//#define GRAVITY_BOMB_RADIUS 12 - -//#define VOID_ZONE_DMG_10 5000 -//#define VOID_ZONE_DMG_25 7500 -//#define VOID_ZONE_RADIUS - -/************************************************ ------------------SPAWN LOCATIONS----------------- -************************************************/ -//Shared Z-level -#define SPAWN_Z 412 -//Lower right -#define LR_X 796 -#define LR_Y -94 -//Lower left -#define LL_X 796 -#define LL_Y 57 -//Upper right -#define UR_X 890 -#define UR_Y -82 -//Upper left -#define UL_X 894 -#define UL_Y 62 +const Position SpawnPos[4] = +{ + {888.69f, 25.63f, 409.81f, 1.58f}, + {896.74f, 68.08f, 412.24f, 4.03f}, + {895.88f, -93.45f, 441.95f, 2.21f}, + {787.33f, -92.33f, 412.01f, 0.83f} +}; + +#define HEART_VEHICLE_SEAT 0 /*------------------------------------------------------- * @@ -166,357 +185,236 @@ enum *///---------------------------------------------------- class boss_xt002 : public CreatureScript { -public: - boss_xt002() : CreatureScript("boss_xt002") { } + public: + boss_xt002() : CreatureScript("boss_xt002") { } - CreatureAI* GetAI(Creature* pCreature) const - { - return GetUlduarAI<boss_xt002_AI>(pCreature); - } - - struct boss_xt002_AI : public BossAI - { - boss_xt002_AI(Creature *pCreature) : BossAI(pCreature, BOSS_XT002) + CreatureAI* GetAI(Creature* pCreature) const { + return GetUlduarAI<boss_xt002_AI>(pCreature); } - uint32 uiSearingLightTimer; - uint32 uiSpawnLifeSparkTimer; - uint32 uiGravityBombTimer; - uint32 uiGravityBombAuraTimer; - uint32 uiTympanicTantrumTimer; - uint32 uiHeartPhaseTimer; - uint32 uiSpawnAddTimer; - uint32 uiEnrageTimer; - - bool searing_light_active; - uint64 uiSearingLightTarget; + struct boss_xt002_AI : public BossAI + { + boss_xt002_AI(Creature *pCreature) : BossAI(pCreature, BOSS_XT002) + { + } - bool gravity_bomb_active; - uint64 uiGravityBombTarget; + uint8 _phase; + uint8 _heartExposed; - uint8 phase; - uint8 heart_exposed; - bool enraged; + uint32 transferHealth; + bool enterHardMode; + bool hardMode; - uint32 transferHealth; - bool enterHardMode; - bool hardMode; + void Reset() + { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); - void Reset() - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); - - //Makes XT-002 to cast a light bomb 10 seconds after aggro. - uiSearingLightTimer = TIMER_SEARING_LIGHT/2; - uiSpawnLifeSparkTimer = TIMER_SPAWN_LIFE_SPARK; - uiGravityBombTimer = TIMER_GRAVITY_BOMB; - uiGravityBombAuraTimer = TIMER_GRAVITY_BOMB_AURA; - uiHeartPhaseTimer = TIMER_HEART_PHASE; - uiSpawnAddTimer = TIMER_SPAWN_ADD; - uiEnrageTimer = TIMER_ENRAGE; - - //Tantrum is casted a bit slower the first time. - uiTympanicTantrumTimer = urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX) * 2; - - searing_light_active = false; - gravity_bomb_active = false; - enraged = false; - hardMode = false; - enterHardMode = false; - - phase = 1; - heart_exposed = 0; - - if (instance) - instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - } + hardMode = false; + enterHardMode = false; - void EnterCombat(Unit* /*who*/) - { - DoScriptText(SAY_AGGRO, me); - _EnterCombat(); + _phase = 1; + _heartExposed = 0; - if (instance) - instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - } + if (instance) + instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + } - void DoAction(const int32 action) - { - switch (action) + void EnterCombat(Unit* /*who*/) { - case ACTION_ENTER_HARD_MODE: - if (!hardMode) - { - hardMode = true; + DoScriptText(SAY_AGGRO, me); + _EnterCombat(); - // Enter hard mode - enterHardMode = true; + events.ScheduleEvent(EVENT_ENRAGE, TIMER_ENRAGE); + events.ScheduleEvent(EVENT_GRAVITY_BOMB, TIMER_GRAVITY_BOMB); + events.ScheduleEvent(EVENT_SEARING_LIGHT, TIMER_SEARING_LIGHT); + //Tantrum is casted a bit slower the first time. + events.ScheduleEvent(EVENT_TYMPANIC_TANTRUM, urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX) * 2); - // set max health - me->SetFullHealth(); - - // Get his heartbreak buff - me->CastSpell(me, RAID_MODE(SPELL_HEARTBREAK_10, SPELL_HEARTBREAK_25), true); - } - break; + if (instance) + instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); } - } - void SetData(uint32 id, uint32 value) - { - switch(id) + void DoAction(const int32 action) { - case DATA_TRANSFERED_HEALTH: - transferHealth = value; - break; - } - } + switch (action) + { + case ACTION_ENTER_HARD_MODE: + if (!hardMode) + { + hardMode = true; - void KilledUnit(Unit* /*victim*/) - { - DoScriptText(RAND(SAY_SLAY_1, SAY_SLAY_2), me); - } + // Enter hard mode + enterHardMode = true; - void JustDied(Unit * /*victim*/) - { - DoScriptText(SAY_DEATH, me); - _JustDied(); - } + // set max health + me->SetFullHealth(); - void UpdateAI(const uint32 diff) - { - if (!UpdateVictim()) - return; + // Get his heartbreak buff + me->CastSpell(me, RAID_MODE(SPELL_HEARTBREAK_10, SPELL_HEARTBREAK_25), true); - if (enterHardMode) - { - SetPhaseOne(); - enterHardMode = false; + me->AddLootMode(LOOT_MODE_HARD_MODE_1); + } + break; + } } - // Handles spell casting. These spells only occur during phase 1 and hard mode - if (phase == 1 || hardMode) + void SetData(uint32 id, uint32 value) { - if (uiSearingLightTimer <= diff) + switch(id) { - if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - DoCast(pTarget, RAID_MODE(SPELL_SEARING_LIGHT_10, SPELL_SEARING_LIGHT_25)); - uiSearingLightTarget = pTarget->GetGUID(); - } - uiSpawnLifeSparkTimer = TIMER_SPAWN_LIFE_SPARK; - if (hardMode) - searing_light_active = true; - uiSearingLightTimer = TIMER_SEARING_LIGHT; - } else uiSearingLightTimer -= diff; + case DATA_TRANSFERED_HEALTH: + transferHealth = value; + break; + } + } - if (uiGravityBombTimer <= diff) - { - if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - DoCast(pTarget, RAID_MODE(SPELL_GRAVITY_BOMB_10, SPELL_GRAVITY_BOMB_25)); - uiGravityBombTarget = pTarget->GetGUID(); - } - uiGravityBombTimer = TIMER_GRAVITY_BOMB; - gravity_bomb_active = true; - } else uiGravityBombTimer -= diff; + void KilledUnit(Unit* /*victim*/) + { + DoScriptText(RAND(SAY_SLAY_1, SAY_SLAY_2), me); + } - if (uiTympanicTantrumTimer <= diff) - { - DoScriptText(SAY_TYMPANIC_TANTRUM, me); - DoCast(SPELL_TYMPANIC_TANTRUM); - uiTympanicTantrumTimer = urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX); - } else uiTympanicTantrumTimer -= diff; + void JustDied(Unit * /*victim*/) + { + DoScriptText(SAY_DEATH, me); + _JustDied(); } - if (!hardMode) + void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) { - if (phase == 1) - { - if (HealthBelowPct(75) && heart_exposed == 0) - { - exposeHeart(); - } - else if (HealthBelowPct(50) && heart_exposed == 1) - { - exposeHeart(); - } - else if (HealthBelowPct(25) && heart_exposed == 2) - { - exposeHeart(); - } + if (!hardMode && _phase == 1 && !HealthAbovePct(100 - 25 * (_heartExposed+1))) + ExposeHeart(); + } - DoMeleeAttackIfReady(); - } - else - { - //Stop moving - me->StopMoving(); + void UpdateAI(const uint32 diff) + { + if (_phase == 1 && !UpdateVictim()) + return; - //Start summoning adds - if (uiSpawnAddTimer <= diff) - { - DoScriptText(SAY_SUMMON, me); + events.Update(diff); - // Spawn Pummeller - switch (rand() % 4) - { - case 0: me->SummonCreature(NPC_XM024_PUMMELLER, LR_X, LR_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 1: me->SummonCreature(NPC_XM024_PUMMELLER, LL_X, LL_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 2: me->SummonCreature(NPC_XM024_PUMMELLER, UR_X, UR_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 3: me->SummonCreature(NPC_XM024_PUMMELLER, UL_X, UL_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - } + if (enterHardMode) + { + SetPhaseOne(); + enterHardMode = false; + } - // Spawn 5 Bombs - for (int8 n = 0; n < 5; n++) - { - //Some randomes are added so they wont spawn in a pile - switch(rand() % 4) - { - case 0: me->SummonCreature(NPC_XS013_SCRAPBOT, float(irand(LR_X - 3, LR_X + 3)), float(irand(LR_Y - 3, LR_Y + 3)), SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 1: me->SummonCreature(NPC_XS013_SCRAPBOT, float(irand(LL_X - 3, LL_X + 3)), float(irand(LL_Y - 3, LL_Y + 3)), SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 2: me->SummonCreature(NPC_XS013_SCRAPBOT, float(irand(UR_X - 3, UR_X + 3)), float(irand(UR_Y - 3, UR_Y + 3)), SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 3: me->SummonCreature(NPC_XS013_SCRAPBOT, float(irand(UL_X - 3, UL_X + 3)), float(irand(UL_Y - 3, UL_Y + 3)), SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - } - } + if (me->HasUnitState(UNIT_STAT_CASTING)) + return; - //Spawn 5 Scrapbots - switch (rand() % 4) + // Handles spell casting. These spells only occur during phase 1 and hard mode + if (_phase == 1) + { + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - case 0: me->SummonCreature(NPC_XE321_BOOMBOT, LR_X, LR_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 1: me->SummonCreature(NPC_XE321_BOOMBOT, LL_X, LL_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 2: me->SummonCreature(NPC_XE321_BOOMBOT, UR_X, UR_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; - case 3: me->SummonCreature(NPC_XE321_BOOMBOT, UL_X, UL_Y, SPAWN_Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); break; + case EVENT_SEARING_LIGHT: + if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(pTarget, RAID_MODE(SPELL_SEARING_LIGHT_10, SPELL_SEARING_LIGHT_25)); + + events.RepeatEvent(TIMER_SEARING_LIGHT); + break; + case EVENT_GRAVITY_BOMB: + if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(pTarget, RAID_MODE(SPELL_GRAVITY_BOMB_10, SPELL_GRAVITY_BOMB_25)); + + events.RepeatEvent(TIMER_GRAVITY_BOMB); + break; + case EVENT_TYMPANIC_TANTRUM: + DoScriptText(SAY_TYMPANIC_TANTRUM, me); + DoCast(SPELL_TYMPANIC_TANTRUM); + events.RepeatEvent(urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX)); + break; + case EVENT_ENRAGE: + DoScriptText(SAY_BERSERK, me); + DoCast(me, SPELL_ENRAGE); + break; } - - uiSpawnAddTimer = TIMER_SPAWN_ADD; - } else uiSpawnAddTimer -= diff; - - // Is the phase over? - if (uiHeartPhaseTimer <= diff) - { - DoScriptText(SAY_HEART_CLOSED, me); - SetPhaseOne(); } - else - uiHeartPhaseTimer -= diff; + DoMeleeAttackIfReady(); } - } - else - { - // Adding life sparks when searing light debuff runs out if hard mode - if (searing_light_active) + else if (_phase == 2) { - if (uiSpawnLifeSparkTimer <= diff) + while (uint32 eventId = events.ExecuteEvent()) { - if (Unit *pSearingLightTarget = me->GetUnit(*me, uiSearingLightTarget)) - pSearingLightTarget->SummonCreature(NPC_LIFE_SPARK, pSearingLightTarget->GetPositionX(), pSearingLightTarget->GetPositionY(), pSearingLightTarget->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); - uiSpawnLifeSparkTimer = TIMER_SPAWN_LIFE_SPARK; - searing_light_active = false; - } else uiSpawnLifeSparkTimer -= diff; + switch (eventId) + case EVENT_DISPOSE_HEART: + SetPhaseOne(); + break; + } } - - DoMeleeAttackIfReady(); } - if (gravity_bomb_active) + void ExposeHeart() { - if (uiGravityBombAuraTimer <= diff) - { - if (Unit *pGravityBombTarget = me->GetUnit(*me, uiGravityBombTarget)) - { - pGravityBombTarget->RemoveAurasDueToSpell(RAID_MODE(SPELL_GRAVITY_BOMB_10, SPELL_GRAVITY_BOMB_25)); - if (hardMode) - { - //Remains spawned for 3 minutes - pGravityBombTarget->SummonCreature(NPC_VOID_ZONE, pGravityBombTarget->GetPositionX(), pGravityBombTarget->GetPositionY(), pGravityBombTarget->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 180000); - } - } + //Make untargetable + //me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_6 | UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_UNK_15 | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); - gravity_bomb_active = false; - uiGravityBombAuraTimer = TIMER_GRAVITY_BOMB_AURA; - //gravityBomb(); - } else uiGravityBombAuraTimer -= diff; - } + //DoCast(SPELL_SUBMERGE); -- Need proper kneel dummy + me->SetReactState(REACT_PASSIVE); + me->AddUnitState(UNIT_STAT_STUNNED); - //Enrage stuff - if (!enraged) - { - if (uiEnrageTimer <= diff) + Unit* heart = me->GetVehicleKit() ? me->GetVehicleKit()->GetPassenger(HEART_VEHICLE_SEAT) : NULL; + if (heart) { - DoScriptText(SAY_BERSERK, me); - DoCast(me, SPELL_ENRAGE); - enraged = true; - } else uiEnrageTimer -= diff; - } - } + heart->CastSpell(heart, SPELL_HEART_OVERLOAD, false); + heart->CastSpell(me, SPELL_HEART_LIGHTNING_TETHER, false); + heart->CastSpell(heart, SPELL_HEART_HEAL_TO_FULL, true); + heart->CastSpell(heart, SPELL_EXPOSED_HEART, false); // Channeled + + //heart->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + heart->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_UNK_15 | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_UNK_29); + heart->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + + } - void exposeHeart() - { - //Make untargetable - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + // Start "end of phase 2 timer" + events.ScheduleEvent(EVENT_DISPOSE_HEART, TIMER_HEART_PHASE); + + // Phase 2 has officially started + _phase = 2; + _heartExposed++; - //Summon the heart npc - me->SummonCreature(NPC_XT002_HEART, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 7, 0, TEMPSUMMON_TIMED_DESPAWN, TIMER_HEART_PHASE); + DoScriptText(SAY_HEART_OPENED, me); + } - // Start "end of phase 2 timer" - uiHeartPhaseTimer = TIMER_HEART_PHASE; + void SetPhaseOne() + { + DoScriptText(SAY_HEART_CLOSED, me); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); - //Phase 2 has offically started - phase = 2; - heart_exposed++; + //DoCast(SPELL_STAND); + me->SetReactState(REACT_AGGRESSIVE); + //me->SetStandState(UNIT_STAND_STATE_STAND); + me->ClearUnitState(UNIT_STAT_STUNNED); - //Reset the add spawning timer - uiSpawnAddTimer = TIMER_SPAWN_ADD; + _phase = 1; - DoScriptText(SAY_HEART_OPENED, me); - } + 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)); - void SetPhaseOne() - { - uiSearingLightTimer = TIMER_SEARING_LIGHT / 2; - uiGravityBombTimer = TIMER_GRAVITY_BOMB; - uiTympanicTantrumTimer = urand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX); - uiSpawnAddTimer = TIMER_SPAWN_ADD; + Unit* heart = me->GetVehicleKit() ? me->GetVehicleKit()->GetPassenger(HEART_VEHICLE_SEAT) : NULL; + if (!heart) + return; - if (!hardMode) - me->ModifyHealth(-((int32)transferHealth)); + heart->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + heart->RemoveAurasDueToSpell(SPELL_EXPOSED_HEART); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - phase = 1; - } + if (!hardMode) + { + if (!transferHealth) + transferHealth = (heart->GetMaxHealth() - heart->GetHealth()); - // TODO: put in comment and kept for reference. The spell should be fixed properly in spell system, if necessary. - ////Have to do this the custom way since the original spell messes up player movement - //void gravityBomb() - //{ - // uint32 maxDamage = RAID_MODE(GRAVITY_BOMB_DMG_MAX_10, GRAVITY_BOMB_DMG_MAX_25); - // uint32 minDamage = RAID_MODE(GRAVITY_BOMB_DMG_MIN_10, GRAVITY_BOMB_DMG_MIN_25); - // uint16 range = GRAVITY_BOMB_RADIUS; - // Map* pMap = me->GetMap(); - // if (pMap && pMap->IsDungeon()) - // { - // Map::PlayerList const &PlayerList = pMap->GetPlayers(); - // for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - // { - // //If a player is within the range of the spell - // if (i->getSource() && i->getSource()->GetDistance2d(pGravityBombTarget) <= range) - // { - // //Deal damage to the victim - // int32 damage = urand(minDamage, maxDamage); - // i->getSource()->ModifyHealth(-damage); - // me->SendSpellNonMeleeDamageLog(i->getSource(), SPELL_GRAVITY_BOMB_AURA_10, damage, SPELL_SCHOOL_MASK_SHADOW, 0, 0, false, 0); - - // //Replacing the tractor beam effect - // i->getSource()->JumpTo(pGravityBombTarget, 5); - // } - // } - // } - //} - }; + me->ModifyHealth(-((int32)transferHealth)); + } + } + }; }; + /*------------------------------------------------------- * * XT-002 HEART @@ -537,36 +435,25 @@ public: mob_xt002_heartAI(Creature* pCreature) : ScriptedAI(pCreature) { m_pInstance = pCreature->GetInstanceScript(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - DoCast(me, SPELL_EXPOSED_HEART); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_PASSIVE); } InstanceScript* m_pInstance; - - void JustDied(Unit * /*victim*/) - { - if (m_pInstance) - if (Creature* pXT002 = me->GetCreature(*me, m_pInstance->GetData64(BOSS_XT002))) - if (pXT002->AI()) - pXT002->AI()->DoAction(ACTION_ENTER_HARD_MODE); - - //removes the aura - me->RemoveAurasDueToSpell(SPELL_EXPOSED_HEART); - } + uint32 _damageTaken; void DamageTaken(Unit * /*pDone*/, uint32 &damage) { - if (Creature* pXT002 = me->GetCreature(*me, m_pInstance->GetData64(BOSS_XT002))) - if (pXT002->AI()) - { - uint32 health = me->GetHealth(); - if (health <= damage) - health = 0; - else - health -= damage; - pXT002->AI()->SetData(DATA_TRANSFERED_HEALTH, me->GetMaxHealth() - health); - } + Creature* XT002 = me->GetCreature(*me, m_pInstance->GetData64(BOSS_XT002)); + if (!XT002 || !XT002->AI()) + return; + + if (damage >= me->GetHealth()) + { + XT002->AI()->SetData(DATA_TRANSFERED_HEALTH, me->GetMaxHealth()); + XT002->AI()->DoAction(ACTION_ENTER_HARD_MODE); + damage = 0; + } } }; @@ -601,7 +488,7 @@ public: me->SetReactState(REACT_PASSIVE); if (Creature* pXT002 = me->GetCreature(*me, m_pInstance->GetData64(BOSS_XT002))) - me->GetMotionMaster()->MoveChase(pXT002); + me->GetMotionMaster()->MoveFollow(pXT002, 0.0f, 0.0f); } void UpdateAI(const uint32 /*diff*/) @@ -719,12 +606,7 @@ public: me->SetReactState(REACT_PASSIVE); if (Creature* pXT002 = me->GetCreature(*me, m_pInstance->GetData64(BOSS_XT002))) - me->GetMotionMaster()->MoveChase(pXT002); - } - - void JustDied(Unit * /*killer*/) - { - DoCast(SPELL_BOOM); + me->GetMotionMaster()->MoveFollow(pXT002, 0.0f, 0.0f); } void UpdateAI(const uint32 /*diff*/) @@ -745,68 +627,6 @@ public: }; -/*------------------------------------------------------- - * - * VOID ZONE - * - *///---------------------------------------------------- -class mob_void_zone : public CreatureScript -{ -public: - mob_void_zone() : CreatureScript("mob_void_zone") { } - - CreatureAI* GetAI(Creature* pCreature) const - { - return new mob_void_zoneAI(pCreature); - } - - struct mob_void_zoneAI : public ScriptedAI - { - mob_void_zoneAI(Creature* pCreature) : ScriptedAI(pCreature) - { - m_pInstance = pCreature->GetInstanceScript(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); - } - - InstanceScript* m_pInstance; - uint32 uiVoidZoneTimer; - - void Reset() - { - uiVoidZoneTimer = TIMER_VOID_ZONE; - } - - void UpdateAI(const uint32 diff) - { - if (uiVoidZoneTimer <= diff) - { - //voidZone(); - uiVoidZoneTimer = TIMER_VOID_ZONE; - } else uiVoidZoneTimer -= diff; - } - - // TODO: put in comment and kept for reference. The spell should be fixed properly in spell system, if necessary. - //void voidZone() - //{ - // Map* pMap = me->GetMap(); - // if (pMap && pMap->IsDungeon()) - // { - // Map::PlayerList const &PlayerList = pMap->GetPlayers(); - // for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - // { - // // If a player is within the range of the spell - // if (i->getSource() && i->getSource()->GetDistance2d(me) <= 16) - // { - // // Deal damage to the victim - // int32 damage = RAID_MODE(VOID_ZONE_DMG_10, VOID_ZONE_DMG_25); - // me->DealDamage(i->getSource(), damage, NULL, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_MASK_SHADOW); - // } - // } - // } - //} - }; - -}; /*------------------------------------------------------- * @@ -858,13 +678,155 @@ public: }; +class spell_xt002_searing_light_spawn_life_spark : public SpellScriptLoader +{ + public: + spell_xt002_searing_light_spawn_life_spark() : SpellScriptLoader("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(SpellEntry const* /*spell*/) + { + if (!sSpellStore.LookupEntry(SPELL_SUMMON_LIFE_SPARK)) + return false; + return true; + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Player* plr = GetOwner()->ToPlayer()) + if (Unit* xt002 = GetCaster()) + if (xt002->HasAura(aurEff->GetAmount())) // Heartbreak aura indicating hard mode + plr->CastSpell(plr, SPELL_SUMMON_LIFE_SPARK, true); + } + + void Register() + { + AfterEffectRemove += AuraEffectRemoveFn(spell_xt002_searing_light_spawn_life_spark_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_xt002_searing_light_spawn_life_spark_AuraScript(); + } +}; + +class spell_xt002_gravity_bomb_spawn_void_zone : public SpellScriptLoader +{ + public: + spell_xt002_gravity_bomb_spawn_void_zone() : SpellScriptLoader("spell_xt002_gravity_bomb_spawn_void_zone") { } + + class spell_xt002_gravity_bomb_spawn_void_zone_AuraScript : public AuraScript + { + PrepareAuraScript(spell_xt002_gravity_bomb_spawn_void_zone_AuraScript); + + bool Validate(SpellEntry const* /*spell*/) + { + if (!sSpellStore.LookupEntry(SPELL_SUMMON_VOID_ZONE)) + return false; + return true; + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Player* plr = GetOwner()->ToPlayer()) + if (Unit* xt002 = GetCaster()) + if (xt002->HasAura(aurEff->GetAmount())) // Heartbreak aura indicating hard mode + plr->CastSpell(plr, SPELL_SUMMON_VOID_ZONE, true); + } + + void Register() + { + AfterEffectRemove += AuraEffectRemoveFn(spell_xt002_gravity_bomb_spawn_void_zone_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_xt002_gravity_bomb_spawn_void_zone_AuraScript(); + } +}; + +class spell_xt002_heart_overload_periodic : public SpellScriptLoader +{ + public: + spell_xt002_heart_overload_periodic() : SpellScriptLoader("spell_xt002_heart_overload_periodic") { } + + class spell_xt002_heart_overload_periodic_SpellScript : public SpellScript + { + PrepareSpellScript(spell_xt002_heart_overload_periodic_SpellScript); + + bool Validate(SpellEntry const* /*spell*/) + { + if (!sSpellStore.LookupEntry(SPELL_ENERGY_ORB)) + return false; + + if (!sSpellStore.LookupEntry(SPELL_RECHARGE_BOOMBOT)) + return false; + + if (!sSpellStore.LookupEntry(SPELL_RECHARGE_PUMMELER)) + return false; + + if (!sSpellStore.LookupEntry(SPELL_RECHARGE_SCRAPBOT)) + return false; + + return true; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + { + if (Unit* toyPile = ObjectAccessor::GetUnit(*caster, instance->GetData64(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, true); + } + } + } + + DoScriptText(SAY_SUMMON, caster->GetVehicleBase()); + } + } + + void Register() + { + OnEffect += SpellEffectFn(spell_xt002_heart_overload_periodic_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_xt002_heart_overload_periodic_SpellScript(); + } +}; + void AddSC_boss_xt002() { new mob_xt002_heart(); new mob_scrapbot(); new mob_pummeller(); new mob_boombot(); - new mob_void_zone(); + new mob_life_spark(); new boss_xt002(); + + new spell_xt002_searing_light_spawn_life_spark(); + new spell_xt002_gravity_bomb_spawn_void_zone(); + new spell_xt002_heart_overload_periodic(); } diff --git a/src/server/scripts/Northrend/Ulduar/ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/ulduar/instance_ulduar.cpp index acccdcfcf26..9208842eae0 100644 --- a/src/server/scripts/Northrend/Ulduar/ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/ulduar/instance_ulduar.cpp @@ -51,6 +51,7 @@ public: uint64 RazorHarpoonGUIDs[4]; uint64 ExpeditionCommanderGUID; uint64 XT002GUID; + uint64 XTToyPileGUIDs[4]; uint64 AssemblyGUIDs[3]; uint64 KologarnGUID; uint64 LeftArmGUID; @@ -117,6 +118,7 @@ public: HodirRareCacheData = 0; memset(Encounter, 0, sizeof(Encounter)); + memset(XTToyPileGUIDs, 0, sizeof(XTToyPileGUIDs)); memset(AssemblyGUIDs, 0, sizeof(AssemblyGUIDs)); memset(RazorHarpoonGUIDs, 0, sizeof(RazorHarpoonGUIDs)); memset(KeeperGUIDs, 0, sizeof(KeeperGUIDs)); @@ -169,6 +171,11 @@ public: case NPC_XT002: XT002GUID = creature->GetGUID(); break; + case NPC_XT_TOY_PILE: + for (uint8 i = 0; i < 4; ++i) + if (!XTToyPileGUIDs[i]) + XTToyPileGUIDs[i] = creature->GetGUID(); + break; // Assembly of Iron case NPC_STEELBREAKER: @@ -497,6 +504,11 @@ public: return RazorscaleController; case BOSS_XT002: return XT002GUID; + case DATA_TOY_PILE_0: + case DATA_TOY_PILE_1: + case DATA_TOY_PILE_2: + case DATA_TOY_PILE_3: + return XTToyPileGUIDs[data - DATA_TOY_PILE_0]; case BOSS_KOLOGARN: return KologarnGUID; case DATA_LEFT_ARM: diff --git a/src/server/scripts/Northrend/Ulduar/ulduar/ulduar.h b/src/server/scripts/Northrend/Ulduar/ulduar/ulduar.h index 2ddcea08ae6..6af8ad534b5 100644 --- a/src/server/scripts/Northrend/Ulduar/ulduar/ulduar.h +++ b/src/server/scripts/Northrend/Ulduar/ulduar/ulduar.h @@ -51,6 +51,12 @@ enum UlduarBosses DATA_EXPEDITION_COMMANDER, DATA_RAZORSCALE_CONTROL, + // XT-002 + DATA_TOY_PILE_0, + DATA_TOY_PILE_1, + DATA_TOY_PILE_2, + DATA_TOY_PILE_3, + // Kologarn DATA_LEFT_ARM, DATA_RIGHT_ARM, @@ -68,6 +74,7 @@ enum UlduarNPCs NPC_STEELFORGED_DEFFENDER = 33236, NPC_EXPEDITION_COMMANDER = 33210, NPC_XT002 = 33293, + NPC_XT_TOY_PILE = 33337, NPC_STEELBREAKER = 32867, NPC_MOLGEIM = 32927, NPC_BRUNDIR = 32857, |