refactor naxxramas

This commit is contained in:
Yunfan Li
2023-07-18 17:58:03 +08:00
parent 4d3abb0538
commit 2afc3a46d0
30 changed files with 5332 additions and 5347 deletions

View File

@@ -17,215 +17,6 @@
#include "boss_anubrekhan.h"
enum Says
{
SAY_AGGRO = 0,
SAY_GREET = 1,
SAY_SLAY = 2,
EMOTE_LOCUST = 3
};
enum GuardSays
{
EMOTE_SPAWN = 1,
EMOTE_SCARAB = 2
};
enum Spells
{
SPELL_IMPALE_10 = 28783,
SPELL_IMPALE_25 = 56090,
SPELL_LOCUST_SWARM_10 = 28785,
SPELL_LOCUST_SWARM_25 = 54021,
SPELL_SUMMON_CORPSE_SCRABS_5 = 29105,
SPELL_SUMMON_CORPSE_SCRABS_10 = 28864,
SPELL_BERSERK = 26662
};
enum Events
{
EVENT_IMPALE = 1,
EVENT_LOCUST_SWARM = 2,
EVENT_BERSERK = 3,
EVENT_SPAWN_GUARD = 4
};
enum Misc
{
NPC_CORPSE_SCARAB = 16698,
NPC_CRYPT_GUARD = 16573,
ACHIEV_TIMED_START_EVENT = 9891
};
CreatureAI* boss_anubrekhan::GetAI(Creature* pCreature) const
{
return GetNaxxramasAI<boss_anubrekhanAI>(pCreature);
}
boss_anubrekhan::boss_anubrekhanAI::boss_anubrekhanAI(Creature* c) : BossAI(c, BOSS_ANUB), summons(me)
{
pInstance = c->GetInstanceScript();
sayGreet = false;
}
void boss_anubrekhan::boss_anubrekhanAI::SummonCryptGuards()
{
if (Is25ManRaid())
{
me->SummonCreature(NPC_CRYPT_GUARD, 3299.732f, -3502.489f, 287.077f, 2.378f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
me->SummonCreature(NPC_CRYPT_GUARD, 3299.086f, -3450.929f, 287.077f, 3.999f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
}
}
void boss_anubrekhan::boss_anubrekhanAI::Reset()
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
SummonCryptGuards();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_ANUB_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void boss_anubrekhan::boss_anubrekhanAI::JustSummoned(Creature* cr)
{
if (me->IsInCombat())
{
cr->SetInCombatWithZone();
if (cr->GetEntry() == NPC_CRYPT_GUARD)
{
cr->AI()->Talk(EMOTE_SPAWN, me);
}
}
summons.Summon(cr);
}
void boss_anubrekhan::boss_anubrekhanAI::SummonedCreatureDies(Creature* cr, Unit*)
{
if (cr->GetEntry() == NPC_CRYPT_GUARD)
{
cr->CastSpell(cr, SPELL_SUMMON_CORPSE_SCRABS_10, true, nullptr, nullptr, me->GetGUID());
cr->AI()->Talk(EMOTE_SCARAB);
}
}
void boss_anubrekhan::boss_anubrekhanAI::SummonedCreatureDespawn(Creature* cr)
{
summons.Despawn(cr);
}
void boss_anubrekhan::boss_anubrekhanAI::JustDied(Unit* killer)
{
BossAI::JustDied(killer);
summons.DespawnAll();
if (pInstance)
{
pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
}
}
void boss_anubrekhan::boss_anubrekhanAI::KilledUnit(Unit* victim)
{
if (victim->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCRABS_5, true, nullptr, nullptr, me->GetGUID());
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void boss_anubrekhan::boss_anubrekhanAI::JustEngagedWith(Unit* who)
{
BossAI::JustEngagedWith(who);
me->CallForHelp(30.0f);
Talk(SAY_AGGRO);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_ANUB_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
events.ScheduleEvent(EVENT_IMPALE, 15s);
events.ScheduleEvent(EVENT_LOCUST_SWARM, 70s, 120s);
events.ScheduleEvent(EVENT_BERSERK, 10min);
if (!summons.HasEntry(NPC_CRYPT_GUARD))
{
SummonCryptGuards();
}
if (!Is25ManRaid())
{
events.ScheduleEvent(EVENT_SPAWN_GUARD, 15s, 20s);
}
}
void boss_anubrekhan::boss_anubrekhanAI::MoveInLineOfSight(Unit* who)
{
if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
{
Talk(SAY_GREET);
sayGreet = true;
}
ScriptedAI::MoveInLineOfSight(who);
}
void boss_anubrekhan::boss_anubrekhanAI::UpdateAI(uint32 diff)
{
if (!me->IsInCombat() && sayGreet)
{
for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (pInstance)
{
if (Creature* cr = pInstance->instance->GetCreature(*itr))
{
if (cr->IsInCombat())
DoZoneInCombat();
}
}
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_IMPALE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->CastSpell(target, RAID_MODE(SPELL_IMPALE_10, SPELL_IMPALE_25), false);
}
events.Repeat(20s);
break;
case EVENT_LOCUST_SWARM:
Talk(EMOTE_LOCUST);
me->CastSpell(me, RAID_MODE(SPELL_LOCUST_SWARM_10, SPELL_LOCUST_SWARM_25), false);
events.ScheduleEvent(EVENT_SPAWN_GUARD, 3s);
events.Repeat(90s);
break;
case EVENT_SPAWN_GUARD:
me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
break;
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
}
DoMeleeAttackIfReady();
}
void AddSC_boss_anubrekhan()
{

View File

@@ -5,41 +5,227 @@
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum AnubrekhanSays
{
ANUBREKHAN_SAY_AGGRO = 0,
ANUBREKHAN_SAY_GREET = 1,
ANUBREKHAN_SAY_SLAY = 2,
ANUBREKHAN_EMOTE_LOCUST = 3
};
enum AnubrekhanGuardSays
{
ANUBREKHAN_EMOTE_SPAWN = 1,
ANUBREKHAN_EMOTE_SCARAB = 2
};
enum AnubrekhanSpells
{
ANUBREKHAN_SPELL_IMPALE_10 = 28783,
ANUBREKHAN_SPELL_IMPALE_25 = 56090,
ANUBREKHAN_SPELL_LOCUST_SWARM_10 = 28785,
ANUBREKHAN_SPELL_LOCUST_SWARM_25 = 54021,
ANUBREKHAN_SPELL_SUMMON_CORPSE_SCRABS_5 = 29105,
ANUBREKHAN_SPELL_SUMMON_CORPSE_SCRABS_10 = 28864,
ANUBREKHAN_SPELL_BERSERK = 26662
};
enum AnubrekhanEvents
{
ANUBREKHAN_EVENT_IMPALE = 1,
ANUBREKHAN_EVENT_LOCUST_SWARM = 2,
ANUBREKHAN_EVENT_BERSERK = 3,
ANUBREKHAN_EVENT_SPAWN_GUARD = 4
};
enum AnubrekhanMisc
{
ANUBREKHAN_NPC_CORPSE_SCARAB = 16698,
ANUBREKHAN_NPC_CRYPT_GUARD = 16573,
ANUBREKHAN_ACHIEV_TIMED_START_EVENT = 9891
};
class boss_anubrekhan : public CreatureScript
{
public:
boss_anubrekhan() : CreatureScript("boss_anubrekhan") { }
CreatureAI* GetAI(Creature* pCreature) const override;
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_anubrekhanAI>(pCreature);
}
struct boss_anubrekhanAI : public BossAI
{
explicit boss_anubrekhanAI(Creature* c);
explicit boss_anubrekhanAI(Creature* c) : BossAI(c, BOSS_ANUB), summons(me)
{
pInstance = c->GetInstanceScript();
sayGreet = false;
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool sayGreet;
void SummonCryptGuards();
void SummonCryptGuards()
{
if (Is25ManRaid())
{
me->SummonCreature(ANUBREKHAN_NPC_CRYPT_GUARD, 3299.732f, -3502.489f, 287.077f, 2.378f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
me->SummonCreature(ANUBREKHAN_NPC_CRYPT_GUARD, 3299.086f, -3450.929f, 287.077f, 3.999f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
}
}
void Reset() override;
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
SummonCryptGuards();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_ANUB_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override;
void JustSummoned(Creature* cr) override
{
if (me->IsInCombat())
{
cr->SetInCombatWithZone();
if (cr->GetEntry() == ANUBREKHAN_NPC_CRYPT_GUARD)
{
cr->AI()->Talk(ANUBREKHAN_EMOTE_SPAWN, me);
}
}
summons.Summon(cr);
}
void SummonedCreatureDies(Creature* cr, Unit*) override;
void SummonedCreatureDies(Creature* cr, Unit*) override
{
if (cr->GetEntry() == ANUBREKHAN_NPC_CRYPT_GUARD)
{
cr->CastSpell(cr, ANUBREKHAN_SPELL_SUMMON_CORPSE_SCRABS_10, true, nullptr, nullptr, me->GetGUID());
cr->AI()->Talk(ANUBREKHAN_EMOTE_SCARAB);
}
}
void SummonedCreatureDespawn(Creature* cr) override;
void SummonedCreatureDespawn(Creature* cr) override
{
summons.Despawn(cr);
}
void JustDied(Unit* killer) override;
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
if (pInstance)
{
pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ANUBREKHAN_ACHIEV_TIMED_START_EVENT);
}
}
void KilledUnit(Unit* victim) override;
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() != TYPEID_PLAYER)
return;
void JustEngagedWith(Unit* who) override;
Talk(ANUBREKHAN_SAY_SLAY);
victim->CastSpell(victim, ANUBREKHAN_SPELL_SUMMON_CORPSE_SCRABS_5, true, nullptr, nullptr, me->GetGUID());
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void MoveInLineOfSight(Unit* who) override;
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->CallForHelp(30.0f);
Talk(ANUBREKHAN_SAY_AGGRO);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_ANUB_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
events.ScheduleEvent(ANUBREKHAN_EVENT_IMPALE, 15s);
events.ScheduleEvent(ANUBREKHAN_EVENT_LOCUST_SWARM, 70s, 120s);
events.ScheduleEvent(ANUBREKHAN_EVENT_BERSERK, 10min);
if (!summons.HasEntry(ANUBREKHAN_NPC_CRYPT_GUARD))
{
SummonCryptGuards();
}
if (!Is25ManRaid())
{
events.ScheduleEvent(ANUBREKHAN_EVENT_SPAWN_GUARD, 15s, 20s);
}
}
void UpdateAI(uint32 diff) override;
void MoveInLineOfSight(Unit* who) override
{
if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
{
Talk(ANUBREKHAN_SAY_GREET);
sayGreet = true;
}
ScriptedAI::MoveInLineOfSight(who);
}
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat() && sayGreet)
{
for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (pInstance)
{
if (Creature* cr = pInstance->instance->GetCreature(*itr))
{
if (cr->IsInCombat())
DoZoneInCombat();
}
}
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case ANUBREKHAN_EVENT_IMPALE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->CastSpell(target, RAID_MODE(ANUBREKHAN_SPELL_IMPALE_10, ANUBREKHAN_SPELL_IMPALE_25), false);
}
events.Repeat(20s);
break;
case ANUBREKHAN_EVENT_LOCUST_SWARM:
Talk(ANUBREKHAN_EMOTE_LOCUST);
me->CastSpell(me, RAID_MODE(ANUBREKHAN_SPELL_LOCUST_SWARM_10, ANUBREKHAN_SPELL_LOCUST_SWARM_25), false);
events.ScheduleEvent(ANUBREKHAN_EVENT_SPAWN_GUARD, 3s);
events.Repeat(90s);
break;
case ANUBREKHAN_EVENT_SPAWN_GUARD:
me->SummonCreature(ANUBREKHAN_NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
break;
case ANUBREKHAN_EVENT_BERSERK:
me->CastSpell(me, ANUBREKHAN_SPELL_BERSERK, true);
break;
}
DoMeleeAttackIfReady();
}
};
};

View File

