Scripts/Naxxramas: Clean-up and bug-fix megacommit. Read the full commit message if you care.

- Maexxna:
  - No longer packages pets and returns them to sender. Web Wrap now only targets players.
- Heigan the Unclean: Ripped its guts out.
  - Instance script hacks are gone.
  - GO spawns (done using the time-honored TC tradition of "eh, this'll work") are gone. You could say I evacuated the dance floor.
  - Sniffed spawns for all segments of the room. Because if you don't dance - well, you're no friends of mine.
- Loatheb:
  - First spore now spawns properly after 18 seconds on 10-man (down from 36 seconds). Time between subsequent spawns is unchanged.
- Thaddius:
  - Instance script hacks are gone. Again. Feels good man.
  - Players no longer drop out of combat during the phase transition. Put your snacks away, this is supposed to be a tense situation!
  - Thaddius will no longer savagely punish the main tank for stepping out of melee range for a fraction of a second by turning around and ball lightning-ing someone in the face. Typically a healer. He hates healers.
  - Thaddius will instead take out his frustration on another melee range target if available by smacking them in the face with his fists. It hurts, but a Ball Lightning would've hurt more. Blame the tank.
  - Feugen and Stalagg have been re-educated to improve their patience when a new target is flying towards them. They will no longer move while Magnetic Pull targets are mid-air, which should prevent them from running off their platform while the warrior helplessly flails trying to get them back.
- Instructor Razuvious:
  - Razuvious has been informed that Rubik's Cubes become noticably easier to solve if you buy six-colored ones. Thus, he will no longer take out his frustration out on raid groups by throwing unsolved two-colored variants at them. Fixes and closes #14966.
  - Death Knight Understudies will now realize that their situation is Hopeless even if they're currently mind controlled by a player. Apparently, if your situation is hopeless you take 5000% extra damage. No, that is supposed to be three zeroes.
- Gothik the Harvester:
  - Gothik has been schooled in proper Shadow Bolt spamming etiquette and will now take a brief pause to catch a breath and recompose himself between bolts.
- The Four Horsemen:
  - Will no longer stop and stare at the group sometimes when engaged instead of starting the encounter.
  - Horsemen can no longer be affected by stun, snare and shackle (heh, yeah, you could totally shackle Baron Rivendare) effects. It's called "Four Horsemen" after all, not "Three Horsemen that never attack because their friend can't move".
- Sapphiron:
  - When attempting to FREEZE THE BLOOD IN YOUR VEINS... Wait, no. Wrong boss. I was thinking of #16634.
  - Anyways, when freezing people into an Ice Block, Sapphiron now takes care to actually place the block on top of the frozen player, even if they were moving or jumping when the bolt hit them. Yay for positioning clarity!
  - Sapphiron has been sent to take multiple mandatory courses in "How to treat raid parties fairly while attempting to murder them" and will thus...
    - ...now properly pause after taking off to allow players to get into position before smacking them with ice bolts
    - ...no longer deal damage to players inside an Ice Block while they cannot fight back.
  - Being forced to attend the courses mentioned above has led to a large amount of pent-up frustration, which Sapphiron relieves by quite forcefully flapping his wings during take-off. Players immediately below him are now properly knocked back.
  - In addition, Kel'thuzad was sick of Sapphiron's continous pouting and allowed him to make Blizzard more malicious in exchange. Blizzard will now properly spawn near players and chase them down instead of pathing randomly.
- Also, tons of code style cleanups and hack removal that wasn't mentioned above. Read the diff if you really care.

Conflicts:
	src/server/game/Spells/Spell.cpp
This commit is contained in:
treeston
2016-02-08 19:41:09 +01:00
committed by Aokromes
parent 72d42bda38
commit afa9a62e78
17 changed files with 686 additions and 541 deletions

View File

@@ -190,6 +190,7 @@ enum SpellCustomAttributes
SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER = 0x00010000,
SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET = 0x00020000,
SPELL_ATTR0_CU_ALLOW_INFLIGHT_TARGET = 0x00040000,
SPELL_ATTR0_CU_NEEDS_AMMO_DATA = 0x00080000,
SPELL_ATTR0_CU_NEGATIVE = SPELL_ATTR0_CU_NEGATIVE_EFF0 | SPELL_ATTR0_CU_NEGATIVE_EFF1 | SPELL_ATTR0_CU_NEGATIVE_EFF2
};

View File

@@ -3124,6 +3124,7 @@ void SpellMgr::LoadSpellInfoCorrections()
case 52479: // Gift of the Harvester
case 61588: // Blazing Harpoon
case 55479: // Force Obedience
case 28560: // Summon Blizzard (Sapphiron)
spellInfo->MaxAffectedTargets = 1;
break;
case 36384: // Skartax Purple Beam

View File

@@ -162,13 +162,13 @@ public:
summons.DoZoneInCombat();
events.SetPhase(PHASE_NORMAL);
events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_LOCUST, urand(80,120) * IN_MILLISECONDS, 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_IMPALE, randtime(Seconds(10), Seconds(20)), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_SCARABS, randtime(Seconds(20), Seconds(30)), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_LOCUST, Minutes(1)+randtime(Seconds(40), Seconds(60)), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
if (!Is25ManRaid())
events.ScheduleEvent(EVENT_SPAWN_GUARD, urand(15, 20) * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SPAWN_GUARD, randtime(Seconds(15), Seconds(20)));
}
void UpdateAI(uint32 diff) override
@@ -189,11 +189,9 @@ public:
else
EnterEvadeMode();
events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL);
events.Repeat(randtime(Seconds(10), Seconds(20)));
break;
case EVENT_SCARABS:
events.ScheduleEvent(EVENT_SCARABS, urand(40 * IN_MILLISECONDS, 60 * IN_MILLISECONDS), 0, PHASE_NORMAL);
if (!guardCorpses.empty())
{
if (ObjectGuid target = Trinity::Containers::SelectRandomContainerElement(guardCorpses))
@@ -204,27 +202,28 @@ public:
creatureTarget->DespawnOrUnsummon();
}
}
events.Repeat(randtime(Seconds(40), Seconds(60)));
break;
case EVENT_LOCUST:
Talk(EMOTE_LOCUST);
DoCast(me, SPELL_LOCUST_SWARM);
events.ScheduleEvent(EVENT_SPAWN_GUARD, 3 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_LOCUST_ENDS, RAID_MODE(19, 23) * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_LOCUST, 90000);
events.SetPhase(PHASE_SWARM);
DoCast(me, SPELL_LOCUST_SWARM);
events.ScheduleEvent(EVENT_SPAWN_GUARD, Seconds(3));
events.ScheduleEvent(EVENT_LOCUST_ENDS, RAID_MODE(Seconds(19), Seconds(23)));
events.Repeat(Minutes(1)+Seconds(30));
break;
case EVENT_LOCUST_ENDS:
events.ScheduleEvent(EVENT_IMPALE, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_SCARABS, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS), 0, PHASE_NORMAL);
events.SetPhase(PHASE_NORMAL);
events.ScheduleEvent(EVENT_IMPALE, randtime(Seconds(10), Seconds(20)), 0, PHASE_NORMAL);
events.ScheduleEvent(EVENT_SCARABS, randtime(Seconds(20), Seconds(30)), 0, PHASE_NORMAL);
break;
case EVENT_SPAWN_GUARD:
me->SummonCreatureGroup(GROUP_SINGLE_SPAWN);
break;
case EVENT_BERSERK:
DoCast(me, SPELL_BERSERK, true);
events.ScheduleEvent(EVENT_BERSERK, 600000);
events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
break;
}
}

View File

@@ -100,9 +100,9 @@ class boss_faerlina : public CreatureScript
_EnterCombat();
Talk(SAY_AGGRO);
summons.DoZoneInCombat();
events.ScheduleEvent(EVENT_POISON, urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS));
events.ScheduleEvent(EVENT_FIRE, urand(6 * IN_MILLISECONDS, 18 * IN_MILLISECONDS));
events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS));
events.ScheduleEvent(EVENT_POISON, randtime(Seconds(10), Seconds(15)));
events.ScheduleEvent(EVENT_FIRE, randtime(Seconds(6), Seconds(18)));
events.ScheduleEvent(EVENT_FRENZY, Minutes(1)+randtime(Seconds(0), Seconds(20)));
}
void Reset() override
@@ -111,9 +111,9 @@ class boss_faerlina : public CreatureScript
_frenzyDispels = 0;
}
void KilledUnit(Unit* /*victim*/) override
void KilledUnit(Unit* victim) override
{
if (!urand(0, 2))
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
@@ -158,21 +158,21 @@ class boss_faerlina : public CreatureScript
case EVENT_POISON:
if (!me->HasAura(SPELL_WIDOWS_EMBRACE_HELPER))
DoCastAOE(SPELL_POISON_BOLT_VOLLEY);
events.ScheduleEvent(EVENT_POISON, urand(8000, 15000));
events.Repeat(randtime(Seconds(8), Seconds(15)));
break;
case EVENT_FIRE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_RAIN_OF_FIRE);
events.ScheduleEvent(EVENT_FIRE, urand(6000, 18000));
events.Repeat(randtime(Seconds(6), Seconds(18)));
break;
case EVENT_FRENZY:
if (Aura* widowsEmbrace = me->GetAura(SPELL_WIDOWS_EMBRACE_HELPER))
events.ScheduleEvent(EVENT_FRENZY, widowsEmbrace->GetDuration()+1 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_FRENZY, Milliseconds(widowsEmbrace->GetDuration()+1));
else
{
DoCast(SPELL_FRENZY);
Talk(EMOTE_FRENZY);
events.ScheduleEvent(EVENT_FRENZY, urand(60 * IN_MILLISECONDS, 80 * IN_MILLISECONDS));
events.Repeat(Minutes(1) + randtime(Seconds(0), Seconds(20)));
}
break;
}

View File

