Scripts/Ulduar: Remove dirty spell and summon hacks from XT-002 script and rewrite with EventMap.

Script is still WIP.
This commit is contained in:
Machiavelli
2011-06-07 16:13:14 +02:00
parent c1fb71fefc
commit e8bc02f3bd
6 changed files with 453 additions and 439 deletions

View File

@@ -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
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 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
#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
{
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)
{
}
uint32 uiSearingLightTimer;
uint32 uiSpawnLifeSparkTimer;
uint32 uiGravityBombTimer;
uint32 uiGravityBombAuraTimer;
uint32 uiTympanicTantrumTimer;
uint32 uiHeartPhaseTimer;
uint32 uiSpawnAddTimer;
uint32 uiEnrageTimer;
bool searing_light_active;
uint64 uiSearingLightTarget;
bool gravity_bomb_active;
uint64 uiGravityBombTarget;
uint8 phase;
uint8 heart_exposed;
bool enraged;
uint32 transferHealth;
bool enterHardMode;
bool hardMode;
void Reset()
struct boss_xt002_AI : public BossAI
{
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);
}
void EnterCombat(Unit* /*who*/)
{
DoScriptText(SAY_AGGRO, me);
_EnterCombat();
if (instance)
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
}
void DoAction(const int32 action)
{
switch (action)
boss_xt002_AI(Creature *pCreature) : BossAI(pCreature, BOSS_XT002)
{
case ACTION_ENTER_HARD_MODE:
if (!hardMode)
{
hardMode = true;
// Enter hard mode
enterHardMode = true;
// set max health
me->SetFullHealth();
// Get his heartbreak buff
me->CastSpell(me, RAID_MODE(SPELL_HEARTBREAK_10, SPELL_HEARTBREAK_25), true);
}
break;
}
}
void SetData(uint32 id, uint32 value)
{
switch(id)
uint8 _phase;
uint8 _heartExposed;
uint32 transferHealth;
bool enterHardMode;
bool hardMode;
void Reset()
{
case DATA_TRANSFERED_HEALTH:
transferHealth = value;
break;
}
}
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE);
void KilledUnit(Unit* /*victim*/)
{
DoScriptText(RAND(SAY_SLAY_1, SAY_SLAY_2), me);
}
void JustDied(Unit * /*victim*/)
{
DoScriptText(SAY_DEATH, me);
_JustDied();
}
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
if (enterHardMode)
{
SetPhaseOne();
hardMode = false;
enterHardMode = false;
_phase = 1;
_heartExposed = 0;
if (instance)
instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
}
// Handles spell casting. These spells only occur during phase 1 and hard mode
if (phase == 1 || hardMode)
void EnterCombat(Unit* /*who*/)
{
if (uiSearingLightTimer <= diff)
{
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;
DoScriptText(SAY_AGGRO, me);
_EnterCombat();
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;
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);
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;
if (instance)
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
}
if (!hardMode)
void DoAction(const int32 action)
{
if (phase == 1)
switch (action)
{
if (HealthBelowPct(75) && heart_exposed == 0)
{
exposeHeart();
}
else if (HealthBelowPct(50) && heart_exposed == 1)
{
exposeHeart();
}
else if (HealthBelowPct(25) && heart_exposed == 2)
{
exposeHeart();
}
case ACTION_ENTER_HARD_MODE:
if (!hardMode)
{
hardMode = true;
// Enter hard mode
enterHardMode = true;
// set max health
me->SetFullHealth();
// Get his heartbreak buff
me->CastSpell(me, RAID_MODE(SPELL_HEARTBREAK_10, SPELL_HEARTBREAK_25), true);
me->AddLootMode(LOOT_MODE_HARD_MODE_1);
}
break;
}
}
void SetData(uint32 id, uint32 value)
{
switch(id)
{
case DATA_TRANSFERED_HEALTH:
transferHealth = value;
break;
}
}
void KilledUnit(Unit* /*victim*/)
{
DoScriptText(RAND(SAY_SLAY_1, SAY_SLAY_2), me);
}
void JustDied(Unit * /*victim*/)
{
DoScriptText(SAY_DEATH, me);
_JustDied();
}
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/)
{
if (!hardMode && _phase == 1 && !HealthAbovePct(100 - 25 * (_heartExposed+1)))
ExposeHeart();
}
void UpdateAI(const uint32 diff)
{
if (_phase == 1 && !UpdateVictim())
return;
events.Update(diff);
if (enterHardMode)
{
SetPhaseOne();
enterHardMode = false;
}
if (me->HasUnitState(UNIT_STAT_CASTING))
return;
// Handles spell casting. These spells only occur during phase 1 and hard mode
if (_phase == 1)
{
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
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;
}
}
DoMeleeAttackIfReady();
}
else
else if (_phase == 2)
{
//Stop moving
me->StopMoving();
//Start summoning adds
if (uiSpawnAddTimer <= diff)
while (uint32 eventId = events.ExecuteEvent())
{
DoScriptText(SAY_SUMMON, me);
// 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;
}
// 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;
}
}
//Spawn 5 Scrapbots
switch (rand() % 4)
{
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;
}
uiSpawnAddTimer = TIMER_SPAWN_ADD;
} else uiSpawnAddTimer -= diff;
// Is the phase over?
if (uiHeartPhaseTimer <= diff)
{
DoScriptText(SAY_HEART_CLOSED, me);
SetPhaseOne();
switch (eventId)
case EVENT_DISPOSE_HEART:
SetPhaseOne();
break;
}
else
uiHeartPhaseTimer -= diff;
}
}
else
void ExposeHeart()
{
// Adding life sparks when searing light debuff runs out if hard mode
if (searing_light_active)
//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);
//DoCast(SPELL_SUBMERGE); -- Need proper kneel dummy
me->SetReactState(REACT_PASSIVE);
me->AddUnitState(UNIT_STAT_STUNNED);
Unit* heart = me->GetVehicleKit() ? me->GetVehicleKit()->GetPassenger(HEART_VEHICLE_SEAT) : NULL;
if (heart)
{
if (uiSpawnLifeSparkTimer <= diff)
{
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;
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);
}
DoMeleeAttackIfReady();
// Start "end of phase 2 timer"
events.ScheduleEvent(EVENT_DISPOSE_HEART, TIMER_HEART_PHASE);
// Phase 2 has officially started
_phase = 2;
_heartExposed++;
DoScriptText(SAY_HEART_OPENED, me);
}
if (gravity_bomb_active)
void SetPhaseOne()
{
if (uiGravityBombAuraTimer <= diff)
DoScriptText(SAY_HEART_CLOSED, me);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
//DoCast(SPELL_STAND);
me->SetReactState(REACT_AGGRESSIVE);
//me->SetStandState(UNIT_STAND_STATE_STAND);
me->ClearUnitState(UNIT_STAT_STUNNED);
_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) : NULL;
if (!heart)
return;
heart->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
heart->RemoveAurasDueToSpell(SPELL_EXPOSED_HEART);
if (!hardMode)
{
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);
}
}
if (!transferHealth)
transferHealth = (heart->GetMaxHealth() - heart->GetHealth());
gravity_bomb_active = false;
uiGravityBombAuraTimer = TIMER_GRAVITY_BOMB_AURA;
//gravityBomb();
} else uiGravityBombAuraTimer -= diff;
me->ModifyHealth(-((int32)transferHealth));
}
}
//Enrage stuff
if (!enraged)
{
if (uiEnrageTimer <= diff)
{
DoScriptText(SAY_BERSERK, me);
DoCast(me, SPELL_ENRAGE);
enraged = true;
} else uiEnrageTimer -= diff;
}
}
void exposeHeart()
{
//Make untargetable
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
//Summon the heart npc
me->SummonCreature(NPC_XT002_HEART, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 7, 0, TEMPSUMMON_TIMED_DESPAWN, TIMER_HEART_PHASE);
// Start "end of phase 2 timer"
uiHeartPhaseTimer = TIMER_HEART_PHASE;
//Phase 2 has offically started
phase = 2;
heart_exposed++;
//Reset the add spawning timer
uiSpawnAddTimer = TIMER_SPAWN_ADD;
DoScriptText(SAY_HEART_OPENED, me);
}
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;
if (!hardMode)
me->ModifyHealth(-((int32)transferHealth));
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
phase = 1;
}
// 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);
// }
// }
// }
//}
};
};
};
/*-------------------------------------------------------
*
* 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();
}

View File

@@ -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:

View File

@@ -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,