@@ -15,236 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "naxxramas.h"
enum Yells
{
SAY_GREET = 0,
SAY_AGGRO = 1,
SAY_SLAY = 2,
SAY_DEATH = 3,
EMOTE_WIDOWS_EMBRACE = 4,
EMOTE_FRENZY = 5,
SAY_FRENZY = 6
};
enum Spells
{
SPELL_POISON_BOLT_VOLLEY_10 = 28796,
SPELL_POISON_BOLT_VOLLEY_25 = 54098,
SPELL_RAIN_OF_FIRE_10 = 28794,
SPELL_RAIN_OF_FIRE_25 = 54099,
SPELL_FRENZY_10 = 28798,
SPELL_FRENZY_25 = 54100,
SPELL_WIDOWS_EMBRACE = 28732,
SPELL_MINION_WIDOWS_EMBRACE = 54097
};
enum Events
{
EVENT_POISON_BOLT = 1,
EVENT_RAIN_OF_FIRE = 2,
EVENT_FRENZY = 3
};
enum Misc
{
NPC_NAXXRAMAS_WORSHIPPER = 16506,
NPC_NAXXRAMAS_FOLLOWER = 16505
};
class boss_faerlina : public CreatureScript
{
public:
boss_faerlina() : CreatureScript("boss_faerlina") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_faerlinaAI>(pCreature);
}
struct boss_faerlinaAI : public BossAI
{
boss_faerlinaAI(Creature* c) : BossAI(c, BOSS_FAERLINA), summons(me)
{
pInstance = me->GetInstanceScript();
sayGreet = false;
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool sayGreet;
void SummonHelpers()
{
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3362.66f, -3620.97f, 261.08f, 4.57276f);
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3344.3f, -3618.31f, 261.08f, 4.69494f);
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3356.71f, -3620.05f, 261.08f, 4.57276f);
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3350.26f, -3619.11f, 261.08f, 4.67748f);
if (Is25ManRaid())
{
me->SummonCreature(NPC_NAXXRAMAS_FOLLOWER, 3347.49f, -3617.59f, 261.0f, 4.49f);
me->SummonCreature(NPC_NAXXRAMAS_FOLLOWER, 3359.64f, -3619.16f, 261.0f, 4.56f);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
SummonHelpers();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->CallForHelp(VISIBLE_RANGE);
summons.DoZoneInCombat();
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_POISON_BOLT, 7s, 15s);
events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 8s, 18s);
events.ScheduleEvent(EVENT_FRENZY, 60s, 80s, 1);
events.SetPhase(1);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
{
Talk(SAY_GREET);
sayGreet = true;
}
ScriptedAI::MoveInLineOfSight(who);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (!urand(0, 3))
{
Talk(SAY_SLAY);
}
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat() && sayGreet)
{
for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (pInstance)
{
if (Creature* cr = pInstance->instance->GetCreature(*itr))
{
if (cr->IsInCombat())
DoZoneInCombat();
}
}
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_POISON_BOLT:
if (!me->HasAura(RAID_MODE(SPELL_WIDOWS_EMBRACE, SPELL_MINION_WIDOWS_EMBRACE)))
{
me->CastCustomSpell(RAID_MODE(SPELL_POISON_BOLT_VOLLEY_10, SPELL_POISON_BOLT_VOLLEY_25), SPELLVALUE_MAX_TARGETS, RAID_MODE(3, 10), me, false);
}
events.Repeat(7s, 15s);
break;
case EVENT_RAIN_OF_FIRE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->CastSpell(target, RAID_MODE(SPELL_RAIN_OF_FIRE_10, SPELL_RAIN_OF_FIRE_25), false);
}
events.Repeat(8s, 18s);
break;
case EVENT_FRENZY:
if (!me->HasAura(RAID_MODE(SPELL_FRENZY_10, SPELL_FRENZY_25)))
{
Talk(SAY_FRENZY);
Talk(EMOTE_FRENZY);
me->CastSpell(me, RAID_MODE(SPELL_FRENZY_10, SPELL_FRENZY_25), true);
events.Repeat(1min);
}
else
{
events.Repeat(30s);
}
break;
}
DoMeleeAttackIfReady();
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (spell->Id == RAID_MODE(SPELL_WIDOWS_EMBRACE, SPELL_MINION_WIDOWS_EMBRACE))
{
Talk(EMOTE_WIDOWS_EMBRACE);
if (me->HasAura(RAID_MODE(SPELL_FRENZY_10, SPELL_FRENZY_25)))
{
me->RemoveAurasDueToSpell(RAID_MODE(SPELL_FRENZY_10, SPELL_FRENZY_25));
events.RescheduleEvent(EVENT_FRENZY, 1min);
}
pInstance->SetData(DATA_FRENZY_REMOVED, 0);
if (Is25ManRaid())
{
Unit::Kill(caster, caster);
}
}
}
};
};
#include "boss_faerlina.h"
void AddSC_boss_faerlina()
{

View File

@@ -0,0 +1,235 @@
#ifndef BOSS_FAERLINA_H_
#define BOSS_FAERLINA_H_
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "naxxramas.h"
enum FaerlinaYells
{
FAERLINA_SAY_GREET = 0,
FAERLINA_SAY_AGGRO = 1,
FAERLINA_SAY_SLAY = 2,
FAERLINA_SAY_DEATH = 3,
FAERLINA_EMOTE_WIDOWS_EMBRACE = 4,
FAERLINA_EMOTE_FRENZY = 5,
FAERLINA_SAY_FRENZY = 6
};
enum Faerlinapells
{
FAERLINA_SPELL_POISON_BOLT_VOLLEY_10 = 28796,
FAERLINA_SPELL_POISON_BOLT_VOLLEY_25 = 54098,
FAERLINA_SPELL_RAIN_OF_FIRE_10 = 28794,
FAERLINA_SPELL_RAIN_OF_FIRE_25 = 54099,
FAERLINA_SPELL_FRENZY_10 = 28798,
FAERLINA_SPELL_FRENZY_25 = 54100,
FAERLINA_SPELL_WIDOWS_EMBRACE = 28732,
FAERLINA_SPELL_MINION_WIDOWS_EMBRACE = 54097
};
enum FaerlinaEvents
{
FAERLINA_EVENT_POISON_BOLT = 1,
FAERLINA_EVENT_RAIN_OF_FIRE = 2,
FAERLINA_EVENT_FRENZY = 3
};
enum FaerlinaMisc
{
FAERLINA_NPC_NAXXRAMAS_WORSHIPPER = 16506,
FAERLINA_NPC_NAXXRAMAS_FOLLOWER = 16505
};
class boss_faerlina : public CreatureScript
{
public:
boss_faerlina() : CreatureScript("boss_faerlina") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_faerlinaAI>(pCreature);
}
struct boss_faerlinaAI : public BossAI
{
boss_faerlinaAI(Creature* c) : BossAI(c, BOSS_FAERLINA), summons(me)
{
pInstance = me->GetInstanceScript();
sayGreet = false;
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool sayGreet;
void SummonHelpers()
{
me->SummonCreature(FAERLINA_NPC_NAXXRAMAS_WORSHIPPER, 3362.66f, -3620.97f, 261.08f, 4.57276f);
me->SummonCreature(FAERLINA_NPC_NAXXRAMAS_WORSHIPPER, 3344.3f, -3618.31f, 261.08f, 4.69494f);
me->SummonCreature(FAERLINA_NPC_NAXXRAMAS_WORSHIPPER, 3356.71f, -3620.05f, 261.08f, 4.57276f);
me->SummonCreature(FAERLINA_NPC_NAXXRAMAS_WORSHIPPER, 3350.26f, -3619.11f, 261.08f, 4.67748f);
if (Is25ManRaid())
{
me->SummonCreature(FAERLINA_NPC_NAXXRAMAS_FOLLOWER, 3347.49f, -3617.59f, 261.0f, 4.49f);
me->SummonCreature(FAERLINA_NPC_NAXXRAMAS_FOLLOWER, 3359.64f, -3619.16f, 261.0f, 4.56f);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
SummonHelpers();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->CallForHelp(VISIBLE_RANGE);
summons.DoZoneInCombat();
Talk(static_cast<uint8>(FAERLINA_SAY_AGGRO));
events.ScheduleEvent(static_cast<uint8>(FAERLINA_EVENT_POISON_BOLT), 7s, 15s);
events.ScheduleEvent(static_cast<uint8>(FAERLINA_EVENT_RAIN_OF_FIRE), 8s, 18s);
events.ScheduleEvent(static_cast<uint8>(FAERLINA_EVENT_FRENZY), 60s, 80s, 1);
events.SetPhase(1);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
{
Talk(FAERLINA_SAY_GREET);
sayGreet = true;
}
ScriptedAI::MoveInLineOfSight(who);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (!urand(0, 3))
{
Talk(FAERLINA_SAY_SLAY);
}
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(FAERLINA_SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat() && sayGreet)
{
for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (pInstance)
{
if (Creature* cr = pInstance->instance->GetCreature(*itr))
{
if (cr->IsInCombat())
DoZoneInCombat();
}
}
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case FAERLINA_EVENT_POISON_BOLT:
if (!me->HasAura(RAID_MODE(FAERLINA_SPELL_WIDOWS_EMBRACE, FAERLINA_SPELL_MINION_WIDOWS_EMBRACE)))
{
me->CastCustomSpell(RAID_MODE(FAERLINA_SPELL_POISON_BOLT_VOLLEY_10, FAERLINA_SPELL_POISON_BOLT_VOLLEY_25), SPELLVALUE_MAX_TARGETS, RAID_MODE(3, 10), me, false);
}
events.Repeat(7s, 15s);
break;
case FAERLINA_EVENT_RAIN_OF_FIRE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->CastSpell(target, RAID_MODE(FAERLINA_SPELL_RAIN_OF_FIRE_10, FAERLINA_SPELL_RAIN_OF_FIRE_25), false);
}
events.Repeat(8s, 18s);
break;
case FAERLINA_EVENT_FRENZY:
if (!me->HasAura(RAID_MODE(FAERLINA_SPELL_FRENZY_10, FAERLINA_SPELL_FRENZY_25)))
{
Talk(FAERLINA_SAY_FRENZY);
Talk(FAERLINA_EMOTE_FRENZY);
me->CastSpell(me, RAID_MODE(FAERLINA_SPELL_FRENZY_10, FAERLINA_SPELL_FRENZY_25), true);
events.Repeat(1min);
}
else
{
events.Repeat(30s);
}
break;
}
DoMeleeAttackIfReady();
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (spell->Id == RAID_MODE(FAERLINA_SPELL_WIDOWS_EMBRACE, FAERLINA_SPELL_MINION_WIDOWS_EMBRACE))
{
Talk(FAERLINA_EMOTE_WIDOWS_EMBRACE);
if (me->HasAura(RAID_MODE(FAERLINA_SPELL_FRENZY_10, FAERLINA_SPELL_FRENZY_25)))
{
me->RemoveAurasDueToSpell(RAID_MODE(FAERLINA_SPELL_FRENZY_10, FAERLINA_SPELL_FRENZY_25));
events.RescheduleEvent(FAERLINA_EVENT_FRENZY, 1min);
}
pInstance->SetData(DATA_FRENZY_REMOVED, 0);
if (Is25ManRaid())
{
Unit::Kill(caster, caster);
}
}
}
};
};
#endif

View File

@@ -15,442 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Spells
{
SPELL_BERSERK = 26662,
// Marks
SPELL_MARK_OF_KORTHAZZ = 28832,
SPELL_MARK_OF_BLAUMEUX = 28833,
SPELL_MARK_OF_RIVENDARE = 28834,
SPELL_MARK_OF_ZELIEK = 28835,
SPELL_MARK_DAMAGE = 28836,
// Korth'azz
SPELL_KORTHAZZ_METEOR_10 = 28884,
SPELL_KORTHAZZ_METEOR_25 = 57467,
// Blaumeux
SPELL_BLAUMEUX_SHADOW_BOLT_10 = 57374,
SPELL_BLAUMEUX_SHADOW_BOLT_25 = 57464,
SPELL_BLAUMEUX_VOID_ZONE_10 = 28863,
SPELL_BLAUMEUX_VOID_ZONE_25 = 57463,
SPELL_BLAUMEUX_UNYIELDING_PAIN = 57381,
// Zeliek
SPELL_ZELIEK_HOLY_WRATH_10 = 28883,
SPELL_ZELIEK_HOLY_WRATH_25 = 57466,
SPELL_ZELIEK_HOLY_BOLT_10 = 57376,
SPELL_ZELIEK_HOLY_BOLT_25 = 57465,
SPELL_ZELIEK_CONDEMNATION = 57377,
// Rivendare
SPELL_RIVENDARE_UNHOLY_SHADOW_10 = 28882,
SPELL_RIVENDARE_UNHOLY_SHADOW_25 = 57369
};
enum Events
{
EVENT_MARK_CAST = 1,
EVENT_PRIMARY_SPELL = 2,
EVENT_SECONDARY_SPELL = 3,
EVENT_BERSERK = 4
};
enum Misc
{
// Movement
MOVE_PHASE_NONE = 0,
MOVE_PHASE_STARTED = 1,
MOVE_PHASE_FINISHED = 2,
// Horseman
HORSEMAN_ZELIEK = 0,
HORSEMAN_BLAUMEUX = 1,
HORSEMAN_RIVENDARE = 2,
HORSEMAN_KORTHAZZ = 3
};
enum FourHorsemen
{
SAY_AGGRO = 0,
SAY_TAUNT = 1,
SAY_SPECIAL = 2,
SAY_SLAY = 3,
SAY_DEATH = 4,
EMOTE_RAGECAST = 7
};
// MARKS
const uint32 TABLE_SPELL_MARK[4] = {SPELL_MARK_OF_ZELIEK, SPELL_MARK_OF_BLAUMEUX, SPELL_MARK_OF_RIVENDARE, SPELL_MARK_OF_KORTHAZZ};
// PRIMARY SPELL
const uint32 TABLE_SPELL_PRIMARY_10[4] = {SPELL_ZELIEK_HOLY_BOLT_10, SPELL_BLAUMEUX_SHADOW_BOLT_10, SPELL_RIVENDARE_UNHOLY_SHADOW_10, SPELL_KORTHAZZ_METEOR_10};
const uint32 TABLE_SPELL_PRIMARY_25[4] = {SPELL_ZELIEK_HOLY_BOLT_25, SPELL_BLAUMEUX_SHADOW_BOLT_25, SPELL_RIVENDARE_UNHOLY_SHADOW_25, SPELL_KORTHAZZ_METEOR_25};
// PUNISH
const uint32 TABLE_SPELL_PUNISH[4] = {SPELL_ZELIEK_CONDEMNATION, SPELL_BLAUMEUX_UNYIELDING_PAIN, 0, 0};
// SECONDARY SPELL
const uint32 TABLE_SPELL_SECONDARY_10[4] = {SPELL_ZELIEK_HOLY_WRATH_10, SPELL_BLAUMEUX_VOID_ZONE_10, 0, 0};
const uint32 TABLE_SPELL_SECONDARY_25[4] = {SPELL_ZELIEK_HOLY_WRATH_25, SPELL_BLAUMEUX_VOID_ZONE_25, 0, 0};
const Position WaypointPositions[12] =
{
// Thane waypoints
{2542.3f, -2984.1f, 241.49f, 5.362f},
{2547.6f, -2999.4f, 241.34f, 5.049f},
{2542.9f, -3015.0f, 241.35f, 4.654f},
// Lady waypoints
{2498.3f, -2961.8f, 241.28f, 3.267f},
{2487.7f, -2959.2f, 241.28f, 2.890f},
{2469.4f, -2947.6f, 241.28f, 2.576f},
// Baron waypoints
{2553.8f, -2968.4f, 241.33f, 5.757f},
{2564.3f, -2972.5f, 241.33f, 5.890f},
{2583.9f, -2971.6f, 241.35f, 0.008f},
// Sir waypoints
{2534.5f, -2921.7f, 241.53f, 1.363f},
{2523.5f, -2902.8f, 241.28f, 2.095f},
{2517.8f, -2896.6f, 241.28f, 2.315f}
};
class boss_four_horsemen : public CreatureScript
{
public:
boss_four_horsemen() : CreatureScript("boss_four_horsemen") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_four_horsemenAI>(pCreature);
}
struct boss_four_horsemenAI : public BossAI
{
explicit boss_four_horsemenAI(Creature* c) : BossAI(c, BOSS_HORSEMAN)
{
pInstance = me->GetInstanceScript();
switch (me->GetEntry())
{
case NPC_SIR_ZELIEK:
horsemanId = HORSEMAN_ZELIEK;
break;
case NPC_LADY_BLAUMEUX:
horsemanId = HORSEMAN_BLAUMEUX;
break;
case NPC_BARON_RIVENDARE:
horsemanId = HORSEMAN_RIVENDARE;
break;
case NPC_THANE_KORTHAZZ:
horsemanId = HORSEMAN_KORTHAZZ;
break;
}
}
EventMap events;
InstanceScript* pInstance;
uint8 currentWaypoint{};
uint8 movementPhase{};
uint8 horsemanId;
void MoveToCorner()
{
switch(me->GetEntry())
{
case NPC_THANE_KORTHAZZ:
currentWaypoint = 0;
break;
case NPC_LADY_BLAUMEUX:
currentWaypoint = 3;
break;
case NPC_BARON_RIVENDARE:
currentWaypoint = 6;
break;
case NPC_SIR_ZELIEK:
currentWaypoint = 9;
break;
}
me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]);
}
bool IsInRoom()
{
if (me->GetExactDist(2535.1f, -2968.7f, 241.3f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
me->SetPosition(me->GetHomePosition());
movementPhase = MOVE_PHASE_NONE;
currentWaypoint = 0;
me->SetReactState(REACT_AGGRESSIVE);
events.Reset();
events.RescheduleEvent(EVENT_MARK_CAST, 24s);
events.RescheduleEvent(EVENT_BERSERK, 10min);
if ((me->GetEntry() != NPC_LADY_BLAUMEUX && me->GetEntry() != NPC_SIR_ZELIEK))
{
events.RescheduleEvent(EVENT_PRIMARY_SPELL, 10s, 15s);
}
else
{
events.RescheduleEvent(EVENT_SECONDARY_SPELL, 15s);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
if (pInstance->GetBossState(BOSS_GOTHIK) == DONE)
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
// final waypoint
if (id % 3 == 2)
{
movementPhase = MOVE_PHASE_FINISHED;
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
if (!UpdateVictim())
{
EnterEvadeMode();
return;
}
if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK)
{
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
}
return;
}
currentWaypoint = id + 1;
}
void AttackStart(Unit* who) override
{
if (movementPhase == MOVE_PHASE_FINISHED)
{
if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK)
{
me->Attack(who, false);
}
else
{
ScriptedAI::AttackStart(who);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
if (pInstance)
{
if (pInstance->GetBossState(BOSS_HORSEMAN) == DONE)
{
if (!me->GetMap()->GetPlayers().IsEmpty())
{
if (Player* player = me->GetMap()->GetPlayers().getFirst()->GetSource())
{
if (GameObject* chest = player->SummonGameObject(RAID_MODE(GO_HORSEMEN_CHEST_10, GO_HORSEMEN_CHEST_25), 2514.8f, -2944.9f, 245.55f, 5.51f, 0, 0, 0, 0, 0))
{
chest->SetLootRecipient(me);
}
}
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
Talk(SAY_DEATH);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (movementPhase == MOVE_PHASE_NONE)
{
Talk(SAY_AGGRO);
me->SetReactState(REACT_PASSIVE);
movementPhase = MOVE_PHASE_STARTED;
me->SetSpeed(MOVE_RUN, me->GetSpeedRate(MOVE_RUN), true);
MoveToCorner();
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void UpdateAI(uint32 diff) override
{
if (movementPhase == MOVE_PHASE_STARTED && currentWaypoint)
{
me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]);
currentWaypoint = 0;
}
if (!IsInRoom())
return;
if (movementPhase < MOVE_PHASE_FINISHED || !UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MARK_CAST:
me->CastSpell(me, TABLE_SPELL_MARK[horsemanId], false);
events.Repeat((me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK) ? 15s : 12s);
return;
case EVENT_BERSERK:
Talk(SAY_SPECIAL);
me->CastSpell(me, SPELL_BERSERK, true);
return;
case EVENT_PRIMARY_SPELL:
Talk(SAY_TAUNT);
me->CastSpell(me->GetVictim(), RAID_MODE(TABLE_SPELL_PRIMARY_10[horsemanId], TABLE_SPELL_PRIMARY_25[horsemanId]), false);
events.Repeat(15s);
return;
case EVENT_SECONDARY_SPELL:
me->CastSpell(me->GetVictim(), RAID_MODE(TABLE_SPELL_SECONDARY_10[horsemanId], TABLE_SPELL_SECONDARY_25[horsemanId]), false);
events.Repeat(15s);
return;
}
if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK)
{
if (Unit* pTarget = me->SelectNearestPlayer(300.0f))
{
if (pTarget && me->IsValidAttackTarget(pTarget))
{
AttackStart(pTarget);
}
}
if (me->IsWithinDistInMap(me->GetVictim(), 45.0f) && me->IsValidAttackTarget(me->GetVictim()))
{
DoCastVictim(RAID_MODE(TABLE_SPELL_PRIMARY_10[horsemanId], TABLE_SPELL_PRIMARY_25[horsemanId]));
}
else if (!me->IsWithinDistInMap(me->GetVictim(), 45.0f) || !me->IsValidAttackTarget(me->GetVictim()))
{
DoCastAOE(TABLE_SPELL_PUNISH[horsemanId]);
Talk(EMOTE_RAGECAST);
}
}
else
{
DoMeleeAttackIfReady();
}
}
};
};
class spell_four_horsemen_mark : public SpellScriptLoader
{
public:
spell_four_horsemen_mark() : SpellScriptLoader("spell_four_horsemen_mark") { }
class spell_four_horsemen_mark_AuraScript : public AuraScript
{
PrepareAuraScript(spell_four_horsemen_mark_AuraScript);
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Unit* caster = GetCaster())
{
int32 damage;
switch (GetStackAmount())
{
case 1:
damage = 0;
break;
case 2:
damage = 500;
break;
case 3:
damage = 1500;
break;
case 4:
damage = 4000;
break;
case 5:
damage = 12500;
break;
case 6:
damage = 20000;
break;
default:
damage = 20000 + 1000 * (GetStackAmount() - 7);
break;
}
if (damage)
{
caster->CastCustomSpell(SPELL_MARK_DAMAGE, SPELLVALUE_BASE_POINT0, damage, GetTarget());
}
}
}
void Register() override
{
AfterEffectApply += AuraEffectApplyFn(spell_four_horsemen_mark_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_four_horsemen_mark_AuraScript();
}
};
class spell_four_horsemen_consumption : public SpellScript
{
PrepareSpellScript(spell_four_horsemen_consumption);
void HandleDamageCalc(SpellEffIndex /*effIndex*/)
{
uint32 damage = GetCaster()->GetMap()->ToInstanceMap()->GetDifficulty() == REGULAR_DIFFICULTY ? 2750 : 4250;
SetHitDamage(damage);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_four_horsemen_consumption::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
}
};
#include "boss_four_horsemen.h"
void AddSC_boss_four_horsemen()
{

View File

@@ -0,0 +1,441 @@
#ifndef BOSS_FOURHORSEMEN_H_
#define BOSS_FOURHORSEMEN_H_
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum FourHorsemenSpells
{
FOURHORSEMEN_SPELL_BERSERK = 26662,
// Marks
SPELL_MARK_OF_KORTHAZZ = 28832,
SPELL_MARK_OF_BLAUMEUX = 28833,
SPELL_MARK_OF_RIVENDARE = 28834,
SPELL_MARK_OF_ZELIEK = 28835,
SPELL_MARK_DAMAGE = 28836,
// Korth'azz
SPELL_KORTHAZZ_METEOR_10 = 28884,
SPELL_KORTHAZZ_METEOR_25 = 57467,
// Blaumeux
SPELL_BLAUMEUX_SHADOW_BOLT_10 = 57374,
SPELL_BLAUMEUX_SHADOW_BOLT_25 = 57464,
SPELL_BLAUMEUX_VOID_ZONE_10 = 28863,
SPELL_BLAUMEUX_VOID_ZONE_25 = 57463,
SPELL_BLAUMEUX_UNYIELDING_PAIN = 57381,
// Zeliek
SPELL_ZELIEK_HOLY_WRATH_10 = 28883,
SPELL_ZELIEK_HOLY_WRATH_25 = 57466,
SPELL_ZELIEK_HOLY_BOLT_10 = 57376,
SPELL_ZELIEK_HOLY_BOLT_25 = 57465,
SPELL_ZELIEK_CONDEMNATION = 57377,
// Rivendare
SPELL_RIVENDARE_UNHOLY_SHADOW_10 = 28882,
SPELL_RIVENDARE_UNHOLY_SHADOW_25 = 57369
};
enum FourHorsemenEvents
{
EVENT_MARK_CAST = 1,
EVENT_PRIMARY_SPELL = 2,
EVENT_SECONDARY_SPELL = 3,
FOURHORSEMEN_EVENT_BERSERK = 4
};
enum FourHorsemenMisc
{
// Movement
MOVE_PHASE_NONE = 0,
MOVE_PHASE_STARTED = 1,
MOVE_PHASE_FINISHED = 2,
// Horseman
HORSEMAN_ZELIEK = 0,
HORSEMAN_BLAUMEUX = 1,
HORSEMAN_RIVENDARE = 2,
HORSEMAN_KORTHAZZ = 3
};
enum FourHorsemen
{
FOURHORSEMEN_SAY_AGGRO = 0,
FOURHORSEMEN_SAY_TAUNT = 1,
FOURHORSEMEN_SAY_SPECIAL = 2,
FOURHORSEMEN_SAY_SLAY = 3,
FOURHORSEMEN_SAY_DEATH = 4,
FOURHORSEMEN_EMOTE_RAGECAST = 7
};
// MARKS
const uint32 TABLE_SPELL_MARK[4] = {SPELL_MARK_OF_ZELIEK, SPELL_MARK_OF_BLAUMEUX, SPELL_MARK_OF_RIVENDARE, SPELL_MARK_OF_KORTHAZZ};
// PRIMARY SPELL
const uint32 TABLE_SPELL_PRIMARY_10[4] = {SPELL_ZELIEK_HOLY_BOLT_10, SPELL_BLAUMEUX_SHADOW_BOLT_10, SPELL_RIVENDARE_UNHOLY_SHADOW_10, SPELL_KORTHAZZ_METEOR_10};
const uint32 TABLE_SPELL_PRIMARY_25[4] = {SPELL_ZELIEK_HOLY_BOLT_25, SPELL_BLAUMEUX_SHADOW_BOLT_25, SPELL_RIVENDARE_UNHOLY_SHADOW_25, SPELL_KORTHAZZ_METEOR_25};
// PUNISH
const uint32 TABLE_SPELL_PUNISH[4] = {SPELL_ZELIEK_CONDEMNATION, SPELL_BLAUMEUX_UNYIELDING_PAIN, 0, 0};
// SECONDARY SPELL
const uint32 TABLE_SPELL_SECONDARY_10[4] = {SPELL_ZELIEK_HOLY_WRATH_10, SPELL_BLAUMEUX_VOID_ZONE_10, 0, 0};
const uint32 TABLE_SPELL_SECONDARY_25[4] = {SPELL_ZELIEK_HOLY_WRATH_25, SPELL_BLAUMEUX_VOID_ZONE_25, 0, 0};
const Position WaypointPositions[12] =
{
// Thane waypoints
{2542.3f, -2984.1f, 241.49f, 5.362f},
{2547.6f, -2999.4f, 241.34f, 5.049f},
{2542.9f, -3015.0f, 241.35f, 4.654f},
// Lady waypoints
{2498.3f, -2961.8f, 241.28f, 3.267f},
{2487.7f, -2959.2f, 241.28f, 2.890f},
{2469.4f, -2947.6f, 241.28f, 2.576f},
// Baron waypoints
{2553.8f, -2968.4f, 241.33f, 5.757f},
{2564.3f, -2972.5f, 241.33f, 5.890f},
{2583.9f, -2971.6f, 241.35f, 0.008f},
// Sir waypoints
{2534.5f, -2921.7f, 241.53f, 1.363f},
{2523.5f, -2902.8f, 241.28f, 2.095f},
{2517.8f, -2896.6f, 241.28f, 2.315f}
};
class boss_four_horsemen : public CreatureScript
{
public:
boss_four_horsemen() : CreatureScript("boss_four_horsemen") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_four_horsemenAI>(pCreature);
}
struct boss_four_horsemenAI : public BossAI
{
explicit boss_four_horsemenAI(Creature* c) : BossAI(c, BOSS_HORSEMAN)
{
pInstance = me->GetInstanceScript();
switch (me->GetEntry())
{
case NPC_SIR_ZELIEK:
horsemanId = HORSEMAN_ZELIEK;
break;
case NPC_LADY_BLAUMEUX:
horsemanId = HORSEMAN_BLAUMEUX;
break;
case NPC_BARON_RIVENDARE:
horsemanId = HORSEMAN_RIVENDARE;
break;
case NPC_THANE_KORTHAZZ:
horsemanId = HORSEMAN_KORTHAZZ;
break;
}
}
EventMap events;
InstanceScript* pInstance;
uint8 currentWaypoint{};
uint8 movementPhase{};
uint8 horsemanId;
void MoveToCorner()
{
switch(me->GetEntry())
{
case NPC_THANE_KORTHAZZ:
currentWaypoint = 0;
break;
case NPC_LADY_BLAUMEUX:
currentWaypoint = 3;
break;
case NPC_BARON_RIVENDARE:
currentWaypoint = 6;
break;
case NPC_SIR_ZELIEK:
currentWaypoint = 9;
break;
}
me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]);
}
bool IsInRoom()
{
if (me->GetExactDist(2535.1f, -2968.7f, 241.3f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
me->SetPosition(me->GetHomePosition());
movementPhase = MOVE_PHASE_NONE;
currentWaypoint = 0;
me->SetReactState(REACT_AGGRESSIVE);
events.Reset();
events.RescheduleEvent(EVENT_MARK_CAST, 24s);
events.RescheduleEvent(FOURHORSEMEN_EVENT_BERSERK, 10min);
if ((me->GetEntry() != NPC_LADY_BLAUMEUX && me->GetEntry() != NPC_SIR_ZELIEK))
{
events.RescheduleEvent(EVENT_PRIMARY_SPELL, 10s, 15s);
}
else
{
events.RescheduleEvent(EVENT_SECONDARY_SPELL, 15s);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
if (pInstance->GetBossState(BOSS_GOTHIK) == DONE)
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
// final waypoint
if (id % 3 == 2)
{
movementPhase = MOVE_PHASE_FINISHED;
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
if (!UpdateVictim())
{
EnterEvadeMode();
return;
}
if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK)
{
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
}
return;
}
currentWaypoint = id + 1;
}
void AttackStart(Unit* who) override
{
if (movementPhase == MOVE_PHASE_FINISHED)
{
if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK)
{
me->Attack(who, false);
}
else
{
ScriptedAI::AttackStart(who);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(FOURHORSEMEN_SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
if (pInstance)
{
if (pInstance->GetBossState(BOSS_HORSEMAN) == DONE)
{
if (!me->GetMap()->GetPlayers().IsEmpty())
{
if (Player* player = me->GetMap()->GetPlayers().getFirst()->GetSource())
{
if (GameObject* chest = player->SummonGameObject(RAID_MODE(GO_HORSEMEN_CHEST_10, GO_HORSEMEN_CHEST_25), 2514.8f, -2944.9f, 245.55f, 5.51f, 0, 0, 0, 0, 0))
{
chest->SetLootRecipient(me);
}
}
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
Talk(FOURHORSEMEN_SAY_DEATH);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (movementPhase == MOVE_PHASE_NONE)
{
Talk(FOURHORSEMEN_SAY_AGGRO);
me->SetReactState(REACT_PASSIVE);
movementPhase = MOVE_PHASE_STARTED;
me->SetSpeed(MOVE_RUN, me->GetSpeedRate(MOVE_RUN), true);
MoveToCorner();
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void UpdateAI(uint32 diff) override
{
if (movementPhase == MOVE_PHASE_STARTED && currentWaypoint)
{
me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]);
currentWaypoint = 0;
}
if (!IsInRoom())
return;
if (movementPhase < MOVE_PHASE_FINISHED || !UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MARK_CAST:
me->CastSpell(me, TABLE_SPELL_MARK[horsemanId], false);
events.Repeat((me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK) ? 15s : 12s);
return;
case FOURHORSEMEN_EVENT_BERSERK:
Talk(FOURHORSEMEN_SAY_SPECIAL);
me->CastSpell(me, FOURHORSEMEN_SPELL_BERSERK, true);
return;
case EVENT_PRIMARY_SPELL:
Talk(FOURHORSEMEN_SAY_TAUNT);
me->CastSpell(me->GetVictim(), RAID_MODE(TABLE_SPELL_PRIMARY_10[horsemanId], TABLE_SPELL_PRIMARY_25[horsemanId]), false);
events.Repeat(15s);
return;
case EVENT_SECONDARY_SPELL:
me->CastSpell(me->GetVictim(), RAID_MODE(TABLE_SPELL_SECONDARY_10[horsemanId], TABLE_SPELL_SECONDARY_25[horsemanId]), false);
events.Repeat(15s);
return;
}
if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK)
{
if (Unit* pTarget = me->SelectNearestPlayer(300.0f))
{
if (pTarget && me->IsValidAttackTarget(pTarget))
{
AttackStart(pTarget);
}
}
if (me->IsWithinDistInMap(me->GetVictim(), 45.0f) && me->IsValidAttackTarget(me->GetVictim()))
{
DoCastVictim(RAID_MODE(TABLE_SPELL_PRIMARY_10[horsemanId], TABLE_SPELL_PRIMARY_25[horsemanId]));
}
else if (!me->IsWithinDistInMap(me->GetVictim(), 45.0f) || !me->IsValidAttackTarget(me->GetVictim()))
{
DoCastAOE(TABLE_SPELL_PUNISH[horsemanId]);
Talk(FOURHORSEMEN_EMOTE_RAGECAST);
}
}
else
{
DoMeleeAttackIfReady();
}
}
};
};
class spell_four_horsemen_mark : public SpellScriptLoader
{
public:
spell_four_horsemen_mark() : SpellScriptLoader("spell_four_horsemen_mark") { }
class spell_four_horsemen_mark_AuraScript : public AuraScript
{
PrepareAuraScript(spell_four_horsemen_mark_AuraScript);
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Unit* caster = GetCaster())
{
int32 damage;
switch (GetStackAmount())
{
case 1:
damage = 0;
break;
case 2:
damage = 500;
break;
case 3:
damage = 1500;
break;
case 4:
damage = 4000;
break;
case 5:
damage = 12500;
break;
case 6:
damage = 20000;
break;
default:
damage = 20000 + 1000 * (GetStackAmount() - 7);
break;
}
if (damage)
{
caster->CastCustomSpell(SPELL_MARK_DAMAGE, SPELLVALUE_BASE_POINT0, damage, GetTarget());
}
}
}
void Register() override
{
AfterEffectApply += AuraEffectApplyFn(spell_four_horsemen_mark_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_four_horsemen_mark_AuraScript();
}
};
class spell_four_horsemen_consumption : public SpellScript
{
PrepareSpellScript(spell_four_horsemen_consumption);
void HandleDamageCalc(SpellEffIndex /*effIndex*/)
{
uint32 damage = GetCaster()->GetMap()->ToInstanceMap()->GetDifficulty() == REGULAR_DIFFICULTY ? 2750 : 4250;
SetHitDamage(damage);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_four_horsemen_consumption::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
}
};
#endif

View File

@@ -15,267 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Spells
{
SPELL_MORTAL_WOUND = 25646,
SPELL_ENRAGE_10 = 28371,
SPELL_ENRAGE_25 = 54427,
SPELL_DECIMATE_10 = 28374,
SPELL_DECIMATE_25 = 54426,
SPELL_BERSERK = 26662,
SPELL_INFECTED_WOUND = 29306,
SPELL_CHOW_SEARCHER = 28404
};
enum Events
{
EVENT_MORTAL_WOUND = 1,
EVENT_ENRAGE = 2,
EVENT_DECIMATE = 3,
EVENT_BERSERK = 4,
EVENT_SUMMON_ZOMBIE = 5,
EVENT_CAN_EAT_ZOMBIE = 6
};
enum Misc
{
NPC_ZOMBIE_CHOW = 16360
};
enum Emotes
{
EMOTE_SPOTS_ONE = 0,
EMOTE_DECIMATE = 1,
EMOTE_ENRAGE = 2,
EMOTE_DEVOURS_ALL = 3,
EMOTE_BERSERK = 4
};
const Position zombiePos[3] =
{
{3267.9f, -3172.1f, 297.42f, 0.94f},
{3253.2f, -3132.3f, 297.42f, 0},
{3308.3f, -3185.8f, 297.42f, 1.58f}
};
class boss_gluth : public CreatureScript
{
public:
boss_gluth() : CreatureScript("boss_gluth") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_gluthAI>(pCreature);
}
struct boss_gluthAI : public BossAI
{
explicit boss_gluthAI(Creature* c) : BossAI(c, BOSS_GLUTH), summons(me)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
void Reset() override
{
BossAI::Reset();
me->ApplySpellImmune(SPELL_INFECTED_WOUND, IMMUNITY_ID, SPELL_INFECTED_WOUND, true);
events.Reset();
summons.DespawnAll();
me->SetReactState(REACT_AGGRESSIVE);
}
void MoveInLineOfSight(Unit* who) override
{
if (!me->GetVictim() || me->GetVictim()->GetEntry() != NPC_ZOMBIE_CHOW)
{
if (who->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinDistInMap(who, 6.5f))
{
SetGazeOn(who);
Talk(EMOTE_SPOTS_ONE);
}
else
{
ScriptedAI::MoveInLineOfSight(who);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_MORTAL_WOUND, 10s);
events.ScheduleEvent(EVENT_ENRAGE, 22s);
events.ScheduleEvent(EVENT_DECIMATE, RAID_MODE(110000, 90000));
events.ScheduleEvent(EVENT_BERSERK, 6min);
events.ScheduleEvent(EVENT_SUMMON_ZOMBIE, 10s);
events.ScheduleEvent(EVENT_CAN_EAT_ZOMBIE, 1s);
}
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() == NPC_ZOMBIE_CHOW)
{
summon->AI()->AttackStart(me);
}
summons.Summon(summon);
}
void SummonedCreatureDies(Creature* cr, Unit*) override { summons.Despawn(cr); }
void KilledUnit(Unit* who) override
{
if (me->IsAlive() && who->GetEntry() == NPC_ZOMBIE_CHOW)
{
me->ModifyHealth(int32(me->GetMaxHealth() * 0.05f));
}
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
bool SelectPlayerInRoom()
{
if (me->IsInCombat())
return false;
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
Player* player = itr.GetSource();
if (!player || !player->IsAlive())
continue;
if (player->GetPositionZ() > 300.0f || me->GetExactDist(player) > 50.0f)
continue;
AttackStart(player);
return true;
}
return false;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictimWithGaze() && !SelectPlayerInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_ENRAGE:
Talk(EMOTE_ENRAGE);
me->CastSpell(me, RAID_MODE(SPELL_ENRAGE_10, SPELL_ENRAGE_25), true);
events.Repeat(22s);
break;
case EVENT_MORTAL_WOUND:
me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false);
events.Repeat(10s);
break;
case EVENT_DECIMATE:
Talk(EMOTE_DECIMATE);
me->CastSpell(me, RAID_MODE(SPELL_DECIMATE_10, SPELL_DECIMATE_25), false);
events.RepeatEvent(RAID_MODE(110000, 90000));
break;
case EVENT_SUMMON_ZOMBIE:
{
uint8 rand = urand(0, 2);
for (int32 i = 0; i < RAID_MODE(1, 2); ++i)
{
// In 10 man raid, normal mode - should spawn only from mid gate
// \1 |0 /2 pos
// In 25 man raid - should spawn from all 3 gates
if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL)
{
me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[0]);
}
else
{
me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[urand(0, 2)]);
}
(rand == 2 ? rand = 0 : rand++);
}
events.Repeat(10s);
break;
}
case EVENT_CAN_EAT_ZOMBIE:
events.RepeatEvent(1000);
if (me->GetVictim()->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinMeleeRange(me->GetVictim()))
{
me->CastCustomSpell(SPELL_CHOW_SEARCHER, SPELLVALUE_RADIUS_MOD, 20000, me, true);
Talk(EMOTE_DEVOURS_ALL);
return; // leave it to skip DoMeleeAttackIfReady
}
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_gluth_decimate : public SpellScriptLoader
{
public:
spell_gluth_decimate() : SpellScriptLoader("spell_gluth_decimate") { }
class spell_gluth_decimate_SpellScript : public SpellScript
{
PrepareSpellScript(spell_gluth_decimate_SpellScript);
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Unit* unitTarget = GetHitUnit())
{
int32 damage = int32(unitTarget->GetHealth()) - int32(unitTarget->CountPctFromMaxHealth(5));
if (damage <= 0)
return;
if (Creature* cTarget = unitTarget->ToCreature())
{
cTarget->SetWalk(true);
cTarget->GetMotionMaster()->MoveFollow(GetCaster(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED);
cTarget->SetReactState(REACT_PASSIVE);
Unit::DealDamage(GetCaster(), cTarget, damage);
return;
}
GetCaster()->CastCustomSpell(28375, SPELLVALUE_BASE_POINT0, damage, unitTarget);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_gluth_decimate_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_gluth_decimate_SpellScript();
}
};
#include "boss_gluth.h"
void AddSC_boss_gluth()
{

View File

@@ -0,0 +1,266 @@
#ifndef BOSSGLUTH_H_
#define BOSSGLUTH_H_
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum GluthSpells
{
GLUTH_SPELL_MORTAL_WOUND = 25646,
SPELL_ENRAGE_10 = 28371,
SPELL_ENRAGE_25 = 54427,
SPELL_DECIMATE_10 = 28374,
SPELL_DECIMATE_25 = 54426,
GLUTH_SPELL_BERSERK = 26662,
SPELL_INFECTED_WOUND = 29306,
SPELL_CHOW_SEARCHER = 28404
};
enum GluthEvents
{
GLUTH_EVENT_MORTAL_WOUND = 1,
GLUTH_EVENT_ENRAGE = 2,
GLUTH_EVENT_DECIMATE = 3,
GLUTH_EVENT_BERSERK = 4,
GLUTH_EVENT_SUMMON_ZOMBIE = 5,
GLUTH_EVENT_CAN_EAT_ZOMBIE = 6
};
enum GluthMisc
{
NPC_ZOMBIE_CHOW = 16360
};
enum GluthEmotes
{
EMOTE_SPOTS_ONE = 0,
EMOTE_DECIMATE = 1,
GLUTH_EMOTE_ENRAGE = 2,
EMOTE_DEVOURS_ALL = 3,
GLUTH_EMOTE_BERSERK = 4
};
const Position zombiePos[3] =
{
{3267.9f, -3172.1f, 297.42f, 0.94f},
{3253.2f, -3132.3f, 297.42f, 0},
{3308.3f, -3185.8f, 297.42f, 1.58f}
};
class boss_gluth : public CreatureScript
{
public:
boss_gluth() : CreatureScript("boss_gluth") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_gluthAI>(pCreature);
}
struct boss_gluthAI : public BossAI
{
explicit boss_gluthAI(Creature* c) : BossAI(c, BOSS_GLUTH), summons(me)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
void Reset() override
{
BossAI::Reset();
me->ApplySpellImmune(SPELL_INFECTED_WOUND, IMMUNITY_ID, SPELL_INFECTED_WOUND, true);
events.Reset();
summons.DespawnAll();
me->SetReactState(REACT_AGGRESSIVE);
}
void MoveInLineOfSight(Unit* who) override
{
if (!me->GetVictim() || me->GetVictim()->GetEntry() != NPC_ZOMBIE_CHOW)
{
if (who->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinDistInMap(who, 6.5f))
{
SetGazeOn(who);
Talk(EMOTE_SPOTS_ONE);
}
else
{
ScriptedAI::MoveInLineOfSight(who);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
events.ScheduleEvent(GLUTH_EVENT_MORTAL_WOUND, 10s);
events.ScheduleEvent(GLUTH_EVENT_ENRAGE, 22s);
events.ScheduleEvent(GLUTH_EVENT_DECIMATE, RAID_MODE(110000, 90000));
events.ScheduleEvent(GLUTH_EVENT_BERSERK, 6min);
events.ScheduleEvent(GLUTH_EVENT_SUMMON_ZOMBIE, 10s);
events.ScheduleEvent(GLUTH_EVENT_CAN_EAT_ZOMBIE, 1s);
}
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() == NPC_ZOMBIE_CHOW)
{
summon->AI()->AttackStart(me);
}
summons.Summon(summon);
}
void SummonedCreatureDies(Creature* cr, Unit*) override { summons.Despawn(cr); }
void KilledUnit(Unit* who) override
{
if (me->IsAlive() && who->GetEntry() == NPC_ZOMBIE_CHOW)
{
me->ModifyHealth(int32(me->GetMaxHealth() * 0.05f));
}
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
bool SelectPlayerInRoom()
{
if (me->IsInCombat())
return false;
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
Player* player = itr.GetSource();
if (!player || !player->IsAlive())
continue;
if (player->GetPositionZ() > 300.0f || me->GetExactDist(player) > 50.0f)
continue;
AttackStart(player);
return true;
}
return false;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictimWithGaze() && !SelectPlayerInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case GLUTH_EVENT_BERSERK:
me->CastSpell(me, GLUTH_SPELL_BERSERK, true);
break;
case GLUTH_EVENT_ENRAGE:
Talk(GLUTH_EMOTE_ENRAGE);
me->CastSpell(me, RAID_MODE(SPELL_ENRAGE_10, SPELL_ENRAGE_25), true);
events.Repeat(22s);
break;
case GLUTH_EVENT_MORTAL_WOUND:
me->CastSpell(me->GetVictim(), GLUTH_SPELL_MORTAL_WOUND, false);
events.Repeat(10s);
break;
case GLUTH_EVENT_DECIMATE:
Talk(EMOTE_DECIMATE);
me->CastSpell(me, RAID_MODE(SPELL_DECIMATE_10, SPELL_DECIMATE_25), false);
events.RepeatEvent(RAID_MODE(110000, 90000));
break;
case GLUTH_EVENT_SUMMON_ZOMBIE:
{
uint8 rand = urand(0, 2);
for (int32 i = 0; i < RAID_MODE(1, 2); ++i)
{
// In 10 man raid, normal mode - should spawn only from mid gate
// \1 |0 /2 pos
// In 25 man raid - should spawn from all 3 gates
if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL)
{
me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[0]);
}
else
{
me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[urand(0, 2)]);
}
(rand == 2 ? rand = 0 : rand++);
}
events.Repeat(10s);
break;
}
case GLUTH_EVENT_CAN_EAT_ZOMBIE:
events.RepeatEvent(1000);
if (me->GetVictim()->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinMeleeRange(me->GetVictim()))
{
me->CastCustomSpell(SPELL_CHOW_SEARCHER, SPELLVALUE_RADIUS_MOD, 20000, me, true);
Talk(EMOTE_DEVOURS_ALL);
return; // leave it to skip DoMeleeAttackIfReady
}
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_gluth_decimate : public SpellScriptLoader
{
public:
spell_gluth_decimate() : SpellScriptLoader("spell_gluth_decimate") { }
class spell_gluth_decimate_SpellScript : public SpellScript
{
PrepareSpellScript(spell_gluth_decimate_SpellScript);
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Unit* unitTarget = GetHitUnit())
{
int32 damage = int32(unitTarget->GetHealth()) - int32(unitTarget->CountPctFromMaxHealth(5));
if (damage <= 0)
return;
if (Creature* cTarget = unitTarget->ToCreature())
{
cTarget->SetWalk(true);
cTarget->GetMotionMaster()->MoveFollow(GetCaster(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED);
cTarget->SetReactState(REACT_PASSIVE);
Unit::DealDamage(GetCaster(), cTarget, damage);
return;
}
GetCaster()->CastCustomSpell(28375, SPELLVALUE_BASE_POINT0, damage, unitTarget);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_gluth_decimate_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_gluth_decimate_SpellScript();
}
};
#endif

View File

@@ -15,703 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CombatAI.h"
#include "GridNotifiers.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Yells
{
SAY_INTRO_1 = 0,
SAY_INTRO_2 = 1,
SAY_INTRO_3 = 2,
SAY_INTRO_4 = 3,
SAY_PHASE_TWO = 4,
SAY_DEATH = 5,
SAY_KILL = 6,
EMOTE_PHASE_TWO = 7,
EMOTE_GATE_OPENED = 8
};
enum Spells
{
// Gothik
SPELL_HARVEST_SOUL = 28679,
SPELL_SHADOW_BOLT_10 = 29317,
SPELL_SHADOW_BOLT_25 = 56405,
// Teleport spells
SPELL_TELEPORT_DEAD = 28025,
SPELL_TELEPORT_LIVE = 28026,
// Visual spells
SPELL_INFORM_LIVING_TRAINEE = 27892,
SPELL_INFORM_LIVING_KNIGHT = 27928,
SPELL_INFORM_LIVING_RIDER = 27935,
SPELL_INFORM_DEAD_TRAINEE = 27915,
SPELL_INFORM_DEAD_KNIGHT = 27931,
SPELL_INFORM_DEAD_RIDER = 27937,
/*SPELL_ANCHOR_2_TRAINEE = 27893,
SPELL_ANCHOR_2_DK = 27929,
SPELL_ANCHOR_2_RIDER = 27936, fix me */
// Living trainee
SPELL_DEATH_PLAGUE = 55604,
// Dead trainee
SPELL_ARCANE_EXPLOSION = 27989,
// Living knight
SPELL_SHADOW_MARK = 27825,
// Dead knight
SPELL_WHIRLWIND = 56408,
// Living rider
SPELL_SHADOW_BOLT_VOLLEY = 27831,
// Dead rider
SPELL_DRAIN_LIFE = 27994,
SPELL_UNHOLY_FRENZY = 55648,
// Horse
SPELL_STOMP = 27993
};
enum Misc
{
NPC_LIVING_TRAINEE = 16124,
NPC_LIVING_KNIGHT = 16125,
NPC_LIVING_RIDER = 16126,
NPC_DEAD_TRAINEE = 16127,
NPC_DEAD_KNIGHT = 16148,
NPC_DEAD_HORSE = 16149,
NPC_DEAD_RIDER = 16150,
//NPC_TRIGGER = 16137, fix me
};
enum Events
{
// Gothik
EVENT_SUMMON_ADDS = 1,
EVENT_HARVEST_SOUL = 2,
EVENT_SHADOW_BOLT = 3,
EVENT_TELEPORT = 4,
EVENT_CHECK_HEALTH = 5,
EVENT_CHECK_PLAYERS = 6,
// Living trainee
EVENT_DEATH_PLAGUE = 7,
// Dead trainee
EVENT_ARCANE_EXPLOSION = 8,
// Living knight
EVENT_SHADOW_MARK = 9,
// Dead knight
EVENT_WHIRLWIND = 10,
// Living rider
EVENT_SHADOW_BOLT_VOLLEY = 11,
// Dead rider
EVENT_DRAIN_LIFE = 12,
EVENT_UNHOLY_FRENZY = 13,
// HORSE
EVENT_STOMP = 14,
// Intro
EVENT_INTRO_2 = 15,
EVENT_INTRO_3 = 16,
EVENT_INTRO_4 = 17
};
const uint32 gothikWaves[24][2] =
{
{NPC_LIVING_TRAINEE, 20000},
{NPC_LIVING_TRAINEE, 20000},
{NPC_LIVING_TRAINEE, 10000},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_TRAINEE, 15000},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_TRAINEE, 15000},
{NPC_LIVING_TRAINEE, 0},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_RIDER, 10000},
{NPC_LIVING_TRAINEE, 5000},
{NPC_LIVING_KNIGHT, 15000},
{NPC_LIVING_RIDER, 0},
{NPC_LIVING_TRAINEE, 10000},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_TRAINEE, 10000},
{NPC_LIVING_RIDER, 5000},
{NPC_LIVING_KNIGHT, 5000},
{NPC_LIVING_TRAINEE, 20000},
{NPC_LIVING_RIDER, 0},
{NPC_LIVING_KNIGHT, 0},
{NPC_LIVING_TRAINEE, 15000},
{NPC_LIVING_TRAINEE, 29000},
{0, 0}
};
const Position PosSummonLiving[6] =
{
{2669.7f, -3428.76f, 268.56f, 1.6f},
{2692.1f, -3428.76f, 268.56f, 1.6f},
{2714.4f, -3428.76f, 268.56f, 1.6f},
{2669.7f, -3431.67f, 268.56f, 1.6f},
{2692.1f, -3431.67f, 268.56f, 1.6f},
{2714.4f, -3431.67f, 268.56f, 1.6f}
};
const Position PosSummonDead[5] =
{
{2725.1f, -3310.0f, 268.85f, 3.4f},
{2699.3f, -3322.8f, 268.60f, 3.3f},
{2733.1f, -3348.5f, 268.84f, 3.1f},
{2682.8f, -3304.2f, 268.85f, 3.9f},
{2664.8f, -3340.7f, 268.23f, 3.7f}
};
//const Position PosGroundLivingSide = {2691.2f, -3387.0f, 267.68f, 1.52f};
//const Position PosGroundDeadSide = {2693.5f, -3334.6f, 267.68f, 4.67f};
//const Position PosPlatform = {2640.5f, -3360.6f, 285.26f, 0.0f};
#define POS_Y_GATE -3360.78f
#define POS_Y_WEST -3285.0f
#define POS_Y_EAST -3434.0f
#define POS_X_NORTH 2750.49f
#define POS_X_SOUTH 2633.84f
#define IN_LIVE_SIDE(who) (who->GetPositionY() < POS_Y_GATE)
// Predicate function to check that the r efzr unit is NOT on the same side as the source.
struct NotOnSameSide
{
public:
explicit NotOnSameSide(Unit* pSource) : m_inLiveSide(IN_LIVE_SIDE(pSource)) { }
bool operator() (Unit const* pTarget)
{
return (m_inLiveSide != IN_LIVE_SIDE(pTarget));
}
private:
bool m_inLiveSide;
};
class boss_gothik : public CreatureScript
{
public:
boss_gothik() : CreatureScript("boss_gothik") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_gothikAI>(pCreature);
}
struct boss_gothikAI : public BossAI
{
explicit boss_gothikAI(Creature* c) : BossAI(c, BOSS_GOTHIK), summons(me)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
bool secondPhase{};
bool gateOpened{};
uint8 waveCount{};
bool IsInRoom()
{
if (me->GetPositionX() > 2767 || me->GetPositionX() < 2618 || me->GetPositionY() > -3285 || me->GetPositionY() < -3435)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE);
me->SetImmuneToPC(false);
me->SetReactState(REACT_PASSIVE);
secondPhase = false;
gateOpened = false;
waveCount = 0;
me->NearTeleportTo(2642.139f, -3386.959f, 285.492f, 6.265f);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_ENTER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_EXIT_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
Talk(SAY_INTRO_1);
events.ScheduleEvent(EVENT_INTRO_2, 4s);
events.ScheduleEvent(EVENT_INTRO_3, 9s);
events.ScheduleEvent(EVENT_INTRO_4, 14s);
me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
events.ScheduleEvent(EVENT_SUMMON_ADDS, 30s);
events.ScheduleEvent(EVENT_CHECK_PLAYERS, 2min);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_ENTER_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 200.0f))
{
if (gateOpened)
{
summon->AI()->AttackStart(target);
summon->CallForHelp(40.0f);
}
else
{
if (summon->GetEntry() == NPC_LIVING_TRAINEE ||
summon->GetEntry() == NPC_LIVING_KNIGHT ||
summon->GetEntry() == NPC_LIVING_RIDER )
{
if (IN_LIVE_SIDE(target))
{
summon->AI()->AttackStart(target);
}
}
else
{
if (!IN_LIVE_SIDE(target))
{
summon->AI()->AttackStart(target);
}
}
}
}
}
void SummonedCreatureDespawn(Creature* cr) override
{
summons.Despawn(cr);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_KILL);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
summons.DespawnAll();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_ENTER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_EXIT_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void SummonHelpers(uint32 entry)
{
switch(entry)
{
case NPC_LIVING_TRAINEE:
me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[0].GetPositionX(), PosSummonLiving[0].GetPositionY(), PosSummonLiving[0].GetPositionZ(), PosSummonLiving[0].GetOrientation());
me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[1].GetPositionX(), PosSummonLiving[1].GetPositionY(), PosSummonLiving[1].GetPositionZ(), PosSummonLiving[1].GetOrientation());
if (Is25ManRaid())
{
me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[2].GetPositionX(), PosSummonLiving[2].GetPositionY(), PosSummonLiving[2].GetPositionZ(), PosSummonLiving[2].GetOrientation());
}
break;
case NPC_LIVING_KNIGHT:
me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[3].GetPositionX(), PosSummonLiving[3].GetPositionY(), PosSummonLiving[3].GetPositionZ(), PosSummonLiving[3].GetOrientation());
if (Is25ManRaid())
{
me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[5].GetPositionX(), PosSummonLiving[5].GetPositionY(), PosSummonLiving[5].GetPositionZ(), PosSummonLiving[5].GetOrientation());
}
break;
case NPC_LIVING_RIDER:
me->SummonCreature(NPC_LIVING_RIDER, PosSummonLiving[4].GetPositionX(), PosSummonLiving[4].GetPositionY(), PosSummonLiving[4].GetPositionZ(), PosSummonLiving[4].GetOrientation());
break;
}
}
bool CheckGroupSplitted()
{
Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers();
if (!PlayerList.IsEmpty())
{
bool checklife = false;
bool checkdead = false;
for (const auto& i : PlayerList)
{
Player* player = i.GetSource();
if (player->IsAlive() &&
player->GetPositionX() <= POS_X_NORTH &&
player->GetPositionX() >= POS_X_SOUTH &&
player->GetPositionY() <= POS_Y_GATE &&
player->GetPositionY() >= POS_Y_EAST)
{
checklife = true;
}
else if (player->IsAlive() &&
player->GetPositionX() <= POS_X_NORTH &&
player->GetPositionX() >= POS_X_SOUTH &&
player->GetPositionY() >= POS_Y_GATE &&
player->GetPositionY() <= POS_Y_WEST)
{
checkdead = true;
}
if (checklife && checkdead)
return true;
}
}
return false;
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
{
uint8 pos = urand(0, 4);
switch (spellInfo->Id)
{
case SPELL_INFORM_LIVING_TRAINEE:
me->SummonCreature(NPC_DEAD_TRAINEE, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
break;
case SPELL_INFORM_LIVING_KNIGHT:
me->SummonCreature(NPC_DEAD_KNIGHT, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
break;
case SPELL_INFORM_LIVING_RIDER:
me->SummonCreature(NPC_DEAD_RIDER, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
me->SummonCreature(NPC_DEAD_HORSE, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
break;
}
me->HandleEmoteCommand(EMOTE_ONESHOT_SPELL_CAST);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!secondPhase)
{
damage = 0;
}
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_INTRO_2:
Talk(SAY_INTRO_2);
break;
case EVENT_INTRO_3:
Talk(SAY_INTRO_3);
break;
case EVENT_INTRO_4:
Talk(SAY_INTRO_4);
break;
case EVENT_SHADOW_BOLT:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_SHADOW_BOLT_10, SPELL_SHADOW_BOLT_25), false);
events.Repeat(1s);
break;
case EVENT_HARVEST_SOUL:
me->CastSpell(me, SPELL_HARVEST_SOUL, false);
events.Repeat(15s);
break;
case EVENT_TELEPORT:
me->AttackStop();
if (IN_LIVE_SIDE(me))
{
me->CastSpell(me, SPELL_TELEPORT_DEAD, false);
}
else
{
me->CastSpell(me, SPELL_TELEPORT_LIVE, false);
}
me->GetThreatMgr().resetAggro(NotOnSameSide(me));
if (Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0))
{
me->GetThreatMgr().AddThreat(pTarget, 100.0f);
AttackStart(pTarget);
}
events.Repeat(20s);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(30) && pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
events.CancelEvent(EVENT_TELEPORT);
break;
}
events.Repeat(1s);
break;
case EVENT_SUMMON_ADDS:
if (gothikWaves[waveCount][0])
{
SummonHelpers(gothikWaves[waveCount][0]);
events.RepeatEvent(gothikWaves[waveCount][1]);
}
else
{
secondPhase = true;
Talk(SAY_PHASE_TWO);
Talk(EMOTE_PHASE_TWO);
me->CastSpell(me, SPELL_TELEPORT_LIVE, false);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE);
me->SetImmuneToPC(false);
me->RemoveAllAuras();
events.ScheduleEvent(EVENT_SHADOW_BOLT, 1s);
events.ScheduleEvent(EVENT_HARVEST_SOUL, 5s, 15s);
events.ScheduleEvent(EVENT_TELEPORT, 20s);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1s);
}
waveCount++;
break;
case EVENT_CHECK_PLAYERS:
if (!CheckGroupSplitted())
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
gateOpened = true;
Talk(EMOTE_GATE_OPENED);
}
break;
}
DoMeleeAttackIfReady();
}
};
};
class npc_boss_gothik_minion : public CreatureScript
{
public:
npc_boss_gothik_minion() : CreatureScript("npc_boss_gothik_minion") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<npc_boss_gothik_minionAI>(pCreature);
}
struct npc_boss_gothik_minionAI : public CombatAI
{
explicit npc_boss_gothik_minionAI(Creature* c) : CombatAI(c)
{
livingSide = IN_LIVE_SIDE(me);
}
EventMap events;
bool livingSide;
bool IsOnSameSide(Unit const* who) const { return livingSide == IN_LIVE_SIDE(who); }
void Reset() override
{
me->SetReactState(REACT_AGGRESSIVE);
me->SetNoCallAssistance(false);
events.Reset();
}
void JustEngagedWith(Unit* /*who*/) override
{
switch (me->GetEntry())
{
case NPC_LIVING_TRAINEE:
events.ScheduleEvent(EVENT_DEATH_PLAGUE, 3s);
break;
case NPC_DEAD_TRAINEE:
events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 2500ms);
break;
case NPC_LIVING_KNIGHT:
events.ScheduleEvent(EVENT_SHADOW_MARK, 3s);
break;
case NPC_DEAD_KNIGHT:
events.ScheduleEvent(EVENT_WHIRLWIND, 2s);
break;
case NPC_LIVING_RIDER:
events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 3s);
break;
case NPC_DEAD_RIDER:
events.ScheduleEvent(EVENT_DRAIN_LIFE, 2000ms, 3500ms);
events.ScheduleEvent(EVENT_UNHOLY_FRENZY, 5s, 9s);
break;
case NPC_DEAD_HORSE:
events.ScheduleEvent(EVENT_STOMP, 2s, 5s);
break;
}
}
void JustDied(Unit*) override
{
switch (me->GetEntry())
{
case NPC_LIVING_TRAINEE:
me->CastSpell(me, SPELL_INFORM_LIVING_TRAINEE, true);
break;
case NPC_LIVING_KNIGHT:
me->CastSpell(me, SPELL_INFORM_LIVING_KNIGHT, true);
break;
case NPC_LIVING_RIDER:
me->CastSpell(me, SPELL_INFORM_LIVING_RIDER, true);
break;
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
if (me->GetUnitState() == UNIT_STATE_CASTING)
return;
switch (events.ExecuteEvent())
{
case EVENT_DEATH_PLAGUE:
me->CastSpell(me->GetVictim(), SPELL_DEATH_PLAGUE, false);
events.Repeat(4s, 7s);
break;
case EVENT_ARCANE_EXPLOSION:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 20))
{
me->CastSpell(victim, SPELL_ARCANE_EXPLOSION, false);
}
}
events.Repeat(2500ms);
break;
case EVENT_SHADOW_MARK:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 10))
{
me->CastSpell(victim, SPELL_SHADOW_MARK, false);
}
}
events.Repeat(5s, 7s);
break;
case EVENT_WHIRLWIND:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 10))
{
me->CastSpell(victim, SPELL_WHIRLWIND, false);
}
}
events.Repeat(4s, 6s);
break;
case EVENT_SHADOW_BOLT_VOLLEY:
me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT_VOLLEY, false);
events.Repeat(5s);
break;
case EVENT_DRAIN_LIFE:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 20))
{
me->CastSpell(victim, SPELL_DRAIN_LIFE, false);
}
}
events.Repeat(8s, 12s);
break;
case EVENT_UNHOLY_FRENZY:
me->AddAura(SPELL_UNHOLY_FRENZY, me);
events.Repeat(15s, 17s);
break;
case EVENT_STOMP:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 10))
{
me->CastSpell(victim, SPELL_STOMP, false);
}
}
events.Repeat(4s, 9s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_gothik_shadow_bolt_volley : public SpellScriptLoader
{
public:
spell_gothik_shadow_bolt_volley() : SpellScriptLoader("spell_gothik_shadow_bolt_volley") { }
class spell_gothik_shadow_bolt_volley_SpellScript : public SpellScript
{
PrepareSpellScript(spell_gothik_shadow_bolt_volley_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if(Acore::UnitAuraCheck(false, SPELL_SHADOW_MARK));
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gothik_shadow_bolt_volley_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_gothik_shadow_bolt_volley_SpellScript();
}
};
#include "boss_gothik.h"
void AddSC_boss_gothik()
{

View File

@@ -0,0 +1,702 @@
#ifndef BOSSGOTHIK_H_
#define BOSSGOTHIK_H_
#include "CombatAI.h"
#include "GridNotifiers.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum GothikYells
{
GOTHIK_SAY_INTRO_1 = 0,
GOTHIK_SAY_INTRO_2 = 1,
GOTHIK_SAY_INTRO_3 = 2,
GOTHIK_SAY_INTRO_4 = 3,
GOTHIK_SAY_PHASE_TWO = 4,
GOTHIK_SAY_DEATH = 5,
GOTHIK_SAY_KILL = 6,
GOTHIK_EMOTE_PHASE_TWO = 7,
GOTHIK_EMOTE_GATE_OPENED = 8
};
enum GothikSpells
{
// Gothik
SPELL_HARVEST_SOUL = 28679,
SPELL_SHADOW_BOLT_10 = 29317,
SPELL_SHADOW_BOLT_25 = 56405,
// Teleport spells
SPELL_TELEPORT_DEAD = 28025,
SPELL_TELEPORT_LIVE = 28026,
// Visual spells
SPELL_INFORM_LIVING_TRAINEE = 27892,
SPELL_INFORM_LIVING_KNIGHT = 27928,
SPELL_INFORM_LIVING_RIDER = 27935,
SPELL_INFORM_DEAD_TRAINEE = 27915,
SPELL_INFORM_DEAD_KNIGHT = 27931,
SPELL_INFORM_DEAD_RIDER = 27937,
/*SPELL_ANCHOR_2_TRAINEE = 27893,
SPELL_ANCHOR_2_DK = 27929,
SPELL_ANCHOR_2_RIDER = 27936, fix me */
// Living trainee
SPELL_DEATH_PLAGUE = 55604,
// Dead trainee
SPELL_ARCANE_EXPLOSION = 27989,
// Living knight
SPELL_SHADOW_MARK = 27825,
// Dead knight
SPELL_WHIRLWIND = 56408,
// Living rider
SPELL_SHADOW_BOLT_VOLLEY = 27831,
// Dead rider
SPELL_DRAIN_LIFE = 27994,
SPELL_UNHOLY_FRENZY = 55648,
// Horse
SPELL_STOMP = 27993
};
enum GothikMisc
{
NPC_LIVING_TRAINEE = 16124,
NPC_LIVING_KNIGHT = 16125,
NPC_LIVING_RIDER = 16126,
NPC_DEAD_TRAINEE = 16127,
NPC_DEAD_KNIGHT = 16148,
NPC_DEAD_HORSE = 16149,
NPC_DEAD_RIDER = 16150,
//NPC_TRIGGER = 16137, fix me
};
enum GothikEvents
{
// Gothik
EVENT_SUMMON_ADDS = 1,
EVENT_HARVEST_SOUL = 2,
EVENT_SHADOW_BOLT = 3,
EVENT_TELEPORT = 4,
EVENT_CHECK_HEALTH = 5,
EVENT_CHECK_PLAYERS = 6,
// Living trainee
EVENT_DEATH_PLAGUE = 7,
// Dead trainee
EVENT_ARCANE_EXPLOSION = 8,
// Living knight
EVENT_SHADOW_MARK = 9,
// Dead knight
EVENT_WHIRLWIND = 10,
// Living rider
EVENT_SHADOW_BOLT_VOLLEY = 11,
// Dead rider
EVENT_DRAIN_LIFE = 12,
EVENT_UNHOLY_FRENZY = 13,
// HORSE
EVENT_STOMP = 14,
// Intro
EVENT_INTRO_2 = 15,
EVENT_INTRO_3 = 16,
EVENT_INTRO_4 = 17
};
const uint32 gothikWaves[24][2] =
{
{NPC_LIVING_TRAINEE, 20000},
{NPC_LIVING_TRAINEE, 20000},
{NPC_LIVING_TRAINEE, 10000},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_TRAINEE, 15000},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_TRAINEE, 15000},
{NPC_LIVING_TRAINEE, 0},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_RIDER, 10000},
{NPC_LIVING_TRAINEE, 5000},
{NPC_LIVING_KNIGHT, 15000},
{NPC_LIVING_RIDER, 0},
{NPC_LIVING_TRAINEE, 10000},
{NPC_LIVING_KNIGHT, 10000},
{NPC_LIVING_TRAINEE, 10000},
{NPC_LIVING_RIDER, 5000},
{NPC_LIVING_KNIGHT, 5000},
{NPC_LIVING_TRAINEE, 20000},
{NPC_LIVING_RIDER, 0},
{NPC_LIVING_KNIGHT, 0},
{NPC_LIVING_TRAINEE, 15000},
{NPC_LIVING_TRAINEE, 29000},
{0, 0}
};
const Position PosSummonLiving[6] =
{
{2669.7f, -3428.76f, 268.56f, 1.6f},
{2692.1f, -3428.76f, 268.56f, 1.6f},
{2714.4f, -3428.76f, 268.56f, 1.6f},
{2669.7f, -3431.67f, 268.56f, 1.6f},
{2692.1f, -3431.67f, 268.56f, 1.6f},
{2714.4f, -3431.67f, 268.56f, 1.6f}
};
const Position PosSummonDead[5] =
{
{2725.1f, -3310.0f, 268.85f, 3.4f},
{2699.3f, -3322.8f, 268.60f, 3.3f},
{2733.1f, -3348.5f, 268.84f, 3.1f},
{2682.8f, -3304.2f, 268.85f, 3.9f},
{2664.8f, -3340.7f, 268.23f, 3.7f}
};
//const Position PosGroundLivingSide = {2691.2f, -3387.0f, 267.68f, 1.52f};
//const Position PosGroundDeadSide = {2693.5f, -3334.6f, 267.68f, 4.67f};
//const Position PosPlatform = {2640.5f, -3360.6f, 285.26f, 0.0f};
#define POS_Y_GATE -3360.78f
#define POS_Y_WEST -3285.0f
#define POS_Y_EAST -3434.0f
#define POS_X_NORTH 2750.49f
#define POS_X_SOUTH 2633.84f
#define IN_LIVE_SIDE(who) (who->GetPositionY() < POS_Y_GATE)
// Predicate function to check that the r efzr unit is NOT on the same side as the source.
struct NotOnSameSide
{
public:
explicit NotOnSameSide(Unit* pSource) : m_inLiveSide(IN_LIVE_SIDE(pSource)) { }
bool operator() (Unit const* pTarget)
{
return (m_inLiveSide != IN_LIVE_SIDE(pTarget));
}
private:
bool m_inLiveSide;
};
class boss_gothik : public CreatureScript
{
public:
boss_gothik() : CreatureScript("boss_gothik") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_gothikAI>(pCreature);
}
struct boss_gothikAI : public BossAI
{
explicit boss_gothikAI(Creature* c) : BossAI(c, BOSS_GOTHIK), summons(me)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
bool secondPhase{};
bool gateOpened{};
uint8 waveCount{};
bool IsInRoom()
{
if (me->GetPositionX() > 2767 || me->GetPositionX() < 2618 || me->GetPositionY() > -3285 || me->GetPositionY() < -3435)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE);
me->SetImmuneToPC(false);
me->SetReactState(REACT_PASSIVE);
secondPhase = false;
gateOpened = false;
waveCount = 0;
me->NearTeleportTo(2642.139f, -3386.959f, 285.492f, 6.265f);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_ENTER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_EXIT_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
Talk(GOTHIK_SAY_INTRO_1);
events.ScheduleEvent(EVENT_INTRO_2, 4s);
events.ScheduleEvent(EVENT_INTRO_3, 9s);
events.ScheduleEvent(EVENT_INTRO_4, 14s);
me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
events.ScheduleEvent(EVENT_SUMMON_ADDS, 30s);
events.ScheduleEvent(EVENT_CHECK_PLAYERS, 2min);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_ENTER_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 200.0f))
{
if (gateOpened)
{
summon->AI()->AttackStart(target);
summon->CallForHelp(40.0f);
}
else
{
if (summon->GetEntry() == NPC_LIVING_TRAINEE ||
summon->GetEntry() == NPC_LIVING_KNIGHT ||
summon->GetEntry() == NPC_LIVING_RIDER )
{
if (IN_LIVE_SIDE(target))
{
summon->AI()->AttackStart(target);
}
}
else
{
if (!IN_LIVE_SIDE(target))
{
summon->AI()->AttackStart(target);
}
}
}
}
}
void SummonedCreatureDespawn(Creature* cr) override
{
summons.Despawn(cr);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(GOTHIK_SAY_KILL);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(GOTHIK_SAY_DEATH);
summons.DespawnAll();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_ENTER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_EXIT_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void SummonHelpers(uint32 entry)
{
switch(entry)
{
case NPC_LIVING_TRAINEE:
me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[0].GetPositionX(), PosSummonLiving[0].GetPositionY(), PosSummonLiving[0].GetPositionZ(), PosSummonLiving[0].GetOrientation());
me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[1].GetPositionX(), PosSummonLiving[1].GetPositionY(), PosSummonLiving[1].GetPositionZ(), PosSummonLiving[1].GetOrientation());
if (Is25ManRaid())
{
me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[2].GetPositionX(), PosSummonLiving[2].GetPositionY(), PosSummonLiving[2].GetPositionZ(), PosSummonLiving[2].GetOrientation());
}
break;
case NPC_LIVING_KNIGHT:
me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[3].GetPositionX(), PosSummonLiving[3].GetPositionY(), PosSummonLiving[3].GetPositionZ(), PosSummonLiving[3].GetOrientation());
if (Is25ManRaid())
{
me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[5].GetPositionX(), PosSummonLiving[5].GetPositionY(), PosSummonLiving[5].GetPositionZ(), PosSummonLiving[5].GetOrientation());
}
break;
case NPC_LIVING_RIDER:
me->SummonCreature(NPC_LIVING_RIDER, PosSummonLiving[4].GetPositionX(), PosSummonLiving[4].GetPositionY(), PosSummonLiving[4].GetPositionZ(), PosSummonLiving[4].GetOrientation());
break;
}
}
bool CheckGroupSplitted()
{
Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers();
if (!PlayerList.IsEmpty())
{
bool checklife = false;
bool checkdead = false;
for (const auto& i : PlayerList)
{
Player* player = i.GetSource();
if (player->IsAlive() &&
player->GetPositionX() <= POS_X_NORTH &&
player->GetPositionX() >= POS_X_SOUTH &&
player->GetPositionY() <= POS_Y_GATE &&
player->GetPositionY() >= POS_Y_EAST)
{
checklife = true;
}
else if (player->IsAlive() &&
player->GetPositionX() <= POS_X_NORTH &&
player->GetPositionX() >= POS_X_SOUTH &&
player->GetPositionY() >= POS_Y_GATE &&
player->GetPositionY() <= POS_Y_WEST)
{
checkdead = true;
}
if (checklife && checkdead)
return true;
}
}
return false;
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
{
uint8 pos = urand(0, 4);
switch (spellInfo->Id)
{
case SPELL_INFORM_LIVING_TRAINEE:
me->SummonCreature(NPC_DEAD_TRAINEE, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
break;
case SPELL_INFORM_LIVING_KNIGHT:
me->SummonCreature(NPC_DEAD_KNIGHT, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
break;
case SPELL_INFORM_LIVING_RIDER:
me->SummonCreature(NPC_DEAD_RIDER, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
me->SummonCreature(NPC_DEAD_HORSE, PosSummonDead[pos].GetPositionX(), PosSummonDead[pos].GetPositionY(), PosSummonDead[pos].GetPositionZ(), PosSummonDead[pos].GetOrientation());
break;
}
me->HandleEmoteCommand(EMOTE_ONESHOT_SPELL_CAST);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!secondPhase)
{
damage = 0;
}
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_INTRO_2:
Talk(GOTHIK_SAY_INTRO_2);
break;
case EVENT_INTRO_3:
Talk(GOTHIK_SAY_INTRO_3);
break;
case EVENT_INTRO_4:
Talk(GOTHIK_SAY_INTRO_4);
break;
case EVENT_SHADOW_BOLT:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_SHADOW_BOLT_10, SPELL_SHADOW_BOLT_25), false);
events.Repeat(1s);
break;
case EVENT_HARVEST_SOUL:
me->CastSpell(me, SPELL_HARVEST_SOUL, false);
events.Repeat(15s);
break;
case EVENT_TELEPORT:
me->AttackStop();
if (IN_LIVE_SIDE(me))
{
me->CastSpell(me, SPELL_TELEPORT_DEAD, false);
}
else
{
me->CastSpell(me, SPELL_TELEPORT_LIVE, false);
}
me->GetThreatMgr().resetAggro(NotOnSameSide(me));
if (Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0))
{
me->GetThreatMgr().AddThreat(pTarget, 100.0f);
AttackStart(pTarget);
}
events.Repeat(20s);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(30) && pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
events.CancelEvent(EVENT_TELEPORT);
break;
}
events.Repeat(1s);
break;
case EVENT_SUMMON_ADDS:
if (gothikWaves[waveCount][0])
{
SummonHelpers(gothikWaves[waveCount][0]);
events.RepeatEvent(gothikWaves[waveCount][1]);
}
else
{
secondPhase = true;
Talk(GOTHIK_SAY_PHASE_TWO);
Talk(GOTHIK_EMOTE_PHASE_TWO);
me->CastSpell(me, SPELL_TELEPORT_LIVE, false);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE);
me->SetImmuneToPC(false);
me->RemoveAllAuras();
events.ScheduleEvent(EVENT_SHADOW_BOLT, 1s);
events.ScheduleEvent(EVENT_HARVEST_SOUL, 5s, 15s);
events.ScheduleEvent(EVENT_TELEPORT, 20s);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1s);
}
waveCount++;
break;
case EVENT_CHECK_PLAYERS:
if (!CheckGroupSplitted())
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_GOTHIK_INNER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
gateOpened = true;
Talk(GOTHIK_EMOTE_GATE_OPENED);
}
break;
}
DoMeleeAttackIfReady();
}
};
};
class npc_boss_gothik_minion : public CreatureScript
{
public:
npc_boss_gothik_minion() : CreatureScript("npc_boss_gothik_minion") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<npc_boss_gothik_minionAI>(pCreature);
}
struct npc_boss_gothik_minionAI : public CombatAI
{
explicit npc_boss_gothik_minionAI(Creature* c) : CombatAI(c)
{
livingSide = IN_LIVE_SIDE(me);
}
EventMap events;
bool livingSide;
bool IsOnSameSide(Unit const* who) const { return livingSide == IN_LIVE_SIDE(who); }
void Reset() override
{
me->SetReactState(REACT_AGGRESSIVE);
me->SetNoCallAssistance(false);
events.Reset();
}
void JustEngagedWith(Unit* /*who*/) override
{
switch (me->GetEntry())
{
case NPC_LIVING_TRAINEE:
events.ScheduleEvent(EVENT_DEATH_PLAGUE, 3s);
break;
case NPC_DEAD_TRAINEE:
events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 2500ms);
break;
case NPC_LIVING_KNIGHT:
events.ScheduleEvent(EVENT_SHADOW_MARK, 3s);
break;
case NPC_DEAD_KNIGHT:
events.ScheduleEvent(EVENT_WHIRLWIND, 2s);
break;
case NPC_LIVING_RIDER:
events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 3s);
break;
case NPC_DEAD_RIDER:
events.ScheduleEvent(EVENT_DRAIN_LIFE, 2000ms, 3500ms);
events.ScheduleEvent(EVENT_UNHOLY_FRENZY, 5s, 9s);
break;
case NPC_DEAD_HORSE:
events.ScheduleEvent(EVENT_STOMP, 2s, 5s);
break;
}
}
void JustDied(Unit*) override
{
switch (me->GetEntry())
{
case NPC_LIVING_TRAINEE:
me->CastSpell(me, SPELL_INFORM_LIVING_TRAINEE, true);
break;
case NPC_LIVING_KNIGHT:
me->CastSpell(me, SPELL_INFORM_LIVING_KNIGHT, true);
break;
case NPC_LIVING_RIDER:
me->CastSpell(me, SPELL_INFORM_LIVING_RIDER, true);
break;
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
if (me->GetUnitState() == UNIT_STATE_CASTING)
return;
switch (events.ExecuteEvent())
{
case EVENT_DEATH_PLAGUE:
me->CastSpell(me->GetVictim(), SPELL_DEATH_PLAGUE, false);
events.Repeat(4s, 7s);
break;
case EVENT_ARCANE_EXPLOSION:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 20))
{
me->CastSpell(victim, SPELL_ARCANE_EXPLOSION, false);
}
}
events.Repeat(2500ms);
break;
case EVENT_SHADOW_MARK:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 10))
{
me->CastSpell(victim, SPELL_SHADOW_MARK, false);
}
}
events.Repeat(5s, 7s);
break;
case EVENT_WHIRLWIND:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 10))
{
me->CastSpell(victim, SPELL_WHIRLWIND, false);
}
}
events.Repeat(4s, 6s);
break;
case EVENT_SHADOW_BOLT_VOLLEY:
me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT_VOLLEY, false);
events.Repeat(5s);
break;
case EVENT_DRAIN_LIFE:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 20))
{
me->CastSpell(victim, SPELL_DRAIN_LIFE, false);
}
}
events.Repeat(8s, 12s);
break;
case EVENT_UNHOLY_FRENZY:
me->AddAura(SPELL_UNHOLY_FRENZY, me);
events.Repeat(15s, 17s);
break;
case EVENT_STOMP:
if (Unit* victim = me->GetVictim())
{
if (victim->IsWithinDist(me, 10))
{
me->CastSpell(victim, SPELL_STOMP, false);
}
}
events.Repeat(4s, 9s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_gothik_shadow_bolt_volley : public SpellScriptLoader
{
public:
spell_gothik_shadow_bolt_volley() : SpellScriptLoader("spell_gothik_shadow_bolt_volley") { }
class spell_gothik_shadow_bolt_volley_SpellScript : public SpellScript
{
PrepareSpellScript(spell_gothik_shadow_bolt_volley_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if(Acore::UnitAuraCheck(false, SPELL_SHADOW_MARK));
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gothik_shadow_bolt_volley_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_gothik_shadow_bolt_volley_SpellScript();
}
};
#endif

View File

@@ -17,252 +17,6 @@
#include "boss_grobbulus.h"
enum Spells
{
SPELL_POISON_CLOUD = 28240,
SPELL_MUTATING_INJECTION = 28169,
SPELL_MUTATING_EXPLOSION = 28206,
SPELL_SLIME_SPRAY_10 = 28157,
SPELL_SLIME_SPRAY_25 = 54364,
SPELL_POISON_CLOUD_DAMAGE_AURA_10 = 28158,
SPELL_POISON_CLOUD_DAMAGE_AURA_25 = 54362,
SPELL_BERSERK = 26662,
SPELL_BOMBARD_SLIME = 28280
};
enum Emotes
{
EMOTE_SLIME = 0
};
enum Events
{
EVENT_BERSERK = 1,
EVENT_POISON_CLOUD = 2,
EVENT_SLIME_SPRAY = 3,
EVENT_MUTATING_INJECTION = 4
};
enum Misc
{
NPC_FALLOUT_SLIME = 16290,
NPC_SEWAGE_SLIME = 16375,
NPC_STICHED_GIANT = 16025
};
CreatureAI* boss_grobbulus::GetAI(Creature* pCreature) const
{
return GetNaxxramasAI<boss_grobbulusAI>(pCreature);
}
boss_grobbulus::boss_grobbulusAI::boss_grobbulusAI(Creature* c) : BossAI(c, BOSS_GROBBULUS), summons(me)
{
pInstance = me->GetInstanceScript();
}
void boss_grobbulus::boss_grobbulusAI::Reset()
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
dropSludgeTimer = 0;
}
void boss_grobbulus::boss_grobbulusAI::PullChamberAdds()
{
std::list<Creature*> StichedGiants;
me->GetCreaturesWithEntryInRange(StichedGiants, 300.0f, NPC_STICHED_GIANT);
for (std::list<Creature*>::const_iterator itr = StichedGiants.begin(); itr != StichedGiants.end(); ++itr)
{
(*itr)->ToCreature()->AI()->AttackStart(me->GetVictim());
}
}
void boss_grobbulus::boss_grobbulusAI::JustEngagedWith(Unit* who)
{
BossAI::JustEngagedWith(who);
PullChamberAdds();
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_POISON_CLOUD, 15s);
events.ScheduleEvent(EVENT_MUTATING_INJECTION, 20s);
events.ScheduleEvent(EVENT_SLIME_SPRAY, 10s);
events.ScheduleEvent(EVENT_BERSERK, RAID_MODE(720000, 540000));
}
void boss_grobbulus::boss_grobbulusAI::SpellHitTarget(Unit* target, SpellInfo const* spellInfo)
{
if (spellInfo->Id == RAID_MODE(SPELL_SLIME_SPRAY_10, SPELL_SLIME_SPRAY_25) && target->GetTypeId() == TYPEID_PLAYER)
{
me->SummonCreature(NPC_FALLOUT_SLIME, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
}
}
void boss_grobbulus::boss_grobbulusAI::JustSummoned(Creature* cr)
{
if (cr->GetEntry() == NPC_FALLOUT_SLIME)
{
cr->SetInCombatWithZone();
}
summons.Summon(cr);
}
void boss_grobbulus::boss_grobbulusAI::SummonedCreatureDespawn(Creature* summon)
{
summons.Despawn(summon);
}
void boss_grobbulus::boss_grobbulusAI::JustDied(Unit* killer)
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
void boss_grobbulus::boss_grobbulusAI::KilledUnit(Unit* who)
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void boss_grobbulus::boss_grobbulusAI::UpdateAI(uint32 diff)
{
dropSludgeTimer += diff;
if (!me->IsInCombat() && dropSludgeTimer >= 5000)
{
if (me->IsWithinDist3d(3178, -3305, 319, 5.0f) && !summons.HasEntry(NPC_SEWAGE_SLIME))
{
me->CastSpell(3128.96f + irand(-20, 20), -3312.96f + irand(-20, 20), 293.25f, SPELL_BOMBARD_SLIME, false);
}
dropSludgeTimer = 0;
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_POISON_CLOUD:
me->CastSpell(me, SPELL_POISON_CLOUD, true);
events.Repeat(15s);
break;
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_SLIME_SPRAY:
Talk(EMOTE_SLIME);
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_SLIME_SPRAY_10, SPELL_SLIME_SPRAY_25), false);
events.Repeat(20s);
break;
case EVENT_MUTATING_INJECTION:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true, true, -SPELL_MUTATING_INJECTION))
{
me->CastSpell(target, SPELL_MUTATING_INJECTION, false);
}
events.RepeatEvent(6000 + uint32(120 * me->GetHealthPct()));
break;
}
DoMeleeAttackIfReady();
}
CreatureAI* boss_grobbulus_poison_cloud::GetAI(Creature* pCreature) const
{
return GetNaxxramasAI<boss_grobbulus_poison_cloudAI>(pCreature);
}
void boss_grobbulus_poison_cloud::boss_grobbulus_poison_cloudAI::Reset()
{
sizeTimer = 0;
auraVisualTimer = 1;
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f);
me->SetFaction(FACTION_BOOTY_BAY);
}
void boss_grobbulus_poison_cloud::boss_grobbulus_poison_cloudAI::KilledUnit(Unit* who)
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void boss_grobbulus_poison_cloud::boss_grobbulus_poison_cloudAI::UpdateAI(uint32 diff)
{
if (auraVisualTimer) // this has to be delayed to be visible
{
auraVisualTimer += diff;
if (auraVisualTimer >= 1000)
{
me->CastSpell(me, (me->GetMap()->Is25ManRaid() ? SPELL_POISON_CLOUD_DAMAGE_AURA_25 : SPELL_POISON_CLOUD_DAMAGE_AURA_10), true);
auraVisualTimer = 0;
}
}
sizeTimer += diff; // increase size to 15yd in 60 seconds, 0.00025 is the growth of size in 1ms
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f + (0.00025f * sizeTimer));
}
void spell_grobbulus_poison::spell_grobbulus_poison_SpellScript::FilterTargets(std::list<WorldObject*>& targets)
{
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (GetCaster()->IsWithinDist3d(target, 0.0f))
{
tmplist.push_back(target);
}
}
targets.clear();
for (auto& itr : tmplist)
{
targets.push_back(itr);
}
}
void spell_grobbulus_poison::spell_grobbulus_poison_SpellScript::Register()
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_grobbulus_poison_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
SpellScript* spell_grobbulus_poison::GetSpellScript() const
{
return new spell_grobbulus_poison_SpellScript();
}
bool spell_grobbulus_mutating_injection::spell_grobbulus_mutating_injection_AuraScript::Validate(SpellInfo const* /*spellInfo*/)
{
return ValidateSpellInfo({ SPELL_MUTATING_EXPLOSION });
}
void spell_grobbulus_mutating_injection::spell_grobbulus_mutating_injection_AuraScript::HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
switch (GetTargetApplication()->GetRemoveMode())
{
case AURA_REMOVE_BY_ENEMY_SPELL:
case AURA_REMOVE_BY_EXPIRE:
if (auto caster = GetCaster())
{
caster->CastSpell(GetTarget(), SPELL_MUTATING_EXPLOSION, true);
}
break;
default:
return;
}
}
void spell_grobbulus_mutating_injection::spell_grobbulus_mutating_injection_AuraScript::Register()
{
AfterEffectRemove += AuraEffectRemoveFn(spell_grobbulus_mutating_injection_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
AuraScript* spell_grobbulus_mutating_injection::GetAuraScript() const
{
return new spell_grobbulus_mutating_injection_AuraScript();
}
void AddSC_boss_grobbulus()
{
new boss_grobbulus();

View File

@@ -9,41 +9,169 @@
#include "SpellScript.h"
#include "naxxramas.h"
enum GrobbulusSpells
{
GROBBULUS_SPELL_POISON_CLOUD = 28240,
GROBBULUS_SPELL_MUTATING_INJECTION = 28169,
GROBBULUS_SPELL_MUTATING_EXPLOSION = 28206,
GROBBULUS_SPELL_SLIME_SPRAY_10 = 28157,
GROBBULUS_SPELL_SLIME_SPRAY_25 = 54364,
GROBBULUS_SPELL_POISON_CLOUD_DAMAGE_AURA_10 = 28158,
GROBBULUS_SPELL_POISON_CLOUD_DAMAGE_AURA_25 = 54362,
GROBBULUS_SPELL_BERSERK = 26662,
GROBBULUS_SPELL_BOMBARD_SLIME = 28280
};
enum GrobbulusEmotes
{
GROBBULUS_EMOTE_SLIME = 0
};
enum GrobbulusEvents
{
GROBBULUS_EVENT_BERSERK = 1,
GROBBULUS_EVENT_POISON_CLOUD = 2,
GROBBULUS_EVENT_SLIME_SPRAY = 3,
GROBBULUS_EVENT_MUTATING_INJECTION = 4
};
enum GrobbulusMisc
{
GROBBULUS_NPC_FALLOUT_SLIME = 16290,
GROBBULUS_NPC_SEWAGE_SLIME = 16375,
GROBBULUS_NPC_STICHED_GIANT = 16025
};
class boss_grobbulus : public CreatureScript
{
public:
boss_grobbulus() : CreatureScript("boss_grobbulus") { }
CreatureAI* GetAI(Creature* pCreature) const override;
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_grobbulusAI>(pCreature);
}
struct boss_grobbulusAI : public BossAI
{
explicit boss_grobbulusAI(Creature* c);
explicit boss_grobbulusAI(Creature* c) : BossAI(c, BOSS_GROBBULUS), summons(me)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
uint32 dropSludgeTimer{};
void Reset() override;
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
dropSludgeTimer = 0;
}
void PullChamberAdds();
void PullChamberAdds()
{
std::list<Creature*> StichedGiants;
me->GetCreaturesWithEntryInRange(StichedGiants, 300.0f, GROBBULUS_NPC_STICHED_GIANT);
for (std::list<Creature*>::const_iterator itr = StichedGiants.begin(); itr != StichedGiants.end(); ++itr)
{
(*itr)->ToCreature()->AI()->AttackStart(me->GetVictim());
}
}
void JustEngagedWith(Unit* who) override;
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
PullChamberAdds();
me->SetInCombatWithZone();
events.ScheduleEvent(GROBBULUS_EVENT_POISON_CLOUD, 15s);
events.ScheduleEvent(GROBBULUS_EVENT_MUTATING_INJECTION, 20s);
events.ScheduleEvent(GROBBULUS_EVENT_SLIME_SPRAY, 10s);
events.ScheduleEvent(GROBBULUS_EVENT_BERSERK, RAID_MODE(720000, 540000));
}
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override;
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == RAID_MODE(GROBBULUS_SPELL_SLIME_SPRAY_10, GROBBULUS_SPELL_SLIME_SPRAY_25) && target->GetTypeId() == TYPEID_PLAYER)
{
me->SummonCreature(GROBBULUS_NPC_FALLOUT_SLIME, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
}
}
void JustSummoned(Creature* cr) override;
void JustSummoned(Creature* cr) override
{
if (cr->GetEntry() == GROBBULUS_NPC_FALLOUT_SLIME)
{
cr->SetInCombatWithZone();
}
summons.Summon(cr);
}
void SummonedCreatureDespawn(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override
{
summons.Despawn(summon);
}
void JustDied(Unit* killer) override;
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
void KilledUnit(Unit* who) override;
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override;
void UpdateAI(uint32 diff) override
{
dropSludgeTimer += diff;
if (!me->IsInCombat() && dropSludgeTimer >= 5000)
{
if (me->IsWithinDist3d(3178, -3305, 319, 5.0f) && !summons.HasEntry(GROBBULUS_NPC_SEWAGE_SLIME))
{
me->CastSpell(3128.96f + irand(-20, 20), -3312.96f + irand(-20, 20), 293.25f, GROBBULUS_SPELL_BOMBARD_SLIME, false);
}
dropSludgeTimer = 0;
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case GROBBULUS_EVENT_POISON_CLOUD:
me->CastSpell(me, GROBBULUS_SPELL_POISON_CLOUD, true);
events.Repeat(15s);
break;
case GROBBULUS_EVENT_BERSERK:
me->CastSpell(me, GROBBULUS_SPELL_BERSERK, true);
break;
case GROBBULUS_EVENT_SLIME_SPRAY:
Talk(GROBBULUS_EMOTE_SLIME);
me->CastSpell(me->GetVictim(), RAID_MODE(GROBBULUS_SPELL_SLIME_SPRAY_10, GROBBULUS_SPELL_SLIME_SPRAY_25), false);
events.Repeat(20s);
break;
case GROBBULUS_EVENT_MUTATING_INJECTION:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true, true, -GROBBULUS_SPELL_MUTATING_INJECTION))
{
me->CastSpell(target, GROBBULUS_SPELL_MUTATING_INJECTION, false);
}
events.RepeatEvent(6000 + uint32(120 * me->GetHealthPct()));
break;
}
DoMeleeAttackIfReady();
}
};
};
@@ -52,7 +180,10 @@ class boss_grobbulus_poison_cloud : public CreatureScript
public:
boss_grobbulus_poison_cloud() : CreatureScript("boss_grobbulus_poison_cloud") { }
CreatureAI* GetAI(Creature* pCreature) const override;
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_grobbulus_poison_cloudAI>(pCreature);
}
struct boss_grobbulus_poison_cloudAI : public NullCreatureAI
{
@@ -61,11 +192,36 @@ public:
uint32 sizeTimer{};
uint32 auraVisualTimer{};
void Reset() override;
void Reset() override
{
sizeTimer = 0;
auraVisualTimer = 1;
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f);
me->SetFaction(FACTION_BOOTY_BAY);
}
void KilledUnit(Unit* who) override;
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override;
void UpdateAI(uint32 diff) override
{
if (auraVisualTimer) // this has to be delayed to be visible
{
auraVisualTimer += diff;
if (auraVisualTimer >= 1000)
{
me->CastSpell(me, (me->GetMap()->Is25ManRaid() ? GROBBULUS_SPELL_POISON_CLOUD_DAMAGE_AURA_25 : GROBBULUS_SPELL_POISON_CLOUD_DAMAGE_AURA_10), true);
auraVisualTimer = 0;
}
}
sizeTimer += diff; // increase size to 15yd in 60 seconds, 0.00025 is the growth of size in 1ms
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f + (0.00025f * sizeTimer));
}
};
};
@@ -78,12 +234,33 @@ public:
{
PrepareSpellScript(spell_grobbulus_poison_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets);
void FilterTargets(std::list<WorldObject*>& targets)
{
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (GetCaster()->IsWithinDist3d(target, 0.0f))
{
tmplist.push_back(target);
}
}
targets.clear();
for (auto& itr : tmplist)
{
targets.push_back(itr);
}
}
void Register() override;
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_grobbulus_poison_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override;
SpellScript* GetSpellScript() const override
{
return new spell_grobbulus_poison_SpellScript();
}
};
class spell_grobbulus_mutating_injection : public SpellScriptLoader
@@ -95,14 +272,37 @@ class spell_grobbulus_mutating_injection : public SpellScriptLoader
{
PrepareAuraScript(spell_grobbulus_mutating_injection_AuraScript);
bool Validate(SpellInfo const* /*spellInfo*/) override;
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ GROBBULUS_SPELL_MUTATING_EXPLOSION });
}
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/);
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
switch (GetTargetApplication()->GetRemoveMode())
{
case AURA_REMOVE_BY_ENEMY_SPELL:
case AURA_REMOVE_BY_EXPIRE:
if (auto caster = GetCaster())
{
caster->CastSpell(GetTarget(), GROBBULUS_SPELL_MUTATING_EXPLOSION, true);
}
break;
default:
return;
}
}
void Register() override;
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_grobbulus_mutating_injection_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const override;
AuraScript* GetAuraScript() const override
{
return new spell_grobbulus_mutating_injection_AuraScript();
}
};
#endif