@@ -285,7 +285,7 @@ struct boss_four_horsemen_baseAI : public BossAI
void EnterCombat(Unit* /*who*/) override
{
if (instance->GetBossState(BOSS_HORSEMEN) != NOT_STARTED) // another horseman already did it
if (instance->GetBossState(BOSS_HORSEMEN) == IN_PROGRESS || instance->GetBossState(BOSS_HORSEMEN) == DONE) // another horseman already did it
return;
Talk(SAY_AGGRO);
BeginEncounter();
@@ -411,9 +411,9 @@ class boss_four_horsemen_baron : public CreatureScript
else
AttackStart(threat.getHostilTarget());
events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(3,7));
events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
events.ScheduleEvent(EVENT_MARK, Seconds(24));
events.ScheduleEvent(EVENT_UNHOLYSHADOW, randtime(Seconds(3), Seconds(7)));
}
void _UpdateAI(uint32 diff) override
@@ -431,11 +431,11 @@ class boss_four_horsemen_baron : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_BARON_MARK, true);
events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS);
events.Repeat(Seconds(12));
break;
case EVENT_UNHOLYSHADOW:
DoCastVictim(SPELL_UNHOLY_SHADOW);
events.ScheduleEvent(EVENT_UNHOLYSHADOW, urandms(10,30));
events.Repeat(randtime(Seconds(10), Seconds(30)));
break;
}
}
@@ -484,9 +484,9 @@ class boss_four_horsemen_thane : public CreatureScript
else
AttackStart(threat.getHostilTarget());
events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_METEOR, urandms(10,15));
events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
events.ScheduleEvent(EVENT_MARK, Seconds(24));
events.ScheduleEvent(EVENT_METEOR, randtime(Seconds(10), Seconds(25)));
}
void _UpdateAI(uint32 diff) override
{
@@ -503,7 +503,7 @@ class boss_four_horsemen_thane : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_THANE_MARK, true);
events.ScheduleEvent(EVENT_MARK, 12 * IN_MILLISECONDS);
events.Repeat(Seconds(12));
break;
case EVENT_METEOR:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true))
@@ -511,7 +511,7 @@ class boss_four_horsemen_thane : public CreatureScript
DoCast(target, SPELL_METEOR);
_shouldSay = true;
}
events.ScheduleEvent(EVENT_METEOR, urandms(13,17));
events.Repeat(randtime(Seconds(13), Seconds(17)));
break;
}
}
@@ -550,9 +550,9 @@ class boss_four_horsemen_lady : public CreatureScript
boss_four_horsemen_ladyAI(Creature* creature) : boss_four_horsemen_baseAI(creature, LADY, ladyPath) { }
void BeginFighting() override
{
events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_VOIDZONE, urandms(5,10));
events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
events.ScheduleEvent(EVENT_MARK, Seconds(24));
events.ScheduleEvent(EVENT_VOIDZONE, randtime(Seconds(5), Seconds(10)));
}
void _UpdateAI(uint32 diff) override
@@ -578,7 +578,7 @@ class boss_four_horsemen_lady : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_LADY_MARK, true);
events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS);
events.Repeat(Seconds(15));
break;
case EVENT_VOIDZONE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true))
@@ -586,7 +586,7 @@ class boss_four_horsemen_lady : public CreatureScript
DoCast(target, SPELL_VOID_ZONE, true);
Talk(SAY_SPECIAL);
}
events.ScheduleEvent(EVENT_VOIDZONE, urandms(12, 18));
events.Repeat(randtime(Seconds(12), Seconds(18)));
break;
}
}
@@ -620,9 +620,9 @@ class boss_four_horsemen_sir : public CreatureScript
boss_four_horsemen_sirAI(Creature* creature) : boss_four_horsemen_baseAI(creature, SIR, sirPath), _shouldSay(true) { }
void BeginFighting() override
{
events.ScheduleEvent(EVENT_BERSERK, 10 * MINUTE * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_MARK, 24 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_HOLYWRATH, urandms(13,18));
events.ScheduleEvent(EVENT_BERSERK, Minutes(10));
events.ScheduleEvent(EVENT_MARK, Seconds(24));
events.ScheduleEvent(EVENT_HOLYWRATH, randtime(Seconds(13), Seconds(18)));
}
void _UpdateAI(uint32 diff) override
@@ -648,7 +648,7 @@ class boss_four_horsemen_sir : public CreatureScript
break;
case EVENT_MARK:
DoCastAOE(SPELL_SIR_MARK, true);
events.ScheduleEvent(EVENT_MARK, 15 * IN_MILLISECONDS);
events.Repeat(Seconds(15));
break;
case EVENT_HOLYWRATH:
if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 45.0f, true))
@@ -656,7 +656,7 @@ class boss_four_horsemen_sir : public CreatureScript
DoCast(target, SPELL_HOLY_WRATH, true);
_shouldSay = true;
}
events.ScheduleEvent(EVENT_HOLYWRATH, urandms(10,18));
events.Repeat(randtime(Seconds(10), Seconds(18)));
break;
}
}

View File

@@ -324,13 +324,13 @@ class boss_gothik : public CreatureScript
{
_EnterCombat();
events.SetPhase(PHASE_ONE);
events.ScheduleEvent(EVENT_SUMMON, 25 * IN_MILLISECONDS, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_DOORS_UNLOCK, 205 * IN_MILLISECONDS, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_PHASE_TWO, 270 * IN_MILLISECONDS, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_SUMMON, Seconds(25), 0, PHASE_ONE);
events.ScheduleEvent(EVENT_DOORS_UNLOCK, Minutes(3) + Seconds(25), 0, PHASE_ONE);
events.ScheduleEvent(EVENT_PHASE_TWO, Minutes(4) + Seconds(30), 0, PHASE_ONE);
Talk(SAY_INTRO_1);
events.ScheduleEvent(EVENT_INTRO_2, 4 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_INTRO_3, 9 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_INTRO_4, 14 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_INTRO_2, Seconds(4));
events.ScheduleEvent(EVENT_INTRO_3, Seconds(9));
events.ScheduleEvent(EVENT_INTRO_4, Seconds(14));
instance->SetData(DATA_GOTHIK_GATE, GO_STATE_READY);
_gateIsOpen = false;
}
@@ -480,7 +480,7 @@ class boss_gothik : public CreatureScript
}
if (uint8 timeToNext = RAID_MODE(waves10, waves25)[_waveCount].second)
events.ScheduleEvent(EVENT_SUMMON, timeToNext * IN_MILLISECONDS, 0, PHASE_ONE);
events.Repeat(Seconds(timeToNext));
++_waveCount;
break;
@@ -497,9 +497,9 @@ class boss_gothik : public CreatureScript
break;
case EVENT_PHASE_TWO:
events.SetPhase(PHASE_TWO);
events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_TELEPORT, Seconds(20), 0, PHASE_TWO);
events.ScheduleEvent(EVENT_HARVEST, Seconds(15), 0, PHASE_TWO);
events.ScheduleEvent(EVENT_RESUME_ATTACK, Seconds(2), 0, PHASE_TWO);
Talk(SAY_PHASE_TWO);
Talk(EMOTE_PHASE_TWO);
me->SetReactState(REACT_PASSIVE);
@@ -518,23 +518,23 @@ class boss_gothik : public CreatureScript
_lastTeleportDead = !_lastTeleportDead;
events.CancelEvent(EVENT_BOLT);
events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO);
events.Repeat(Seconds(20));
}
break;
case EVENT_HARVEST:
DoCastAOE(SPELL_HARVEST_SOUL, true); // triggered allows this to go "through" shadow bolt
events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO);
events.Repeat(Seconds(15));
break;
case EVENT_RESUME_ATTACK:
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_BOLT, 0, 0, PHASE_TWO);
events.ScheduleEvent(EVENT_BOLT, Seconds(0), 0, PHASE_TWO);
// return to the start of this method so victim side etc is re-evaluated
return UpdateAI(0u); // tail recursion for efficiency
case EVENT_BOLT:
DoCastVictim(SPELL_SHADOW_BOLT);
events.ScheduleEvent(EVENT_BOLT, 1 * IN_MILLISECONDS, 0, PHASE_TWO);
events.Repeat(Seconds(2));
break;
case EVENT_INTRO_2:
Talk(SAY_INTRO_2);

View File

@@ -57,10 +57,10 @@ class boss_grobbulus : public CreatureScript
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
events.ScheduleEvent(EVENT_CLOUD, 15000);
events.ScheduleEvent(EVENT_INJECT, 20000);
events.ScheduleEvent(EVENT_SPRAY, urand(15000, 30000)); // not sure
events.ScheduleEvent(EVENT_BERSERK, 12 * 60000);
events.ScheduleEvent(EVENT_CLOUD, Seconds(15));
events.ScheduleEvent(EVENT_INJECT, Seconds(20));
events.ScheduleEvent(EVENT_SPRAY, randtime(Seconds(15), Seconds(30))); // not sure
events.ScheduleEvent(EVENT_BERSERK, Minutes(12));
}
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
@@ -82,19 +82,19 @@ class boss_grobbulus : public CreatureScript
{
case EVENT_CLOUD:
DoCastAOE(SPELL_POISON_CLOUD);
events.ScheduleEvent(EVENT_CLOUD, 15000);
events.Repeat(Seconds(15));
return;
case EVENT_BERSERK:
DoCastAOE(SPELL_BERSERK, true);
return;
case EVENT_SPRAY:
DoCastAOE(SPELL_SLIME_SPRAY);
events.ScheduleEvent(EVENT_SPRAY, urand(15000, 30000));
events.Repeat(randtime(Seconds(15), Seconds(30)));
return;
case EVENT_INJECT:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_MUTATING_INJECTION))
DoCast(target, SPELL_MUTATING_INJECTION);
events.ScheduleEvent(EVENT_INJECT, 8000 + uint32(120 * me->GetHealthPct()));
events.Repeat(Seconds(8) + Milliseconds(uint32(std::round(120 * me->GetHealthPct()))));
return;
default:
break;

View File