View File

@@ -18,216 +18,6 @@
#include "boss_heigan.h"
enum Says
{
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_TAUNT = 2,
EMOTE_DEATH = 3,
EMOTE_DANCE = 4,
EMOTE_DANCE_END = 5,
SAY_DANCE = 6
};
enum Spells
{
SPELL_SPELL_DISRUPTION = 29310,
SPELL_DECREPIT_FEVER_10 = 29998,
SPELL_DECREPIT_FEVER_25 = 55011,
SPELL_PLAGUE_CLOUD = 29350,
SPELL_TELEPORT_SELF = 30211
};
enum Events
{
EVENT_DISRUPTION = 1,
EVENT_DECEPIT_FEVER = 2,
EVENT_ERUPT_SECTION = 3,
EVENT_SWITCH_PHASE = 4,
EVENT_SAFETY_DANCE = 5,
EVENT_PLAGUE_CLOUD = 6
};
enum Misc
{
PHASE_SLOW_DANCE = 0,
PHASE_FAST_DANCE = 1
};
CreatureAI* boss_heigan::GetAI(Creature* pCreature) const
{
return GetNaxxramasAI<boss_heiganAI>(pCreature);
}
boss_heigan::boss_heiganAI::boss_heiganAI(Creature* c) : BossAI(c, BOSS_HEIGAN)
{
pInstance = me->GetInstanceScript();
}
void boss_heigan::boss_heiganAI::Reset()
{
BossAI::Reset();
events.Reset();
currentPhase = 0;
currentSection = 3;
moveRight = true;
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HEIGAN_ENTER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void boss_heigan::boss_heiganAI::KilledUnit(Unit* who)
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void boss_heigan::boss_heiganAI::JustDied(Unit* killer)
{
BossAI::JustDied(killer);
Talk(EMOTE_DEATH);
}
void boss_heigan::boss_heiganAI::JustEngagedWith(Unit* who)
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
Talk(SAY_AGGRO);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HEIGAN_ENTER_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
StartFightPhase(PHASE_SLOW_DANCE);
}
void boss_heigan::boss_heiganAI::StartFightPhase(uint8 phase)
{
currentSection = 3;
currentPhase = phase;
events.Reset();
if (phase == PHASE_SLOW_DANCE)
{
me->CastStop();
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
events.ScheduleEvent(EVENT_DISRUPTION, 12s, 15s);
events.ScheduleEvent(EVENT_DECEPIT_FEVER, 17s);
events.ScheduleEvent(EVENT_ERUPT_SECTION, 15s);
events.ScheduleEvent(EVENT_SWITCH_PHASE, 90s);
}
else // if (phase == PHASE_FAST_DANCE)
{
Talk(EMOTE_DANCE);
Talk(SAY_DANCE);
me->AttackStop();
me->StopMoving();
me->SetReactState(REACT_PASSIVE);
me->CastSpell(me, SPELL_TELEPORT_SELF, false);
me->SetFacingTo(2.40f);
events.ScheduleEvent(EVENT_PLAGUE_CLOUD, 1s);
events.ScheduleEvent(EVENT_ERUPT_SECTION, 7s);
events.ScheduleEvent(EVENT_SWITCH_PHASE, 45s);
}
events.ScheduleEvent(EVENT_SAFETY_DANCE, 5s);
}
bool boss_heigan::boss_heiganAI::IsInRoom(Unit* who)
{
if (who->GetPositionX() > 2826 || who->GetPositionX() < 2723 || who->GetPositionY() > -3641 || who->GetPositionY() < -3736)
{
if (who->GetGUID() == me->GetGUID())
EnterEvadeMode();
return false;
}
return true;
}
void boss_heigan::boss_heiganAI::UpdateAI(uint32 diff)
{
if (!IsInRoom(me))
return;
if (!UpdateVictim())
return;
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_DISRUPTION:
me->CastSpell(me, SPELL_SPELL_DISRUPTION, false);
events.Repeat(10s);
break;
case EVENT_DECEPIT_FEVER:
me->CastSpell(me, RAID_MODE(SPELL_DECREPIT_FEVER_10, SPELL_DECREPIT_FEVER_25), false);
events.Repeat(22s, 25s);
break;
case EVENT_PLAGUE_CLOUD:
me->CastSpell(me, SPELL_PLAGUE_CLOUD, false);
break;
case EVENT_SWITCH_PHASE:
if (currentPhase == PHASE_SLOW_DANCE)
{
StartFightPhase(PHASE_FAST_DANCE);
}
else
{
StartFightPhase(PHASE_SLOW_DANCE);
Talk(EMOTE_DANCE_END); // avoid play the emote on aggro
}
break;
case EVENT_ERUPT_SECTION:
if (pInstance)
{
pInstance->SetData(DATA_HEIGAN_ERUPTION, currentSection);
if (currentSection == 3)
{
moveRight = false;
}
else if (currentSection == 0)
{
moveRight = true;
}
moveRight ? currentSection++ : currentSection--;
}
if (currentPhase == PHASE_SLOW_DANCE)
{
Talk(SAY_TAUNT);
}
events.Repeat(currentPhase == PHASE_SLOW_DANCE ? 10s : 4s);
break;
case EVENT_SAFETY_DANCE:
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
if (IsInRoom(itr.GetSource()) && !itr.GetSource()->IsAlive())
{
pInstance->SetData(DATA_DANCE_FAIL, 0);
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
return;
}
}
events.Repeat(5s);
return;
}
}
DoMeleeAttackIfReady();
}
void AddSC_boss_heigan()
{
new boss_heigan();

View File

@@ -6,18 +6,58 @@
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum HeiganSays
{
HEIGAN_SAY_AGGRO = 0,
HEIGAN_SAY_SLAY = 1,
HEIGAN_SAY_TAUNT = 2,
HEIGAN_EMOTE_DEATH = 3,
HEIGAN_EMOTE_DANCE = 4,
HEIGAN_EMOTE_DANCE_END = 5,
HEIGAN_SAY_DANCE = 6
};
enum HeiganSpells
{
HEIGAN_SPELL_SPELL_DISRUPTION = 29310,
HEIGAN_SPELL_DECREPIT_FEVER_10 = 29998,
HEIGAN_SPELL_DECREPIT_FEVER_25 = 55011,
HEIGAN_SPELL_PLAGUE_CLOUD = 29350,
HEIGAN_SPELL_TELEPORT_SELF = 30211
};
enum HeiganEvents
{
HEIGAN_EVENT_DISRUPTION = 1,
HEIGAN_EVENT_DECEPIT_FEVER = 2,
HEIGAN_EVENT_ERUPT_SECTION = 3,
HEIGAN_EVENT_SWITCH_PHASE = 4,
HEIGAN_EVENT_SAFETY_DANCE = 5,
HEIGAN_EVENT_PLAGUE_CLOUD = 6
};
enum HeiganMisc
{
HEIGAN_PHASE_SLOW_DANCE = 0,
HEIGAN_PHASE_FAST_DANCE = 1
};
class boss_heigan : public CreatureScript
{
public:
boss_heigan() : CreatureScript("boss_heigan") { }
CreatureAI* GetAI(Creature* pCreature) const override;
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_heiganAI>(pCreature);
}
struct boss_heiganAI : public BossAI
{
explicit boss_heiganAI(Creature* c);
explicit boss_heiganAI(Creature* c) : BossAI(c, BOSS_HEIGAN)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
@@ -25,19 +65,170 @@ public:
uint8 currentSection{};
bool moveRight{};
void Reset() override;
void Reset() override
{
BossAI::Reset();
events.Reset();
currentPhase = 0;
currentSection = 3;
moveRight = true;
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HEIGAN_ENTER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void KilledUnit(Unit* who) override;
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
void JustDied(Unit* killer) override;
Talk(HEIGAN_SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustEngagedWith(Unit* who) override;
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(HEIGAN_EMOTE_DEATH);
}
void StartFightPhase(uint8 phase);
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
Talk(HEIGAN_SAY_AGGRO);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HEIGAN_ENTER_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
StartFightPhase(HEIGAN_PHASE_SLOW_DANCE);
}
bool IsInRoom(Unit* who);
void StartFightPhase(uint8 phase)
{
currentSection = 3;
currentPhase = phase;
events.Reset();
if (phase == HEIGAN_PHASE_SLOW_DANCE)
{
me->CastStop();
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat();
events.ScheduleEvent(HEIGAN_EVENT_DISRUPTION, 12s, 15s);
events.ScheduleEvent(HEIGAN_EVENT_DECEPIT_FEVER, 17s);
events.ScheduleEvent(HEIGAN_EVENT_ERUPT_SECTION, 15s);
events.ScheduleEvent(HEIGAN_EVENT_SWITCH_PHASE, 90s);
}
else // if (phase == HEIGAN_PHASE_FAST_DANCE)
{
Talk(HEIGAN_EMOTE_DANCE);
Talk(HEIGAN_SAY_DANCE);
me->AttackStop();
me->StopMoving();
me->SetReactState(REACT_PASSIVE);
me->CastSpell(me, HEIGAN_SPELL_TELEPORT_SELF, false);
me->SetFacingTo(2.40f);
events.ScheduleEvent(HEIGAN_EVENT_PLAGUE_CLOUD, 1s);
events.ScheduleEvent(HEIGAN_EVENT_ERUPT_SECTION, 7s);
events.ScheduleEvent(HEIGAN_EVENT_SWITCH_PHASE, 45s);
}
events.ScheduleEvent(HEIGAN_EVENT_SAFETY_DANCE, 5s);
}
void UpdateAI(uint32 diff) override;
bool IsInRoom(Unit* who)
{
if (who->GetPositionX() > 2826 || who->GetPositionX() < 2723 || who->GetPositionY() > -3641 || who->GetPositionY() < -3736)
{
if (who->GetGUID() == me->GetGUID())
EnterEvadeMode();
return false;
}
return true;
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom(me))
return;
if (!UpdateVictim())
return;
events.Update(diff);
switch (events.ExecuteEvent())
{
case HEIGAN_EVENT_DISRUPTION:
me->CastSpell(me, HEIGAN_SPELL_SPELL_DISRUPTION, false);
events.Repeat(10s);
break;
case HEIGAN_EVENT_DECEPIT_FEVER:
me->CastSpell(me, RAID_MODE(HEIGAN_SPELL_DECREPIT_FEVER_10, HEIGAN_SPELL_DECREPIT_FEVER_25), false);
events.Repeat(22s, 25s);
break;
case HEIGAN_EVENT_PLAGUE_CLOUD:
me->CastSpell(me, HEIGAN_SPELL_PLAGUE_CLOUD, false);
break;
case HEIGAN_EVENT_SWITCH_PHASE:
if (currentPhase == HEIGAN_PHASE_SLOW_DANCE)
{
StartFightPhase(HEIGAN_PHASE_FAST_DANCE);
}
else
{
StartFightPhase(HEIGAN_PHASE_SLOW_DANCE);
Talk(HEIGAN_EMOTE_DANCE_END); // avoid play the emote on aggro
}
break;
case HEIGAN_EVENT_ERUPT_SECTION:
if (pInstance)
{
pInstance->SetData(DATA_HEIGAN_ERUPTION, currentSection);
if (currentSection == 3)
{
moveRight = false;
}
else if (currentSection == 0)
{
moveRight = true;
}
moveRight ? currentSection++ : currentSection--;
}
if (currentPhase == HEIGAN_PHASE_SLOW_DANCE)
{
Talk(HEIGAN_SAY_TAUNT);
}
events.Repeat(currentPhase == HEIGAN_PHASE_SLOW_DANCE ? 10s : 4s);
break;
case HEIGAN_EVENT_SAFETY_DANCE:
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
if (IsInRoom(itr.GetSource()) && !itr.GetSource()->IsAlive())
{
pInstance->SetData(DATA_DANCE_FAIL, 0);
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
return;
}
}
events.Repeat(5s);
return;
}
}
DoMeleeAttackIfReady();
}
};
};