@@ -27,6 +27,7 @@ enum Spells
SPELL_SPELL_DISRUPTION = 29310,
SPELL_PLAGUE_CLOUD = 29350,
SPELL_TELEPORT_SELF = 30211,
SPELL_ERUPTION = 29371
};
enum Yells
@@ -60,6 +61,15 @@ enum Misc
DATA_SAFETY_DANCE = 19962139
};
static const uint32 firstEruptionDBGUID = 84980;
static const uint8 numSections = 4;
static const uint8 numEruptions[numSections] = { // count of sequential GO DBGUIDs in the respective section of the room
15,
25,
23,
13
};
class boss_heigan : public CreatureScript
{
public:
@@ -72,7 +82,7 @@ public:
struct boss_heiganAI : public BossAI
{
boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN), eruptSection(0), eruptDirection(false), safetyDance(false) { }
boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN), _safeSection(0), _danceDirection(false), _safetyDance(false) { }
void Reset() override
{
@@ -82,15 +92,16 @@ public:
void KilledUnit(Unit* who) override
{
Talk(SAY_SLAY);
if (who->GetTypeId() == TYPEID_PLAYER)
safetyDance = false;
{
Talk(SAY_SLAY);
_safetyDance = false;
}
}
uint32 GetData(uint32 type) const override
{
return (type == DATA_SAFETY_DANCE && safetyDance) ? 1u : 0u;
return (type == DATA_SAFETY_DANCE && _safetyDance) ? 1u : 0u;
}
void JustDied(Unit* /*killer*/) override
@@ -104,13 +115,27 @@ public:
_EnterCombat();
Talk(SAY_AGGRO);
eruptSection = 3;
events.ScheduleEvent(EVENT_DISRUPT, urand(15 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_FEVER, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT);
_safeSection = 0;
events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(10), Seconds(20)), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT);
safetyDance = true;
_safetyDance = true;
// figure out the current GUIDs of our eruption tiles and which segment they belong in
std::unordered_multimap<uint32, GameObject*> const& mapGOs = me->GetMap()->GetGameObjectBySpawnIdStore();
uint32 spawnId = firstEruptionDBGUID;
for (uint8 section = 0; section < numSections; ++section)
{
_eruptTiles[section].clear();
for (uint8 i = 0; i < numEruptions[section]; ++i)
{
std::pair<std::unordered_multimap<uint32, GameObject*>::const_iterator, std::unordered_multimap<uint32, GameObject*>::const_iterator> tileIt = mapGOs.equal_range(spawnId++);
for (std::unordered_multimap<uint32, GameObject*>::const_iterator it = tileIt.first; it != tileIt.second; ++it)
_eruptTiles[section].push_back(it->second->GetGUID());
}
}
}
void UpdateAI(uint32 diff) override
@@ -126,52 +151,56 @@ public:
{
case EVENT_DISRUPT:
DoCastAOE(SPELL_SPELL_DISRUPTION);
events.ScheduleEvent(EVENT_DISRUPT, 11 * IN_MILLISECONDS);
events.Repeat(Seconds(11));
break;
case EVENT_FEVER:
DoCastAOE(SPELL_DECREPIT_FEVER);
events.ScheduleEvent(EVENT_FEVER, urand(20 * IN_MILLISECONDS, 25 * IN_MILLISECONDS));
events.Repeat(randtime(Seconds(20), Seconds(25)));
break;
case EVENT_DANCE:
events.SetPhase(PHASE_DANCE);
Talk(SAY_TAUNT);
Talk(EMOTE_DANCE);
eruptSection = 3;
_safeSection = 0;
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->StopMoving();
DoCast(SPELL_TELEPORT_SELF);
DoCastAOE(SPELL_PLAGUE_CLOUD);
events.ScheduleEvent(EVENT_DANCE_END, 45 * IN_MILLISECONDS, 0, PHASE_DANCE);
events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_DANCE_END, Seconds(45), 0, PHASE_DANCE);
events.ScheduleEvent(EVENT_ERUPT, Seconds(10));
break;
case EVENT_DANCE_END:
events.SetPhase(PHASE_FIGHT);
Talk(EMOTE_DANCE_END);
eruptSection = 3;
events.ScheduleEvent(EVENT_DISRUPT, urand(10, 25) * IN_MILLISECONDS, 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_FEVER, urand(15, 20) * IN_MILLISECONDS, 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT);
_safeSection = 0;
events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(10), Seconds(25)), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT);
events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT);
me->CastStop();
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
break;
case EVENT_ERUPT:
instance->SetData(DATA_HEIGAN_ERUPT, eruptSection);
TeleportCheaters();
for (uint8 section = 0; section < numSections; ++section)
if (section != _safeSection)
for (ObjectGuid tileGUID : _eruptTiles[section])
if (GameObject* tile = ObjectAccessor::GetGameObject(*me, tileGUID))
{
tile->SendCustomAnim(0);
tile->CastSpell(nullptr, SPELL_ERUPTION);
}
if (eruptSection == 0)
eruptDirection = true;
else if (eruptSection == 3)
eruptDirection = false;
if (_safeSection == 0)
_danceDirection = true;
else if (_safeSection == numSections-1)
_danceDirection = false;
eruptDirection ? ++eruptSection : --eruptSection;
_danceDirection ? ++_safeSection : --_safeSection;
if (events.IsInPhase(PHASE_DANCE))
events.ScheduleEvent(EVENT_ERUPT, 3 * IN_MILLISECONDS, 0, PHASE_DANCE);
else
events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS, 0, PHASE_FIGHT);
events.Repeat(events.IsInPhase(PHASE_DANCE) ? Seconds(3) : Seconds(10));
break;
}
}
@@ -180,10 +209,11 @@ public:
}
private:
uint32 eruptSection;
bool eruptDirection;
std::vector<ObjectGuid> _eruptTiles[numSections]; // populated on encounter start
bool safetyDance; // is achievement still possible? (= no player deaths yet)
uint32 _safeSection; // 0 is next to the entrance
bool _danceDirection; // true is counter-clockwise, false is clock-wise
bool _safetyDance; // is achievement still possible? (= no player deaths yet)
};
};

View File

@@ -24,7 +24,6 @@
enum Spells
{
SPELL_NECROTIC_AURA = 55593,
SPELL_WARN_NECROTIC_AURA = 59481,
SPELL_SUMMON_SPORE = 29234,
SPELL_DEATHBLOOM = 29865,
SPELL_INEVITABLE_DOOM = 29204,
@@ -42,11 +41,12 @@ enum Texts
enum Events
{
EVENT_NECROTIC_AURA = 1,
EVENT_DEATHBLOOM = 2,
EVENT_INEVITABLE_DOOM = 3,
EVENT_SPORE = 4,
EVENT_NECROTIC_AURA_FADING = 5,
EVENT_NECROTIC_AURA = 1,
EVENT_DEATHBLOOM,
EVENT_INEVITABLE_DOOM,
EVENT_SPORE,
EVENT_NECROTIC_AURA_FADING,
EVENT_NECROTIC_AURA_FADED
};
enum Achievement
@@ -82,10 +82,10 @@ class boss_loatheb : public CreatureScript
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
events.ScheduleEvent(EVENT_NECROTIC_AURA, 17 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_DEATHBLOOM, 5 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SPORE, RAID_MODE(36,18) * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2 * MINUTE * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_NECROTIC_AURA, Seconds(17));
events.ScheduleEvent(EVENT_DEATHBLOOM, Seconds(5));
events.ScheduleEvent(EVENT_SPORE, Seconds(18));
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, Minutes(2));
}
void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
@@ -94,13 +94,6 @@ class boss_loatheb : public CreatureScript
summon->CastSpell(summon,SPELL_FUNGAL_CREEP,true);
}
void SummonedCreatureDespawn(Creature* summon) override
{
summons.Despawn(summon);
if (summon->IsAlive())
summon->CastSpell(summon,SPELL_FUNGAL_CREEP,true);
}
uint32 GetData(uint32 id) const override
{
return (_sporeLoserData && id == DATA_ACHIEVEMENT_SPORE_LOSER) ? 1u : 0u;
@@ -119,34 +112,33 @@ class boss_loatheb : public CreatureScript
{
case EVENT_NECROTIC_AURA:
DoCastAOE(SPELL_NECROTIC_AURA);
DoCast(me, SPELL_WARN_NECROTIC_AURA);
events.ScheduleEvent(EVENT_NECROTIC_AURA, 20 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14 * IN_MILLISECONDS);
Talk(SAY_NECROTIC_AURA_APPLIED);
events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, Seconds(14));
events.ScheduleEvent(EVENT_NECROTIC_AURA_FADED, Seconds(17));
events.Repeat(Seconds(20));
break;
case EVENT_DEATHBLOOM:
DoCastAOE(SPELL_DEATHBLOOM);
events.ScheduleEvent(EVENT_DEATHBLOOM, 30 * IN_MILLISECONDS);
events.Repeat(Seconds(30));
break;
case EVENT_INEVITABLE_DOOM:
_doomCounter++;
DoCastAOE(SPELL_INEVITABLE_DOOM);
if (_doomCounter > 6)
{
if (_doomCounter & 1) // odd
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 14 * IN_MILLISECONDS);
else
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 17 * IN_MILLISECONDS);
}
events.Repeat((_doomCounter & 1) ? Seconds(14) : Seconds(17));
else
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 30 * IN_MILLISECONDS);
events.Repeat(30);
break;
case EVENT_SPORE:
DoCast(me, SPELL_SUMMON_SPORE, false);
events.ScheduleEvent(EVENT_SPORE, RAID_MODE(36,18) * IN_MILLISECONDS);
events.Repeat(RAID_MODE(Seconds(36), Seconds(15)));
break;
case EVENT_NECROTIC_AURA_FADING:
Talk(SAY_NECROTIC_AURA_FADING);
break;
case EVENT_NECROTIC_AURA_FADED:
Talk(SAY_NECROTIC_AURA_REMOVED);
break;
default:
break;
}
@@ -177,49 +169,6 @@ class achievement_spore_loser : public AchievementCriteriaScript
}
};
typedef boss_loatheb::boss_loathebAI LoathebAI;
class spell_loatheb_necrotic_aura_warning : public SpellScriptLoader
{
public:
spell_loatheb_necrotic_aura_warning() : SpellScriptLoader("spell_loatheb_necrotic_aura_warning") { }
class spell_loatheb_necrotic_aura_warning_AuraScript : public AuraScript
{
PrepareAuraScript(spell_loatheb_necrotic_aura_warning_AuraScript);
bool Validate(SpellInfo const* /*spell*/) override
{
if (!sSpellStore.LookupEntry(SPELL_WARN_NECROTIC_AURA))
return false;
return true;
}
void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTarget()->IsAIEnabled)
ENSURE_AI(LoathebAI, GetTarget()->GetAI())->Talk(SAY_NECROTIC_AURA_APPLIED);
}
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTarget()->IsAIEnabled)
ENSURE_AI(LoathebAI, GetTarget()->GetAI())->Talk(SAY_NECROTIC_AURA_REMOVED);
}
void Register() override
{
AfterEffectApply += AuraEffectApplyFn(spell_loatheb_necrotic_aura_warning_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
AfterEffectRemove += AuraEffectRemoveFn(spell_loatheb_necrotic_aura_warning_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_loatheb_necrotic_aura_warning_AuraScript();
}
};
class spell_loatheb_deathbloom : public SpellScriptLoader
{
public:
@@ -260,6 +209,5 @@ void AddSC_boss_loatheb()
{
new boss_loatheb();
new achievement_spore_loser();
new spell_loatheb_necrotic_aura_warning();
new spell_loatheb_deathbloom();
}

View File

@@ -73,6 +73,8 @@ struct WebTargetSelector : public std::unary_function<Unit*, bool>
WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {}
bool operator()(Unit const* target) const
{
if (target->GetTypeId() != TYPEID_PLAYER) // never web nonplayers (pets, guardians, etc.)
return false;
if (_maexxna->GetVictim() == target) // never target tank
return false;
if (target->HasAura(SPELL_WEB_WRAP)) // never target targets that are already webbed
@@ -101,11 +103,11 @@ public:
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
events.ScheduleEvent(EVENT_WRAP, 20 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SPRAY, 40 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SHOCK, urandms(5, 10));
events.ScheduleEvent(EVENT_POISON, urandms(10, 15));
events.ScheduleEvent(EVENT_SUMMON, 30 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_WRAP, Seconds(20));
events.ScheduleEvent(EVENT_SPRAY, Seconds(40));
events.ScheduleEvent(EVENT_SHOCK, randtime(Seconds(5), Seconds(10)));
events.ScheduleEvent(EVENT_POISON, randtime(Seconds(10), Seconds(15)));
events.ScheduleEvent(EVENT_SUMMON, Seconds(30));
}
void Reset() override
@@ -153,28 +155,28 @@ public:
}
}
}
events.ScheduleEvent(EVENT_WRAP, 40000);
events.Repeat(Seconds(40));
break;
}
case EVENT_SPRAY:
Talk(EMOTE_WEB_SPRAY);
DoCastAOE(SPELL_WEB_SPRAY);
events.ScheduleEvent(EVENT_SPRAY, 40000);
events.Repeat(Seconds(40));
break;
case EVENT_SHOCK:
DoCastAOE(SPELL_POISON_SHOCK);
events.ScheduleEvent(EVENT_SHOCK, urandms(10, 20));
events.Repeat(randtime(Seconds(10), Seconds(20)));
break;
case EVENT_POISON:
DoCastVictim(SPELL_NECROTIC_POISON);
events.ScheduleEvent(EVENT_POISON, urandms(10, 20));
events.Repeat(randtime(Seconds(10), Seconds(20)));
break;
case EVENT_SUMMON:
Talk(EMOTE_SPIDERS);
uint8 amount = urand(8, 10);
for (uint8 i = 0; i < amount; ++i)
DoSummon(NPC_SPIDERLING, me, 4.0f, 5 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
events.ScheduleEvent(EVENT_SUMMON, 40000);
events.Repeat(Seconds(40));
break;
}
}

View File

@@ -131,25 +131,25 @@ public:
Reset();
else
{
uint8 secondsGround;
uint8 timeGround;
switch (balconyCount)
{
case 0:
secondsGround = 90;
timeGround = 90;
break;
case 1:
secondsGround = 110;
timeGround = 110;
break;
case 2:
default:
secondsGround = 180;
timeGround = 180;
}
events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, 2 * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_BALCONY, secondsGround * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_CURSE, urand(10,25) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_WARRIOR, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, Seconds(2), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_BALCONY, Seconds(timeGround), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_CURSE, randtime(Seconds(10), Seconds(25)), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_WARRIOR, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
events.ScheduleEvent(EVENT_BLINK, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_BLINK, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND);
}
}
@@ -220,7 +220,7 @@ public:
case EVENT_CURSE:
{
DoCastAOE(SPELL_CURSE);
events.ScheduleEvent(EVENT_CURSE, urand(50, 70) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.Repeat(randtime(Seconds(50), Seconds(70)));
break;
}
case EVENT_WARRIOR:
@@ -229,7 +229,7 @@ public:
CastSummon(RAID_MODE(2, 3), 0, 0);
events.ScheduleEvent(EVENT_WARRIOR, 40 * IN_MILLISECONDS, 0, PHASE_GROUND);
events.Repeat(Seconds(40));
break;
case EVENT_BLINK:
DoCastAOE(SPELL_CRIPPLE, true);
@@ -237,7 +237,7 @@ public:
DoResetThreat();
justBlinked = true;
events.ScheduleEvent(EVENT_BLINK, 40000, 0, PHASE_GROUND);
events.Repeat(Seconds(40));
break;
case EVENT_BALCONY:
events.SetPhase(PHASE_BALCONY);
@@ -247,24 +247,24 @@ public:
me->StopMoving();
me->RemoveAllAuras();
events.ScheduleEvent(EVENT_BALCONY_TELEPORT, 3 * IN_MILLISECONDS, 0, PHASE_BALCONY);
events.ScheduleEvent(EVENT_WAVE, urand(5 * IN_MILLISECONDS, 8 * IN_MILLISECONDS), 0, PHASE_BALCONY);
events.ScheduleEvent(EVENT_BALCONY_TELEPORT, Seconds(3), 0, PHASE_BALCONY);
events.ScheduleEvent(EVENT_WAVE, randtime(Seconds(5), Seconds(8)), 0, PHASE_BALCONY);
uint8 secondsBalcony;
uint8 timeBalcony;
switch (balconyCount)
{
case 0:
secondsBalcony = 70;
timeBalcony = 70;
break;
case 1:
secondsBalcony = 97;
timeBalcony = 97;
break;
case 2:
default:
secondsBalcony = 120;
timeBalcony = 120;
break;
}
events.ScheduleEvent(EVENT_GROUND, secondsBalcony * IN_MILLISECONDS, 0, PHASE_BALCONY);
events.ScheduleEvent(EVENT_GROUND, Seconds(timeBalcony), 0, PHASE_BALCONY);
break;
case EVENT_BALCONY_TELEPORT:
Talk(EMOTE_TELEPORT_1);
@@ -287,7 +287,7 @@ public:
CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10));
break;
}
events.ScheduleEvent(EVENT_WAVE, urand(30, 45) * IN_MILLISECONDS, 0, PHASE_BALCONY);
events.Repeat(randtime(Seconds(30), Seconds(45)));
break;
case EVENT_GROUND:
++balconyCount;

View File

@@ -97,8 +97,8 @@ public:
_EnterCombat();
Enraged = false;
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_HATEFUL, 1 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_HATEFUL, Seconds(1));
events.ScheduleEvent(EVENT_BERSERK, Minutes(6));
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_MAKE_QUICK_WERK_OF_HIM_STARTING_EVENT);
}
@@ -167,17 +167,17 @@ public:
if (thirdThreatTarget)
me->getThreatManager().addThreat(thirdThreatTarget, HATEFUL_THREAT_AMT); // this will only ever be used in 25m
events.ScheduleEvent(EVENT_HATEFUL, 1 * IN_MILLISECONDS);
events.Repeat(Seconds(1));
break;
}
case EVENT_BERSERK:
DoCast(me, SPELL_BERSERK, true);
Talk(EMOTE_BERSERK);
events.ScheduleEvent(EVENT_SLIME, 2 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SLIME, Seconds(2));
break;
case EVENT_SLIME:
DoCastVictim(SPELL_SLIME_BOLT, true);
events.ScheduleEvent(EVENT_SLIME, 2 * IN_MILLISECONDS);
DoCastAOE(SPELL_SLIME_BOLT, true);
events.Repeat(Seconds(2));
break;
}
}

View File