View File

@@ -15,733 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Yells
{
SAY_ANSWER_REQUEST = 3,
SAY_TAUNT = 6,
SAY_AGGRO = 7,
SAY_SLAY = 8,
SAY_DEATH = 9,
SAY_CHAIN = 10,
SAY_FROST_BLAST = 11,
SAY_REQUEST_AID = 12,
EMOTE_PHASE_TWO = 13,
SAY_SUMMON_MINIONS = 14,
SAY_SPECIAL = 15,
EMOTE_GUARDIAN_FLEE = 0,
EMOTE_GUARDIAN_APPEAR = 1
};
enum Spells
{
// Kel'Thzuad
SPELL_FROST_BOLT_SINGLE_10 = 28478,
SPELL_FROST_BOLT_SINGLE_25 = 55802,
SPELL_FROST_BOLT_MULTI_10 = 28479,
SPELL_FROST_BOLT_MULTI_25 = 55807,
SPELL_SHADOW_FISURE = 27810,
SPELL_VOID_BLAST = 27812,
SPELL_DETONATE_MANA = 27819,
SPELL_MANA_DETONATION_DAMAGE = 27820,
SPELL_FROST_BLAST = 27808,
SPELL_CHAINS_OF_KELTHUZAD = 28410, // 28408 script effect
SPELL_BERSERK = 28498,
SPELL_KELTHUZAD_CHANNEL = 29423,
// Minions
SPELL_FRENZY = 28468,
SPELL_MORTAL_WOUND = 28467,
SPELL_BLOOD_TAP = 28470
};
enum Misc
{
NPC_SOLDIER_OF_THE_FROZEN_WASTES = 16427,
NPC_UNSTOPPABLE_ABOMINATION = 16428,
NPC_SOUL_WEAVER = 16429,
NPC_GUARDIAN_OF_ICECROWN = 16441,
ACTION_CALL_HELP_ON = 1,
ACTION_CALL_HELP_OFF = 2,
ACTION_SECOND_PHASE = 3,
ACTION_GUARDIANS_OFF = 4
};
enum Event
{
// Kel'Thuzad
EVENT_SUMMON_SOLDIER = 1,
EVENT_SUMMON_UNSTOPPABLE_ABOMINATION = 2,
EVENT_SUMMON_SOUL_WEAVER = 3,
EVENT_PHASE_2 = 4,
EVENT_FROST_BOLT_SINGLE = 5,
EVENT_FROST_BOLT_MULTI = 6,
EVENT_DETONATE_MANA = 7,
EVENT_PHASE_3 = 8,
EVENT_P3_LICH_KING_SAY = 9,
EVENT_SHADOW_FISSURE = 10,
EVENT_FROST_BLAST = 11,
EVENT_CHAINS = 12,
EVENT_SUMMON_GUARDIAN_OF_ICECROWN = 13,
EVENT_FLOOR_CHANGE = 14,
EVENT_ENRAGE = 15,
EVENT_SPAWN_POOL = 16,
// Minions
EVENT_MINION_FRENZY = 17,
EVENT_MINION_MORTAL_WOUND = 18,
EVENT_MINION_BLOOD_TAP = 19
};
const Position SummonGroups[12] =
{
// Portals
{3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR
{3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE
{3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR
{3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR
{3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE
{3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR
// Middle
{3769.34f, -5071.80f, 143.2082f, 3.658f},
{3729.78f, -5043.56f, 143.3867f, 4.475f},
{3682.75f, -5055.26f, 143.1848f, 5.295f},
{3752.58f, -5161.82f, 143.2944f, 2.126f},
{3702.83f, -5171.70f, 143.4356f, 1.305f},
{3665.30f, -5141.55f, 143.1846f, 0.566f}
};
const Position SpawnPool[7] =
{
// Portals
{3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR
{3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE
{3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR
{3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR
{3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE
{3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR
{3651.729980f, -5092.620117f, 143.380005f, 6.050000f} // GATE
};
class boss_kelthuzad : public CreatureScript
{
public:
boss_kelthuzad() : CreatureScript("boss_kelthuzad") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_kelthuzadAI>(pCreature);
}
struct boss_kelthuzadAI : public BossAI
{
explicit boss_kelthuzadAI(Creature* c) : BossAI(c, BOSS_KELTHUZAD), summons(me)
{
pInstance = me->GetInstanceScript();
_justSpawned = true;
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
bool _justSpawned;
float NormalizeOrientation(float o)
{
return std::fmod(o, 2.0f * static_cast<float>(M_PI)); // Only positive values will be passed
}
void SpawnHelpers()
{
// spawn at gate
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3656.19f, -5093.78f, 143.33f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo center
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3657.94f, -5087.68f, 143.60f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo left
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3655.48f, -5100.05f, 143.53f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo right
me->SummonCreature(NPC_SOUL_WEAVER, 3651.73f, -5092.62f, 143.38f, 6.05, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // soul behind
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3660.17f, -5092.45f, 143.37f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.39f, -5096.21f, 143.29f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front right
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.29f, -5090.19f, 143.48f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske left left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3657.43f, -5098.03f, 143.41f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3654.36f, -5090.51f, 143.48f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske behind left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3653.35f, -5095.91f, 143.41f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right
// 6 rooms, 8 soldiers, 3 abominations and 1 weaver in each room | middle positions in table starts from 6
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 0; j < 8; ++j)
{
float angle = M_PI * 2 / 8 * j;
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 1; j < 4; ++j)
{
float dist = j == 2 ? 0.0f : 8.0f; // second in middle
float angle = SummonGroups[i].GetOrientation() + M_PI * 2 / 4 * j;
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, SummonGroups[i].GetPositionX() + dist * cos(angle), SummonGroups[i].GetPositionY() + dist * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 0; j < 1; ++j)
{
float angle = SummonGroups[i].GetOrientation() + M_PI;
me->SummonCreature(NPC_SOUL_WEAVER, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ() + 0.5f, SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
}
void SummonHelper(uint32 entry, uint32 count)
{
for (uint8 i = 0; i < count; ++i)
{
if (Creature* cr = me->SummonCreature(entry, SpawnPool[urand(0, 6)], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000))
{
if (Unit* target = SelectTargetFromPlayerList(100.0f))
{
cr->AI()->DoAction(ACTION_CALL_HELP_OFF);
cr->AI()->AttackStart(target);
}
}
}
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetReactState(REACT_AGGRESSIVE);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
go->SetPhaseMask(1, true);
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
if(!_justSpawned) // Don't open the door if we just spawned and are still doing the conversation
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
_justSpawned = false;
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_1)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_2)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_3)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_4)))
{
go->SetGoState(GO_STATE_READY);
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
ScriptedAI::EnterEvadeMode(why);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DoAction(ACTION_GUARDIANS_OFF);
if (Creature* guardian = summons.GetCreatureWithEntry(NPC_GUARDIAN_OF_ICECROWN))
{
guardian->AI()->Talk(EMOTE_GUARDIAN_FLEE);
}
Talk(SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!me->IsInCombat() && who->GetTypeId() == TYPEID_PLAYER && who->IsAlive() && me->GetDistance(who) <= 50.0f)
AttackStart(who);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_SUMMON_MINIONS);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
me->RemoveAllAttackers();
me->SetTarget();
me->SetReactState(REACT_PASSIVE);
me->CastSpell(me, SPELL_KELTHUZAD_CHANNEL, false);
events.ScheduleEvent(EVENT_SPAWN_POOL, 5s);
events.ScheduleEvent(EVENT_SUMMON_SOLDIER, 6400ms);
events.ScheduleEvent(EVENT_SUMMON_UNSTOPPABLE_ABOMINATION, 10s);
events.ScheduleEvent(EVENT_SUMMON_SOUL_WEAVER, 12s);
events.ScheduleEvent(EVENT_PHASE_2, 228s);
events.ScheduleEvent(EVENT_ENRAGE, 15min);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s);
go->SetGoState(GO_STATE_ACTIVE);
}
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
if (!cr->IsInCombat())
{
cr->GetMotionMaster()->MoveRandom(5);
}
if (cr->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
cr->SetHomePosition(cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ(), cr->GetOrientation());
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (!me->HasAura(SPELL_KELTHUZAD_CHANNEL))
{
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
switch (events.ExecuteEvent())
{
case EVENT_FLOOR_CHANGE:
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s);
go->SetGoState(GO_STATE_READY);
go->SetPhaseMask(2, true);
}
}
break;
case EVENT_SPAWN_POOL:
SpawnHelpers();
break;
case EVENT_SUMMON_SOLDIER:
SummonHelper(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 1);
events.Repeat(3100ms);
break;
case EVENT_SUMMON_UNSTOPPABLE_ABOMINATION:
SummonHelper(NPC_UNSTOPPABLE_ABOMINATION, 1);
events.Repeat(18s + 500ms);
break;
case EVENT_SUMMON_SOUL_WEAVER:
SummonHelper(NPC_SOUL_WEAVER, 1);
events.Repeat(30s);
break;
case EVENT_PHASE_2:
Talk(EMOTE_PHASE_TWO);
Talk(SAY_AGGRO);
events.Reset();
summons.DoAction(ACTION_SECOND_PHASE);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
me->GetMotionMaster()->MoveChase(me->GetVictim());
me->RemoveAura(SPELL_KELTHUZAD_CHANNEL);
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_FROST_BOLT_SINGLE, 2s, 10s);
events.ScheduleEvent(EVENT_FROST_BOLT_MULTI, 15s, 30s);
events.ScheduleEvent(EVENT_DETONATE_MANA, 30s);
events.ScheduleEvent(EVENT_PHASE_3, 1s);
events.ScheduleEvent(EVENT_SHADOW_FISSURE, 25s);
events.ScheduleEvent(EVENT_FROST_BLAST, 45s);
if (Is25ManRaid())
{
events.ScheduleEvent(EVENT_CHAINS, 90s);
}
break;
case EVENT_ENRAGE:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_FROST_BOLT_SINGLE:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_FROST_BOLT_SINGLE_10, SPELL_FROST_BOLT_SINGLE_25), false);
events.Repeat(2s, 10s);
break;
case EVENT_FROST_BOLT_MULTI:
me->CastSpell(me, RAID_MODE(SPELL_FROST_BOLT_MULTI_10, SPELL_FROST_BOLT_MULTI_25), false);
events.Repeat(15s, 30s);
break;
case EVENT_SHADOW_FISSURE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
me->CastSpell(target, SPELL_SHADOW_FISURE, false);
}
events.Repeat(25s);
break;
case EVENT_FROST_BLAST:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, RAID_MODE(1, 0), 0, true))
{
me->CastSpell(target, SPELL_FROST_BLAST, false);
}
Talk(SAY_FROST_BLAST);
events.Repeat(45s);
break;
case EVENT_CHAINS:
for (uint8 i = 0; i < 3; ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 200, true, true, -SPELL_CHAINS_OF_KELTHUZAD))
{
me->CastSpell(target, SPELL_CHAINS_OF_KELTHUZAD, true);
}
}
Talk(SAY_CHAIN);
events.Repeat(90s);
break;
case EVENT_DETONATE_MANA:
{
std::vector<Unit*> unitList;
ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList();
for (auto itr : threatList)
{
if (itr->getTarget()->GetTypeId() == TYPEID_PLAYER
&& itr->getTarget()->getPowerType() == POWER_MANA
&& itr->getTarget()->GetPower(POWER_MANA))
{
unitList.push_back(itr->getTarget());
}
}
if (!unitList.empty())
{
auto itr = unitList.begin();
advance(itr, urand(0, unitList.size() - 1));
me->CastSpell(*itr, SPELL_DETONATE_MANA, false);
Talk(SAY_SPECIAL);
}
events.Repeat(30s);
break;
}
case EVENT_PHASE_3:
if (me->HealthBelowPct(45))
{
Talk(SAY_REQUEST_AID);
events.DelayEvents(5500ms);
events.ScheduleEvent(EVENT_P3_LICH_KING_SAY, 5s);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_1)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_2)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_3)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_4)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
break;
}
events.Repeat(1s);
break;
case EVENT_P3_LICH_KING_SAY:
if (pInstance)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_LICH_KING_BOSS)))
{
cr->AI()->Talk(SAY_ANSWER_REQUEST);
}
}
for (uint8 i = 0 ; i < RAID_MODE(2, 4); ++i)
{
events.ScheduleEvent(EVENT_SUMMON_GUARDIAN_OF_ICECROWN, 10000 + (i * 5000));
}
break;
case EVENT_SUMMON_GUARDIAN_OF_ICECROWN:
if (Creature* cr = me->SummonCreature(NPC_GUARDIAN_OF_ICECROWN, SpawnPool[RAND(0, 1, 3, 4)]))
{
cr->AI()->Talk(EMOTE_GUARDIAN_APPEAR);
cr->AI()->AttackStart(me->GetVictim());
}
break;
}
if (!me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
DoMeleeAttackIfReady();
}
};
};
class boss_kelthuzad_minion : public CreatureScript
{
public:
boss_kelthuzad_minion() : CreatureScript("boss_kelthuzad_minion") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_kelthuzad_minionAI>(pCreature);
}
struct boss_kelthuzad_minionAI : public ScriptedAI
{
explicit boss_kelthuzad_minionAI(Creature* c) : ScriptedAI(c) { }
EventMap events;
bool callHelp{};
void Reset() override
{
me->SetNoCallAssistance(true);
callHelp = true;
events.Reset();
}
void DoAction(int32 param) override
{
if (param == ACTION_CALL_HELP_ON)
{
callHelp = true;
}
else if (param == ACTION_CALL_HELP_OFF)
{
callHelp = false;
}
else if (param == ACTION_SECOND_PHASE)
{
if (!me->IsInCombat())
{
me->DespawnOrUnsummon(500);
}
}
if (param == ACTION_GUARDIANS_OFF)
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAllAuras();
EnterEvadeMode();
me->SetPosition(me->GetHomePosition());
}
}
void MoveInLineOfSight(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER && !who->IsPet())
return;
ScriptedAI::MoveInLineOfSight(who);
}
void JustDied(Unit* /*killer*/) override
{
if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_ABOMINATION_KILLED, 0);
}
}
void AttackStart(Unit* who) override
{
ScriptedAI::AttackStart(who);
if (callHelp)
{
std::list<Creature*> targets;
me->GetCreaturesWithEntryInRange(targets, 15.0f, me->GetEntry());
for (std::list<Creature*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
{
if ((*itr)->GetGUID() != me->GetGUID())
{
(*itr)->ToCreature()->AI()->DoAction(ACTION_CALL_HELP_OFF);
(*itr)->ToCreature()->AI()->AttackStart(who);
}
}
}
if (me->GetEntry() != NPC_UNSTOPPABLE_ABOMINATION && me->GetEntry() != NPC_GUARDIAN_OF_ICECROWN)
{
me->AddThreat(who, 1000000.0f);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
me->SetInCombatWithZone();
if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION)
{
events.ScheduleEvent(EVENT_MINION_FRENZY, 1s);
events.ScheduleEvent(EVENT_MINION_MORTAL_WOUND, 5s);
}
else if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
events.ScheduleEvent(EVENT_MINION_BLOOD_TAP, 15s);
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustReachedHome() override
{
if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
me->DespawnOrUnsummon();
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MINION_MORTAL_WOUND:
me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false);
events.Repeat(15s);
break;
case EVENT_MINION_FRENZY:
if (me->HealthBelowPct(35))
{
me->CastSpell(me, SPELL_FRENZY, true);
break;
}
events.Repeat(1s);
break;
case EVENT_MINION_BLOOD_TAP:
me->CastSpell(me->GetVictim(), SPELL_BLOOD_TAP, false);
events.Repeat(15s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_kelthuzad_frost_blast : public SpellScriptLoader
{
public:
spell_kelthuzad_frost_blast() : SpellScriptLoader("spell_kelthuzad_frost_blast") { }
class spell_kelthuzad_frost_blast_SpellScript : public SpellScript
{
PrepareSpellScript(spell_kelthuzad_frost_blast_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
if (!caster || !caster->ToCreature())
return;
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (!target->ToUnit()->HasAura(SPELL_FROST_BLAST))
{
tmplist.push_back(target);
}
}
targets.clear();
for (auto& itr : tmplist)
{
targets.push_back(itr);
}
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kelthuzad_frost_blast_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_kelthuzad_frost_blast_SpellScript();
}
};
class spell_kelthuzad_detonate_mana : public SpellScriptLoader
{
public:
spell_kelthuzad_detonate_mana() : SpellScriptLoader("spell_kelthuzad_detonate_mana") { }
class spell_kelthuzad_detonate_mana_AuraScript : public AuraScript
{
PrepareAuraScript(spell_kelthuzad_detonate_mana_AuraScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_MANA_DETONATION_DAMAGE });
}
void HandleScript(AuraEffect const* aurEff)
{
PreventDefaultAction();
Unit* target = GetTarget();
if (auto mana = int32(target->GetMaxPower(POWER_MANA) / 10))
{
mana = target->ModifyPower(POWER_MANA, -mana);
target->CastCustomSpell(SPELL_MANA_DETONATION_DAMAGE, SPELLVALUE_BASE_POINT0, -mana * 10, target, true, nullptr, aurEff);
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_kelthuzad_detonate_mana_AuraScript::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_kelthuzad_detonate_mana_AuraScript();
}
};
#include "boss_kelthuzad.h"
void AddSC_boss_kelthuzad()
{

View File

@@ -0,0 +1,732 @@
#ifndef BOSS_KELTHUZAD_H_
#define BOSS_KELTHUZAD_H_
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum KelthuzadYells
{
KELTHUZAD_SAY_ANSWER_REQUEST = 3,
KELTHUZAD_SAY_TAUNT = 6,
KELTHUZAD_SAY_AGGRO = 7,
KELTHUZAD_SAY_SLAY = 8,
KELTHUZAD_SAY_DEATH = 9,
KELTHUZAD_SAY_CHAIN = 10,
KELTHUZAD_SAY_FROST_BLAST = 11,
KELTHUZAD_SAY_REQUEST_AID = 12,
KELTHUZAD_EMOTE_PHASE_TWO = 13,
KELTHUZAD_SAY_SUMMON_MINIONS = 14,
KELTHUZAD_SAY_SPECIAL = 15,
KELTHUZAD_EMOTE_GUARDIAN_FLEE = 0,
KELTHUZAD_EMOTE_GUARDIAN_APPEAR = 1
};
enum KelthuzadSpells
{
// Kel'Thzuad
SPELL_FROST_BOLT_SINGLE_10 = 28478,
SPELL_FROST_BOLT_SINGLE_25 = 55802,
SPELL_FROST_BOLT_MULTI_10 = 28479,
SPELL_FROST_BOLT_MULTI_25 = 55807,
SPELL_SHADOW_FISURE = 27810,
SPELL_VOID_BLAST = 27812,
SPELL_DETONATE_MANA = 27819,
SPELL_MANA_DETONATION_DAMAGE = 27820,
SPELL_FROST_BLAST = 27808,
SPELL_CHAINS_OF_KELTHUZAD = 28410, // 28408 script effect
KELTHUZAD_SPELL_BERSERK = 28498,
SPELL_KELTHUZAD_CHANNEL = 29423,
// Minions
KELTHUZAD_SPELL_FRENZY = 28468,
KELTHUZAD_SPELL_MORTAL_WOUND = 28467,
SPELL_BLOOD_TAP = 28470
};
enum KelthuzadMisc
{
NPC_SOLDIER_OF_THE_FROZEN_WASTES = 16427,
NPC_UNSTOPPABLE_ABOMINATION = 16428,
NPC_SOUL_WEAVER = 16429,
NPC_GUARDIAN_OF_ICECROWN = 16441,
ACTION_CALL_HELP_ON = 1,
ACTION_CALL_HELP_OFF = 2,
ACTION_SECOND_PHASE = 3,
ACTION_GUARDIANS_OFF = 4
};
enum KelthuzadEvent
{
// Kel'Thuzad
EVENT_SUMMON_SOLDIER = 1,
EVENT_SUMMON_UNSTOPPABLE_ABOMINATION = 2,
EVENT_SUMMON_SOUL_WEAVER = 3,
KELTHUZAD_EVENT_PHASE_2 = 4,
EVENT_FROST_BOLT_SINGLE = 5,
EVENT_FROST_BOLT_MULTI = 6,
EVENT_DETONATE_MANA = 7,
KELTHUZAD_EVENT_PHASE_3 = 8,
EVENT_P3_LICH_KING_SAY = 9,
EVENT_SHADOW_FISSURE = 10,
EVENT_FROST_BLAST = 11,
EVENT_CHAINS = 12,
EVENT_SUMMON_GUARDIAN_OF_ICECROWN = 13,
EVENT_FLOOR_CHANGE = 14,
KELTHUZAD_EVENT_ENRAGE = 15,
EVENT_SPAWN_POOL = 16,
// Minions
EVENT_MINION_FRENZY = 17,
EVENT_MINION_MORTAL_WOUND = 18,
EVENT_MINION_BLOOD_TAP = 19
};
const Position SummonGroups[12] =
{
// Portals
{3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR
{3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE
{3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR
{3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR
{3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE
{3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR
// Middle
{3769.34f, -5071.80f, 143.2082f, 3.658f},
{3729.78f, -5043.56f, 143.3867f, 4.475f},
{3682.75f, -5055.26f, 143.1848f, 5.295f},
{3752.58f, -5161.82f, 143.2944f, 2.126f},
{3702.83f, -5171.70f, 143.4356f, 1.305f},
{3665.30f, -5141.55f, 143.1846f, 0.566f}
};
const Position SpawnPool[7] =
{
// Portals
{3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR
{3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE
{3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR
{3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR
{3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE
{3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR
{3651.729980f, -5092.620117f, 143.380005f, 6.050000f} // GATE
};
class boss_kelthuzad : public CreatureScript
{
public:
boss_kelthuzad() : CreatureScript("boss_kelthuzad") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_kelthuzadAI>(pCreature);
}
struct boss_kelthuzadAI : public BossAI
{
explicit boss_kelthuzadAI(Creature* c) : BossAI(c, BOSS_KELTHUZAD), summons(me)
{
pInstance = me->GetInstanceScript();
_justSpawned = true;
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
bool _justSpawned;
float NormalizeOrientation(float o)
{
return std::fmod(o, 2.0f * static_cast<float>(M_PI)); // Only positive values will be passed
}
void SpawnHelpers()
{
// spawn at gate
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3656.19f, -5093.78f, 143.33f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo center
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3657.94f, -5087.68f, 143.60f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo left
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3655.48f, -5100.05f, 143.53f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo right
me->SummonCreature(NPC_SOUL_WEAVER, 3651.73f, -5092.62f, 143.38f, 6.05, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // soul behind
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3660.17f, -5092.45f, 143.37f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.39f, -5096.21f, 143.29f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front right
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.29f, -5090.19f, 143.48f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske left left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3657.43f, -5098.03f, 143.41f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3654.36f, -5090.51f, 143.48f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske behind left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3653.35f, -5095.91f, 143.41f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right
// 6 rooms, 8 soldiers, 3 abominations and 1 weaver in each room | middle positions in table starts from 6
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 0; j < 8; ++j)
{
float angle = M_PI * 2 / 8 * j;
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 1; j < 4; ++j)
{
float dist = j == 2 ? 0.0f : 8.0f; // second in middle
float angle = SummonGroups[i].GetOrientation() + M_PI * 2 / 4 * j;
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, SummonGroups[i].GetPositionX() + dist * cos(angle), SummonGroups[i].GetPositionY() + dist * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 0; j < 1; ++j)
{
float angle = SummonGroups[i].GetOrientation() + M_PI;
me->SummonCreature(NPC_SOUL_WEAVER, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ() + 0.5f, SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
}
void SummonHelper(uint32 entry, uint32 count)
{
for (uint8 i = 0; i < count; ++i)
{
if (Creature* cr = me->SummonCreature(entry, SpawnPool[urand(0, 6)], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000))
{
if (Unit* target = SelectTargetFromPlayerList(100.0f))
{
cr->AI()->DoAction(ACTION_CALL_HELP_OFF);
cr->AI()->AttackStart(target);
}
}
}
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetReactState(REACT_AGGRESSIVE);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
go->SetPhaseMask(1, true);
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
if(!_justSpawned) // Don't open the door if we just spawned and are still doing the conversation
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
_justSpawned = false;
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_1)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_2)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_3)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_4)))
{
go->SetGoState(GO_STATE_READY);
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
ScriptedAI::EnterEvadeMode(why);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(KELTHUZAD_SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DoAction(ACTION_GUARDIANS_OFF);
if (Creature* guardian = summons.GetCreatureWithEntry(NPC_GUARDIAN_OF_ICECROWN))
{
guardian->AI()->Talk(KELTHUZAD_EMOTE_GUARDIAN_FLEE);
}
Talk(KELTHUZAD_SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!me->IsInCombat() && who->GetTypeId() == TYPEID_PLAYER && who->IsAlive() && me->GetDistance(who) <= 50.0f)
AttackStart(who);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(KELTHUZAD_SAY_SUMMON_MINIONS);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
me->RemoveAllAttackers();
me->SetTarget();
me->SetReactState(REACT_PASSIVE);
me->CastSpell(me, SPELL_KELTHUZAD_CHANNEL, false);
events.ScheduleEvent(EVENT_SPAWN_POOL, 5s);
events.ScheduleEvent(EVENT_SUMMON_SOLDIER, 6400ms);
events.ScheduleEvent(EVENT_SUMMON_UNSTOPPABLE_ABOMINATION, 10s);
events.ScheduleEvent(EVENT_SUMMON_SOUL_WEAVER, 12s);
events.ScheduleEvent(KELTHUZAD_EVENT_PHASE_2, 228s);
events.ScheduleEvent(KELTHUZAD_EVENT_ENRAGE, 15min);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s);
go->SetGoState(GO_STATE_ACTIVE);
}
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
if (!cr->IsInCombat())
{
cr->GetMotionMaster()->MoveRandom(5);
}
if (cr->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
cr->SetHomePosition(cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ(), cr->GetOrientation());
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (!me->HasAura(SPELL_KELTHUZAD_CHANNEL))
{
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
switch (events.ExecuteEvent())
{
case EVENT_FLOOR_CHANGE:
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s);
go->SetGoState(GO_STATE_READY);
go->SetPhaseMask(2, true);
}
}
break;
case EVENT_SPAWN_POOL:
SpawnHelpers();
break;
case EVENT_SUMMON_SOLDIER:
SummonHelper(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 1);
events.Repeat(3100ms);
break;
case EVENT_SUMMON_UNSTOPPABLE_ABOMINATION:
SummonHelper(NPC_UNSTOPPABLE_ABOMINATION, 1);
events.Repeat(18s + 500ms);
break;
case EVENT_SUMMON_SOUL_WEAVER:
SummonHelper(NPC_SOUL_WEAVER, 1);
events.Repeat(30s);
break;
case KELTHUZAD_EVENT_PHASE_2:
Talk(KELTHUZAD_EMOTE_PHASE_TWO);
Talk(KELTHUZAD_SAY_AGGRO);
events.Reset();
summons.DoAction(ACTION_SECOND_PHASE);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
me->GetMotionMaster()->MoveChase(me->GetVictim());
me->RemoveAura(SPELL_KELTHUZAD_CHANNEL);
me->SetReactState(REACT_AGGRESSIVE);
events.ScheduleEvent(EVENT_FROST_BOLT_SINGLE, 2s, 10s);
events.ScheduleEvent(EVENT_FROST_BOLT_MULTI, 15s, 30s);
events.ScheduleEvent(EVENT_DETONATE_MANA, 30s);
events.ScheduleEvent(KELTHUZAD_EVENT_PHASE_3, 1s);
events.ScheduleEvent(EVENT_SHADOW_FISSURE, 25s);
events.ScheduleEvent(EVENT_FROST_BLAST, 45s);
if (Is25ManRaid())
{
events.ScheduleEvent(EVENT_CHAINS, 90s);
}
break;
case KELTHUZAD_EVENT_ENRAGE:
me->CastSpell(me, KELTHUZAD_SPELL_BERSERK, true);
break;
case EVENT_FROST_BOLT_SINGLE:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_FROST_BOLT_SINGLE_10, SPELL_FROST_BOLT_SINGLE_25), false);
events.Repeat(2s, 10s);
break;
case EVENT_FROST_BOLT_MULTI:
me->CastSpell(me, RAID_MODE(SPELL_FROST_BOLT_MULTI_10, SPELL_FROST_BOLT_MULTI_25), false);
events.Repeat(15s, 30s);
break;
case EVENT_SHADOW_FISSURE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
me->CastSpell(target, SPELL_SHADOW_FISURE, false);
}
events.Repeat(25s);
break;
case EVENT_FROST_BLAST:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, RAID_MODE(1, 0), 0, true))
{
me->CastSpell(target, SPELL_FROST_BLAST, false);
}
Talk(KELTHUZAD_SAY_FROST_BLAST);
events.Repeat(45s);
break;
case EVENT_CHAINS:
for (uint8 i = 0; i < 3; ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 200, true, true, -SPELL_CHAINS_OF_KELTHUZAD))
{
me->CastSpell(target, SPELL_CHAINS_OF_KELTHUZAD, true);
}
}
Talk(KELTHUZAD_SAY_CHAIN);
events.Repeat(90s);
break;
case EVENT_DETONATE_MANA:
{
std::vector<Unit*> unitList;
ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList();
for (auto itr : threatList)
{
if (itr->getTarget()->GetTypeId() == TYPEID_PLAYER
&& itr->getTarget()->getPowerType() == POWER_MANA
&& itr->getTarget()->GetPower(POWER_MANA))
{
unitList.push_back(itr->getTarget());
}
}
if (!unitList.empty())
{
auto itr = unitList.begin();
advance(itr, urand(0, unitList.size() - 1));
me->CastSpell(*itr, SPELL_DETONATE_MANA, false);
Talk(KELTHUZAD_SAY_SPECIAL);
}
events.Repeat(30s);
break;
}
case KELTHUZAD_EVENT_PHASE_3:
if (me->HealthBelowPct(45))
{
Talk(KELTHUZAD_SAY_REQUEST_AID);
events.DelayEvents(5500ms);
events.ScheduleEvent(EVENT_P3_LICH_KING_SAY, 5s);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_1)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_2)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_3)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_4)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
break;
}
events.Repeat(1s);
break;
case EVENT_P3_LICH_KING_SAY:
if (pInstance)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_LICH_KING_BOSS)))
{
cr->AI()->Talk(KELTHUZAD_SAY_ANSWER_REQUEST);
}
}
for (uint8 i = 0 ; i < RAID_MODE(2, 4); ++i)
{
events.ScheduleEvent(EVENT_SUMMON_GUARDIAN_OF_ICECROWN, 10000 + (i * 5000));
}
break;
case EVENT_SUMMON_GUARDIAN_OF_ICECROWN:
if (Creature* cr = me->SummonCreature(NPC_GUARDIAN_OF_ICECROWN, SpawnPool[RAND(0, 1, 3, 4)]))
{
cr->AI()->Talk(KELTHUZAD_EMOTE_GUARDIAN_APPEAR);
cr->AI()->AttackStart(me->GetVictim());
}
break;
}
if (!me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
DoMeleeAttackIfReady();
}
};
};
class boss_kelthuzad_minion : public CreatureScript
{
public:
boss_kelthuzad_minion() : CreatureScript("boss_kelthuzad_minion") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_kelthuzad_minionAI>(pCreature);
}
struct boss_kelthuzad_minionAI : public ScriptedAI
{
explicit boss_kelthuzad_minionAI(Creature* c) : ScriptedAI(c) { }
EventMap events;
bool callHelp{};
void Reset() override
{
me->SetNoCallAssistance(true);
callHelp = true;
events.Reset();
}
void DoAction(int32 param) override
{
if (param == ACTION_CALL_HELP_ON)
{
callHelp = true;
}
else if (param == ACTION_CALL_HELP_OFF)
{
callHelp = false;
}
else if (param == ACTION_SECOND_PHASE)
{
if (!me->IsInCombat())
{
me->DespawnOrUnsummon(500);
}
}
if (param == ACTION_GUARDIANS_OFF)
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAllAuras();
EnterEvadeMode();
me->SetPosition(me->GetHomePosition());
}
}
void MoveInLineOfSight(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER && !who->IsPet())
return;
ScriptedAI::MoveInLineOfSight(who);
}
void JustDied(Unit* /*killer*/) override
{
if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_ABOMINATION_KILLED, 0);
}
}
void AttackStart(Unit* who) override
{
ScriptedAI::AttackStart(who);
if (callHelp)
{
std::list<Creature*> targets;
me->GetCreaturesWithEntryInRange(targets, 15.0f, me->GetEntry());
for (std::list<Creature*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
{
if ((*itr)->GetGUID() != me->GetGUID())
{
(*itr)->ToCreature()->AI()->DoAction(ACTION_CALL_HELP_OFF);
(*itr)->ToCreature()->AI()->AttackStart(who);
}
}
}
if (me->GetEntry() != NPC_UNSTOPPABLE_ABOMINATION && me->GetEntry() != NPC_GUARDIAN_OF_ICECROWN)
{
me->AddThreat(who, 1000000.0f);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
me->SetInCombatWithZone();
if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION)
{
events.ScheduleEvent(EVENT_MINION_FRENZY, 1s);
events.ScheduleEvent(EVENT_MINION_MORTAL_WOUND, 5s);
}
else if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
events.ScheduleEvent(EVENT_MINION_BLOOD_TAP, 15s);
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustReachedHome() override
{
if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
me->DespawnOrUnsummon();
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MINION_MORTAL_WOUND:
me->CastSpell(me->GetVictim(), KELTHUZAD_SPELL_MORTAL_WOUND, false);
events.Repeat(15s);
break;
case EVENT_MINION_FRENZY:
if (me->HealthBelowPct(35))
{
me->CastSpell(me, KELTHUZAD_SPELL_FRENZY, true);
break;
}
events.Repeat(1s);
break;
case EVENT_MINION_BLOOD_TAP:
me->CastSpell(me->GetVictim(), SPELL_BLOOD_TAP, false);
events.Repeat(15s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_kelthuzad_frost_blast : public SpellScriptLoader
{
public:
spell_kelthuzad_frost_blast() : SpellScriptLoader("spell_kelthuzad_frost_blast") { }
class spell_kelthuzad_frost_blast_SpellScript : public SpellScript
{
PrepareSpellScript(spell_kelthuzad_frost_blast_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
if (!caster || !caster->ToCreature())
return;
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (!target->ToUnit()->HasAura(SPELL_FROST_BLAST))
{
tmplist.push_back(target);
}
}
targets.clear();
for (auto& itr : tmplist)
{
targets.push_back(itr);
}
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kelthuzad_frost_blast_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_kelthuzad_frost_blast_SpellScript();
}
};
class spell_kelthuzad_detonate_mana : public SpellScriptLoader
{
public:
spell_kelthuzad_detonate_mana() : SpellScriptLoader("spell_kelthuzad_detonate_mana") { }
class spell_kelthuzad_detonate_mana_AuraScript : public AuraScript
{
PrepareAuraScript(spell_kelthuzad_detonate_mana_AuraScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_MANA_DETONATION_DAMAGE });
}
void HandleScript(AuraEffect const* aurEff)
{
PreventDefaultAction();
Unit* target = GetTarget();
if (auto mana = int32(target->GetMaxPower(POWER_MANA) / 10))
{
mana = target->ModifyPower(POWER_MANA, -mana);
target->CastCustomSpell(SPELL_MANA_DETONATION_DAMAGE, SPELLVALUE_BASE_POINT0, -mana * 10, target, true, nullptr, aurEff);
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_kelthuzad_detonate_mana_AuraScript::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_kelthuzad_detonate_mana_AuraScript();
}
};
#endif

View File

@@ -15,187 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Spells
{
SPELL_NECROTIC_AURA = 55593,
SPELL_SUMMON_SPORE = 29234,
SPELL_DEATHBLOOM_10 = 29865,
SPELL_DEATHBLOOM_25 = 55053,
SPELL_INEVITABLE_DOOM_10 = 29204,
SPELL_INEVITABLE_DOOM_25 = 55052,
SPELL_BERSERK = 26662
};
enum Events
{
EVENT_NECROTIC_AURA = 1,
EVENT_DEATHBLOOM = 2,
EVENT_INEVITABLE_DOOM = 3,
EVENT_BERSERK = 4,
EVENT_SUMMON_SPORE = 5,
EVENT_NECROTIC_AURA_FADING = 6,
EVENT_NECROTIC_AURA_REMOVED = 7
};
enum Texts
{
SAY_NECROTIC_AURA_APPLIED = 0,
SAY_NECROTIC_AURA_REMOVED = 1,
SAY_NECROTIC_AURA_FADING = 2
};
class boss_loatheb : public CreatureScript
{
public:
boss_loatheb() : CreatureScript("boss_loatheb") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_loathebAI>(pCreature);
}
struct boss_loathebAI : public BossAI
{
explicit boss_loathebAI(Creature* c) : BossAI(c, BOSS_LOATHEB), summons(me)
{
pInstance = me->GetInstanceScript();
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
InstanceScript* pInstance;
uint8 doomCounter;
EventMap events;
SummonList summons;
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
doomCounter = 0;
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, NOT_STARTED);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_LOATHEB_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override
{
cr->SetInCombatWithZone();
summons.Summon(cr);
}
void SummonedCreatureDies(Creature* /*cr*/, Unit*) override
{
if (pInstance)
{
pInstance->SetData(DATA_SPORE_KILLED, 0);
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_NECROTIC_AURA, 10s);
events.ScheduleEvent(EVENT_DEATHBLOOM, 5s);
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2min);
events.ScheduleEvent(EVENT_SUMMON_SPORE, 15s);
events.ScheduleEvent(EVENT_BERSERK, 12min);
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, IN_PROGRESS);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_LOATHEB_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, DONE);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || !IsInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SUMMON_SPORE:
me->CastSpell(me, SPELL_SUMMON_SPORE, true);
events.Repeat(35s);
break;
case EVENT_NECROTIC_AURA:
me->CastSpell(me, SPELL_NECROTIC_AURA, true);
Talk(SAY_NECROTIC_AURA_APPLIED);
events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14s);
events.ScheduleEvent(EVENT_NECROTIC_AURA_REMOVED, 17s);
events.Repeat(20s);
break;
case EVENT_DEATHBLOOM:
me->CastSpell(me, RAID_MODE(SPELL_DEATHBLOOM_10, SPELL_DEATHBLOOM_25), false);
events.Repeat(30s);
break;
case EVENT_INEVITABLE_DOOM:
me->CastSpell(me, RAID_MODE(SPELL_INEVITABLE_DOOM_10, SPELL_INEVITABLE_DOOM_25), false);
doomCounter++;
events.Repeat(doomCounter < 6 ? 30s : 15s);
break;
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_NECROTIC_AURA_FADING:
Talk(SAY_NECROTIC_AURA_FADING);
break;
case EVENT_NECROTIC_AURA_REMOVED:
Talk(SAY_NECROTIC_AURA_REMOVED);
break;
}
DoMeleeAttackIfReady();
}
bool IsInRoom()
{
// Calculate the distance between his home position to the gate
if (me->GetExactDist(me->GetHomePosition().GetPositionX(),
me->GetHomePosition().GetPositionY(),
me->GetHomePosition().GetPositionZ()) > 50.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
};
};
#include "boss_loatheb.h"
void AddSC_boss_loatheb()
{

View File

@@ -0,0 +1,186 @@
#ifndef BOSS_LOATHEB_H_
#define BOSS_LOATHEB_H_
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum LoathebSpells
{
SPELL_NECROTIC_AURA = 55593,
SPELL_SUMMON_SPORE = 29234,
SPELL_DEATHBLOOM_10 = 29865,
SPELL_DEATHBLOOM_25 = 55053,
SPELL_INEVITABLE_DOOM_10 = 29204,
SPELL_INEVITABLE_DOOM_25 = 55052,
LOATHEB_SPELL_BERSERK = 26662
};
enum LoathebEvents
{
EVENT_NECROTIC_AURA = 1,
EVENT_DEATHBLOOM = 2,
EVENT_INEVITABLE_DOOM = 3,
LOATHEB_EVENT_BERSERK = 4,
EVENT_SUMMON_SPORE = 5,
EVENT_NECROTIC_AURA_FADING = 6,
EVENT_NECROTIC_AURA_REMOVED = 7
};
enum LoathebTexts
{
SAY_NECROTIC_AURA_APPLIED = 0,
SAY_NECROTIC_AURA_REMOVED = 1,
SAY_NECROTIC_AURA_FADING = 2
};
class boss_loatheb : public CreatureScript
{
public:
boss_loatheb() : CreatureScript("boss_loatheb") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_loathebAI>(pCreature);
}
struct boss_loathebAI : public BossAI
{
explicit boss_loathebAI(Creature* c) : BossAI(c, BOSS_LOATHEB), summons(me)
{
pInstance = me->GetInstanceScript();
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
InstanceScript* pInstance;
uint8 doomCounter;
EventMap events;
SummonList summons;
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
doomCounter = 0;
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, NOT_STARTED);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_LOATHEB_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override
{
cr->SetInCombatWithZone();
summons.Summon(cr);
}
void SummonedCreatureDies(Creature* /*cr*/, Unit*) override
{
if (pInstance)
{
pInstance->SetData(DATA_SPORE_KILLED, 0);
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_NECROTIC_AURA, 10s);
events.ScheduleEvent(EVENT_DEATHBLOOM, 5s);
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2min);
events.ScheduleEvent(EVENT_SUMMON_SPORE, 15s);
events.ScheduleEvent(LOATHEB_EVENT_BERSERK, 12min);
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, IN_PROGRESS);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_LOATHEB_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, DONE);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || !IsInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SUMMON_SPORE:
me->CastSpell(me, SPELL_SUMMON_SPORE, true);
events.Repeat(35s);
break;
case EVENT_NECROTIC_AURA:
me->CastSpell(me, SPELL_NECROTIC_AURA, true);
Talk(SAY_NECROTIC_AURA_APPLIED);
events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14s);
events.ScheduleEvent(EVENT_NECROTIC_AURA_REMOVED, 17s);
events.Repeat(20s);
break;
case EVENT_DEATHBLOOM:
me->CastSpell(me, RAID_MODE(SPELL_DEATHBLOOM_10, SPELL_DEATHBLOOM_25), false);
events.Repeat(30s);
break;
case EVENT_INEVITABLE_DOOM:
me->CastSpell(me, RAID_MODE(SPELL_INEVITABLE_DOOM_10, SPELL_INEVITABLE_DOOM_25), false);
doomCounter++;
events.Repeat(doomCounter < 6 ? 30s : 15s);
break;
case LOATHEB_EVENT_BERSERK:
me->CastSpell(me, LOATHEB_SPELL_BERSERK, true);
break;
case EVENT_NECROTIC_AURA_FADING:
Talk(SAY_NECROTIC_AURA_FADING);
break;
case EVENT_NECROTIC_AURA_REMOVED:
Talk(SAY_NECROTIC_AURA_REMOVED);
break;
}
DoMeleeAttackIfReady();
}
bool IsInRoom()
{
// Calculate the distance between his home position to the gate
if (me->GetExactDist(me->GetHomePosition().GetPositionX(),
me->GetHomePosition().GetPositionY(),
me->GetHomePosition().GetPositionZ()) > 50.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
};
};
#endif

View File

@@ -15,252 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PassiveAI.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Spells
{
SPELL_WEB_WRAP = 28622,
SPELL_WEB_SPRAY_10 = 29484,
SPELL_WEB_SPRAY_25 = 54125,
SPELL_POISON_SHOCK_10 = 28741,
SPELL_POISON_SHOCK_25 = 54122,
SPELL_NECROTIC_POISON_10 = 54121,
SPELL_NECROTIC_POISON_25 = 28776,
SPELL_FRENZY_10 = 54123,
SPELL_FRENZY_25 = 54124
};
enum Events
{
EVENT_WEB_SPRAY = 1,
EVENT_POISON_SHOCK = 2,
EVENT_NECROTIC_POISON = 3,
EVENT_WEB_WRAP = 4,
EVENT_HEALTH_CHECK = 5,
EVENT_SUMMON_SPIDERLINGS = 6
};
enum Emotes
{
EMOTE_SPIDERS = 0,
EMOTE_WEB_WRAP = 1,
EMOTE_WEB_SPRAY = 2
};
enum Misc
{
NPC_WEB_WRAP = 16486,
NPC_MAEXXNA_SPIDERLING = 17055
};
const Position PosWrap[3] =
{
{3546.796f, -3869.082f, 296.450f, 0.0f},
{3531.271f, -3847.424f, 299.450f, 0.0f},
{3497.067f, -3843.384f, 302.384f, 0.0f}
};
class boss_maexxna : public CreatureScript
{
public:
boss_maexxna() : CreatureScript("boss_maexxna") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_maexxnaAI>(pCreature);
}
struct boss_maexxnaAI : public BossAI
{
explicit boss_maexxnaAI(Creature* c) : BossAI(c, BOSS_MAEXXNA), summons(me)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool IsInRoom()
{
if (me->GetExactDist(3486.6f, -3890.6f, 291.8f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_MAEXXNA_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_WEB_WRAP, 20s);
events.ScheduleEvent(EVENT_WEB_SPRAY, 40s);
events.ScheduleEvent(EVENT_POISON_SHOCK, 10s);
events.ScheduleEvent(EVENT_NECROTIC_POISON, 5s);
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s);
events.ScheduleEvent(EVENT_SUMMON_SPIDERLINGS, 30s);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_MAEXXNA_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* cr) override
{
if (cr->GetEntry() == NPC_MAEXXNA_SPIDERLING)
{
cr->SetInCombatWithZone();
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
cr->AI()->AttackStart(target);
}
}
summons.Summon(cr);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_WEB_SPRAY:
Talk(EMOTE_WEB_SPRAY);
me->CastSpell(me, RAID_MODE(SPELL_WEB_SPRAY_10, SPELL_WEB_SPRAY_25), true);
events.Repeat(40s);
break;
case EVENT_POISON_SHOCK:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_POISON_SHOCK_10, SPELL_POISON_SHOCK_25), false);
events.Repeat(10s);
break;
case EVENT_NECROTIC_POISON:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_NECROTIC_POISON_10, SPELL_NECROTIC_POISON_25), false);
events.Repeat(30s);
break;
case EVENT_SUMMON_SPIDERLINGS:
Talk(EMOTE_SPIDERS);
for (uint8 i = 0; i < 8; ++i)
{
me->SummonCreature(NPC_MAEXXNA_SPIDERLING, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
events.Repeat(40s);
break;
case EVENT_HEALTH_CHECK:
if (me->GetHealthPct() < 30)
{
me->CastSpell(me, RAID_MODE(SPELL_FRENZY_10, SPELL_FRENZY_25), true);
break;
}
events.Repeat(1s);
break;
case EVENT_WEB_WRAP:
Talk(EMOTE_WEB_WRAP);
for (uint8 i = 0; i < RAID_MODE(1, 2); ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0, true, true, -SPELL_WEB_WRAP))
{
target->RemoveAura(RAID_MODE(SPELL_WEB_SPRAY_10, SPELL_WEB_SPRAY_25));
uint8 pos = urand(0, 2);
if (Creature* wrap = me->SummonCreature(NPC_WEB_WRAP, PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
wrap->AI()->SetGUID(target->GetGUID());
target->GetMotionMaster()->MoveJump(PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 20, 20);
}
}
}
events.Repeat(40s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class boss_maexxna_webwrap : public CreatureScript
{
public:
boss_maexxna_webwrap() : CreatureScript("boss_maexxna_webwrap") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_maexxna_webwrapAI>(pCreature);
}
struct boss_maexxna_webwrapAI : public NullCreatureAI
{
explicit boss_maexxna_webwrapAI(Creature* c) : NullCreatureAI(c) {}
ObjectGuid victimGUID;
void SetGUID(ObjectGuid guid, int32 /*param*/) override
{
victimGUID = guid;
if (me->m_spells[0] && victimGUID)
{
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
{
victim->CastSpell(victim, me->m_spells[0], true, nullptr, nullptr, me->GetGUID());
}
}
}
void JustDied(Unit* /*killer*/) override
{
if (me->m_spells[0] && victimGUID)
{
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
{
victim->RemoveAurasDueToSpell(me->m_spells[0], me->GetGUID());
}
}
}
};
};
#include "boss_maexxna.h"
void AddSC_boss_maexxna()
{

View File

@@ -0,0 +1,251 @@
#ifndef BOSS_MAEXXNA_H_
#define BOSS_MAEXXNA_H_
#include "PassiveAI.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum MaexxnaSpells
{
SPELL_WEB_WRAP = 28622,
SPELL_WEB_SPRAY_10 = 29484,
SPELL_WEB_SPRAY_25 = 54125,
SPELL_POISON_SHOCK_10 = 28741,
SPELL_POISON_SHOCK_25 = 54122,
SPELL_NECROTIC_POISON_10 = 54121,
SPELL_NECROTIC_POISON_25 = 28776,
SPELL_FRENZY_10 = 54123,
SPELL_FRENZY_25 = 54124
};
enum MaexxnaEvents
{
EVENT_WEB_SPRAY = 1,
EVENT_POISON_SHOCK = 2,
EVENT_NECROTIC_POISON = 3,
EVENT_WEB_WRAP = 4,
EVENT_HEALTH_CHECK = 5,
EVENT_SUMMON_SPIDERLINGS = 6
};
enum MaexxnaEmotes
{
EMOTE_SPIDERS = 0,
EMOTE_WEB_WRAP = 1,
EMOTE_WEB_SPRAY = 2
};
enum MaexxnaMisc
{
NPC_WEB_WRAP = 16486,
NPC_MAEXXNA_SPIDERLING = 17055
};
const Position PosWrap[3] =
{
{3546.796f, -3869.082f, 296.450f, 0.0f},
{3531.271f, -3847.424f, 299.450f, 0.0f},
{3497.067f, -3843.384f, 302.384f, 0.0f}
};
class boss_maexxna : public CreatureScript
{
public:
boss_maexxna() : CreatureScript("boss_maexxna") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_maexxnaAI>(pCreature);
}
struct boss_maexxnaAI : public BossAI
{
explicit boss_maexxnaAI(Creature* c) : BossAI(c, BOSS_MAEXXNA), summons(me)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool IsInRoom()
{
if (me->GetExactDist(3486.6f, -3890.6f, 291.8f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_MAEXXNA_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_WEB_WRAP, 20s);
events.ScheduleEvent(EVENT_WEB_SPRAY, 40s);
events.ScheduleEvent(EVENT_POISON_SHOCK, 10s);
events.ScheduleEvent(EVENT_NECROTIC_POISON, 5s);
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s);
events.ScheduleEvent(EVENT_SUMMON_SPIDERLINGS, 30s);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_MAEXXNA_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* cr) override
{
if (cr->GetEntry() == NPC_MAEXXNA_SPIDERLING)
{
cr->SetInCombatWithZone();
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
cr->AI()->AttackStart(target);
}
}
summons.Summon(cr);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_WEB_SPRAY:
Talk(EMOTE_WEB_SPRAY);
me->CastSpell(me, RAID_MODE(SPELL_WEB_SPRAY_10, SPELL_WEB_SPRAY_25), true);
events.Repeat(40s);
break;
case EVENT_POISON_SHOCK:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_POISON_SHOCK_10, SPELL_POISON_SHOCK_25), false);
events.Repeat(10s);
break;
case EVENT_NECROTIC_POISON:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_NECROTIC_POISON_10, SPELL_NECROTIC_POISON_25), false);
events.Repeat(30s);
break;
case EVENT_SUMMON_SPIDERLINGS:
Talk(EMOTE_SPIDERS);
for (uint8 i = 0; i < 8; ++i)
{
me->SummonCreature(NPC_MAEXXNA_SPIDERLING, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
events.Repeat(40s);
break;
case EVENT_HEALTH_CHECK:
if (me->GetHealthPct() < 30)
{
me->CastSpell(me, RAID_MODE(SPELL_FRENZY_10, SPELL_FRENZY_25), true);
break;
}
events.Repeat(1s);
break;
case EVENT_WEB_WRAP:
Talk(EMOTE_WEB_WRAP);
for (uint8 i = 0; i < RAID_MODE(1, 2); ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0, true, true, -SPELL_WEB_WRAP))
{
target->RemoveAura(RAID_MODE(SPELL_WEB_SPRAY_10, SPELL_WEB_SPRAY_25));
uint8 pos = urand(0, 2);
if (Creature* wrap = me->SummonCreature(NPC_WEB_WRAP, PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
wrap->AI()->SetGUID(target->GetGUID());
target->GetMotionMaster()->MoveJump(PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 20, 20);
}
}
}
events.Repeat(40s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class boss_maexxna_webwrap : public CreatureScript
{
public:
boss_maexxna_webwrap() : CreatureScript("boss_maexxna_webwrap") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_maexxna_webwrapAI>(pCreature);
}
struct boss_maexxna_webwrapAI : public NullCreatureAI
{
explicit boss_maexxna_webwrapAI(Creature* c) : NullCreatureAI(c) {}
ObjectGuid victimGUID;
void SetGUID(ObjectGuid guid, int32 /*param*/) override
{
victimGUID = guid;
if (me->m_spells[0] && victimGUID)
{
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
{
victim->CastSpell(victim, me->m_spells[0], true, nullptr, nullptr, me->GetGUID());
}
}
}
void JustDied(Unit* /*killer*/) override
{
if (me->m_spells[0] && victimGUID)
{
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
{
victim->RemoveAurasDueToSpell(me->m_spells[0], me->GetGUID());
}
}
}
};
};
#endif

View File

@@ -15,289 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Says
{
SAY_AGGRO = 0,
SAY_SUMMON = 1,
SAY_SLAY = 2,
SAY_DEATH = 3,
EMOTE_SUMMON = 4,
EMOTE_SUMMON_WAVE = 5,
EMOTE_TELEPORT_BALCONY = 6,
EMOTE_TELEPORT_BACK = 7,
EMOTE_BLINK = 8
};
enum Spells
{
SPELL_CURSE_OF_THE_PLAGUEBRINGER_10 = 29213,
SPELL_CURSE_OF_THE_PLAGUEBRINGER_25 = 54835,
SPELL_CRIPPLE_10 = 29212,
SPELL_CRIPPLE_25 = 54814,
SPELL_SUMMON_PLAGUED_WARRIORS = 29237,
SPELL_TELEPORT = 29216,
SPELL_TELEPORT_BACK = 29231,
SPELL_BERSERK = 68378,
SPELL_BLINK = 29208
};
enum Events
{
EVENT_CURSE = 1,
EVENT_CRIPPLE = 2,
EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE = 3,
EVENT_MOVE_TO_BALCONY = 4,
EVENT_BLINK = 5,
EVENT_MOVE_TO_GROUND = 6,
EVENT_SUMMON_PLAGUED_WARRIOR_REAL = 7,
EVENT_BALCONY_SUMMON_ANNOUNCE = 8,
EVENT_BALCONY_SUMMON_REAL = 9
};
enum Misc
{
NPC_PLAGUED_WARRIOR = 16984,
NPC_PLAGUED_CHAMPION = 16983,
NPC_PLAGUED_GUARDIAN = 16981
};
const Position summoningPosition[5] =
{
{2728.06f, -3535.38f, 263.21f, 2.75f},
{2725.71f, -3514.80f, 263.23f, 2.86f},
{2728.24f, -3465.08f, 264.20f, 3.56f},
{2704.79f, -3459.17f, 263.74f, 4.25f},
{2652.02f, -3459.13f, 262.50f, 5.39f}
};
const Position nothPosition = {2684.94f, -3502.53f, 261.31f, 4.7f};
class boss_noth : public CreatureScript
{
public:
boss_noth() : CreatureScript("boss_noth") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_nothAI>(pCreature);
}
struct boss_nothAI : public BossAI
{
explicit boss_nothAI(Creature* c) : BossAI(c, BOSS_NOTH), summons(me)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
uint8 timesInBalcony;
EventMap events;
SummonList summons;
void StartGroundPhase()
{
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetControlled(false, UNIT_STATE_ROOT);
events.Reset();
events.ScheduleEvent(EVENT_MOVE_TO_BALCONY, 110s);
events.ScheduleEvent(EVENT_CURSE, 15s);
events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE, 10s);
if (Is25ManRaid())
{
events.ScheduleEvent(EVENT_BLINK, 26s);
}
}
void StartBalconyPhase()
{
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetControlled(true, UNIT_STATE_ROOT);
events.Reset();
events.ScheduleEvent(EVENT_BALCONY_SUMMON_ANNOUNCE, 4s);
events.ScheduleEvent(EVENT_MOVE_TO_GROUND, 70s);
}
void SummonHelper(uint32 entry, uint32 count)
{
for (uint8 i = 0; i < count; ++i)
{
me->SummonCreature(entry, summoningPosition[urand(0, 4)]);
}
}
bool IsInRoom()
{
if (me->GetExactDist(2684.8f, -3502.5f, 261.3f) > 80.0f)
{
EnterEvadeMode(EVADE_REASON_OTHER);
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->CastSpell(me, SPELL_TELEPORT_BACK, true);
me->SetControlled(false, UNIT_STATE_ROOT);
me->SetReactState(REACT_AGGRESSIVE);
timesInBalcony = 0;
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetControlled(false, UNIT_STATE_ROOT);
ScriptedAI::EnterEvadeMode(why);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
StartGroundPhase();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->SetInCombatWithZone();
}
void JustDied(Unit* killer) override
{
if (me->GetPositionZ() > 270.27f)
{
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->NearTeleportTo(nothPosition.GetPositionX(), nothPosition.GetPositionY(), nothPosition.GetPositionZ(), nothPosition.GetOrientation(), true);
}
BossAI::JustDied(killer);
Talk(SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
// GROUND
case EVENT_CURSE:
if (events.GetPhaseMask() == 0)
{
me->CastCustomSpell(RAID_MODE(SPELL_CURSE_OF_THE_PLAGUEBRINGER_10, SPELL_CURSE_OF_THE_PLAGUEBRINGER_25), SPELLVALUE_MAX_TARGETS, RAID_MODE(3, 10), me, false);
}
events.Repeat(25s);
break;
case EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE:
Talk(SAY_SUMMON);
Talk(EMOTE_SUMMON);
events.Repeat(30s);
events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_REAL, 4s);
break;
case EVENT_SUMMON_PLAGUED_WARRIOR_REAL:
me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true);
SummonHelper(NPC_PLAGUED_WARRIOR, RAID_MODE(2, 3));
break;
case EVENT_MOVE_TO_BALCONY:
Talk(EMOTE_TELEPORT_BALCONY);
me->CastSpell(me, SPELL_TELEPORT, true);
StartBalconyPhase();
break;
case EVENT_BLINK:
DoResetThreatList();
me->CastSpell(me, RAID_MODE(SPELL_CRIPPLE_10, SPELL_CRIPPLE_25), false);
me->CastSpell(me, SPELL_BLINK, true);
Talk(EMOTE_BLINK);
events.Repeat(30s);
break;
// BALCONY
case EVENT_BALCONY_SUMMON_ANNOUNCE:
Talk(EMOTE_SUMMON_WAVE);
events.Repeat(30s);
events.ScheduleEvent(EVENT_BALCONY_SUMMON_REAL, 4s);
break;
case EVENT_BALCONY_SUMMON_REAL:
me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true); // visual
switch (timesInBalcony)
{
case 0:
SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(2, 4));
break;
case 1:
SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(1, 2));
SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(1, 2));
break;
default:
SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(2, 4));
break;
}
break;
case EVENT_MOVE_TO_GROUND:
Talk(EMOTE_TELEPORT_BACK);
me->CastSpell(me, SPELL_TELEPORT_BACK, true);
timesInBalcony++;
if (timesInBalcony == 3)
{
DoCastSelf(SPELL_BERSERK);
}
StartGroundPhase();
break;
}
if (me->HasReactState(REACT_AGGRESSIVE))
DoMeleeAttackIfReady();
}
};
};
#include "boss_noth.h"
void AddSC_boss_noth()
{

View File

@@ -0,0 +1,289 @@
#ifndef BOSS_NOTH_H_
#define BOSS_NOTH_H_
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum NothSays
{
NOTH_SAY_AGGRO = 0,
NOTH_SAY_SUMMON = 1,
NOTH_SAY_SLAY = 2,
NOTH_SAY_DEATH = 3,
NOTH_EMOTE_SUMMON = 4,
NOTH_EMOTE_SUMMON_WAVE = 5,
NOTH_EMOTE_TELEPORT_BALCONY = 6,
NOTH_EMOTE_TELEPORT_BACK = 7,
NOTH_EMOTE_BLINK = 8
};
enum NothSpells
{
SPELL_CURSE_OF_THE_PLAGUEBRINGER_10 = 29213,
SPELL_CURSE_OF_THE_PLAGUEBRINGER_25 = 54835,
SPELL_CRIPPLE_10 = 29212,
SPELL_CRIPPLE_25 = 54814,
SPELL_SUMMON_PLAGUED_WARRIORS = 29237,
SPELL_TELEPORT = 29216,
SPELL_TELEPORT_BACK = 29231,
NOTH_SPELL_BERSERK = 68378,
SPELL_BLINK = 29208
};
enum NothEvents
{
EVENT_CURSE = 1,
EVENT_CRIPPLE = 2,
EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE = 3,
EVENT_MOVE_TO_BALCONY = 4,
EVENT_BLINK = 5,
EVENT_MOVE_TO_GROUND = 6,
EVENT_SUMMON_PLAGUED_WARRIOR_REAL = 7,
EVENT_BALCONY_SUMMON_ANNOUNCE = 8,
EVENT_BALCONY_SUMMON_REAL = 9
};
enum NothMisc
{
NPC_PLAGUED_WARRIOR = 16984,
NPC_PLAGUED_CHAMPION = 16983,
NPC_PLAGUED_GUARDIAN = 16981
};
const Position summoningPosition[5] =
{
{2728.06f, -3535.38f, 263.21f, 2.75f},
{2725.71f, -3514.80f, 263.23f, 2.86f},
{2728.24f, -3465.08f, 264.20f, 3.56f},
{2704.79f, -3459.17f, 263.74f, 4.25f},
{2652.02f, -3459.13f, 262.50f, 5.39f}
};
const Position nothPosition = {2684.94f, -3502.53f, 261.31f, 4.7f};
class boss_noth : public CreatureScript
{
public:
boss_noth() : CreatureScript("boss_noth") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_nothAI>(pCreature);
}
struct boss_nothAI : public BossAI
{
explicit boss_nothAI(Creature* c) : BossAI(c, BOSS_NOTH), summons(me)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
uint8 timesInBalcony;
EventMap events;
SummonList summons;
void StartGroundPhase()
{
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetControlled(false, UNIT_STATE_ROOT);
events.Reset();
events.ScheduleEvent(EVENT_MOVE_TO_BALCONY, 110s);
events.ScheduleEvent(EVENT_CURSE, 15s);
events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE, 10s);
if (Is25ManRaid())
{
events.ScheduleEvent(EVENT_BLINK, 26s);
}
}
void StartBalconyPhase()
{
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetControlled(true, UNIT_STATE_ROOT);
events.Reset();
events.ScheduleEvent(EVENT_BALCONY_SUMMON_ANNOUNCE, 4s);
events.ScheduleEvent(EVENT_MOVE_TO_GROUND, 70s);
}
void SummonHelper(uint32 entry, uint32 count)
{
for (uint8 i = 0; i < count; ++i)
{
me->SummonCreature(entry, summoningPosition[urand(0, 4)]);
}
}
bool IsInRoom()
{
if (me->GetExactDist(2684.8f, -3502.5f, 261.3f) > 80.0f)
{
EnterEvadeMode(EVADE_REASON_OTHER);
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->CastSpell(me, SPELL_TELEPORT_BACK, true);
me->SetControlled(false, UNIT_STATE_ROOT);
me->SetReactState(REACT_AGGRESSIVE);
timesInBalcony = 0;
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetControlled(false, UNIT_STATE_ROOT);
ScriptedAI::EnterEvadeMode(why);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(NOTH_SAY_AGGRO);
StartGroundPhase();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->SetInCombatWithZone();
}
void JustDied(Unit* killer) override
{
if (me->GetPositionZ() > 270.27f)
{
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->NearTeleportTo(nothPosition.GetPositionX(), nothPosition.GetPositionY(), nothPosition.GetPositionZ(), nothPosition.GetOrientation(), true);
}
BossAI::JustDied(killer);
Talk(NOTH_SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(NOTH_SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
// GROUND
case EVENT_CURSE:
if (events.GetPhaseMask() == 0)
{
me->CastCustomSpell(RAID_MODE(SPELL_CURSE_OF_THE_PLAGUEBRINGER_10, SPELL_CURSE_OF_THE_PLAGUEBRINGER_25), SPELLVALUE_MAX_TARGETS, RAID_MODE(3, 10), me, false);
}
events.Repeat(25s);
break;
case EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE:
Talk(NOTH_SAY_SUMMON);
Talk(NOTH_EMOTE_SUMMON);
events.Repeat(30s);
events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_REAL, 4s);
break;
case EVENT_SUMMON_PLAGUED_WARRIOR_REAL:
me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true);
SummonHelper(NPC_PLAGUED_WARRIOR, RAID_MODE(2, 3));
break;
case EVENT_MOVE_TO_BALCONY:
Talk(NOTH_EMOTE_TELEPORT_BALCONY);
me->CastSpell(me, SPELL_TELEPORT, true);
StartBalconyPhase();
break;
case EVENT_BLINK:
DoResetThreatList();
me->CastSpell(me, RAID_MODE(SPELL_CRIPPLE_10, SPELL_CRIPPLE_25), false);
me->CastSpell(me, SPELL_BLINK, true);
Talk(NOTH_EMOTE_BLINK);
events.Repeat(30s);
break;
// BALCONY
case EVENT_BALCONY_SUMMON_ANNOUNCE:
Talk(NOTH_EMOTE_SUMMON_WAVE);
events.Repeat(30s);
events.ScheduleEvent(EVENT_BALCONY_SUMMON_REAL, 4s);
break;
case EVENT_BALCONY_SUMMON_REAL:
me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true); // visual
switch (timesInBalcony)
{
case 0:
SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(2, 4));
break;
case 1:
SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(1, 2));
SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(1, 2));
break;
default:
SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(2, 4));
break;
}
break;
case EVENT_MOVE_TO_GROUND:
Talk(NOTH_EMOTE_TELEPORT_BACK);
me->CastSpell(me, SPELL_TELEPORT_BACK, true);
timesInBalcony++;
if (timesInBalcony == 3)
{
DoCastSelf(NOTH_SPELL_BERSERK);
}
StartGroundPhase();
break;
}
if (me->HasReactState(REACT_AGGRESSIVE))
DoMeleeAttackIfReady();
}
};
};
#endif