@@ -104,6 +104,9 @@ public:
void JustDied(Unit* /*killer*/) override
{
for (ObjectGuid summonGuid : summons)
if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid))
summon->RemoveCharmAuras();
Talk(SAY_DEATH);
DoCastAOE(SPELL_HOPELESS, true);
@@ -117,10 +120,10 @@ public:
me->StopMoving();
summons.DoZoneInCombat();
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_ATTACK, 7 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_STRIKE, 21 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_KNIFE, 10 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_ATTACK, Seconds(7));
events.ScheduleEvent(EVENT_STRIKE, Seconds(21));
events.ScheduleEvent(EVENT_SHOUT, Seconds(16));
events.ScheduleEvent(EVENT_KNIFE, Seconds(10));
}
void UpdateAI(uint32 diff) override
@@ -141,16 +144,16 @@ public:
break;
case EVENT_STRIKE:
DoCastVictim(SPELL_UNBALANCING_STRIKE);
events.ScheduleEvent(EVENT_STRIKE, 6 * IN_MILLISECONDS);
events.Repeat(Seconds(6));
return;
case EVENT_SHOUT:
DoCastAOE(SPELL_DISRUPTING_SHOUT);
events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS);
events.Repeat(Seconds(16));
return;
case EVENT_KNIFE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f))
DoCast(target, SPELL_JAGGED_KNIFE);
events.ScheduleEvent(EVENT_KNIFE, urandms(10,15));
events.Repeat(randtime(Seconds(10), Seconds(15)));
return;
}
}
@@ -174,7 +177,10 @@ class npc_dk_understudy : public CreatureScript
struct npc_dk_understudyAI : public ScriptedAI
{
npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0) { }
npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0)
{
creature->LoadEquipment(1);
}
void EnterCombat(Unit* /*who*/) override
{

View File

@@ -16,7 +16,9 @@
*/
#include "ScriptMgr.h"
#include "GameObjectAI.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "Player.h"
#include "SpellInfo.h"
#include "naxxramas.h"
@@ -31,25 +33,24 @@ enum Yells
enum Spells
{
SPELL_FROST_AURA = 28531,
SPELL_CLEAVE = 19983,
SPELL_TAIL_SWEEP = 55697,
SPELL_SUMMON_BLIZZARD = 28560,
SPELL_LIFE_DRAIN = 28542,
SPELL_ICEBOLT = 28522,
SPELL_FROST_BREATH = 29318,
SPELL_FROST_EXPLOSION = 28524,
SPELL_FROST_MISSILE = 30101,
SPELL_BERSERK = 26662,
SPELL_DIES = 29357,
SPELL_CHILL = 28547,
SPELL_CHECK_RESISTS = 60539,
SPELL_FROST_AURA = 28531,
SPELL_CLEAVE = 19983,
SPELL_TAIL_SWEEP = 55697,
SPELL_SUMMON_BLIZZARD = 28560,
SPELL_LIFE_DRAIN = 28542,
SPELL_ICEBOLT = 28522,
SPELL_FROST_BREATH_ANTICHEAT = 29318, // damage effect ignoring LoS on the entrance platform to prevent cheese
SPELL_FROST_BREATH = 28524, // damage effect below sapphiron
SPELL_FROST_MISSILE = 30101, // visual only
SPELL_BERSERK = 26662,
SPELL_DIES = 29357,
SPELL_CHECK_RESISTS = 60539,
SPELL_SAPPHIRON_WING_BUFFET = 29328
};
enum Phases
{
PHASE_NULL = 0,
PHASE_BIRTH,
PHASE_BIRTH = 1,
PHASE_GROUND,
PHASE_FLIGHT
};
@@ -72,9 +73,15 @@ enum Events
EVENT_CHECK_RESISTS
};
enum Actions
{
ACTION_BIRTH = 1
};
enum Misc
{
NPC_BLIZZARD = 16474,
NPC_WING_BUFFET = 17025,
GO_ICEBLOCK = 181247,
// The Hundred Club
@@ -92,77 +99,74 @@ class boss_sapphiron : public CreatureScript
struct boss_sapphironAI : public BossAI
{
boss_sapphironAI(Creature* creature) :
BossAI(creature, BOSS_SAPPHIRON), _iceboltCount(0), _map(me->GetMap())
BossAI(creature, BOSS_SAPPHIRON)
{
Initialize();
}
void Initialize()
{
_phase = PHASE_NULL;
_delayedDrain = false;
_canTheHundredClub = true;
}
void InitializeAI() override
{
if (instance->GetBossState(BOSS_SAPPHIRON) == DONE)
return;
_canTheHundredClub = true;
float x, y, z;
me->GetPosition(x, y, z);
me->SummonGameObject(GO_BIRTH, x, y, z, 0, 0, 0, 0, 0, 0);
me->SetVisible(false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
if (!instance->GetData(DATA_HAD_SAPPHIRON_BIRTH))
{
me->SetVisible(false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
}
BossAI::InitializeAI();
}
void Reset() override
{
_Reset();
if (_phase == PHASE_FLIGHT)
if (events.IsInPhase(PHASE_FLIGHT))
{
ClearIceBlock();
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ICEBOLT);
me->SetReactState(REACT_AGGRESSIVE);
if (me->IsHovering())
{
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetHover(false);
}
me->SetDisableGravity(false);
}
_Reset();
Initialize();
}
void DamageTaken(Unit* /*who*/, uint32& damage) override
{
if (damage < me->GetHealth() || !events.IsInPhase(PHASE_FLIGHT))
return;
damage = me->GetHealth()-1; // don't die during air phase
}
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
me->CastSpell(me, SPELL_FROST_AURA, true);
DoCast(me, SPELL_CHECK_RESISTS);
events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_BERSERK, 15 * MINUTE * IN_MILLISECONDS);
EnterPhaseGround();
events.SetPhase(PHASE_GROUND);
events.ScheduleEvent(EVENT_CHECK_RESISTS, Seconds(0));
events.ScheduleEvent(EVENT_BERSERK, Minutes(15));
EnterPhaseGround(true);
}
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
{
switch(spell->Id)
{
case SPELL_ICEBOLT:
{
IceBlockMap::iterator itr = _iceblocks.find(target->GetGUID());
if (itr != _iceblocks.end() && !itr->second)
{
if (GameObject* iceblock = me->SummonGameObject(GO_ICEBLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, 0, 0, 0, 0, 25))
itr->second = iceblock->GetGUID();
}
break;
}
case SPELL_CHECK_RESISTS:
if (target && target->GetResistance(SPELL_SCHOOL_FROST) > MAX_FROST_RESISTANCE)
_canTheHundredClub = false;
@@ -179,41 +183,37 @@ class boss_sapphiron : public CreatureScript
void MovementInform(uint32 /*type*/, uint32 id) override
{
if (id == 1)
events.ScheduleEvent(EVENT_LIFTOFF, 0);
events.ScheduleEvent(EVENT_LIFTOFF, Seconds(0), 0, PHASE_FLIGHT);
}
void DoAction(int32 param) override
{
if (param == DATA_SAPPHIRON_BIRTH)
if (param == ACTION_BIRTH)
{
_phase = PHASE_BIRTH;
events.ScheduleEvent(EVENT_BIRTH, 23 * IN_MILLISECONDS);
events.SetPhase(PHASE_BIRTH);
events.ScheduleEvent(EVENT_BIRTH, Seconds(23));
}
}
void EnterPhaseGround()
void EnterPhaseGround(bool initial)
{
_phase = PHASE_GROUND;
me->SetReactState(REACT_AGGRESSIVE);
events.SetPhase(PHASE_GROUND);
events.ScheduleEvent(EVENT_CLEAVE, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_TAIL, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_DRAIN, 24 * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_BLIZZARD, urand(5, 10) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_FLIGHT, 45 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(5), Seconds(15)), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_TAIL, randtime(Seconds(7), Seconds(10)), 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_BLIZZARD, randtime(Seconds(5), Seconds(10)), 0, PHASE_GROUND);
if (initial)
{
events.ScheduleEvent(EVENT_DRAIN, randtime(Seconds(22), Seconds(28)));
events.ScheduleEvent(EVENT_FLIGHT, Seconds(48) + Milliseconds(500), 0, PHASE_GROUND);
}
else
events.ScheduleEvent(EVENT_FLIGHT, Minutes(1), 0, PHASE_GROUND);
}
void ClearIceBlock()
inline void CastDrain()
{
for (IceBlockMap::const_iterator itr = _iceblocks.begin(); itr != _iceblocks.end(); ++itr)
{
if (Player* player = ObjectAccessor::GetPlayer(*me, itr->first))
player->RemoveAura(SPELL_ICEBOLT);
if (GameObject* go = ObjectAccessor::GetGameObject(*me, itr->second))
go->Delete();
}
_iceblocks.clear();
DoCastAOE(SPELL_LIFE_DRAIN);
events.ScheduleEvent(EVENT_DRAIN, randtime(Seconds(22), Seconds(28)));
}
uint32 GetData(uint32 data) const override
@@ -226,15 +226,12 @@ class boss_sapphiron : public CreatureScript
void UpdateAI(uint32 diff) override
{
if (!_phase)
return;
events.Update(diff);
if (_phase != PHASE_BIRTH && !UpdateVictim())
if (!events.IsInPhase(PHASE_BIRTH) && !UpdateVictim())
return;
if (_phase == PHASE_GROUND)
if (events.IsInPhase(PHASE_GROUND))
{
while (uint32 eventId = events.ExecuteEvent())
{
@@ -242,7 +239,10 @@ class boss_sapphiron : public CreatureScript
{
case EVENT_CHECK_RESISTS:
DoCast(me, SPELL_CHECK_RESISTS);
events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS);
events.Repeat(Seconds(30));
return;
case EVENT_GROUND:
EnterPhaseGround(false);
return;
case EVENT_BERSERK:
Talk(EMOTE_ENRAGE);
@@ -250,27 +250,26 @@ class boss_sapphiron : public CreatureScript
return;
case EVENT_CLEAVE:
DoCastVictim(SPELL_CLEAVE);
events.ScheduleEvent(EVENT_CLEAVE, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(5), Seconds(15)), 0, PHASE_GROUND);
return;
case EVENT_TAIL:
DoCastAOE(SPELL_TAIL_SWEEP);
events.ScheduleEvent(EVENT_TAIL, urand(5, 15) * IN_MILLISECONDS, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_TAIL, randtime(Seconds(7), Seconds(10)), 0, PHASE_GROUND);
return;
case EVENT_DRAIN:
DoCastAOE(SPELL_LIFE_DRAIN);
events.ScheduleEvent(EVENT_DRAIN, 24 * IN_MILLISECONDS, 0, PHASE_GROUND);
if (events.IsInPhase(PHASE_FLIGHT))
_delayedDrain = true;
else
CastDrain();
return;
case EVENT_BLIZZARD:
{
if (Creature* summon = DoSummon(NPC_BLIZZARD, me, 0.0f, urand(25, 30) * IN_MILLISECONDS, TEMPSUMMON_TIMED_DESPAWN))
summon->GetMotionMaster()->MoveRandom(40);
events.ScheduleEvent(EVENT_BLIZZARD, RAID_MODE(20, 7) * IN_MILLISECONDS, 0, PHASE_GROUND);
DoCastAOE(SPELL_SUMMON_BLIZZARD);
events.ScheduleEvent(EVENT_BLIZZARD, RAID_MODE(Seconds(20), Seconds(7)), 0, PHASE_GROUND);
break;
}
case EVENT_FLIGHT:
if (HealthAbovePct(10))
{
_phase = PHASE_FLIGHT;
_delayedDrain = false;
events.SetPhase(PHASE_FLIGHT);
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
@@ -293,61 +292,69 @@ class boss_sapphiron : public CreatureScript
{
case EVENT_CHECK_RESISTS:
DoCast(me, SPELL_CHECK_RESISTS);
events.ScheduleEvent(EVENT_CHECK_RESISTS, 30 * IN_MILLISECONDS);
events.Repeat(Seconds(30));
return;
case EVENT_LIFTOFF:
{
Talk(EMOTE_AIR_PHASE);
me->SetDisableGravity(true);
if (Creature* buffet = DoSummon(NPC_WING_BUFFET, me, 0.0f, 0, TEMPSUMMON_MANUAL_DESPAWN))
_buffet = buffet->GetGUID();
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetHover(true);
events.ScheduleEvent(EVENT_ICEBOLT, 1500);
_iceboltCount = RAID_MODE(2, 3);
events.ScheduleEvent(EVENT_ICEBOLT, Seconds(7), 0, PHASE_FLIGHT);
_iceboltTargets.clear();
std::list<Unit*> targets;
SelectTargetList(targets, RAID_MODE(2, 3), SELECT_TARGET_RANDOM, 200.0f, true);
for (Unit* target : targets)
if (target)
_iceboltTargets.push_back(target->GetGUID());
return;
}
case EVENT_ICEBOLT:
{
std::vector<Unit*> targets;
std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin();
for (; i != me->getThreatManager().getThreatList().end(); ++i)
if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER && !(*i)->getTarget()->HasAura(SPELL_ICEBOLT))
targets.push_back((*i)->getTarget());
if (targets.empty())
_iceboltCount = 0;
else
if (_iceboltTargets.empty())
{
std::vector<Unit*>::const_iterator itr = targets.begin();
advance(itr, rand32() % targets.size());
_iceblocks.insert(std::make_pair((*itr)->GetGUID(), ObjectGuid::Empty));
DoCast(*itr, SPELL_ICEBOLT);
--_iceboltCount;
events.ScheduleEvent(EVENT_BREATH, Seconds(2), 0, PHASE_FLIGHT);
return;
}
ObjectGuid target = _iceboltTargets.back();
if (Player* pTarget = ObjectAccessor::GetPlayer(*me, target))
if (pTarget->IsAlive())
DoCast(pTarget, SPELL_ICEBOLT);
_iceboltTargets.pop_back();
if (_iceboltCount)
events.ScheduleEvent(EVENT_ICEBOLT, 1 * IN_MILLISECONDS);
if (_iceboltTargets.empty())
events.ScheduleEvent(EVENT_BREATH, Seconds(2), 0, PHASE_FLIGHT);
else
events.ScheduleEvent(EVENT_BREATH, 1 * IN_MILLISECONDS);
events.Repeat(Seconds(3));
return;
}
case EVENT_BREATH:
{
Talk(EMOTE_BREATH);
DoCastAOE(SPELL_FROST_MISSILE);
events.ScheduleEvent(EVENT_EXPLOSION, 8 * IN_MILLISECONDS);
events.ScheduleEvent(EVENT_EXPLOSION, Seconds(8), 0, PHASE_FLIGHT);
return;
}
case EVENT_EXPLOSION:
CastExplosion();
ClearIceBlock();
events.ScheduleEvent(EVENT_LAND, 3 * IN_MILLISECONDS);
DoCastAOE(SPELL_FROST_BREATH);
DoCastAOE(SPELL_FROST_BREATH_ANTICHEAT);
events.ScheduleEvent(EVENT_LAND, Seconds(3) + Milliseconds(500), 0, PHASE_FLIGHT);
return;
case EVENT_LAND:
if (_delayedDrain)
CastDrain();
if (Creature* cBuffet = ObjectAccessor::GetCreature(*me, _buffet))
{
cBuffet->DespawnOrUnsummon(1 * IN_MILLISECONDS);
_buffet.Clear();
}
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
Talk(EMOTE_GROUND_PHASE);
me->SetHover(false);
me->SetDisableGravity(false);
events.ScheduleEvent(EVENT_GROUND, 1500);
return;
case EVENT_GROUND:
EnterPhaseGround();
events.SetPhase(PHASE_GROUND);
events.ScheduleEvent(EVENT_GROUND, Seconds(3) + Milliseconds(500), 0, PHASE_GROUND);
return;
case EVENT_BIRTH:
me->SetVisible(true);
@@ -359,51 +366,11 @@ class boss_sapphiron : public CreatureScript
}
}
void CastExplosion()
{
DoZoneInCombat(); // make sure everyone is in threatlist
std::vector<Unit*> targets;
std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin();
for (; i != me->getThreatManager().getThreatList().end(); ++i)
{
Unit* target = (*i)->getTarget();
if (target->GetTypeId() != TYPEID_PLAYER)
continue;
if (target->HasAura(SPELL_ICEBOLT))
{
target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, true);
targets.push_back(target);
continue;
}
for (IceBlockMap::const_iterator itr = _iceblocks.begin(); itr != _iceblocks.end(); ++itr)
{
if (GameObject* go = ObjectAccessor::GetGameObject(*me, itr->second))
{
if (go->IsInBetween(me, target, 2.0f)
&& me->GetExactDist2d(target->GetPositionX(), target->GetPositionY()) - me->GetExactDist2d(go->GetPositionX(), go->GetPositionY()) < 5.0f)
{
target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, true);
targets.push_back(target);
break;
}
}
}
}
me->CastSpell(me, SPELL_FROST_EXPLOSION, true);
for (std::vector<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
(*itr)->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, false);
}
private:
Phases _phase;
uint32 _iceboltCount;
IceBlockMap _iceblocks;
std::vector<ObjectGuid> _iceboltTargets;
ObjectGuid _buffet;
bool _delayedDrain;
bool _canTheHundredClub;
Map* _map;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -412,6 +379,251 @@ class boss_sapphiron : public CreatureScript
}
};
class go_sapphiron_birth : public GameObjectScript
{
public:
go_sapphiron_birth() : GameObjectScript("go_sapphiron_birth") { }
struct go_sapphiron_birthAI : public GameObjectAI
{
go_sapphiron_birthAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { }
void OnStateChanged(uint32 state, Unit* who) override
{
if (state == GO_ACTIVATED)
{
if (who)
{
if (Creature* sapphiron = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_SAPPHIRON)))
sapphiron->AI()->DoAction(ACTION_BIRTH);
instance->SetData(DATA_HAD_SAPPHIRON_BIRTH, 1u);
}
}
else if (state == GO_JUST_DEACTIVATED)
{ // prevent ourselves from going back to _READY and resetting the client anim
go->SetRespawnTime(0);
go->Delete();
}
}
InstanceScript* instance;
};
GameObjectAI* GetAI(GameObject* go) const override
{
return GetInstanceAI<go_sapphiron_birthAI>(go);
}
};
class spell_sapphiron_change_blizzard_target : public SpellScriptLoader
{
public:
spell_sapphiron_change_blizzard_target() : SpellScriptLoader("spell_sapphiron_change_blizzard_target") { }
class spell_sapphiron_change_blizzard_target_AuraScript : public AuraScript
{
PrepareAuraScript(spell_sapphiron_change_blizzard_target_AuraScript);
void HandlePeriodic(AuraEffect const* /*eff*/)
{
TempSummon* me = GetTarget()->ToTempSummon();
if (Creature* owner = me ? me->GetSummonerCreatureBase() : nullptr)
{
Unit* newTarget = owner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true);
if (!newTarget)
newTarget = owner->getAttackerForHelper();
if (newTarget)
me->GetMotionMaster()->MoveFollow(newTarget, 0.1f, 0.0f);
else
{
me->StopMoving();
me->GetMotionMaster()->Clear();
}
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_sapphiron_change_blizzard_target_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_sapphiron_change_blizzard_target_AuraScript();
}
};
class spell_sapphiron_icebolt : public SpellScriptLoader
{
public:
spell_sapphiron_icebolt() : SpellScriptLoader("spell_sapphiron_icebolt") { }
class spell_sapphiron_icebolt_AuraScript : public AuraScript
{
PrepareAuraScript(spell_sapphiron_icebolt_AuraScript);
void HandleApply(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/)
{
GetTarget()->ApplySpellImmune(SPELL_ICEBOLT, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FROST, true);
}
void HandleRemove(AuraEffect const* /*eff*/, AuraEffectHandleModes /*mode*/)
{
if (_block)
if (GameObject* oBlock = ObjectAccessor::GetGameObject(*GetTarget(), _block))
oBlock->Delete();
GetTarget()->ApplySpellImmune(SPELL_ICEBOLT, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FROST, false);
}
void HandlePeriodic(AuraEffect const* /*eff*/)
{
if (_block)
return;
if (GetTarget()->isMoving())
return;
float x, y, z;
GetTarget()->GetPosition(x, y, z);
if (GameObject* block = GetTarget()->SummonGameObject(GO_ICEBLOCK, x, y, z, 0, 0, 0, 0, 0, 25))
_block = block->GetGUID();
}
void Register() override
{
AfterEffectApply += AuraEffectApplyFn(spell_sapphiron_icebolt_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
AfterEffectRemove += AuraEffectRemoveFn(spell_sapphiron_icebolt_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
OnEffectPeriodic += AuraEffectPeriodicFn(spell_sapphiron_icebolt_AuraScript::HandlePeriodic, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
ObjectGuid _block;
};
AuraScript* GetAuraScript() const override
{
return new spell_sapphiron_icebolt_AuraScript();
}
};
// @hack Hello, developer from the future! How has your day been?
// Anyway, this is, as you can undoubtedly see, a hack to emulate line of sight checks on a spell that abides line of sight anyway.
// In the current core, line of sight is not properly checked for people standing behind an ice block. This is not a good thing and kills people.
// Thus, we have this hack to check for ice block LoS in a "safe" way. Kind of. It's inaccurate, but in a good way (tends to save people when it shouldn't in edge cases).
// If LoS handling is better in whatever the current revision is when you read this, please get rid of the hack. Thanks!
class spell_sapphiron_frost_breath : public SpellScriptLoader
{
public:
spell_sapphiron_frost_breath() : SpellScriptLoader("spell_sapphiron_frost_breath") { }
class spell_sapphiron_frost_breath_SpellScript : public SpellScript
{
PrepareSpellScript(spell_sapphiron_frost_breath_SpellScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return !!sSpellMgr->GetSpellInfo(SPELL_FROST_BREATH);
}
void HandleTargets(std::list<WorldObject*>& targetList)
{
std::list<GameObject*> blocks;
if(GetCaster())
GetCaster()->GetGameObjectListWithEntryInGrid(blocks, GO_ICEBLOCK, 200.0f);
std::vector<Unit*> toRemove;
toRemove.reserve(3);
std::list<WorldObject*>::iterator it = targetList.begin();
while (it != targetList.end())
{
Unit* target = (*it)->ToUnit();
if (!target)
{
it = targetList.erase(it);
continue;
}
if (target->HasAura(SPELL_ICEBOLT))
{
it = targetList.erase(it);
toRemove.push_back(target);
continue;
}
bool found = false;
for (GameObject* block : blocks)
if (block->IsInBetween(GetCaster(), target, 2.0f) && GetCaster()->GetExactDist2d(block) + 5 >= GetCaster()->GetExactDist2d(target))
{
found = true;
break;
}
if (found)
{
it = targetList.erase(it);
continue;
}
++it;
}
for (Unit* block : toRemove)
block->RemoveAura(SPELL_ICEBOLT);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sapphiron_frost_breath_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_sapphiron_frost_breath_SpellScript();
}
};
class spell_sapphiron_summon_blizzard : public SpellScriptLoader
{
public:
spell_sapphiron_summon_blizzard() : SpellScriptLoader("spell_sapphiron_summon_blizzard") { }
class spell_sapphiron_summon_blizzard_SpellScript : public SpellScript
{
PrepareSpellScript(spell_sapphiron_summon_blizzard_SpellScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return !!sSpellMgr->GetSpellInfo(SPELL_SUMMON_BLIZZARD);
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (Unit* target = GetHitUnit())
if (Creature* blizzard = GetCaster()->SummonCreature(NPC_BLIZZARD, *target, TEMPSUMMON_TIMED_DESPAWN, urandms(25, 30)))
{
blizzard->CastSpell(nullptr, blizzard->m_spells[0], TRIGGERED_NONE);
if (Creature* creatureCaster = GetCaster()->ToCreature())
{
if (Unit* newTarget = creatureCaster->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true))
{
blizzard->GetMotionMaster()->MoveFollow(newTarget, 0.1f, 0.0f);
return;
}
}
blizzard->GetMotionMaster()->MoveFollow(target, 0.1f, 0.0f);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_sapphiron_summon_blizzard_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_sapphiron_summon_blizzard_SpellScript();
}
};
class achievement_the_hundred_club : public AchievementCriteriaScript
{
public:
@@ -426,5 +638,10 @@ class achievement_the_hundred_club : public AchievementCriteriaScript
void AddSC_boss_sapphiron()
{
new boss_sapphiron();
new go_sapphiron_birth();
new spell_sapphiron_change_blizzard_target();
new spell_sapphiron_icebolt();
new spell_sapphiron_frost_breath();
new spell_sapphiron_summon_blizzard();
new achievement_the_hundred_club();
}

View File

@@ -109,6 +109,10 @@ enum PetSpells
SPELL_MAGNETIC_PULL = 54517,
SPELL_MAGNETIC_PULL_EFFECT = 28337,
// @hack feugen/stalagg use this in P1 after gripping tanks to prevent mmaps from pathing them off the platform once they approach the ramp
// developer from the future, if you read this and mmaps in the room has been fixed, then get rid of the hackfix, please
SPELL_ROOT_SELF = 75215,
SPELL_TESLA_SHOCK = 28099
};
@@ -166,10 +170,12 @@ public:
struct boss_thaddiusAI : public BossAI
{
public:
boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningEnabled(false), shockingEligibility(true)
boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningUnlocked(false), ballLightningEnabled(false), shockingEligibility(true) {}
void InitializeAI() override
{
if (instance->GetBossState(BOSS_THADDIUS) != DONE)
{
{
events.SetPhase(PHASE_NOT_ENGAGED);
SetCombatMovement(false);
@@ -184,12 +190,33 @@ public:
Talk(SAY_SLAY);
}
void Reset() override
void Reset() override { }
void EnterEvadeMode(EvadeReason why) override
{
if (!ballLightningEnabled && why == EVADE_REASON_NO_HOSTILES)
{
ballLightningEnabled = true;
return; // try again
}
if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive()))
BeginResetEncounter();
}
bool CanAIAttack(Unit const* who) const override
{
if (ballLightningEnabled || me->IsWithinMeleeRange(who))
return BossAI::CanAIAttack(who);
else
return false;
}
void JustRespawned() override
{
if (events.IsInPhase(PHASE_RESETTING))
ResetEncounter();
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
@@ -217,7 +244,10 @@ public:
case ACTION_FEUGEN_AGGRO:
case ACTION_STALAGG_AGGRO:
if (events.IsInPhase(PHASE_RESETTING))
return BeginResetEncounter();
{
BeginResetEncounter();
return;
}
if (!events.IsInPhase(PHASE_NOT_ENGAGED))
return;
events.SetPhase(PHASE_PETS);
@@ -225,10 +255,14 @@ public:
shockingEligibility = true;
if (!instance->CheckRequiredBosses(BOSS_THADDIUS))
return BeginResetEncounter();
{
BeginResetEncounter();
return;
}
instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS);
me->setActive(true);
DoZoneInCombat();
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
stalagg->setActive(true);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
@@ -239,7 +273,7 @@ public:
feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX);
feugenAlive = false;
if (stalaggAlive)
events.ScheduleEvent(EVENT_REVIVE_FEUGEN, 5 * IN_MILLISECONDS, 0, PHASE_PETS);
events.ScheduleEvent(EVENT_REVIVE_FEUGEN, Seconds(5), 0, PHASE_PETS);
else
Transition();
@@ -249,7 +283,7 @@ public:
stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX);
stalaggAlive = false;
if (feugenAlive)
events.ScheduleEvent(EVENT_REVIVE_STALAGG, 5 * IN_MILLISECONDS, 0, PHASE_PETS);
events.ScheduleEvent(EVENT_REVIVE_STALAGG, Seconds(5), 0, PHASE_PETS);
else
Transition();
@@ -274,9 +308,9 @@ public:
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_TRANSITION_1, 10 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_2, 12 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_3, 14 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_1, Seconds(10), 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_2, Seconds(12), 0, PHASE_TRANSITION);
events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION);
}
void BeginResetEncounter(bool initial = false)
@@ -286,16 +320,12 @@ public:
if (events.IsInPhase(PHASE_RESETTING))
return;
if (initial) // signal shorter spawn timer to instance script
instance->SetBossState(BOSS_THADDIUS, SPECIAL);
instance->ProcessEvent(me, EVENT_THADDIUS_BEGIN_RESET);
instance->SetBossState(BOSS_THADDIUS, NOT_STARTED);
// remove polarity shift debuffs on reset
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY);
me->DespawnOrUnsummon();
me->SetRespawnTime(initial ? 5 : 30);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
events.SetPhase(PHASE_RESETTING);
@@ -312,11 +342,9 @@ public:
feugenAlive = true;
stalaggAlive = true;
me->Respawn(true);
_Reset();
events.SetPhase(PHASE_NOT_ENGAGED);
me->CastSpell(me, SPELL_THADDIUS_INACTIVE_VISUAL, true);
me->SetReactState(REACT_PASSIVE);
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER);
@@ -361,16 +389,20 @@ public:
break;
case EVENT_TRANSITION_3: // thaddius becomes active
me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
ballLightningEnabled = false;
ballLightningUnlocked = false;
me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
if (Unit* closest = SelectTarget(SELECT_TARGET_NEAREST, 0, 500.0f))
AttackStart(closest);
else // if there is no nearest target, then there is no target, meaning we should reset
return BeginResetEncounter();
{
BeginResetEncounter();
return;
}
if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
feugen->AI()->DoAction(ACTION_TRANSITION_3);
@@ -381,20 +413,20 @@ public:
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, 5 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, 10 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS, 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, Seconds(5), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, Seconds(10), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_CHAIN, randtime(Seconds(10), Seconds(20)), 0, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_BERSERK, Minutes(6), 0, PHASE_THADDIUS);
break;
case EVENT_ENABLE_BALL_LIGHTNING:
ballLightningEnabled = true;
ballLightningUnlocked = true;
break;
case EVENT_SHIFT:
me->CastStop(); // shift overrides all other spells
DoCastAOE(SPELL_POLARITY_SHIFT);
events.ScheduleEvent(EVENT_SHIFT_TALK, 3 * IN_MILLISECONDS, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, 30 * IN_MILLISECONDS, PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT_TALK, Seconds(3), PHASE_THADDIUS);
events.ScheduleEvent(EVENT_SHIFT, Seconds(30), PHASE_THADDIUS);
break;
case EVENT_SHIFT_TALK:
Talk(SAY_ELECT);
@@ -402,12 +434,12 @@ public:
break;
case EVENT_CHAIN:
if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over
events.ScheduleEvent(EVENT_CHAIN, 3 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
events.Repeat(Seconds(3));
else
{
me->CastStop();
DoCastVictim(SPELL_CHAIN_LIGHTNING);
events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, PHASE_THADDIUS);
events.Repeat(randtime(Seconds(10), Seconds(20)));
}
break;
case EVENT_BERSERK:
@@ -418,22 +450,24 @@ public:
break;
}
}
if (events.IsInPhase(PHASE_THADDIUS))
if (events.IsInPhase(PHASE_THADDIUS) && !me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady())
{
if (me->IsWithinMeleeRange(me->GetVictim()))
{
ballLightningEnabled = false;
DoMeleeAttackIfReady();
else
if (ballLightningEnabled)
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
DoCast(target, SPELL_BALL_LIGHTNING);
}
else if (ballLightningUnlocked)
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
DoCast(target, SPELL_BALL_LIGHTNING);
}
}
private:
bool stalaggAlive;
bool feugenAlive;
bool ballLightningEnabled;
bool ballLightningUnlocked; // whether the initial ball lightning grace period has expired and we should proceed to exterminate with extreme prejudice
bool ballLightningEnabled; // switch that is flipped to true if we try to evade due to no eligible targets in melee range
bool shockingEligibility;
};
@@ -652,7 +686,7 @@ public:
else
{
DoCast(me, SPELL_STALAGG_POWERSURGE);
powerSurgeTimer = urand(25, 30) * IN_MILLISECONDS;
powerSurgeTimer = urandms(25, 30);
}
}
else
@@ -836,7 +870,7 @@ public:
Talk(SAY_FEUGEN_AGGRO);
if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
thaddius->AI()->DoAction(ACTION_STALAGG_AGGRO);
thaddius->AI()->DoAction(ACTION_FEUGEN_AGGRO);
if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
if (!stalagg->IsInCombat())
@@ -1094,7 +1128,7 @@ class spell_thaddius_polarity_charge : public SpellScriptLoader
SpellScript* GetSpellScript() const override
{
return new spell_thaddius_polarity_charge_SpellScript;
return new spell_thaddius_polarity_charge_SpellScript();
}
};
@@ -1212,6 +1246,10 @@ class spell_thaddius_magnetic_pull : public SpellScriptLoader
feugenTank->CastSpell(stalaggTank, SPELL_MAGNETIC_PULL_EFFECT, true);
stalaggTank->CastSpell(feugenTank, SPELL_MAGNETIC_PULL_EFFECT, true);
// @hack prevent mmaps clusterfucks from breaking tesla while tanks are midair
feugen->AddAura(SPELL_ROOT_SELF, feugen);
stalagg->AddAura(SPELL_ROOT_SELF, stalagg);
// and make both attack their respective new tanks
if (feugen->GetAI())
feugen->GetAI()->AttackStart(stalaggTank);

View File

@@ -100,36 +100,6 @@ ObjectData const objectData[] =
{ 0, 0, }
};
// from P2 teleport spell stored target
float const HeiganPos[2] = { 2793.86f, -3707.38f };
float const HeiganEruptionSlope[3] =
{
(-3703.303223f - HeiganPos[1]) / (2777.494141f - HeiganPos[0]), // between right center and far right
(-3696.948242f - HeiganPos[1]) / (2785.624268f - HeiganPos[0]), // between left and right halves
(-3691.880615f - HeiganPos[1]) / (2790.280029f - HeiganPos[0]) // between far left and left center
};
// 0 H x
// 1 ^
// 2 |
// 3 y<--o
inline uint32 GetEruptionSection(float x, float y)
{
y -= HeiganPos[1];
if (y < 1.0f)
return 0;
x -= HeiganPos[0];
if (x > -1.0f)
return 3;
float slope = y/x;
for (uint32 i = 0; i < 3; ++i)
if (slope > HeiganEruptionSlope[i])
return i;
return 3;
}
class instance_naxxramas : public InstanceMapScript
{
public:
@@ -149,6 +119,7 @@ class instance_naxxramas : public InstanceMapScript
hadAnubRekhanGreet = false;
hadFaerlinaGreet = false;
hadThaddiusGreet = false;
hadSapphironBirth = false;
CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT;
playerDied = 0;
@@ -211,28 +182,8 @@ class instance_naxxramas : public InstanceMapScript
}
}
void ProcessEvent(WorldObject* /*source*/, uint32 eventId) override
{
switch (eventId)
{
case EVENT_THADDIUS_BEGIN_RESET:
if (GetBossState(BOSS_THADDIUS) == SPECIAL) // this is the initial spawn, we want a shorter spawn time
events.ScheduleEvent(EVENT_THADDIUS_RESET, 5 * IN_MILLISECONDS);
else
events.ScheduleEvent(EVENT_THADDIUS_RESET, 30 * IN_MILLISECONDS);
break;
}
}
void OnGameObjectCreate(GameObject* go) override
{
if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287)
{
uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY());
HeiganEruptionGUID[section].insert(go->GetGUID());
return;
}
switch (go->GetEntry())
{
case GO_GOTHIK_GATE:
@@ -276,37 +227,18 @@ class instance_naxxramas : public InstanceMapScript
if (GetBossState(BOSS_HORSEMEN) == DONE)
go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
break;
default:
break;
}
InstanceScript::OnGameObjectCreate(go);
}
void OnGameObjectRemove(GameObject* go) override
{
if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287)
{
uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY());
HeiganEruptionGUID[section].erase(go->GetGUID());
return;
}
switch (go->GetEntry())
{
case GO_BIRTH:
if (SapphironGUID)
if (hadSapphironBirth || GetBossState(BOSS_SAPPHIRON) == DONE)
{
if (Creature* sapphiron = instance->GetCreature(SapphironGUID))
sapphiron->AI()->DoAction(DATA_SAPPHIRON_BIRTH);
return;
hadSapphironBirth = true;
go->Delete();
}
break;
default:
break;
}
InstanceScript::OnGameObjectRemove(go);
InstanceScript::OnGameObjectCreate(go);
}
void OnUnitDeath(Unit* unit) override
@@ -331,9 +263,6 @@ class instance_naxxramas : public InstanceMapScript
{
switch (id)
{
case DATA_HEIGAN_ERUPT:
HeiganErupt(value);
break;
case DATA_GOTHIK_GATE:
if (GameObject* gate = instance->GetGameObject(GothikGateGUID))
gate->SetGoState(GOState(value));
@@ -350,6 +279,9 @@ class instance_naxxramas : public InstanceMapScript
case DATA_HAD_THADDIUS_GREET:
hadThaddiusGreet = (value == 1u);
break;
case DATA_HAD_SAPPHIRON_BIRTH:
hadSapphironBirth = (value == 1u);
break;
default:
break;
}
@@ -367,6 +299,8 @@ class instance_naxxramas : public InstanceMapScript
return hadFaerlinaGreet ? 1u : 0u;
case DATA_HAD_THADDIUS_GREET:
return hadThaddiusGreet ? 1u : 0u;
case DATA_HAD_SAPPHIRON_BIRTH:
return hadSapphironBirth ? 1u : 0u;
default:
break;
}
@@ -404,6 +338,8 @@ class instance_naxxramas : public InstanceMapScript
return StalaggGUID;
case DATA_THADDIUS:
return ThaddiusGUID;
case DATA_SAPPHIRON:
return SapphironGUID;
case DATA_KELTHUZAD:
return KelthuzadGUID;
case DATA_KELTHUZAD_PORTAL01:
@@ -436,7 +372,7 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_ARACHNID))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_LOATHEB:
@@ -445,7 +381,7 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_PLAGUE))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_THADDIUS:
@@ -454,12 +390,12 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_CONSTRUCT))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_GOTHIK:
if (state == DONE)
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ, 10000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ, Seconds(10));
break;
case BOSS_HORSEMEN:
if (state == DONE)
@@ -473,12 +409,12 @@ class instance_naxxramas : public InstanceMapScript
if (GameObject* teleporter = GetGameObject(DATA_NAXX_PORTAL_MILITARY))
teleporter->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000);
events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, Seconds(6));
}
break;
case BOSS_SAPPHIRON:
if (state == DONE)
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD, Seconds(6));
break;
default:
break;
@@ -498,37 +434,37 @@ class instance_naxxramas : public InstanceMapScript
case EVENT_DIALOGUE_GOTHIK_KORTHAZZ:
if (Creature* korthazz = instance->GetCreature(ThaneGUID))
korthazz->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK, 5000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK, Seconds(5));
break;
case EVENT_DIALOGUE_GOTHIK_ZELIEK:
if (Creature* zeliek = instance->GetCreature(SirGUID))
zeliek->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_BLAUMEUX:
if (Creature* blaumeux = instance->GetCreature(LadyGUID))
blaumeux->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_RIVENDARE:
if (Creature* rivendare = instance->GetCreature(BaronGUID))
rivendare->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX2, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_BLAUMEUX2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_BLAUMEUX2:
if (Creature* blaumeux = instance->GetCreature(LadyGUID))
blaumeux->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK2, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_ZELIEK2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_ZELIEK2:
if (Creature* zeliek = instance->GetCreature(SirGUID))
zeliek->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_KORTHAZZ2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_KORTHAZZ2:
if (Creature* korthazz = instance->GetCreature(ThaneGUID))
korthazz->AI()->Talk(SAY_DIALOGUE_GOTHIK_HORSEMAN2);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE2, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_GOTHIK_RIVENDARE2, Seconds(6));
break;
case EVENT_DIALOGUE_GOTHIK_RIVENDARE2:
if (Creature* rivendare = instance->GetCreature(BaronGUID))
@@ -545,62 +481,39 @@ class instance_naxxramas : public InstanceMapScript
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD);
HandleGameObject(KelthuzadDoorGUID, false);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING, Seconds(6));
break;
case EVENT_DIALOGUE_SAPPHIRON_LICHKING:
if (Creature* lichKing = instance->GetCreature(LichKingGUID))
lichKing->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_LICH_KING);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2, 16000);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2, Seconds(16));
break;
case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD2:
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD2);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING2, 9000);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_LICHKING2, Seconds(9));
break;
case EVENT_DIALOGUE_SAPPHIRON_LICHKING2:
if (Creature* lichKing = instance->GetCreature(LichKingGUID))
lichKing->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_LICH_KING2);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3, 12000);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3, Seconds(12));
break;
case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD3:
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD3);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4, 6000);
events.ScheduleEvent(EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4, Seconds(6));
break;
case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD4:
if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID))
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD4);
HandleGameObject(KelthuzadDoorGUID, true);
break;
case EVENT_THADDIUS_RESET:
if (GetBossState(BOSS_THADDIUS) != DONE)
if (Creature* thaddius = instance->GetCreature(ThaddiusGUID))
thaddius->AI()->DoAction(-1);
break;
default:
break;
}
}
}
void HeiganErupt(uint32 section)
{
for (uint32 i = 0; i < 4; ++i)
{
if (i == section)
continue;
for (ObjectGuid guid : HeiganEruptionGUID[i])
{
if (GameObject* heiganEruption = instance->GetGameObject(guid))
{
heiganEruption->SendCustomAnim(heiganEruption->GetGoAnimProgress());
heiganEruption->CastSpell(NULL, SPELL_ERUPTION);
}
}
}
}
// This Function is called in CheckAchievementCriteriaMeet and CheckAchievementCriteriaMeet is called before SetBossState(bossId, DONE),
// so to check if all bosses are done the checker must exclude 1 boss, the last done, if there is at most 1 encouter in progress when is
// called this function then all bosses are done. The one boss that check is the boss that calls this function, so it is dead.
@@ -658,7 +571,6 @@ class instance_naxxramas : public InstanceMapScript
/* The Plague Quarter */
// Heigan the Unclean
GuidSet HeiganEruptionGUID[4];
ObjectGuid HeiganGUID;
/* The Military Quarter */
@@ -695,6 +607,7 @@ class instance_naxxramas : public InstanceMapScript
bool hadAnubRekhanGreet;
bool hadFaerlinaGreet;
bool hadThaddiusGreet;
bool hadSapphironBirth;
uint8 CurrentWingTaunt;
/* The Immortal / The Undying */