View File

@@ -15,184 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Yells
{
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_DEATH = 2,
EMOTE_BERSERK = 3,
EMOTE_ENRAGE = 4
};
enum Spells
{
SPELL_HATEFUL_STRIKE_10 = 41926,
SPELL_HATEFUL_STRIKE_25 = 59192,
SPELL_FRENZY = 28131,
SPELL_BERSERK = 26662,
SPELL_SLIME_BOLT = 32309
};
enum Events
{
EVENT_HEALTH_CHECK = 1,
EVENT_HATEFUL_STRIKE = 2,
EVENT_SLIME_BOLT = 3,
EVENT_BERSERK = 4
};
enum Misc
{
ACHIEV_TIMED_START_EVENT = 10286
};
class boss_patchwerk : public CreatureScript
{
public:
boss_patchwerk() : CreatureScript("boss_patchwerk") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_patchwerkAI>(pCreature);
}
struct boss_patchwerkAI : public BossAI
{
explicit boss_patchwerkAI(Creature* c) : BossAI(c, BOSS_PATCHWERK)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
InstanceScript* pInstance;
void Reset() override
{
BossAI::Reset();
events.Reset();
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (!urand(0, 3))
{
Talk(SAY_SLAY);
}
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_HATEFUL_STRIKE, 1500ms);
events.ScheduleEvent(EVENT_BERSERK, 6min);
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s);
if (pInstance)
{
pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_HATEFUL_STRIKE:
{
// Cast Hateful strike on the player with the highest amount of HP within melee distance, and second threat amount
std::list<Unit*> meleeRangeTargets;
Unit* finalTarget = nullptr;
uint8 counter = 0;
auto i = me->GetThreatMgr().GetThreatList().begin();
for (; i != me->GetThreatMgr().GetThreatList().end(); ++i, ++counter)
{
// Gather all units with melee range
Unit* target = (*i)->getTarget();
if (me->IsWithinMeleeRange(target))
{
meleeRangeTargets.push_back(target);
}
// and add threat to most hated
if (counter < RAID_MODE(2, 3))
{
me->AddThreat(target, 500.0f);
}
}
counter = 0;
std::list<Unit*, std::allocator<Unit*>>::iterator itr;
for (itr = meleeRangeTargets.begin(); itr != meleeRangeTargets.end(); ++itr, ++counter)
{
// if there is only one target available
if (meleeRangeTargets.size() == 1)
{
finalTarget = (*itr);
}
else if (counter > 0) // skip first target
{
if (!finalTarget || (*itr)->GetHealth() > finalTarget->GetHealth())
{
finalTarget = (*itr);
}
// third loop
if (counter >= 2)
break;
}
}
if (finalTarget)
{
me->CastSpell(finalTarget, RAID_MODE(SPELL_HATEFUL_STRIKE_10, SPELL_HATEFUL_STRIKE_25), false);
}
events.Repeat(1s);
break;
}
case EVENT_BERSERK:
Talk(EMOTE_BERSERK);
me->CastSpell(me, SPELL_BERSERK, true);
events.ScheduleEvent(EVENT_SLIME_BOLT, 3s);
break;
case EVENT_SLIME_BOLT:
me->CastSpell(me, SPELL_SLIME_BOLT, false);
events.Repeat(3s);
break;
case EVENT_HEALTH_CHECK:
if (me->GetHealthPct() <= 5)
{
Talk(EMOTE_ENRAGE);
me->CastSpell(me, SPELL_FRENZY, true);
break;
}
events.Repeat(1s);
break;
}
DoMeleeAttackIfReady();
}
};
};
#include "boss_patchwerk.h"
void AddSC_boss_patchwerk()
{

View File

@@ -0,0 +1,183 @@
#ifndef BOSS_PATCHWERK_H_
#define BOSS_PATCHWERK_H_
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum PatchwerkYells
{
PATCHWERK_SAY_AGGRO = 0,
PATCHWERK_SAY_SLAY = 1,
PATCHWERK_SAY_DEATH = 2,
PATCHWERK_EMOTE_BERSERK = 3,
PATCHWERK_EMOTE_ENRAGE = 4
};
enum PatchwerkSpells
{
SPELL_HATEFUL_STRIKE_10 = 41926,
SPELL_HATEFUL_STRIKE_25 = 59192,
PATCHWERK_SPELL_FRENZY = 28131,
PATCHWERK_SPELL_BERSERK = 26662,
SPELL_SLIME_BOLT = 32309
};
enum PatchwerkEvents
{
PATCHWERK_EVENT_HEALTH_CHECK = 1,
EVENT_HATEFUL_STRIKE = 2,
EVENT_SLIME_BOLT = 3,
PATCHWERK_EVENT_BERSERK = 4
};
enum PatchwerkMisc
{
ACHIEV_TIMED_START_EVENT = 10286
};
class boss_patchwerk : public CreatureScript
{
public:
boss_patchwerk() : CreatureScript("boss_patchwerk") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_patchwerkAI>(pCreature);
}
struct boss_patchwerkAI : public BossAI
{
explicit boss_patchwerkAI(Creature* c) : BossAI(c, BOSS_PATCHWERK)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
InstanceScript* pInstance;
void Reset() override
{
BossAI::Reset();
events.Reset();
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (!urand(0, 3))
{
Talk(PATCHWERK_SAY_SLAY);
}
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(PATCHWERK_SAY_DEATH);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(PATCHWERK_SAY_AGGRO);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_HATEFUL_STRIKE, 1500ms);
events.ScheduleEvent(PATCHWERK_EVENT_BERSERK, 6min);
events.ScheduleEvent(PATCHWERK_EVENT_HEALTH_CHECK, 1s);
if (pInstance)
{
pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_HATEFUL_STRIKE:
{
// Cast Hateful strike on the player with the highest amount of HP within melee distance, and second threat amount
std::list<Unit*> meleeRangeTargets;
Unit* finalTarget = nullptr;
uint8 counter = 0;
auto i = me->GetThreatMgr().GetThreatList().begin();
for (; i != me->GetThreatMgr().GetThreatList().end(); ++i, ++counter)
{
// Gather all units with melee range
Unit* target = (*i)->getTarget();
if (me->IsWithinMeleeRange(target))
{
meleeRangeTargets.push_back(target);
}
// and add threat to most hated
if (counter < RAID_MODE(2, 3))
{
me->AddThreat(target, 500.0f);
}
}
counter = 0;
std::list<Unit*, std::allocator<Unit*>>::iterator itr;
for (itr = meleeRangeTargets.begin(); itr != meleeRangeTargets.end(); ++itr, ++counter)
{
// if there is only one target available
if (meleeRangeTargets.size() == 1)
{
finalTarget = (*itr);
}
else if (counter > 0) // skip first target
{
if (!finalTarget || (*itr)->GetHealth() > finalTarget->GetHealth())
{
finalTarget = (*itr);
}
// third loop
if (counter >= 2)
break;
}
}
if (finalTarget)
{
me->CastSpell(finalTarget, RAID_MODE(SPELL_HATEFUL_STRIKE_10, SPELL_HATEFUL_STRIKE_25), false);
}
events.Repeat(1s);
break;
}
case PATCHWERK_EVENT_BERSERK:
Talk(PATCHWERK_EMOTE_BERSERK);
me->CastSpell(me, PATCHWERK_SPELL_BERSERK, true);
events.ScheduleEvent(EVENT_SLIME_BOLT, 3s);
break;
case EVENT_SLIME_BOLT:
me->CastSpell(me, SPELL_SLIME_BOLT, false);
events.Repeat(3s);
break;
case PATCHWERK_EVENT_HEALTH_CHECK:
if (me->GetHealthPct() <= 5)
{
Talk(PATCHWERK_EMOTE_ENRAGE);
me->CastSpell(me, PATCHWERK_SPELL_FRENZY, true);
break;
}
events.Repeat(1s);
break;
}
DoMeleeAttackIfReady();
}
};
};
#endif