View File

@@ -43,12 +43,11 @@ enum Encounter
enum Data
{
DATA_HEIGAN_ERUPT,
DATA_GOTHIK_GATE,
DATA_SAPPHIRON_BIRTH,
DATA_HAD_ANUBREKHAN_GREET,
DATA_HAD_FAERLINA_GREET,
DATA_HAD_THADDIUS_GREET,
DATA_HAD_SAPPHIRON_BIRTH,
DATA_HORSEMEN_CHECK_ACHIEVEMENT_CREDIT,
DATA_ABOMINATION_KILLED,
@@ -74,6 +73,7 @@ enum Data64
DATA_HEIGAN,
DATA_FEUGEN,
DATA_STALAGG,
DATA_SAPPHIRON,
DATA_KELTHUZAD,
DATA_KELTHUZAD_PORTAL01,
DATA_KELTHUZAD_PORTAL02,
@@ -160,12 +160,6 @@ enum GameObjectsIds
GO_NAXX_PORTAL_MILITARY = 181578
};
enum SpellIds
{
SPELL_ERUPTION = 29371,
SPELL_SLIME = 28801
};
enum InstanceEvents
{
// Dialogue that happens after Gothik's death.
@@ -178,10 +172,6 @@ enum InstanceEvents
EVENT_DIALOGUE_GOTHIK_KORTHAZZ2,
EVENT_DIALOGUE_GOTHIK_RIVENDARE2,
// Thaddius AI requesting timed encounter (re-)spawn
EVENT_THADDIUS_BEGIN_RESET,
EVENT_THADDIUS_RESET,
// Dialogue that happens after each wing.
EVENT_KELTHUZAD_WING_TAUNT,