View File

@@ -15,216 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Says
{
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_TAUNTED = 2,
SAY_DEATH = 3
};
enum Spells
{
SPELL_UNBALANCING_STRIKE = 26613,
SPELL_DISRUPTING_SHOUT_10 = 55543,
SPELL_DISRUPTING_SHOUT_25 = 29107,
SPELL_JAGGED_KNIFE = 55550,
SPELL_HOPELESS = 29125,
SPELL_TAUNT = 29060
};
enum Events
{
EVENT_UNBALANCING_STRIKE = 1,
EVENT_DISRUPTING_SHOUT = 2,
EVENT_JAGGED_KNIFE = 3
};
enum Misc
{
NPC_DEATH_KNIGHT_UNDERSTUDY = 16803,
NPC_RAZUVIOUS = 16061
};
class boss_razuvious : public CreatureScript
{
public:
boss_razuvious() : CreatureScript("boss_razuvious") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_razuviousAI>(pCreature);
}
struct boss_razuviousAI : public BossAI
{
explicit boss_razuviousAI(Creature* c) : BossAI(c, BOSS_RAZUVIOUS), summons(me)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
void SpawnHelpers()
{
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2762.23f, -3085.07f, 267.685f, 1.95f);
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2758.24f, -3110.97f, 267.685f, 3.94f);
if (Is25ManRaid())
{
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2782.45f, -3088.03f, 267.685f, 0.75f);
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2778.56f, -3113.74f, 267.685f, 5.28f);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void Reset() override
{
BossAI::Reset();
summons.DespawnAll();
events.Reset();
SpawnHelpers();
}
void KilledUnit(Unit* who) override
{
if (roll_chance_i(30))
{
Talk(SAY_SLAY);
}
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
// Damage done by the controlled Death Knight understudies should also count toward damage done by players
if(who && who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_DEATH_KNIGHT_UNDERSTUDY)
{
me->LowerPlayerDamageReq(damage);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
me->CastSpell(me, SPELL_HOPELESS, true);
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (spell->Id == SPELL_TAUNT)
{
Talk(SAY_TAUNTED, caster);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, 20s);
events.ScheduleEvent(EVENT_DISRUPTING_SHOUT, 15s);
events.ScheduleEvent(EVENT_JAGGED_KNIFE, 10s);
summons.DoZoneInCombat();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_UNBALANCING_STRIKE:
me->CastSpell(me->GetVictim(), SPELL_UNBALANCING_STRIKE, false);
events.Repeat(20s);
break;
case EVENT_DISRUPTING_SHOUT:
me->CastSpell(me, RAID_MODE(SPELL_DISRUPTING_SHOUT_10, SPELL_DISRUPTING_SHOUT_25), false);
events.Repeat(15s);
break;
case EVENT_JAGGED_KNIFE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f))
{
me->CastSpell(target, SPELL_JAGGED_KNIFE, false);
}
events.Repeat(10s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class boss_razuvious_minion : public CreatureScript
{
public:
boss_razuvious_minion() : CreatureScript("boss_razuvious_minion") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_razuvious_minionAI>(pCreature);
}
struct boss_razuvious_minionAI : public ScriptedAI
{
explicit boss_razuvious_minionAI(Creature* c) : ScriptedAI(c) { }
EventMap events;
void Reset() override
{
events.Reset();
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustEngagedWith(Unit* who) override
{
if (Creature* cr = me->FindNearestCreature(NPC_RAZUVIOUS, 100.0f))
{
cr->SetInCombatWithZone();
cr->AI()->AttackStart(who);
}
}
void UpdateAI(uint32 diff) override
{
if (UpdateVictim())
{
events.Update(diff);
if (!me->HasUnitState(UNIT_STATE_CASTING) || !me->IsCharmed())
{
DoMeleeAttackIfReady();
}
}
}
};
};
#include "boss_razuvious.h"
void AddSC_boss_razuvious()
{

View File

@@ -0,0 +1,215 @@
#ifndef BOSS_RAZUVIOUS_H_
#define BOSS_RAZUVIOUS_H_
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum RazuviousSays
{
RAZUVIOUS_SAY_AGGRO = 0,
RAZUVIOUS_SAY_SLAY = 1,
RAZUVIOUS_SAY_TAUNTED = 2,
RAZUVIOUS_SAY_DEATH = 3
};
enum RazuviousSpells
{
SPELL_UNBALANCING_STRIKE = 26613,
SPELL_DISRUPTING_SHOUT_10 = 55543,
SPELL_DISRUPTING_SHOUT_25 = 29107,
SPELL_JAGGED_KNIFE = 55550,
SPELL_HOPELESS = 29125,
RAZUVIOUS_SPELL_TAUNT = 29060
};
enum RazuviousEvents
{
EVENT_UNBALANCING_STRIKE = 1,
EVENT_DISRUPTING_SHOUT = 2,
EVENT_JAGGED_KNIFE = 3
};
enum RazuviousMisc
{
NPC_DEATH_KNIGHT_UNDERSTUDY = 16803,
NPC_RAZUVIOUS = 16061
};
class boss_razuvious : public CreatureScript
{
public:
boss_razuvious() : CreatureScript("boss_razuvious") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_razuviousAI>(pCreature);
}
struct boss_razuviousAI : public BossAI
{
explicit boss_razuviousAI(Creature* c) : BossAI(c, BOSS_RAZUVIOUS), summons(me)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
void SpawnHelpers()
{
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2762.23f, -3085.07f, 267.685f, 1.95f);
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2758.24f, -3110.97f, 267.685f, 3.94f);
if (Is25ManRaid())
{
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2782.45f, -3088.03f, 267.685f, 0.75f);
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2778.56f, -3113.74f, 267.685f, 5.28f);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void Reset() override
{
BossAI::Reset();
summons.DespawnAll();
events.Reset();
SpawnHelpers();
}
void KilledUnit(Unit* who) override
{
if (roll_chance_i(30))
{
Talk(RAZUVIOUS_SAY_SLAY);
}
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
// Damage done by the controlled Death Knight understudies should also count toward damage done by players
if(who && who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_DEATH_KNIGHT_UNDERSTUDY)
{
me->LowerPlayerDamageReq(damage);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(RAZUVIOUS_SAY_DEATH);
me->CastSpell(me, SPELL_HOPELESS, true);
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (spell->Id == RAZUVIOUS_SPELL_TAUNT)
{
Talk(RAZUVIOUS_SAY_TAUNTED, caster);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(RAZUVIOUS_SAY_AGGRO);
events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, 20s);
events.ScheduleEvent(EVENT_DISRUPTING_SHOUT, 15s);
events.ScheduleEvent(EVENT_JAGGED_KNIFE, 10s);
summons.DoZoneInCombat();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_UNBALANCING_STRIKE:
me->CastSpell(me->GetVictim(), SPELL_UNBALANCING_STRIKE, false);
events.Repeat(20s);
break;
case EVENT_DISRUPTING_SHOUT:
me->CastSpell(me, RAID_MODE(SPELL_DISRUPTING_SHOUT_10, SPELL_DISRUPTING_SHOUT_25), false);
events.Repeat(15s);
break;
case EVENT_JAGGED_KNIFE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f))
{
me->CastSpell(target, SPELL_JAGGED_KNIFE, false);
}
events.Repeat(10s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class boss_razuvious_minion : public CreatureScript
{
public:
boss_razuvious_minion() : CreatureScript("boss_razuvious_minion") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_razuvious_minionAI>(pCreature);
}
struct boss_razuvious_minionAI : public ScriptedAI
{
explicit boss_razuvious_minionAI(Creature* c) : ScriptedAI(c) { }
EventMap events;
void Reset() override
{
events.Reset();
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustEngagedWith(Unit* who) override
{
if (Creature* cr = me->FindNearestCreature(NPC_RAZUVIOUS, 100.0f))
{
cr->SetInCombatWithZone();
cr->AI()->AttackStart(who);
}
}
void UpdateAI(uint32 diff) override
{
if (UpdateVictim())
{
events.Update(diff);
if (!me->HasUnitState(UNIT_STATE_CASTING) || !me->IsCharmed())
{
DoMeleeAttackIfReady();
}
}
}
};
};
#endif

View File

@@ -15,448 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Yells
{
EMOTE_AIR_PHASE = 0,
EMOTE_GROUND_PHASE = 1,
EMOTE_BREATH = 2,
EMOTE_ENRAGE = 3
};
enum Spells
{
// Fight
SPELL_FROST_AURA_10 = 28531,
SPELL_FROST_AURA_25 = 55799,
SPELL_CLEAVE = 19983,
SPELL_TAIL_SWEEP_10 = 55697,
SPELL_TAIL_SWEEP_25 = 55696,
SPELL_SUMMON_BLIZZARD = 28560,
SPELL_LIFE_DRAIN_10 = 28542,
SPELL_LIFE_DRAIN_25 = 55665,
SPELL_BERSERK = 26662,
// Ice block
SPELL_ICEBOLT_CAST = 28526,
SPELL_ICEBOLT_TRIGGER = 28522,
SPELL_FROST_MISSILE = 30101,
SPELL_FROST_EXPLOSION = 28524,
// Visuals
SPELL_SAPPHIRON_DIES = 29357
};
enum Misc
{
GO_ICE_BLOCK = 181247,
NPC_BLIZZARD = 16474,
POINT_CENTER = 1
};
enum Events
{
EVENT_BERSERK = 1,
EVENT_CLEAVE = 2,
EVENT_TAIL_SWEEP = 3,
EVENT_LIFE_DRAIN = 4,
EVENT_BLIZZARD = 5,
EVENT_FLIGHT_START = 6,
EVENT_FLIGHT_LIFTOFF = 7,
EVENT_FLIGHT_ICEBOLT = 8,
EVENT_FLIGHT_BREATH = 9,
EVENT_FLIGHT_SPELL_EXPLOSION = 10,
EVENT_FLIGHT_START_LAND = 11,
EVENT_LAND = 12,
EVENT_GROUND = 13,
EVENT_HUNDRED_CLUB = 14
};
class boss_sapphiron : public CreatureScript
{
public:
boss_sapphiron() : CreatureScript("boss_sapphiron") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_sapphironAI>(pCreature);
}
struct boss_sapphironAI : public BossAI
{
explicit boss_sapphironAI(Creature* c) : BossAI(c, BOSS_SAPPHIRON)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
InstanceScript* pInstance;
uint8 iceboltCount{};
uint32 spawnTimer{};
GuidList blockList;
ObjectGuid currentTarget;
void InitializeAI() override
{
me->SummonGameObject(GO_SAPPHIRON_BIRTH, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, 0, 0, 0, 0, 0);
me->SetVisible(false);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
ScriptedAI::InitializeAI();
}
bool IsInRoom()
{
if (me->GetExactDist(3523.5f, -5235.3f, 137.6f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
if (me->IsVisible())
{
me->SetReactState(REACT_AGGRESSIVE);
}
events.Reset();
iceboltCount = 0;
spawnTimer = 0;
currentTarget.Clear();
blockList.clear();
}
void EnterCombatSelfFunction()
{
Map::PlayerList const& PlList = me->GetMap()->GetPlayers();
if (PlList.IsEmpty())
return;
for (const auto& i : PlList)
{
if (Player* player = i.GetSource())
{
if (player->IsGameMaster())
continue;
if (player->IsAlive() && me->GetDistance(player) < 80.0f)
{
me->SetInCombatWith(player);
player->SetInCombatWith(me);
me->AddThreat(player, 0.0f);
}
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
EnterCombatSelfFunction();
me->CastSpell(me, RAID_MODE(SPELL_FROST_AURA_10, SPELL_FROST_AURA_25), true);
events.ScheduleEvent(EVENT_BERSERK, 15min);
events.ScheduleEvent(EVENT_CLEAVE, 5s);
events.ScheduleEvent(EVENT_TAIL_SWEEP, 10s);
events.ScheduleEvent(EVENT_LIFE_DRAIN, 17s);
events.ScheduleEvent(EVENT_BLIZZARD, 17s);
events.ScheduleEvent(EVENT_FLIGHT_START, 45s);
events.ScheduleEvent(EVENT_HUNDRED_CLUB, 5s);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
me->CastSpell(me, SPELL_SAPPHIRON_DIES, true);
}
void DoAction(int32 param) override
{
if (param == ACTION_SAPPHIRON_BIRTH)
{
spawnTimer = 1;
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE && id == POINT_CENTER)
{
events.ScheduleEvent(EVENT_FLIGHT_LIFTOFF, 500ms);
}
}
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_ICEBOLT_CAST)
{
me->CastSpell(target, SPELL_ICEBOLT_TRIGGER, true);
}
}
bool IsValidExplosionTarget(WorldObject* target)
{
for (ObjectGuid const& guid : blockList)
{
if (target->GetGUID() == guid)
return false;
if (Unit* block = ObjectAccessor::GetUnit(*me, guid))
{
if (block->IsInBetween(me, target, 2.0f) && block->IsWithinDist(target, 10.0f))
return false;
}
}
return true;
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
if (spawnTimer)
{
spawnTimer += diff;
if (spawnTimer >= 21500)
{
me->SetVisible(true);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_AGGRESSIVE);
spawnTimer = 0;
}
return;
}
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_BERSERK:
Talk(EMOTE_ENRAGE);
me->CastSpell(me, SPELL_BERSERK, true);
return;
case EVENT_CLEAVE:
me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
events.Repeat(10s);
return;
case EVENT_TAIL_SWEEP:
me->CastSpell(me, RAID_MODE(SPELL_TAIL_SWEEP_10, SPELL_TAIL_SWEEP_25), false);
events.Repeat(10s);
return;
case EVENT_LIFE_DRAIN:
me->CastCustomSpell(RAID_MODE(SPELL_LIFE_DRAIN_10, SPELL_LIFE_DRAIN_25), SPELLVALUE_MAX_TARGETS, RAID_MODE(2, 5), me, false);
events.Repeat(24s);
return;
case EVENT_BLIZZARD:
{
Creature* cr;
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true))
{
cr = me->SummonCreature(NPC_BLIZZARD, *target, TEMPSUMMON_TIMED_DESPAWN, 16000);
}
else
{
cr = me->SummonCreature(NPC_BLIZZARD, *me, TEMPSUMMON_TIMED_DESPAWN, 16000);
}
if (cr)
{
cr->GetMotionMaster()->MoveRandom(40);
}
events.RepeatEvent(RAID_MODE(8000, 6500));
return;
}
case EVENT_FLIGHT_START:
if (me->HealthBelowPct(11))
{
return;
}
events.Repeat(45s);
events.DelayEvents(35s);
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
float x, y, z, o;
me->GetHomePosition(x, y, z, o);
me->GetMotionMaster()->MovePoint(POINT_CENTER, x, y, z);
return;
case EVENT_FLIGHT_LIFTOFF:
Talk(EMOTE_AIR_PHASE);
me->GetMotionMaster()->MoveIdle();
me->SendMeleeAttackStop(me->GetVictim());
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
currentTarget.Clear();
events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, 3s);
iceboltCount = RAID_MODE(2, 3);
return;
case EVENT_FLIGHT_ICEBOLT:
{
if (currentTarget)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, currentTarget))
{
me->SummonGameObject(GO_ICE_BLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0);
}
}
std::vector<Unit*> targets;
auto i = me->GetThreatMgr().GetThreatList().begin();
for (; i != me->GetThreatMgr().GetThreatList().end(); ++i)
{
if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER)
{
bool inList = false;
if (!blockList.empty())
{
for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
{
if ((*i)->getTarget()->GetGUID() == *itr)
{
inList = true;
break;
}
}
}
if (!inList)
{
targets.push_back((*i)->getTarget());
}
}
}
if (!targets.empty() && iceboltCount)
{
auto itr = targets.begin();
advance(itr, urand(0, targets.size() - 1));
me->CastSpell(*itr, SPELL_ICEBOLT_CAST, false);
blockList.push_back((*itr)->GetGUID());
currentTarget = (*itr)->GetGUID();
--iceboltCount;
events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, (me->GetExactDist(*itr) / 13.0f)*IN_MILLISECONDS);
}
else
{
events.ScheduleEvent(EVENT_FLIGHT_BREATH, 1s);
}
return;
}
case EVENT_FLIGHT_BREATH:
currentTarget.Clear();
Talk(EMOTE_BREATH);
me->CastSpell(me, SPELL_FROST_MISSILE, false);
events.ScheduleEvent(EVENT_FLIGHT_SPELL_EXPLOSION, 8500ms);
return;
case EVENT_FLIGHT_SPELL_EXPLOSION:
me->CastSpell(me, SPELL_FROST_EXPLOSION, true);
events.ScheduleEvent(EVENT_FLIGHT_START_LAND, 3s);
return;
case EVENT_FLIGHT_START_LAND:
if (!blockList.empty())
{
for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
{
if (Unit* block = ObjectAccessor::GetUnit(*me, *itr))
{
block->RemoveAurasDueToSpell(SPELL_ICEBOLT_TRIGGER);
}
}
}
blockList.clear();
me->RemoveAllGameObjects();
events.ScheduleEvent(EVENT_LAND, 1s);
return;
case EVENT_LAND:
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
events.ScheduleEvent(EVENT_GROUND, 1500ms);
return;
case EVENT_GROUND:
Talk(EMOTE_GROUND_PHASE);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
return;
case EVENT_HUNDRED_CLUB:
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
if (itr.GetSource()->GetResistance(SPELL_SCHOOL_FROST) > 100 && pInstance)
{
pInstance->SetData(DATA_HUNDRED_CLUB, 0);
return;
}
}
events.Repeat(5s);
return;
}
}
DoMeleeAttackIfReady();
}
};
};
class spell_sapphiron_frost_explosion : public SpellScriptLoader
{
public:
spell_sapphiron_frost_explosion() : SpellScriptLoader("spell_sapphiron_frost_explosion") { }
class spell_sapphiron_frost_explosion_SpellScript : public SpellScript
{
PrepareSpellScript(spell_sapphiron_frost_explosion_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
if (!caster || !caster->ToCreature())
return;
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (CAST_AI(boss_sapphiron::boss_sapphironAI, caster->ToCreature()->AI())->IsValidExplosionTarget(target))
{
tmplist.push_back(target);
}
}
targets.clear();
for (auto& itr : tmplist)
{
targets.push_back(itr);
}
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sapphiron_frost_explosion_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_sapphiron_frost_explosion_SpellScript();
}
};
#include "boss_sapphiron.h"
void AddSC_boss_sapphiron()
{

View File

@@ -0,0 +1,447 @@
#ifndef BOSS_SAPPHIRON_H_
#define BOSS_SAPPHIRON_H_
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum SapphironYells
{
EMOTE_AIR_PHASE = 0,
EMOTE_GROUND_PHASE = 1,
EMOTE_BREATH = 2,
SAPPHIRON_EMOTE_ENRAGE = 3
};
enum SapphironSpells
{
// Fight
SPELL_FROST_AURA_10 = 28531,
SPELL_FROST_AURA_25 = 55799,
SPELL_CLEAVE = 19983,
SPELL_TAIL_SWEEP_10 = 55697,
SPELL_TAIL_SWEEP_25 = 55696,
SPELL_SUMMON_BLIZZARD = 28560,
SPELL_LIFE_DRAIN_10 = 28542,
SPELL_LIFE_DRAIN_25 = 55665,
SPELL_BERSERK = 26662,
// Ice block
SPELL_ICEBOLT_CAST = 28526,
SPELL_ICEBOLT_TRIGGER = 28522,
SPELL_FROST_MISSILE = 30101,
SPELL_FROST_EXPLOSION = 28524,
// Visuals
SPELL_SAPPHIRON_DIES = 29357
};
enum SapphironMisc
{
GO_ICE_BLOCK = 181247,
NPC_BLIZZARD = 16474,
POINT_CENTER = 1
};
enum SapphironEvents
{
SAPPHIRON_EVENT_BERSERK = 1,
EVENT_CLEAVE = 2,
EVENT_TAIL_SWEEP = 3,
EVENT_LIFE_DRAIN = 4,
EVENT_BLIZZARD = 5,
EVENT_FLIGHT_START = 6,
EVENT_FLIGHT_LIFTOFF = 7,
EVENT_FLIGHT_ICEBOLT = 8,
EVENT_FLIGHT_BREATH = 9,
EVENT_FLIGHT_SPELL_EXPLOSION = 10,
EVENT_FLIGHT_START_LAND = 11,
EVENT_LAND = 12,
EVENT_GROUND = 13,
EVENT_HUNDRED_CLUB = 14
};
class boss_sapphiron : public CreatureScript
{
public:
boss_sapphiron() : CreatureScript("boss_sapphiron") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_sapphironAI>(pCreature);
}
struct boss_sapphironAI : public BossAI
{
explicit boss_sapphironAI(Creature* c) : BossAI(c, BOSS_SAPPHIRON)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
InstanceScript* pInstance;
uint8 iceboltCount{};
uint32 spawnTimer{};
GuidList blockList;
ObjectGuid currentTarget;
void InitializeAI() override
{
me->SummonGameObject(GO_SAPPHIRON_BIRTH, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, 0, 0, 0, 0, 0);
me->SetVisible(false);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
ScriptedAI::InitializeAI();
}
bool IsInRoom()
{
if (me->GetExactDist(3523.5f, -5235.3f, 137.6f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
if (me->IsVisible())
{
me->SetReactState(REACT_AGGRESSIVE);
}
events.Reset();
iceboltCount = 0;
spawnTimer = 0;
currentTarget.Clear();
blockList.clear();
}
void EnterCombatSelfFunction()
{
Map::PlayerList const& PlList = me->GetMap()->GetPlayers();
if (PlList.IsEmpty())
return;
for (const auto& i : PlList)
{
if (Player* player = i.GetSource())
{
if (player->IsGameMaster())
continue;
if (player->IsAlive() && me->GetDistance(player) < 80.0f)
{
me->SetInCombatWith(player);
player->SetInCombatWith(me);
me->AddThreat(player, 0.0f);
}
}
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
EnterCombatSelfFunction();
me->CastSpell(me, RAID_MODE(SPELL_FROST_AURA_10, SPELL_FROST_AURA_25), true);
events.ScheduleEvent(SAPPHIRON_EVENT_BERSERK, 15min);
events.ScheduleEvent(EVENT_CLEAVE, 5s);
events.ScheduleEvent(EVENT_TAIL_SWEEP, 10s);
events.ScheduleEvent(EVENT_LIFE_DRAIN, 17s);
events.ScheduleEvent(EVENT_BLIZZARD, 17s);
events.ScheduleEvent(EVENT_FLIGHT_START, 45s);
events.ScheduleEvent(EVENT_HUNDRED_CLUB, 5s);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
me->CastSpell(me, SPELL_SAPPHIRON_DIES, true);
}
void DoAction(int32 param) override
{
if (param == ACTION_SAPPHIRON_BIRTH)
{
spawnTimer = 1;
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE && id == POINT_CENTER)
{
events.ScheduleEvent(EVENT_FLIGHT_LIFTOFF, 500ms);
}
}
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_ICEBOLT_CAST)
{
me->CastSpell(target, SPELL_ICEBOLT_TRIGGER, true);
}
}
bool IsValidExplosionTarget(WorldObject* target)
{
for (ObjectGuid const& guid : blockList)
{
if (target->GetGUID() == guid)
return false;
if (Unit* block = ObjectAccessor::GetUnit(*me, guid))
{
if (block->IsInBetween(me, target, 2.0f) && block->IsWithinDist(target, 10.0f))
return false;
}
}
return true;
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
if (spawnTimer)
{
spawnTimer += diff;
if (spawnTimer >= 21500)
{
me->SetVisible(true);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_AGGRESSIVE);
spawnTimer = 0;
}
return;
}
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case SAPPHIRON_EVENT_BERSERK:
Talk(SAPPHIRON_EMOTE_ENRAGE);
me->CastSpell(me, SPELL_BERSERK, true);
return;
case EVENT_CLEAVE:
me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
events.Repeat(10s);
return;
case EVENT_TAIL_SWEEP:
me->CastSpell(me, RAID_MODE(SPELL_TAIL_SWEEP_10, SPELL_TAIL_SWEEP_25), false);
events.Repeat(10s);
return;
case EVENT_LIFE_DRAIN:
me->CastCustomSpell(RAID_MODE(SPELL_LIFE_DRAIN_10, SPELL_LIFE_DRAIN_25), SPELLVALUE_MAX_TARGETS, RAID_MODE(2, 5), me, false);
events.Repeat(24s);
return;
case EVENT_BLIZZARD:
{
Creature* cr;
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true))
{
cr = me->SummonCreature(NPC_BLIZZARD, *target, TEMPSUMMON_TIMED_DESPAWN, 16000);
}
else
{
cr = me->SummonCreature(NPC_BLIZZARD, *me, TEMPSUMMON_TIMED_DESPAWN, 16000);
}
if (cr)
{
cr->GetMotionMaster()->MoveRandom(40);
}
events.RepeatEvent(RAID_MODE(8000, 6500));
return;
}
case EVENT_FLIGHT_START:
if (me->HealthBelowPct(11))
{
return;
}
events.Repeat(45s);
events.DelayEvents(35s);
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
float x, y, z, o;
me->GetHomePosition(x, y, z, o);
me->GetMotionMaster()->MovePoint(POINT_CENTER, x, y, z);
return;
case EVENT_FLIGHT_LIFTOFF:
Talk(EMOTE_AIR_PHASE);
me->GetMotionMaster()->MoveIdle();
me->SendMeleeAttackStop(me->GetVictim());
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
currentTarget.Clear();
events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, 3s);
iceboltCount = RAID_MODE(2, 3);
return;
case EVENT_FLIGHT_ICEBOLT:
{
if (currentTarget)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, currentTarget))
{
me->SummonGameObject(GO_ICE_BLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0);
}
}
std::vector<Unit*> targets;
auto i = me->GetThreatMgr().GetThreatList().begin();
for (; i != me->GetThreatMgr().GetThreatList().end(); ++i)
{
if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER)
{
bool inList = false;
if (!blockList.empty())
{
for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
{
if ((*i)->getTarget()->GetGUID() == *itr)
{
inList = true;
break;
}
}
}
if (!inList)
{
targets.push_back((*i)->getTarget());
}
}
}
if (!targets.empty() && iceboltCount)
{
auto itr = targets.begin();
advance(itr, urand(0, targets.size() - 1));
me->CastSpell(*itr, SPELL_ICEBOLT_CAST, false);
blockList.push_back((*itr)->GetGUID());
currentTarget = (*itr)->GetGUID();
--iceboltCount;
events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, (me->GetExactDist(*itr) / 13.0f)*IN_MILLISECONDS);
}
else
{
events.ScheduleEvent(EVENT_FLIGHT_BREATH, 1s);
}
return;
}
case EVENT_FLIGHT_BREATH:
currentTarget.Clear();
Talk(EMOTE_BREATH);
me->CastSpell(me, SPELL_FROST_MISSILE, false);
events.ScheduleEvent(EVENT_FLIGHT_SPELL_EXPLOSION, 8500ms);
return;
case EVENT_FLIGHT_SPELL_EXPLOSION:
me->CastSpell(me, SPELL_FROST_EXPLOSION, true);
events.ScheduleEvent(EVENT_FLIGHT_START_LAND, 3s);
return;
case EVENT_FLIGHT_START_LAND:
if (!blockList.empty())
{
for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
{
if (Unit* block = ObjectAccessor::GetUnit(*me, *itr))
{
block->RemoveAurasDueToSpell(SPELL_ICEBOLT_TRIGGER);
}
}
}
blockList.clear();
me->RemoveAllGameObjects();
events.ScheduleEvent(EVENT_LAND, 1s);
return;
case EVENT_LAND:
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
events.ScheduleEvent(EVENT_GROUND, 1500ms);
return;
case EVENT_GROUND:
Talk(EMOTE_GROUND_PHASE);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
return;
case EVENT_HUNDRED_CLUB:
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
if (itr.GetSource()->GetResistance(SPELL_SCHOOL_FROST) > 100 && pInstance)
{
pInstance->SetData(DATA_HUNDRED_CLUB, 0);
return;
}
}
events.Repeat(5s);
return;
}
}
DoMeleeAttackIfReady();
}
};
};
class spell_sapphiron_frost_explosion : public SpellScriptLoader
{
public:
spell_sapphiron_frost_explosion() : SpellScriptLoader("spell_sapphiron_frost_explosion") { }
class spell_sapphiron_frost_explosion_SpellScript : public SpellScript
{
PrepareSpellScript(spell_sapphiron_frost_explosion_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
if (!caster || !caster->ToCreature())
return;
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (CAST_AI(boss_sapphiron::boss_sapphironAI, caster->ToCreature()->AI())->IsValidExplosionTarget(target))
{
tmplist.push_back(target);
}
}
targets.clear();
for (auto& itr : tmplist)
{
targets.push_back(itr);
}
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sapphiron_frost_explosion_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_sapphiron_frost_explosion_SpellScript();
}
};
#endif

View File

@@ -15,754 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Says
{
// Stalagg
SAY_STAL_AGGRO = 0,
SAY_STAL_SLAY = 1,
SAY_STAL_DEATH = 2,
EMOTE_STAL_DEATH = 3,
EMOTE_STAL_REVIVE = 4,
// Feugen
SAY_FEUG_AGGRO = 0,
SAY_FEUG_SLAY = 1,
SAY_FEUG_DEATH = 2,
EMOTE_FEUG_DEATH = 3,
EMOTE_FEUG_REVIVE = 4,
// Thaddius
SAY_GREET = 0,
SAY_AGGRO = 1,
SAY_SLAY = 2,
SAY_ELECT = 3,
SAY_DEATH = 4,
EMOTE_POLARITY_SHIFTED = 6,
// Tesla Coil
EMOTE_TESLA_LINK_BREAKS = 0,
EMOTE_TESLA_OVERLOAD = 1
};
enum Spells
{
SPELL_MAGNETIC_PULL = 28337,
SPELL_TESLA_SHOCK = 28099,
SPELL_SHOCK_VISUAL = 28159,
// Stalagg
SPELL_POWER_SURGE_10 = 54529,
SPELL_POWER_SURGE_25 = 28134,
SPELL_STALAGG_CHAIN = 28096,
// Feugen
SPELL_STATIC_FIELD_10 = 28135,
SPELL_STATIC_FIELD_25 = 54528,
SPELL_FEUGEN_CHAIN = 28111,
// Thaddius
SPELL_POLARITY_SHIFT = 28089,
SPELL_BALL_LIGHTNING = 28299,
SPELL_CHAIN_LIGHTNING_10 = 28167,
SPELL_CHAIN_LIGHTNING_25 = 54531,
SPELL_BERSERK = 27680,
SPELL_THADDIUS_VISUAL_LIGHTNING = 28136,
SPELL_THADDIUS_SPAWN_STUN = 28160,
SPELL_POSITIVE_CHARGE = 28062,
SPELL_POSITIVE_CHARGE_STACK = 29659,
SPELL_NEGATIVE_CHARGE = 28085,
SPELL_NEGATIVE_CHARGE_STACK = 29660,
SPELL_POSITIVE_POLARITY = 28059,
SPELL_NEGATIVE_POLARITY = 28084
};
enum Events
{
EVENT_MINION_POWER_SURGE = 1,
EVENT_MINION_MAGNETIC_PULL = 2,
EVENT_MINION_CHECK_DISTANCE = 3,
EVENT_MINION_STATIC_FIELD = 4,
EVENT_THADDIUS_INIT = 5,
EVENT_THADDIUS_ENTER_COMBAT = 6,
EVENT_THADDIUS_CHAIN_LIGHTNING = 7,
EVENT_THADDIUS_BERSERK = 8,
EVENT_THADDIUS_POLARITY_SHIFT = 9,
EVENT_ALLOW_BALL_LIGHTNING = 10
};
enum Misc
{
ACTION_MAGNETIC_PULL = 1,
ACTION_SUMMON_DIED = 2,
ACTION_RESTORE = 3,
GO_TESLA_COIL_LEFT = 181478,
GO_TESLA_COIL_RIGHT = 181477,
NPC_TESLA_COIL = 16218
};
class boss_thaddius : public CreatureScript
{
public:
boss_thaddius() : CreatureScript("boss_thaddius") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_thaddiusAI>(pCreature);
}
struct boss_thaddiusAI : public BossAI
{
explicit boss_thaddiusAI(Creature* c) : BossAI(c, BOSS_THADDIUS), summons(me), ballLightningEnabled(false)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
uint32 summonTimer{};
uint32 reviveTimer{};
uint32 resetTimer{};
bool ballLightningEnabled;
void DoAction(int32 param) override
{
if (param == ACTION_SUMMON_DIED)
{
if (summonTimer)
{
summonTimer = 0;
reviveTimer = 1;
return;
}
summonTimer = 1;
}
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetControlled(true, UNIT_STATE_ROOT);
summonTimer = 0;
reviveTimer = 0;
resetTimer = 1;
me->SetPosition(me->GetHomePosition());
ballLightningEnabled = false;
me->SummonCreature(NPC_STALAGG, 3450.45f, -2931.42f, 312.091f, 5.49779f);
me->SummonCreature(NPC_FEUGEN, 3508.14f, -2988.65f, 312.092f, 2.37365f);
if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3527.34f, -2951.56f, 318.75f, 0.0f))
{
cr->RemoveAllAuras();
cr->InterruptNonMeleeSpells(true);
cr->CastSpell(cr, SPELL_FEUGEN_CHAIN, false);
cr->SetDisableGravity(true);
cr->SetImmuneToPC(false);
cr->SetControlled(true, UNIT_STATE_ROOT);
}
if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3487.04f, -2911.68f, 318.75f, 0.0f))
{
cr->RemoveAllAuras();
cr->InterruptNonMeleeSpells(true);
cr->CastSpell(cr, SPELL_STALAGG_CHAIN, false);
cr->SetDisableGravity(true);
cr->SetImmuneToPC(false);
cr->SetControlled(true, UNIT_STATE_ROOT);
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_LEFT, 100.0f))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_RIGHT, 100.0f))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
if (pInstance->GetBossState(BOSS_GLUTH) == DONE)
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
if (pInstance)
{
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
summons.DoZoneInCombat(NPC_FEUGEN);
summons.DoZoneInCombat(NPC_STALAGG);
}
void UpdateAI(uint32 diff) override
{
if (resetTimer)
{
resetTimer += diff;
if (resetTimer > 1000)
{
resetTimer = 0;
me->CastSpell(me, SPELL_THADDIUS_SPAWN_STUN, true);
}
return;
}
if (reviveTimer)
{
reviveTimer += diff;
if (reviveTimer >= 12000)
{
for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, (*itr)))
{
if (cr->GetEntry() == NPC_TESLA_COIL)
{
cr->AI()->Talk(EMOTE_TESLA_OVERLOAD);
cr->CastSpell(me, SPELL_SHOCK_VISUAL, true);
}
}
}
reviveTimer = 0;
events.ScheduleEvent(EVENT_THADDIUS_INIT, 750ms);
}
return;
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (summonTimer) // Revive
{
summonTimer += diff;
if (summonTimer >= 5000)
{
summons.DoAction(ACTION_RESTORE);
summonTimer = 0;
}
}
switch (events.ExecuteEvent())
{
case EVENT_THADDIUS_INIT:
{
me->RemoveAllAuras();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, (*itr)))
{
if (cr->GetEntry() == NPC_TESLA_COIL)
{
Unit::Kill(cr, cr);
}
}
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_LEFT, 100.0f))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_RIGHT, 100.0f))
{
go->SetGoState(GO_STATE_READY);
}
me->CastSpell(me, SPELL_THADDIUS_VISUAL_LIGHTNING, true);
events.ScheduleEvent(EVENT_THADDIUS_ENTER_COMBAT, 1s);
break;
}
case EVENT_THADDIUS_ENTER_COMBAT:
Talk(SAY_AGGRO);
me->SetReactState(REACT_AGGRESSIVE);
me->SetControlled(false, UNIT_STATE_STUNNED);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
events.ScheduleEvent(EVENT_THADDIUS_CHAIN_LIGHTNING, 14s);
events.ScheduleEvent(EVENT_THADDIUS_BERSERK, 6min);
events.ScheduleEvent(EVENT_THADDIUS_POLARITY_SHIFT, 30s);
events.ScheduleEvent(EVENT_ALLOW_BALL_LIGHTNING, 5s);
return;
case EVENT_THADDIUS_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_THADDIUS_CHAIN_LIGHTNING:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_CHAIN_LIGHTNING_10, SPELL_CHAIN_LIGHTNING_25), false);
events.Repeat(15s);
break;
case EVENT_THADDIUS_POLARITY_SHIFT:
me->CastSpell(me, SPELL_POLARITY_SHIFT, false);
events.Repeat(30s);
break;
case EVENT_ALLOW_BALL_LIGHTNING:
ballLightningEnabled = true;
break;
}
if (me->IsWithinMeleeRange(me->GetVictim()))
{
DoMeleeAttackIfReady();
}
else if (ballLightningEnabled)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat))
{
me->CastSpell(target, SPELL_BALL_LIGHTNING, false);
}
}
}
};
};
class boss_thaddius_summon : public CreatureScript
{
public:
boss_thaddius_summon() : CreatureScript("boss_thaddius_summon") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_thaddius_summonAI>(pCreature);
}
struct boss_thaddius_summonAI : public ScriptedAI
{
explicit boss_thaddius_summonAI(Creature* c) : ScriptedAI(c)
{
pInstance = me->GetInstanceScript();
overload = false;
}
InstanceScript* pInstance;
EventMap events;
uint32 pullTimer{};
uint32 visualTimer{};
bool overload;
ObjectGuid myCoil;
void Reset() override
{
pullTimer = 0;
visualTimer = 1;
overload = false;
events.Reset();
me->SetControlled(false, UNIT_STATE_STUNNED);
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
{
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
cr->SetImmuneToPC(false);
myCoil = cr->GetGUID();
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetControlled(false, UNIT_STATE_STUNNED);
ScriptedAI::EnterEvadeMode(why);
}
void JustEngagedWith(Unit* pWho) override
{
me->SetInCombatWithZone();
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.f, true))
{
myCoil = cr->GetGUID();
}
if (me->GetEntry() == NPC_STALAGG)
{
events.ScheduleEvent(EVENT_MINION_POWER_SURGE, 10s);
Talk(SAY_STAL_AGGRO);
}
else
{
events.ScheduleEvent(EVENT_MINION_STATIC_FIELD, 5s);
Talk(SAY_FEUG_AGGRO);
}
events.ScheduleEvent(EVENT_MINION_CHECK_DISTANCE, 5s);
if (me->GetEntry() == NPC_STALAGG) // This event needs synchronisation, called for stalagg only
{
events.ScheduleEvent(EVENT_MINION_MAGNETIC_PULL, 20s);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_THADDIUS_BOSS)))
{
cr->AI()->AttackStart(pWho);
cr->AddThreat(pWho, 10.0f);
}
}
}
void DoAction(int32 param) override
{
if (param == ACTION_MAGNETIC_PULL)
{
pullTimer = 1;
me->SetControlled(true, UNIT_STATE_STUNNED);
}
else if (param == ACTION_RESTORE)
{
if (!me->IsAlive())
{
me->Respawn();
me->SetInCombatWithZone();
Talk(me->GetEntry() == NPC_STALAGG ? EMOTE_STAL_REVIVE : EMOTE_FEUG_REVIVE);
}
else
{
me->SetHealth(me->GetMaxHealth());
}
}
}
void JustDied(Unit* /*killer*/) override
{
Talk(me->GetEntry() == NPC_STALAGG ? SAY_STAL_DEATH : SAY_FEUG_DEATH);
Talk(me->GetEntry() == NPC_STALAGG ? EMOTE_STAL_DEATH : EMOTE_FEUG_DEATH);
if (pInstance)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_THADDIUS_BOSS)))
{
cr->AI()->DoAction(ACTION_SUMMON_DIED);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
if (!urand(0, 2))
{
Talk(me->GetEntry() == NPC_STALAGG ? SAY_STAL_SLAY : SAY_FEUG_SLAY);
}
}
void UpdateAI(uint32 diff) override
{
if (visualTimer)
{
visualTimer += diff;
if (visualTimer >= 3000)
{
visualTimer = 0;
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
{
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
}
}
}
if (!UpdateVictim())
return;
if (pullTimer) // Disable AI during pull
{
pullTimer += diff;
if (pullTimer >= 3000)
{
me->SetControlled(false, UNIT_STATE_STUNNED);
pullTimer = 0;
}
return;
}
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MINION_POWER_SURGE:
me->CastSpell(me, RAID_MODE(SPELL_POWER_SURGE_10, SPELL_POWER_SURGE_25), false);
events.Repeat(19s);
break;
case EVENT_MINION_STATIC_FIELD:
me->CastSpell(me, RAID_MODE(SPELL_STATIC_FIELD_10, SPELL_STATIC_FIELD_25), false);
events.Repeat(3s);
break;
case EVENT_MINION_MAGNETIC_PULL:
events.Repeat(20s);
if (pInstance)
{
if (Creature* feugen = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_FEUGEN_BOSS)))
{
if (!feugen->IsAlive() || !feugen->GetVictim() || !me->GetVictim())
return;
float threatFeugen = feugen->GetThreatMgr().GetThreat(feugen->GetVictim());
float threatStalagg = me->GetThreatMgr().GetThreat(me->GetVictim());
Unit* tankFeugen = feugen->GetVictim();
Unit* tankStalagg = me->GetVictim();
feugen->GetThreatMgr().ModifyThreatByPercent(tankFeugen, -100);
feugen->AddThreat(tankStalagg, threatFeugen);
feugen->CastSpell(tankStalagg, SPELL_MAGNETIC_PULL, true);
feugen->AI()->DoAction(ACTION_MAGNETIC_PULL);
me->GetThreatMgr().ModifyThreatByPercent(tankStalagg, -100);
me->AddThreat(tankFeugen, threatStalagg);
me->CastSpell(tankFeugen, SPELL_MAGNETIC_PULL, true);
DoAction(ACTION_MAGNETIC_PULL);
}
}
break;
case EVENT_MINION_CHECK_DISTANCE:
if (Creature* cr = ObjectAccessor::GetCreature(*me, myCoil))
{
if (!me->GetHomePosition().IsInDist(me, 28) && me->IsInCombat())
{
if (!overload)
{
overload = true;
cr->AI()->Talk(EMOTE_TESLA_LINK_BREAKS);
me->RemoveAurasDueToSpell(me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN);
cr->InterruptNonMeleeSpells(true);
}
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 1000.f, true))
{
cr->CastStop(SPELL_TESLA_SHOCK);
cr->CastSpell(target, SPELL_TESLA_SHOCK, true);
}
events.Repeat(1500ms);
break;
}
else
{
overload = false;
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
}
}
events.Repeat(5s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_thaddius_pos_neg_charge : public SpellScriptLoader
{
public:
spell_thaddius_pos_neg_charge() : SpellScriptLoader("spell_thaddius_pos_neg_charge") { }
class spell_thaddius_pos_neg_charge_SpellScript : public SpellScript
{
PrepareSpellScript(spell_thaddius_pos_neg_charge_SpellScript);
void HandleTargets(std::list<WorldObject*>& targets)
{
uint8 count = 0;
for (auto& ihit : targets)
{
if (ihit->GetGUID() != GetCaster()->GetGUID())
{
if (Player* target = ihit->ToPlayer())
{
if (target->HasAura(GetTriggeringSpell()->Id))
{
++count;
}
}
}
}
if (count)
{
uint32 spellId = GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE ? SPELL_POSITIVE_CHARGE_STACK : SPELL_NEGATIVE_CHARGE_STACK;
GetCaster()->SetAuraStack(spellId, GetCaster(), count);
}
}
void HandleDamage(SpellEffIndex /*effIndex*/)
{
if (!GetTriggeringSpell())
return;
Unit* target = GetHitUnit();
if (!target)
return;
if (target->HasAura(GetTriggeringSpell()->Id) || target->GetTypeId() != TYPEID_PLAYER)
{
SetHitDamage(0);
}
else if (target->GetInstanceScript())
{
target->GetInstanceScript()->SetData(DATA_CHARGES_CROSSED, 0);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_thaddius_pos_neg_charge_SpellScript();
}
};
class spell_thaddius_polarity_shift : public SpellScriptLoader
{
public:
spell_thaddius_polarity_shift() : SpellScriptLoader("spell_thaddius_polarity_shift") { }
class spell_thaddius_polarity_shift_SpellScript : public SpellScript
{
PrepareSpellScript(spell_thaddius_polarity_shift_SpellScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_POSITIVE_POLARITY, SPELL_NEGATIVE_POLARITY });
}
void HandleDummy(SpellEffIndex /* effIndex */)
{
Unit* caster = GetCaster();
if (Unit* target = GetHitUnit())
{
target->RemoveAurasDueToSpell(SPELL_POSITIVE_CHARGE_STACK);
target->RemoveAurasDueToSpell(SPELL_NEGATIVE_CHARGE_STACK);
target->CastSpell(target, roll_chance_i(50) ? SPELL_POSITIVE_POLARITY : SPELL_NEGATIVE_POLARITY, true, nullptr, nullptr, caster->GetGUID());
}
}
void HandleAfterCast()
{
if (GetCaster())
{
if (Creature* caster = GetCaster()->ToCreature())
{
if (caster->GetEntry() == NPC_THADDIUS)
{
caster->AI()->Talk(SAY_ELECT);
caster->AI()->Talk(EMOTE_POLARITY_SHIFTED);
}
}
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_thaddius_polarity_shift_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
AfterCast += SpellCastFn(spell_thaddius_polarity_shift_SpellScript::HandleAfterCast);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_thaddius_polarity_shift_SpellScript();
}
};
class npc_tesla : public CreatureScript
{
public:
npc_tesla() : CreatureScript("npc_tesla") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetNaxxramasAI<npc_teslaAI>(creature);
}
struct npc_teslaAI : public ScriptedAI
{
public:
npc_teslaAI(Creature* creature) : ScriptedAI(creature) { }
void EnterEvadeMode(EvadeReason /*why*/) override { } // never stop casting due to evade
void UpdateAI(uint32 /*diff*/) override { } // never do anything unless told
void JustEngagedWith(Unit* /*who*/) override { }
void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType, SpellSchoolMask) override { damage = 0; } // no, you can't kill it
};
};
class at_thaddius_entrance : public AreaTriggerScript
{
public:
at_thaddius_entrance() : AreaTriggerScript("at_thaddius_entrance") { }
bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override
{
InstanceScript* instance = player->GetInstanceScript();
if (!instance || instance->GetData(DATA_HAD_THADDIUS_GREET) || instance->GetBossState(BOSS_THADDIUS) == DONE)
return true;
if (Creature* thaddius = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THADDIUS_BOSS)))
{
thaddius->AI()->Talk(SAY_GREET);
}
instance->SetData(DATA_HAD_THADDIUS_GREET, 1);
return true;
}
};
#include "boss_thaddius.h"
void AddSC_boss_thaddius()
{

View File

@@ -0,0 +1,753 @@
#ifndef BOSS_THADDIUS_H_
#define BOSS_THADDIUS_H_
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum ThaddiusSays
{
// Stalagg
SAY_STAL_AGGRO = 0,
SAY_STAL_SLAY = 1,
SAY_STAL_DEATH = 2,
EMOTE_STAL_DEATH = 3,
EMOTE_STAL_REVIVE = 4,
// Feugen
SAY_FEUG_AGGRO = 0,
SAY_FEUG_SLAY = 1,
SAY_FEUG_DEATH = 2,
EMOTE_FEUG_DEATH = 3,
EMOTE_FEUG_REVIVE = 4,
// Thaddius
THADDIUS_SAY_GREET = 0,
THADDIUS_SAY_AGGRO = 1,
THADDIUS_SAY_SLAY = 2,
THADDIUS_SAY_ELECT = 3,
THADDIUS_SAY_DEATH = 4,
EMOTE_POLARITY_SHIFTED = 6,
// Tesla Coil
EMOTE_TESLA_LINK_BREAKS = 0,
EMOTE_TESLA_OVERLOAD = 1
};
enum ThaddiusSpells
{
SPELL_MAGNETIC_PULL = 28337,
SPELL_TESLA_SHOCK = 28099,
SPELL_SHOCK_VISUAL = 28159,
// Stalagg
SPELL_POWER_SURGE_10 = 54529,
SPELL_POWER_SURGE_25 = 28134,
SPELL_STALAGG_CHAIN = 28096,
// Feugen
SPELL_STATIC_FIELD_10 = 28135,
SPELL_STATIC_FIELD_25 = 54528,
SPELL_FEUGEN_CHAIN = 28111,
// Thaddius
SPELL_POLARITY_SHIFT = 28089,
SPELL_BALL_LIGHTNING = 28299,
SPELL_CHAIN_LIGHTNING_10 = 28167,
SPELL_CHAIN_LIGHTNING_25 = 54531,
THADDIUS_SPELL_BERSERK = 27680,
SPELL_THADDIUS_VISUAL_LIGHTNING = 28136,
SPELL_THADDIUS_SPAWN_STUN = 28160,
SPELL_POSITIVE_CHARGE = 28062,
SPELL_POSITIVE_CHARGE_STACK = 29659,
SPELL_NEGATIVE_CHARGE = 28085,
SPELL_NEGATIVE_CHARGE_STACK = 29660,
SPELL_POSITIVE_POLARITY = 28059,
SPELL_NEGATIVE_POLARITY = 28084
};
enum ThaddiusEvents
{
EVENT_MINION_POWER_SURGE = 1,
EVENT_MINION_MAGNETIC_PULL = 2,
EVENT_MINION_CHECK_DISTANCE = 3,
EVENT_MINION_STATIC_FIELD = 4,
EVENT_THADDIUS_INIT = 5,
EVENT_THADDIUS_ENTER_COMBAT = 6,
EVENT_THADDIUS_CHAIN_LIGHTNING = 7,
EVENT_THADDIUS_BERSERK = 8,
EVENT_THADDIUS_POLARITY_SHIFT = 9,
EVENT_ALLOW_BALL_LIGHTNING = 10
};
enum ThaddiusMisc
{
ACTION_MAGNETIC_PULL = 1,
ACTION_SUMMON_DIED = 2,
ACTION_RESTORE = 3,
GO_TESLA_COIL_LEFT = 181478,
GO_TESLA_COIL_RIGHT = 181477,
NPC_TESLA_COIL = 16218
};
class boss_thaddius : public CreatureScript
{
public:
boss_thaddius() : CreatureScript("boss_thaddius") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_thaddiusAI>(pCreature);
}
struct boss_thaddiusAI : public BossAI
{
explicit boss_thaddiusAI(Creature* c) : BossAI(c, BOSS_THADDIUS), summons(me), ballLightningEnabled(false)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
uint32 summonTimer{};
uint32 reviveTimer{};
uint32 resetTimer{};
bool ballLightningEnabled;
void DoAction(int32 param) override
{
if (param == ACTION_SUMMON_DIED)
{
if (summonTimer)
{
summonTimer = 0;
reviveTimer = 1;
return;
}
summonTimer = 1;
}
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetControlled(true, UNIT_STATE_ROOT);
summonTimer = 0;
reviveTimer = 0;
resetTimer = 1;
me->SetPosition(me->GetHomePosition());
ballLightningEnabled = false;
me->SummonCreature(NPC_STALAGG, 3450.45f, -2931.42f, 312.091f, 5.49779f);
me->SummonCreature(NPC_FEUGEN, 3508.14f, -2988.65f, 312.092f, 2.37365f);
if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3527.34f, -2951.56f, 318.75f, 0.0f))
{
cr->RemoveAllAuras();
cr->InterruptNonMeleeSpells(true);
cr->CastSpell(cr, SPELL_FEUGEN_CHAIN, false);
cr->SetDisableGravity(true);
cr->SetImmuneToPC(false);
cr->SetControlled(true, UNIT_STATE_ROOT);
}
if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3487.04f, -2911.68f, 318.75f, 0.0f))
{
cr->RemoveAllAuras();
cr->InterruptNonMeleeSpells(true);
cr->CastSpell(cr, SPELL_STALAGG_CHAIN, false);
cr->SetDisableGravity(true);
cr->SetImmuneToPC(false);
cr->SetControlled(true, UNIT_STATE_ROOT);
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_LEFT, 100.0f))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_RIGHT, 100.0f))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
if (pInstance->GetBossState(BOSS_GLUTH) == DONE)
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(THADDIUS_SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(THADDIUS_SAY_DEATH);
if (pInstance)
{
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetInCombatWithZone();
summons.DoZoneInCombat(NPC_FEUGEN);
summons.DoZoneInCombat(NPC_STALAGG);
}
void UpdateAI(uint32 diff) override
{
if (resetTimer)
{
resetTimer += diff;
if (resetTimer > 1000)
{
resetTimer = 0;
me->CastSpell(me, SPELL_THADDIUS_SPAWN_STUN, true);
}
return;
}
if (reviveTimer)
{
reviveTimer += diff;
if (reviveTimer >= 12000)
{
for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, (*itr)))
{
if (cr->GetEntry() == NPC_TESLA_COIL)
{
cr->AI()->Talk(EMOTE_TESLA_OVERLOAD);
cr->CastSpell(me, SPELL_SHOCK_VISUAL, true);
}
}
}
reviveTimer = 0;
events.ScheduleEvent(EVENT_THADDIUS_INIT, 750ms);
}
return;
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (summonTimer) // Revive
{
summonTimer += diff;
if (summonTimer >= 5000)
{
summons.DoAction(ACTION_RESTORE);
summonTimer = 0;
}
}
switch (events.ExecuteEvent())
{
case EVENT_THADDIUS_INIT:
{
me->RemoveAllAuras();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, (*itr)))
{
if (cr->GetEntry() == NPC_TESLA_COIL)
{
Unit::Kill(cr, cr);
}
}
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_LEFT, 100.0f))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_RIGHT, 100.0f))
{
go->SetGoState(GO_STATE_READY);
}
me->CastSpell(me, SPELL_THADDIUS_VISUAL_LIGHTNING, true);
events.ScheduleEvent(EVENT_THADDIUS_ENTER_COMBAT, 1s);
break;
}
case EVENT_THADDIUS_ENTER_COMBAT:
Talk(THADDIUS_SAY_AGGRO);
me->SetReactState(REACT_AGGRESSIVE);
me->SetControlled(false, UNIT_STATE_STUNNED);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
events.ScheduleEvent(EVENT_THADDIUS_CHAIN_LIGHTNING, 14s);
events.ScheduleEvent(EVENT_THADDIUS_BERSERK, 6min);
events.ScheduleEvent(EVENT_THADDIUS_POLARITY_SHIFT, 30s);
events.ScheduleEvent(EVENT_ALLOW_BALL_LIGHTNING, 5s);
return;
case EVENT_THADDIUS_BERSERK:
me->CastSpell(me, THADDIUS_SPELL_BERSERK, true);
break;
case EVENT_THADDIUS_CHAIN_LIGHTNING:
me->CastSpell(me->GetVictim(), RAID_MODE(SPELL_CHAIN_LIGHTNING_10, SPELL_CHAIN_LIGHTNING_25), false);
events.Repeat(15s);
break;
case EVENT_THADDIUS_POLARITY_SHIFT:
me->CastSpell(me, SPELL_POLARITY_SHIFT, false);
events.Repeat(30s);
break;
case EVENT_ALLOW_BALL_LIGHTNING:
ballLightningEnabled = true;
break;
}
if (me->IsWithinMeleeRange(me->GetVictim()))
{
DoMeleeAttackIfReady();
}
else if (ballLightningEnabled)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat))
{
me->CastSpell(target, SPELL_BALL_LIGHTNING, false);
}
}
}
};
};
class boss_thaddius_summon : public CreatureScript
{
public:
boss_thaddius_summon() : CreatureScript("boss_thaddius_summon") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_thaddius_summonAI>(pCreature);
}
struct boss_thaddius_summonAI : public ScriptedAI
{
explicit boss_thaddius_summonAI(Creature* c) : ScriptedAI(c)
{
pInstance = me->GetInstanceScript();
overload = false;
}
InstanceScript* pInstance;
EventMap events;
uint32 pullTimer{};
uint32 visualTimer{};
bool overload;
ObjectGuid myCoil;
void Reset() override
{
pullTimer = 0;
visualTimer = 1;
overload = false;
events.Reset();
me->SetControlled(false, UNIT_STATE_STUNNED);
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
{
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
cr->SetImmuneToPC(false);
myCoil = cr->GetGUID();
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetControlled(false, UNIT_STATE_STUNNED);
ScriptedAI::EnterEvadeMode(why);
}
void JustEngagedWith(Unit* pWho) override
{
me->SetInCombatWithZone();
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.f, true))
{
myCoil = cr->GetGUID();
}
if (me->GetEntry() == NPC_STALAGG)
{
events.ScheduleEvent(EVENT_MINION_POWER_SURGE, 10s);
Talk(SAY_STAL_AGGRO);
}
else
{
events.ScheduleEvent(EVENT_MINION_STATIC_FIELD, 5s);
Talk(SAY_FEUG_AGGRO);
}
events.ScheduleEvent(EVENT_MINION_CHECK_DISTANCE, 5s);
if (me->GetEntry() == NPC_STALAGG) // This event needs synchronisation, called for stalagg only
{
events.ScheduleEvent(EVENT_MINION_MAGNETIC_PULL, 20s);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_THADDIUS_BOSS)))
{
cr->AI()->AttackStart(pWho);
cr->AddThreat(pWho, 10.0f);
}
}
}
void DoAction(int32 param) override
{
if (param == ACTION_MAGNETIC_PULL)
{
pullTimer = 1;
me->SetControlled(true, UNIT_STATE_STUNNED);
}
else if (param == ACTION_RESTORE)
{
if (!me->IsAlive())
{
me->Respawn();
me->SetInCombatWithZone();
Talk(me->GetEntry() == NPC_STALAGG ? EMOTE_STAL_REVIVE : EMOTE_FEUG_REVIVE);
}
else
{
me->SetHealth(me->GetMaxHealth());
}
}
}
void JustDied(Unit* /*killer*/) override
{
Talk(me->GetEntry() == NPC_STALAGG ? SAY_STAL_DEATH : SAY_FEUG_DEATH);
Talk(me->GetEntry() == NPC_STALAGG ? EMOTE_STAL_DEATH : EMOTE_FEUG_DEATH);
if (pInstance)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_THADDIUS_BOSS)))
{
cr->AI()->DoAction(ACTION_SUMMON_DIED);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
if (!urand(0, 2))
{
Talk(me->GetEntry() == NPC_STALAGG ? SAY_STAL_SLAY : SAY_FEUG_SLAY);
}
}
void UpdateAI(uint32 diff) override
{
if (visualTimer)
{
visualTimer += diff;
if (visualTimer >= 3000)
{
visualTimer = 0;
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
{
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
}
}
}
if (!UpdateVictim())
return;
if (pullTimer) // Disable AI during pull
{
pullTimer += diff;
if (pullTimer >= 3000)
{
me->SetControlled(false, UNIT_STATE_STUNNED);
pullTimer = 0;
}
return;
}
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MINION_POWER_SURGE:
me->CastSpell(me, RAID_MODE(SPELL_POWER_SURGE_10, SPELL_POWER_SURGE_25), false);
events.Repeat(19s);
break;
case EVENT_MINION_STATIC_FIELD:
me->CastSpell(me, RAID_MODE(SPELL_STATIC_FIELD_10, SPELL_STATIC_FIELD_25), false);
events.Repeat(3s);
break;
case EVENT_MINION_MAGNETIC_PULL:
events.Repeat(20s);
if (pInstance)
{
if (Creature* feugen = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_FEUGEN_BOSS)))
{
if (!feugen->IsAlive() || !feugen->GetVictim() || !me->GetVictim())
return;
float threatFeugen = feugen->GetThreatMgr().GetThreat(feugen->GetVictim());
float threatStalagg = me->GetThreatMgr().GetThreat(me->GetVictim());
Unit* tankFeugen = feugen->GetVictim();
Unit* tankStalagg = me->GetVictim();
feugen->GetThreatMgr().ModifyThreatByPercent(tankFeugen, -100);
feugen->AddThreat(tankStalagg, threatFeugen);
feugen->CastSpell(tankStalagg, SPELL_MAGNETIC_PULL, true);
feugen->AI()->DoAction(ACTION_MAGNETIC_PULL);
me->GetThreatMgr().ModifyThreatByPercent(tankStalagg, -100);
me->AddThreat(tankFeugen, threatStalagg);
me->CastSpell(tankFeugen, SPELL_MAGNETIC_PULL, true);
DoAction(ACTION_MAGNETIC_PULL);
}
}
break;
case EVENT_MINION_CHECK_DISTANCE:
if (Creature* cr = ObjectAccessor::GetCreature(*me, myCoil))
{
if (!me->GetHomePosition().IsInDist(me, 28) && me->IsInCombat())
{
if (!overload)
{
overload = true;
cr->AI()->Talk(EMOTE_TESLA_LINK_BREAKS);
me->RemoveAurasDueToSpell(me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN);
cr->InterruptNonMeleeSpells(true);
}
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 1000.f, true))
{
cr->CastStop(SPELL_TESLA_SHOCK);
cr->CastSpell(target, SPELL_TESLA_SHOCK, true);
}
events.Repeat(1500ms);
break;
}
else
{
overload = false;
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
}
}
events.Repeat(5s);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_thaddius_pos_neg_charge : public SpellScriptLoader
{
public:
spell_thaddius_pos_neg_charge() : SpellScriptLoader("spell_thaddius_pos_neg_charge") { }
class spell_thaddius_pos_neg_charge_SpellScript : public SpellScript
{
PrepareSpellScript(spell_thaddius_pos_neg_charge_SpellScript);
void HandleTargets(std::list<WorldObject*>& targets)
{
uint8 count = 0;
for (auto& ihit : targets)
{
if (ihit->GetGUID() != GetCaster()->GetGUID())
{
if (Player* target = ihit->ToPlayer())
{
if (target->HasAura(GetTriggeringSpell()->Id))
{
++count;
}
}
}
}
if (count)
{
uint32 spellId = GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE ? SPELL_POSITIVE_CHARGE_STACK : SPELL_NEGATIVE_CHARGE_STACK;
GetCaster()->SetAuraStack(spellId, GetCaster(), count);
}
}
void HandleDamage(SpellEffIndex /*effIndex*/)
{
if (!GetTriggeringSpell())
return;
Unit* target = GetHitUnit();
if (!target)
return;
if (target->HasAura(GetTriggeringSpell()->Id) || target->GetTypeId() != TYPEID_PLAYER)
{
SetHitDamage(0);
}
else if (target->GetInstanceScript())
{
target->GetInstanceScript()->SetData(DATA_CHARGES_CROSSED, 0);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_thaddius_pos_neg_charge_SpellScript();
}
};
class spell_thaddius_polarity_shift : public SpellScriptLoader
{
public:
spell_thaddius_polarity_shift() : SpellScriptLoader("spell_thaddius_polarity_shift") { }
class spell_thaddius_polarity_shift_SpellScript : public SpellScript
{
PrepareSpellScript(spell_thaddius_polarity_shift_SpellScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_POSITIVE_POLARITY, SPELL_NEGATIVE_POLARITY });
}
void HandleDummy(SpellEffIndex /* effIndex */)
{
Unit* caster = GetCaster();
if (Unit* target = GetHitUnit())
{
target->RemoveAurasDueToSpell(SPELL_POSITIVE_CHARGE_STACK);
target->RemoveAurasDueToSpell(SPELL_NEGATIVE_CHARGE_STACK);
target->CastSpell(target, roll_chance_i(50) ? SPELL_POSITIVE_POLARITY : SPELL_NEGATIVE_POLARITY, true, nullptr, nullptr, caster->GetGUID());
}
}
void HandleAfterCast()
{
if (GetCaster())
{
if (Creature* caster = GetCaster()->ToCreature())
{
if (caster->GetEntry() == NPC_THADDIUS)
{
caster->AI()->Talk(THADDIUS_SAY_ELECT);
caster->AI()->Talk(EMOTE_POLARITY_SHIFTED);
}
}
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_thaddius_polarity_shift_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
AfterCast += SpellCastFn(spell_thaddius_polarity_shift_SpellScript::HandleAfterCast);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_thaddius_polarity_shift_SpellScript();
}
};
class npc_tesla : public CreatureScript
{
public:
npc_tesla() : CreatureScript("npc_tesla") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetNaxxramasAI<npc_teslaAI>(creature);
}
struct npc_teslaAI : public ScriptedAI
{
public:
npc_teslaAI(Creature* creature) : ScriptedAI(creature) { }
void EnterEvadeMode(EvadeReason /*why*/) override { } // never stop casting due to evade
void UpdateAI(uint32 /*diff*/) override { } // never do anything unless told
void JustEngagedWith(Unit* /*who*/) override { }
void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType, SpellSchoolMask) override { damage = 0; } // no, you can't kill it
};
};
class at_thaddius_entrance : public AreaTriggerScript
{
public:
at_thaddius_entrance() : AreaTriggerScript("at_thaddius_entrance") { }
bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override
{
InstanceScript* instance = player->GetInstanceScript();
if (!instance || instance->GetData(DATA_HAD_THADDIUS_GREET) || instance->GetBossState(BOSS_THADDIUS) == DONE)
return true;
if (Creature* thaddius = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THADDIUS_BOSS)))
{
thaddius->AI()->Talk(THADDIUS_SAY_GREET);
}
instance->SetData(DATA_HAD_THADDIUS_GREET, 1);
return true;
}
};
#endif