вывод из самбомудлей модов для азероткор
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "naxxramas.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 Misc
|
||||
{
|
||||
// NPC_CORPSE_SCARAB = 16698,
|
||||
// NPC_CRYPT_GUARD = 16573,
|
||||
|
||||
ACHIEV_TIMED_START_EVENT = 9891
|
||||
};
|
||||
|
||||
class boss_anubrekhan_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_anubrekhan_40() : CreatureScript("boss_anubrekhan_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_anubrekhan_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_anubrekhan_40AI : public BossAI
|
||||
{
|
||||
boss_anubrekhan_40AI(Creature* c) : BossAI(c, BOSS_ANUB)
|
||||
{
|
||||
sayGreet = false;
|
||||
}
|
||||
|
||||
void 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 Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
SummonCryptGuards();
|
||||
me->m_Events.KillAllEvents(false);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* cr) override
|
||||
{
|
||||
if (me->IsInCombat())
|
||||
{
|
||||
cr->SetInCombatWithZone();
|
||||
if (cr->GetEntry() == NPC_CRYPT_GUARD)
|
||||
cr->AI()->Talk(EMOTE_SPAWN, me);
|
||||
}
|
||||
summons.Summon(cr);
|
||||
}
|
||||
|
||||
void SummonedCreatureDies(Creature* cr, Unit*) override
|
||||
{
|
||||
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 JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
|
||||
} */
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (!victim->IsPlayer())
|
||||
return;
|
||||
|
||||
Talk(SAY_SLAY);
|
||||
victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCRABS_5, true, nullptr, nullptr, me->GetGUID());
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
BossAI::JustEngagedWith(who);
|
||||
me->CallForHelp(30.0f);
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
if (!summons.HasEntry(NPC_CRYPT_GUARD))
|
||||
SummonCryptGuards();
|
||||
if (!Is25ManRaid())
|
||||
{
|
||||
me->m_Events.AddEventAtOffset([&]
|
||||
{
|
||||
me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
|
||||
}, Milliseconds(urand(15000, 20000)));
|
||||
}
|
||||
|
||||
ScheduleTimedEvent(15s, [&] {
|
||||
int32 bp1 = IMPALE_BP1;
|
||||
int32 bp2 = IMPALE_BP2;
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true))
|
||||
me->CastCustomSpell(target, SPELL_IMPALE_10, 0, &bp1, &bp2, false, nullptr, nullptr, ObjectGuid::Empty);
|
||||
}, 20s);
|
||||
|
||||
ScheduleTimedEvent(70s, 2min, [&] {
|
||||
Talk(EMOTE_LOCUST);
|
||||
DoCastSelf(RAID_MODE(SPELL_LOCUST_SWARM_10, SPELL_LOCUST_SWARM_25, SPELL_LOCUST_SWARM_10, SPELL_LOCUST_SWARM_25));
|
||||
|
||||
me->m_Events.AddEventAtOffset([&]
|
||||
{
|
||||
me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
|
||||
}, 3s);
|
||||
|
||||
}, 90s);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&]
|
||||
{
|
||||
DoCastSelf(SPELL_BERSERK, true);
|
||||
}, 10min);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (!sayGreet && who->IsPlayer())
|
||||
{
|
||||
Talk(SAY_GREET);
|
||||
sayGreet = true;
|
||||
}
|
||||
ScriptedAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
private:
|
||||
bool sayGreet;
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_boss_anubrekhan_40()
|
||||
{
|
||||
new boss_anubrekhan_40();
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.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 = 28796,
|
||||
SPELL_RAIN_OF_FIRE = 28794,
|
||||
SPELL_FRENZY = 28798,
|
||||
SPELL_WIDOWS_EMBRACE = 28732,
|
||||
SPELL_MINION_WIDOWS_EMBRACE = 54097
|
||||
};
|
||||
|
||||
enum SpellValues : int32
|
||||
{
|
||||
POISON_BOLT_VOLLEY_BP0 = 1224,
|
||||
POISON_BOLT_VOLLEY_BP1 = 416,
|
||||
RAIN_OF_FIRE_BP0 = 1849,
|
||||
FRENZY_BP0 = 149,
|
||||
FRENZY_BP1 = 74,
|
||||
FRENZY_BP2 = 49
|
||||
};
|
||||
|
||||
enum Groups
|
||||
{
|
||||
GROUP_FRENZY = 1
|
||||
};
|
||||
|
||||
enum Misc
|
||||
{
|
||||
// NPC_NAXXRAMAS_WORSHIPPER = 16506,
|
||||
// NPC_NAXXRAMAS_FOLLOWER = 16505
|
||||
};
|
||||
|
||||
class boss_faerlina_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_faerlina_40() : CreatureScript("boss_faerlina_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_faerlina_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_faerlina_40AI : public BossAI
|
||||
{
|
||||
boss_faerlina_40AI(Creature* c) : BossAI(c, BOSS_FAERLINA), _introDone(false) { }
|
||||
|
||||
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 Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
summons.DespawnAll();
|
||||
SummonHelpers();
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
BossAI::JustEngagedWith(who);
|
||||
me->CallForHelp(VISIBLE_RANGE);
|
||||
summons.DoZoneInCombat();
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
ScheduleTimedEvent(7s, 15s, [&]{
|
||||
if (!me->HasAura(SPELL_WIDOWS_EMBRACE))
|
||||
{
|
||||
CustomSpellValues values;
|
||||
int32 bp0 = POISON_BOLT_VOLLEY_BP0;
|
||||
int32 bp1 = POISON_BOLT_VOLLEY_BP1;
|
||||
values.AddSpellMod(SPELLVALUE_MAX_TARGETS, 10);
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, bp0);
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT1, bp1);
|
||||
me->CastCustomSpell(SPELL_POISON_BOLT_VOLLEY, values, me, TRIGGERED_NONE, nullptr, nullptr, ObjectGuid::Empty);
|
||||
}
|
||||
}, 7s, 15s);
|
||||
|
||||
ScheduleTimedEvent(8s, 18s, [&] {
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
{
|
||||
int32 bp0 = RAIN_OF_FIRE_BP0;
|
||||
me->CastCustomSpell(target, SPELL_RAIN_OF_FIRE, &bp0, 0, 0, false, nullptr, nullptr, ObjectGuid::Empty);
|
||||
}
|
||||
}, 8s, 18s);
|
||||
|
||||
scheduler.Schedule(60s, 80s, GROUP_FRENZY, [this](TaskContext context) {
|
||||
if (!me->HasAura(SPELL_WIDOWS_EMBRACE))
|
||||
{
|
||||
Talk(SAY_FRENZY);
|
||||
Talk(EMOTE_FRENZY);
|
||||
int32 bp0 = FRENZY_BP0;
|
||||
int32 bp1 = FRENZY_BP1;
|
||||
int32 bp2 = FRENZY_BP2;
|
||||
me->CastCustomSpell(me, SPELL_FRENZY, &bp0, &bp1, &bp2, true, nullptr, nullptr, ObjectGuid::Empty);
|
||||
context.Repeat(1min);
|
||||
}
|
||||
else
|
||||
context.Repeat(30s);
|
||||
});
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (!_introDone && who->IsPlayer())
|
||||
{
|
||||
Talk(SAY_GREET);
|
||||
_introDone = true;
|
||||
}
|
||||
ScriptedAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
if (!urand(0, 3))
|
||||
Talk(SAY_SLAY);
|
||||
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
Talk(SAY_DEATH);
|
||||
}
|
||||
|
||||
void SpellHit(Unit* caster, SpellInfo const* spell) override
|
||||
{
|
||||
if (spell->Id == SPELL_WIDOWS_EMBRACE)
|
||||
{
|
||||
Talk(EMOTE_WIDOWS_EMBRACE); // %s is affected by Widow's Embrace!
|
||||
if (me->HasAura(SPELL_FRENZY))
|
||||
{
|
||||
scheduler.RescheduleGroup(GROUP_FRENZY, 1min); // You must sacrifice the worshiper AFTER she enrages if you want to stop her for the full 60 seconds.
|
||||
me->RemoveAurasDueToSpell(SPELL_FRENZY);
|
||||
instance->SetData(DATA_FRENZY_REMOVED, 0); // achievement
|
||||
}
|
||||
else
|
||||
scheduler.RescheduleGroup(GROUP_FRENZY, 30s); // If you sacrifice the Worshiper before the enrage, it will merely delay the enrage for 30 seconds.
|
||||
caster->KillSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool _introDone;
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_boss_faerlina_40()
|
||||
{
|
||||
new boss_faerlina_40();
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* 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_MOGRAINE = 28834, // TODO: Requires Spell DBC Edit
|
||||
SPELL_MARK_OF_ZELIEK = 28835,
|
||||
SPELL_MARK_DAMAGE = 28836,
|
||||
// Korth'azz
|
||||
SPELL_KORTHAZZ_METEOR = 28884,
|
||||
// Blaumeux
|
||||
SPELL_BLAUMEUX_SHADOW_BOLT = 57374,
|
||||
SPELL_BLAUMEUX_VOID_ZONE = 28863,
|
||||
// Zeliek
|
||||
SPELL_ZELIEK_HOLY_WRATH = 28883,
|
||||
SPELL_ZELIEK_HOLY_BOLT = 57376,
|
||||
// Mograine
|
||||
SPELL_RIVENDARE_UNHOLY_SHADOW = 28882,
|
||||
|
||||
// NX40
|
||||
SPELL_SHIELDWALL = 29061, // Shield Wall - All 4 horsemen will shield wall at 50% hp and 20% hp for 20 seconds
|
||||
SPELL_SUMMON_PLAYER = 25104,
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
EVENT_MARK_CAST = 1,
|
||||
EVENT_PRIMARY_SPELL = 2,
|
||||
EVENT_SECONDARY_SPELL = 3,
|
||||
EVENT_BERSERK = 4,
|
||||
EVENT_HEALTH_CHECK = 5
|
||||
};
|
||||
|
||||
enum Misc
|
||||
{
|
||||
// Horseman
|
||||
HORSEMAN_ZELIEK = 0,
|
||||
HORSEMAN_BLAUMEUX = 1,
|
||||
HORSEMAN_MOGRAINE = 2,
|
||||
HORSEMAN_KORTHAZZ = 3
|
||||
};
|
||||
|
||||
enum Spirits
|
||||
{
|
||||
// Spells
|
||||
SPELL_SUMMON_SPIRIT_ZELIEK = 28934,
|
||||
SPELL_SUMMON_SPIRIT_BLAUMEUX = 28931,
|
||||
SPELL_SUMMON_SPIRIT_MOGRAINE = 28928,
|
||||
SPELL_SUMMON_SPIRIT_KORTHAZZ = 28932,
|
||||
// NPCs
|
||||
NPC_SPIRIT_ZELIEK = 16777,
|
||||
NPC_SPIRIT_BLAUMEUX = 16776,
|
||||
NPC_SPIRIT_MOGRAINE = 16775,
|
||||
NPC_SPIRIT_KORTHAZZ = 16778
|
||||
};
|
||||
|
||||
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_MOGRAINE, SPELL_MARK_OF_KORTHAZZ};
|
||||
// SPIRITS
|
||||
const uint32 TABLE_SPELL_SUMMON_SPIRIT[4] = {SPELL_SUMMON_SPIRIT_ZELIEK, SPELL_SUMMON_SPIRIT_BLAUMEUX, SPELL_SUMMON_SPIRIT_MOGRAINE, SPELL_SUMMON_SPIRIT_KORTHAZZ};
|
||||
|
||||
class boss_four_horsemen_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_four_horsemen_40() : CreatureScript("boss_four_horsemen_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_four_horsemen_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_four_horsemen_40AI : public BossAI
|
||||
{
|
||||
explicit boss_four_horsemen_40AI(Creature* c) : BossAI(c, BOSS_HORSEMAN)
|
||||
{
|
||||
switch (me->GetEntry())
|
||||
{
|
||||
case NPC_SIR_ZELIEK_40:
|
||||
horsemanId = HORSEMAN_ZELIEK;
|
||||
break;
|
||||
case NPC_LADY_BLAUMEUX_40:
|
||||
horsemanId = HORSEMAN_BLAUMEUX;
|
||||
break;
|
||||
case NPC_HIGHLORD_MOGRAINE_40:
|
||||
horsemanId = HORSEMAN_MOGRAINE;
|
||||
break;
|
||||
case NPC_THANE_KORTHAZZ_40:
|
||||
horsemanId = HORSEMAN_KORTHAZZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EventMap events;
|
||||
uint8 horsemanId;
|
||||
bool doneFirstShieldWall;
|
||||
|
||||
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());
|
||||
events.Reset();
|
||||
events.RescheduleEvent(EVENT_MARK_CAST, 20000);
|
||||
events.RescheduleEvent(EVENT_BERSERK, 600000);
|
||||
summons.DespawnAll(); // despawn spirits
|
||||
if ((me->GetEntry() != NPC_LADY_BLAUMEUX_40 && me->GetEntry() != NPC_SIR_ZELIEK_40))
|
||||
{
|
||||
events.RescheduleEvent(EVENT_PRIMARY_SPELL, 10s, 15s);
|
||||
}
|
||||
else
|
||||
{
|
||||
events.RescheduleEvent(EVENT_SECONDARY_SPELL, 15s);
|
||||
}
|
||||
doneFirstShieldWall = false;
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
Talk(SAY_SLAY);
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
|
||||
{
|
||||
if (spellInfo->Id == TABLE_SPELL_MARK[horsemanId])
|
||||
DoModifyThreatByPercent(target, -50);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
Talk(SAY_DEATH);
|
||||
|
||||
if (instance->GetBossState(BOSS_HORSEMAN) == DONE)
|
||||
{
|
||||
if (!me->GetMap()->GetPlayers().IsEmpty())
|
||||
{
|
||||
if (Creature* spirit = GetClosestCreatureWithEntry(me, NPC_SPIRIT_ZELIEK, 200.0f))
|
||||
spirit->DespawnOrUnsummon();
|
||||
if (Creature* spirit = GetClosestCreatureWithEntry(me, NPC_SPIRIT_BLAUMEUX, 200.0f))
|
||||
spirit->DespawnOrUnsummon();
|
||||
if (Creature* spirit = GetClosestCreatureWithEntry(me, NPC_SPIRIT_MOGRAINE, 200.0f))
|
||||
spirit->DespawnOrUnsummon();
|
||||
if (Creature* spirit = GetClosestCreatureWithEntry(me, NPC_SPIRIT_KORTHAZZ, 200.0f))
|
||||
spirit->DespawnOrUnsummon();
|
||||
if (Player* player = me->GetMap()->GetPlayers().getFirst()->GetSource())
|
||||
if (GameObject* chest = player->SummonGameObject(GO_HORSEMEN_CHEST_40, 2514.8f, -2944.9f, 245.55f, 5.51f, 0, 0, 0, 0, 0))
|
||||
chest->SetLootRecipient(me);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DoCastSelf(TABLE_SPELL_SUMMON_SPIRIT[horsemanId], true);
|
||||
}
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
summons.Summon(summon);
|
||||
summons.DoZoneInCombat();
|
||||
summon->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
BossAI::JustEngagedWith(who);
|
||||
Talk(SAY_AGGRO);
|
||||
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!IsInRoom())
|
||||
return;
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
if (Unit* victim = me->GetVictim())
|
||||
if (!me->IsWithinDistInMap(victim, VISIBILITY_DISTANCE_NORMAL))
|
||||
me->CastSpell(victim, SPELL_SUMMON_PLAYER, true);
|
||||
|
||||
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(12s);
|
||||
return;
|
||||
case EVENT_BERSERK:
|
||||
Talk(SAY_SPECIAL);
|
||||
me->CastSpell(me, SPELL_BERSERK, true);
|
||||
return;
|
||||
case EVENT_PRIMARY_SPELL:
|
||||
Talk(SAY_TAUNT);
|
||||
if (horsemanId == HORSEMAN_ZELIEK)
|
||||
{
|
||||
int32 bp0 = 1109; // spell not used in vanilla, reduced damage from ~2.5 to ~1.2k
|
||||
me->CastCustomSpell(me->GetVictim(), SPELL_ZELIEK_HOLY_BOLT, &bp0, 0, 0, false);
|
||||
}
|
||||
else if (horsemanId == HORSEMAN_BLAUMEUX)
|
||||
{
|
||||
int32 bp0 = 1109; // spell not used in vanilla, reduced damage from ~2.5 to ~1.2k
|
||||
me->CastCustomSpell(me->GetVictim(), SPELL_BLAUMEUX_SHADOW_BOLT, &bp0, 0, 0, false);
|
||||
}
|
||||
else if (horsemanId == HORSEMAN_MOGRAINE)
|
||||
{
|
||||
// same dbc as vanilla. Shadow damage instead of fire
|
||||
me->CastSpell(me->GetVictim(), SPELL_RIVENDARE_UNHOLY_SHADOW, false);
|
||||
}
|
||||
else // HORSEMAN_KORTHAZZ
|
||||
{
|
||||
int32 bp0 = 12824; // 14.5k to 13.5k
|
||||
me->CastCustomSpell(me->GetVictim(), SPELL_KORTHAZZ_METEOR, &bp0, 0, 0, false);
|
||||
}
|
||||
events.Repeat(15s);
|
||||
return;
|
||||
case EVENT_SECONDARY_SPELL:
|
||||
if (horsemanId == HORSEMAN_ZELIEK)
|
||||
{
|
||||
int32 bp0 = 443;
|
||||
CustomSpellValues values;
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, bp0);
|
||||
values.AddSpellMod(SPELLVALUE_MAX_TARGETS, 50); // 30yd
|
||||
me->CastCustomSpell(SPELL_ZELIEK_HOLY_WRATH, values, me->GetVictim(), TRIGGERED_NONE, nullptr, nullptr, ObjectGuid::Empty);
|
||||
}
|
||||
else // HORSEMAN_BLAUMEUX
|
||||
me->CastSpell(me->GetVictim(), SPELL_BLAUMEUX_VOID_ZONE, false);
|
||||
events.Repeat(15s);
|
||||
return;
|
||||
case EVENT_HEALTH_CHECK:
|
||||
if (!doneFirstShieldWall && me->GetHealthPct() <= 50.0f)
|
||||
{
|
||||
DoCastSelf(SPELL_SHIELDWALL, true);
|
||||
doneFirstShieldWall = true;
|
||||
events.Repeat(1s);
|
||||
break;
|
||||
}
|
||||
if (doneFirstShieldWall && me->GetHealthPct() <= 20.0f)
|
||||
{
|
||||
if (!me->HasAura(SPELL_SHIELDWALL)) // prevent refresh of first shield wall
|
||||
DoCastSelf(SPELL_SHIELDWALL, true);
|
||||
break;
|
||||
}
|
||||
events.Repeat(1s);
|
||||
return;
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class spell_four_horsemen_mark_aura : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_four_horsemen_mark_aura);
|
||||
|
||||
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 = 12000;
|
||||
break;
|
||||
case 6:
|
||||
damage = 20000;
|
||||
break;
|
||||
default:
|
||||
damage = 20000 + 1000 * (GetStackAmount() - 7);
|
||||
break;
|
||||
}
|
||||
|
||||
if (caster->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
{
|
||||
switch (GetStackAmount())
|
||||
{
|
||||
case 1: damage = 0; break;
|
||||
case 2: damage = 250; break;
|
||||
case 3: damage = 1000; break;
|
||||
case 4: damage = 3000; break;
|
||||
default:
|
||||
damage = 1000 * GetStackAmount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (damage)
|
||||
{
|
||||
caster->CastCustomSpell(SPELL_MARK_DAMAGE, SPELLVALUE_BASE_POINT0, damage, GetTarget());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectApply += AuraEffectApplyFn(spell_four_horsemen_mark_aura::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_four_horsemen_40()
|
||||
{
|
||||
new boss_four_horsemen_40();
|
||||
RegisterSpellScript(spell_four_horsemen_mark_aura);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.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_DECIMATE_DAMAGE = 28375,
|
||||
SPELL_BERSERK = 26662,
|
||||
SPELL_INFECTED_WOUND = 29306,
|
||||
SPELL_TERRIFYING_ROAR = 29685,
|
||||
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,
|
||||
EVENT_TERRIFYING_ROAR = 7
|
||||
};
|
||||
|
||||
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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_gluth_40() : CreatureScript("boss_gluth_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_gluth_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_gluth_40AI : public BossAI
|
||||
{
|
||||
explicit boss_gluth_40AI(Creature* c) : BossAI(c, BOSS_GLUTH), summons(me)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
|
||||
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, 10s); // VMangos value
|
||||
events.ScheduleEvent(EVENT_DECIMATE, RAID_MODE(110000, 90000, 110000, 90000));
|
||||
events.ScheduleEvent(EVENT_BERSERK, 6min);
|
||||
events.ScheduleEvent(EVENT_SUMMON_ZOMBIE, 6s); // VMangos value
|
||||
events.ScheduleEvent(EVENT_CAN_EAT_ZOMBIE, 3s); // VMangos value
|
||||
events.ScheduleEvent(EVENT_TERRIFYING_ROAR, 20s); // VMangos value
|
||||
}
|
||||
|
||||
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->IsPlayer())
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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 (auto const& 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);
|
||||
int32 bp1 = 99; // Enrage melee haste
|
||||
int32 bp2 = 49; // Enrage damage percent
|
||||
if (me->CastCustomSpell(me, SPELL_ENRAGE_10, &bp1, &bp2, 0, true) == SPELL_CAST_OK)
|
||||
events.Repeat(10s);
|
||||
else
|
||||
events.Repeat(100ms);
|
||||
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, SPELL_DECIMATE_10, false);
|
||||
// Apply Decimate effect to zombies
|
||||
// TODO: Is this block required?
|
||||
{
|
||||
std::list<Creature*> zombies;
|
||||
me->GetCreatureListWithEntryInGrid(zombies, NPC_ZOMBIE_CHOW, 150.0f);
|
||||
for (Creature* zombie : zombies)
|
||||
{
|
||||
if (zombie->IsAlive())
|
||||
{
|
||||
uint32 reduceHp = uint32(zombie->GetMaxHealth() * 0.05f);
|
||||
if (zombie->GetHealth() > reduceHp)
|
||||
zombie->SetHealth(reduceHp); // Reduce HP to 5%
|
||||
zombie->SetWalk(true); // Set to walk
|
||||
zombie->GetMotionMaster()->MoveFollow(me,
|
||||
0.0f,
|
||||
0.0f,
|
||||
MOTION_SLOT_CONTROLLED); // Move to boss
|
||||
zombie->SetReactState(REACT_PASSIVE); // Set to passive
|
||||
}
|
||||
}
|
||||
}
|
||||
events.RepeatEvent(105000);
|
||||
break;
|
||||
case EVENT_SUMMON_ZOMBIE:
|
||||
{
|
||||
uint8 rand = urand(0, 2);
|
||||
for (int32 i = 0; i < RAID_MODE(1, 2, 2, 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(6s);
|
||||
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);
|
||||
|
||||
Unit* unitTarget = me->GetVictim();
|
||||
int32 damage = int32(unitTarget->GetHealth());
|
||||
Unit::DealDamage(me, unitTarget, damage);
|
||||
|
||||
uint32 hp = uint32(me->GetMaxHealth() * 0.05f);
|
||||
me->SetHealth(me->GetHealth() + hp);
|
||||
return; // leave it to skip DoMeleeAttackIfReady
|
||||
}
|
||||
break;
|
||||
case EVENT_TERRIFYING_ROAR:
|
||||
if (me->CastSpell(me, SPELL_TERRIFYING_ROAR, true) == SPELL_CAST_OK)
|
||||
events.Repeat(20s);
|
||||
else
|
||||
events.Repeat(100ms);
|
||||
break;
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class spell_gluth_decimate : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_gluth_decimate);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_DECIMATE_DAMAGE });
|
||||
}
|
||||
|
||||
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(SPELL_DECIMATE_DAMAGE, SPELLVALUE_BASE_POINT0, damage, unitTarget);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_gluth_decimate::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_gluth_40()
|
||||
{
|
||||
new boss_gluth_40();
|
||||
RegisterSpellScript(spell_gluth_decimate);
|
||||
}
|
||||
@@ -0,0 +1,758 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CombatAI.h"
|
||||
#include "CreatureScript.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.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_ANCHOR_1_TRAINEE = 27892,
|
||||
SPELL_ANCHOR_1_DK = 27928,
|
||||
SPELL_ANCHOR_1_RIDER = 27935,
|
||||
SPELL_ANCHOR_2_TRAINEE = 27893,
|
||||
SPELL_ANCHOR_2_DK = 27929,
|
||||
SPELL_ANCHOR_2_RIDER = 27936,
|
||||
SPELL_SKULLS_TRAINEE = 27915,
|
||||
SPELL_SKULLS_DK = 27931,
|
||||
SPELL_SKULLS_RIDER = 27937,
|
||||
// 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
|
||||
};
|
||||
|
||||
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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_gothik_40() : CreatureScript("boss_gothik_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_gothik_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_gothik_40AI : public BossAI
|
||||
{
|
||||
explicit boss_gothik_40AI(Creature* c) : BossAI(c, BOSS_GOTHIK), summons(me)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
summons.Summon(summon);
|
||||
// If central gate is open, attack any one
|
||||
if (gateOpened)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 200.0f))
|
||||
{
|
||||
summon->AI()->AttackStart(target);
|
||||
summon->SetInCombatWithZone();
|
||||
summon->SetReactState(REACT_AGGRESSIVE);
|
||||
summon->CallForHelp(150.0f);
|
||||
}
|
||||
}
|
||||
// Else look for a random target on the side the summoned NPC is
|
||||
else
|
||||
{
|
||||
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
|
||||
std::vector<Player*> tList;
|
||||
for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
|
||||
{
|
||||
if (!me->IsWithinDistInMap(itr->GetSource(), 200.0f, true, false) || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (IN_LIVE_SIDE(itr->GetSource()) != IN_LIVE_SIDE(summon))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tList.push_back(itr->GetSource());
|
||||
}
|
||||
if (!tList.empty())
|
||||
{
|
||||
Player* target = tList[urand(0, tList.size() - 1)];
|
||||
summon->AI()->AttackStart(target);
|
||||
summon->SetInCombatWithZone();
|
||||
summon->SetReactState(REACT_AGGRESSIVE);
|
||||
summon->CallForHelp(150.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SummonedCreatureDespawn(Creature* cr) override
|
||||
{
|
||||
summons.Despawn(cr);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
Talk(SAY_KILL);
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
Talk(SAY_DEATH);
|
||||
summons.DespawnAll();
|
||||
}
|
||||
|
||||
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 (auto const& 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 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, 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))
|
||||
{
|
||||
if (GameObject* go = instance->GetGameObject(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 = instance->GetGameObject(DATA_GOTHIK_INNER_GATE))
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
|
||||
gateOpened = true;
|
||||
Talk(EMOTE_GATE_OPENED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class npc_boss_gothik_minion_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_boss_gothik_minion_40() : CreatureScript("npc_boss_gothik_minion_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<npc_boss_gothik_minion_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct npc_boss_gothik_minion_40AI : public CombatAI
|
||||
{
|
||||
explicit npc_boss_gothik_minion_40AI(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:
|
||||
DoCastAOE(SPELL_ANCHOR_1_TRAINEE, true);
|
||||
break;
|
||||
case NPC_LIVING_KNIGHT:
|
||||
DoCastAOE(SPELL_ANCHOR_1_DK, true);
|
||||
break;
|
||||
case NPC_LIVING_RIDER:
|
||||
DoCastAOE(SPELL_ANCHOR_1_RIDER, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (who->IsPlayer())
|
||||
me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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 npc_gothik_trigger_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_gothik_trigger_40() : CreatureScript("npc_gothik_trigger_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new npc_gothik_trigger_40AI(creature);
|
||||
}
|
||||
|
||||
struct npc_gothik_trigger_40AI : public ScriptedAI
|
||||
{
|
||||
npc_gothik_trigger_40AI(Creature* creature) : ScriptedAI(creature) { creature->SetDisableGravity(true); }
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override {}
|
||||
void UpdateAI(uint32 /*diff*/) override {}
|
||||
void JustEngagedWith(Unit* /*who*/) override {}
|
||||
void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override { damage = 0; }
|
||||
|
||||
Creature* SelectRandomSkullPile()
|
||||
{
|
||||
std::list<Creature*> triggers;
|
||||
me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f);
|
||||
// Remove triggers that are on live side or soul triggers on the platform
|
||||
triggers.remove_if([](Creature* trigger){
|
||||
return ((trigger->GetPositionY() < POS_Y_GATE) || (trigger->GetPositionZ() > 280.0f));
|
||||
});
|
||||
if (!triggers.empty())
|
||||
{
|
||||
std::list<Creature*>::iterator itr = triggers.begin();
|
||||
std::advance(itr, urand(0, triggers.size() - 1));
|
||||
return *itr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
|
||||
{
|
||||
if (!spell)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (spell->Id)
|
||||
{
|
||||
case SPELL_ANCHOR_1_TRAINEE:
|
||||
DoCastAOE(SPELL_ANCHOR_2_TRAINEE, true);
|
||||
break;
|
||||
case SPELL_ANCHOR_1_DK:
|
||||
DoCastAOE(SPELL_ANCHOR_2_DK, true);
|
||||
break;
|
||||
case SPELL_ANCHOR_1_RIDER:
|
||||
DoCastAOE(SPELL_ANCHOR_2_RIDER, true);
|
||||
break;
|
||||
case SPELL_ANCHOR_2_TRAINEE:
|
||||
if (Creature* target = SelectRandomSkullPile())
|
||||
{
|
||||
DoCast(target, SPELL_SKULLS_TRAINEE, true);
|
||||
}
|
||||
break;
|
||||
case SPELL_ANCHOR_2_DK:
|
||||
if (Creature* target = SelectRandomSkullPile())
|
||||
{
|
||||
DoCast(target, SPELL_SKULLS_DK, true);
|
||||
}
|
||||
break;
|
||||
case SPELL_ANCHOR_2_RIDER:
|
||||
if (Creature* target = SelectRandomSkullPile())
|
||||
{
|
||||
DoCast(target, SPELL_SKULLS_RIDER, true);
|
||||
}
|
||||
break;
|
||||
case SPELL_SKULLS_TRAINEE:
|
||||
DoSummon(NPC_DEAD_TRAINEE, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
|
||||
break;
|
||||
case SPELL_SKULLS_DK:
|
||||
DoSummon(NPC_DEAD_KNIGHT, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
|
||||
break;
|
||||
case SPELL_SKULLS_RIDER:
|
||||
DoSummon(NPC_DEAD_RIDER, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
|
||||
DoSummon(NPC_DEAD_HORSE, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// dead side summons are "owned" by gothik
|
||||
void JustSummoned(Creature* summon) override
|
||||
{
|
||||
if (Creature* gothik = me->GetInstanceScript()->GetCreature(DATA_GOTHIK_BOSS))
|
||||
gothik->AI()->JustSummoned(summon);
|
||||
}
|
||||
|
||||
void SummonedCreatureDespawn(Creature* summon) override
|
||||
{
|
||||
if (Creature* gothik = me->GetInstanceScript()->GetCreature(DATA_GOTHIK_BOSS))
|
||||
gothik->AI()->SummonedCreatureDespawn(summon);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class spell_gothik_shadow_bolt_volley : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_gothik_shadow_bolt_volley);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_SHADOW_MARK });
|
||||
}
|
||||
|
||||
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::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_gothik_40()
|
||||
{
|
||||
new boss_gothik_40();
|
||||
new npc_boss_gothik_minion_40();
|
||||
new npc_gothik_trigger_40();
|
||||
// RegisterSpellScript(spell_gothik_shadow_bolt_volley);
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.h"
|
||||
#include "PassiveAI.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.h"
|
||||
#include "naxxramas.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
|
||||
};
|
||||
|
||||
class boss_grobbulus_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_grobbulus_40() : CreatureScript("boss_grobbulus_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_grobbulus_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_grobbulus_40AI : public BossAI
|
||||
{
|
||||
explicit boss_grobbulus_40AI(Creature* c) : BossAI(c, BOSS_GROBBULUS), summons(me)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
uint32 dropSludgeTimer{};
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
events.Reset();
|
||||
summons.DespawnAll();
|
||||
dropSludgeTimer = 0;
|
||||
}
|
||||
|
||||
void 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 JustEngagedWith(Unit* who) override
|
||||
{
|
||||
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, 540000, 540000));
|
||||
}
|
||||
|
||||
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
|
||||
{
|
||||
if (spellInfo->Id == RAID_MODE(SPELL_SLIME_SPRAY_10, SPELL_SLIME_SPRAY_25, SPELL_SLIME_SPRAY_10, SPELL_SLIME_SPRAY_25) && target->IsPlayer())
|
||||
{
|
||||
me->SummonCreature(NPC_FALLOUT_SLIME, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
|
||||
}
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* cr) override
|
||||
{
|
||||
if (cr->GetEntry() == NPC_FALLOUT_SLIME)
|
||||
{
|
||||
cr->SetInCombatWithZone();
|
||||
}
|
||||
summons.Summon(cr);
|
||||
}
|
||||
|
||||
void SummonedCreatureDespawn(Creature* summon) override
|
||||
{
|
||||
summons.Despawn(summon);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
summons.DespawnAll();
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (who->IsPlayer())
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
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, 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();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class boss_grobbulus_poison_cloud_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_grobbulus_poison_cloud_40() : CreatureScript("boss_grobbulus_poison_cloud_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_grobbulus_poison_cloud_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_grobbulus_poison_cloud_40AI : public NullCreatureAI
|
||||
{
|
||||
explicit boss_grobbulus_poison_cloud_40AI(Creature* pCreature) : NullCreatureAI(pCreature) { }
|
||||
|
||||
uint32 sizeTimer{};
|
||||
uint32 auraVisualTimer{};
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
sizeTimer = 0;
|
||||
auraVisualTimer = 1;
|
||||
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f);
|
||||
me->SetFaction(FACTION_BOOTY_BAY);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (who->IsPlayer())
|
||||
me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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() ? 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));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class spell_grobbulus_poison : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_grobbulus_poison);
|
||||
|
||||
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
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_grobbulus_poison::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
|
||||
}
|
||||
};
|
||||
|
||||
// This will overwrite the declared 10 and 25 man mutating injection to handle all versions of the spell script
|
||||
class spell_grobbulus_mutating_injection_aura : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_grobbulus_mutating_injection_aura);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_MUTATING_EXPLOSION });
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
if (caster->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
{
|
||||
int32 modifiedMutatingExplosionDamage = 2379;
|
||||
caster->CastCustomSpell(GetTarget(), SPELL_MUTATING_EXPLOSION, &modifiedMutatingExplosionDamage, 0, 0, true);
|
||||
}
|
||||
else
|
||||
caster->CastSpell(GetTarget(), SPELL_MUTATING_EXPLOSION, true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_grobbulus_mutating_injection_aura::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_grobbulus_40()
|
||||
{
|
||||
new boss_grobbulus_40();
|
||||
new boss_grobbulus_poison_cloud_40();
|
||||
// RegisterSpellScript(spell_grobbulus_poison);
|
||||
RegisterSpellScript(spell_grobbulus_mutating_injection_aura);
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ScriptedGossip.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.h"
|
||||
#include "naxxramas.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_DISRUPTION = 29310,
|
||||
SPELL_DECREPIT_FEVER_10 = 29998,
|
||||
SPELL_DECREPIT_FEVER_25 = 55011,
|
||||
SPELL_PLAGUE_CLOUD = 29350,
|
||||
SPELL_TELEPORT_SELF = 30211,
|
||||
|
||||
SPELL_TELEPORT_PLAYERS = 29273, // updated target in db
|
||||
SPELL_SUMMON_PLAYER = 25104,
|
||||
};
|
||||
|
||||
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,
|
||||
EVENT_TELEPORT_PLAYER = 7
|
||||
};
|
||||
|
||||
enum Misc
|
||||
{
|
||||
PHASE_SLOW_DANCE = 0,
|
||||
PHASE_FAST_DANCE = 1
|
||||
};
|
||||
|
||||
const Position EyeStalkPositions[20] =
|
||||
{
|
||||
{ 2761.28f, -3765.37f, 275.08f, 1.24f },
|
||||
{ 2770.17f, -3782.11f, 275.08f, 1.33f },
|
||||
{ 2798.11f, -3788.94f, 275.08f, 2.35f },
|
||||
{ 2797.91f, -3776.86f, 275.08f, 2.25f },
|
||||
{ 2792.06f, -3762.52f, 275.08f, 2.9f, },
|
||||
{ 2789.87f, -3752.15f, 275.08f, 2.74f },
|
||||
{ 2804.21f, -3757.96f, 275.08f, 3.9f },
|
||||
{ 2821.16f, -3759.75f, 275.08f, 4.47f },
|
||||
{ 2834.64f, -3751.23f, 275.08f, 4.27f },
|
||||
{ 2843.54f, -3768.08f, 275.08f, 3.06f },
|
||||
{ 2862.4f, -3758.3f, 275.08f, 4.8f },
|
||||
{ 2877.8f, -3762.46f, 275.08f, 4.8f },
|
||||
{ 2894.11f, -3757.89f, 275.08f, 4.56f },
|
||||
{ 2895.25f, -3779.5f, 275.08f, 2.4f },
|
||||
{ 2881.59f, -3782.22f, 275.08f, 2.79f },
|
||||
{ 2867.2f, -3778.21f, 275.08f, 3.01f },
|
||||
{ 2851.39f, -3776.54f, 275.08f, 2.69f },
|
||||
{ 2846.16f, -3789.13f, 275.08f, 1.79f },
|
||||
{ 2830.09f, -3776.49f, 275.08f, 0.94f },
|
||||
{ 2813.34f, -3780.97f, 275.08f, 1.84f },
|
||||
};
|
||||
|
||||
class boss_heigan_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_heigan_40() : CreatureScript("boss_heigan_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_heigan_40AI>(creature);
|
||||
}
|
||||
|
||||
struct boss_heigan_40AI : public BossAI
|
||||
{
|
||||
explicit boss_heigan_40AI(Creature* c) : BossAI(c, BOSS_HEIGAN)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
uint8 currentPhase{};
|
||||
uint8 currentSection{};
|
||||
bool moveRight{};
|
||||
|
||||
GuidList portedPlayersThisPhase;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
events.Reset();
|
||||
currentPhase = 0;
|
||||
currentSection = 3;
|
||||
portedPlayersThisPhase.clear();
|
||||
KillPlayersInTheTunnel();
|
||||
moveRight = true;
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
Talk(SAY_SLAY);
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
Talk(EMOTE_DEATH);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
BossAI::JustEngagedWith(who);
|
||||
me->SetInCombatWithZone();
|
||||
Talk(SAY_AGGRO);
|
||||
StartFightPhase(PHASE_SLOW_DANCE);
|
||||
}
|
||||
|
||||
void 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);
|
||||
events.ScheduleEvent(EVENT_TELEPORT_PLAYER, 40s);
|
||||
portedPlayersThisPhase.clear();
|
||||
}
|
||||
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 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 KillPlayersInTheTunnel()
|
||||
{
|
||||
// hackfix: kill everyone in the tunnel
|
||||
Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers();
|
||||
for (auto const& itr : PlayerList)
|
||||
if (Player* player = itr.GetSource())
|
||||
if (player->IsAlive() && !player->IsGameMaster())
|
||||
if (player->GetPositionY() <= -3735.0f)
|
||||
player->KillSelf();
|
||||
}
|
||||
|
||||
void DoEventTeleportPlayer()
|
||||
{
|
||||
std::list<Unit*> candidates;
|
||||
SelectTargetList(candidates, 3, SelectTargetMethod::Random, 0, [&](Unit* target)
|
||||
{
|
||||
if (!target->IsPlayer()) // never target nonplayers (pets, guardians, etc.)
|
||||
return false;
|
||||
if (!target->IsAlive())
|
||||
return false;
|
||||
if (me->GetVictim() == target) // never target tank
|
||||
return false;
|
||||
// skip players who already have been teleported this phase
|
||||
if (std::find(portedPlayersThisPhase.begin(), portedPlayersThisPhase.end(), target->GetGUID()) != portedPlayersThisPhase.end())
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (candidates.empty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 3 ; i++)
|
||||
{
|
||||
if (candidates.empty())
|
||||
break;
|
||||
auto itr = candidates.begin();
|
||||
if (candidates.size() > 1)
|
||||
std::advance(itr, urand(0, candidates.size() - 1));
|
||||
Unit *target = *itr;
|
||||
candidates.erase(itr);
|
||||
portedPlayersThisPhase.push_back(target->GetGUID());
|
||||
DoModifyThreatByPercent(target, -99); // prevent heigan chasing and resetting
|
||||
target->CastSpell(target, SPELL_TELEPORT_PLAYERS, true);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!IsInRoom(me))
|
||||
return;
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
if (Unit* victim = me->GetVictim())
|
||||
{
|
||||
if (!me->IsWithinDistInMap(victim, VISIBILITY_DISTANCE_NORMAL))
|
||||
me->CastSpell(victim, SPELL_SUMMON_PLAYER, true);
|
||||
}
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
switch (events.ExecuteEvent())
|
||||
{
|
||||
case EVENT_DISRUPTION:
|
||||
me->CastCustomSpell(SPELL_DISRUPTION, SPELLVALUE_RADIUS_MOD, 2500, me, false); // 25yd
|
||||
events.Repeat(10s);
|
||||
break;
|
||||
case EVENT_DECEPIT_FEVER:
|
||||
{
|
||||
int32 bp1 = 499;
|
||||
me->CastCustomSpell(me, SPELL_DECREPIT_FEVER_10, 0, &bp1, 0, false, nullptr, nullptr, ObjectGuid::Empty);
|
||||
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:
|
||||
{
|
||||
instance->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;
|
||||
}
|
||||
break;
|
||||
case EVENT_SAFETY_DANCE:
|
||||
{
|
||||
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
|
||||
for (auto const& itr : pList)
|
||||
{
|
||||
if (IsInRoom(itr.GetSource()) && !itr.GetSource()->IsAlive())
|
||||
{
|
||||
instance->SetData(DATA_DANCE_FAIL, 0);
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
events.Repeat(5s);
|
||||
return;
|
||||
}
|
||||
case EVENT_TELEPORT_PLAYER:
|
||||
DoEventTeleportPlayer();
|
||||
break;
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_boss_heigan_40()
|
||||
{
|
||||
new boss_heigan_40();
|
||||
}
|
||||
@@ -0,0 +1,690 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.h"
|
||||
#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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_kelthuzad_40() : CreatureScript("boss_kelthuzad_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_kelthuzad_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_kelthuzad_40AI : public BossAI
|
||||
{
|
||||
explicit boss_kelthuzad_40AI(Creature* c) : BossAI(c, BOSS_KELTHUZAD), summons(me)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
|
||||
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 = instance->GetGameObject(DATA_KELTHUZAD_FLOOR))
|
||||
{
|
||||
go->SetPhaseMask(1, true);
|
||||
go->SetGoState(GO_STATE_READY);
|
||||
}
|
||||
|
||||
if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_1))
|
||||
go->SetGoState(GO_STATE_READY);
|
||||
|
||||
if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_2))
|
||||
go->SetGoState(GO_STATE_READY);
|
||||
|
||||
if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_3))
|
||||
go->SetGoState(GO_STATE_READY);
|
||||
|
||||
if (GameObject* go = instance->GetGameObject(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->IsPlayer())
|
||||
return;
|
||||
|
||||
Talk(SAY_SLAY);
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (!me->IsInCombat() && who->IsPlayer() && 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 (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_FLOOR))
|
||||
{
|
||||
events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s);
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
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 (GameObject* go = instance->GetGameObject(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, 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, 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, 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()->IsPlayer()
|
||||
&& 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 = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_1))
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
|
||||
if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_2))
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
|
||||
if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_3))
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
|
||||
if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_4))
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
|
||||
break;
|
||||
}
|
||||
events.Repeat(1s);
|
||||
break;
|
||||
case EVENT_P3_LICH_KING_SAY:
|
||||
{
|
||||
if (Creature* cr = instance->GetCreature(DATA_LICH_KING_BOSS))
|
||||
cr->AI()->Talk(SAY_ANSWER_REQUEST);
|
||||
|
||||
for (uint8 i = 0 ; i < RAID_MODE(2, 4, 4, 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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_kelthuzad_minion_40() : CreatureScript("boss_kelthuzad_minion_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_kelthuzad_minion_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_kelthuzad_minion_40AI : public ScriptedAI
|
||||
{
|
||||
explicit boss_kelthuzad_minion_40AI(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->IsPlayer() && !who->IsPet())
|
||||
return;
|
||||
|
||||
ScriptedAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION)
|
||||
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->IsPlayer())
|
||||
me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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 SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_kelthuzad_frost_blast);
|
||||
|
||||
bool Validate(SpellInfo const* /*spell*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_FROST_BLAST });
|
||||
}
|
||||
|
||||
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::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY);
|
||||
}
|
||||
};
|
||||
|
||||
class spell_kelthuzad_detonate_mana_aura : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_kelthuzad_detonate_mana_aura);
|
||||
|
||||
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_aura::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_kelthuzad_40()
|
||||
{
|
||||
new boss_kelthuzad_40();
|
||||
new boss_kelthuzad_minion_40();
|
||||
// RegisterSpellScript(spell_kelthuzad_frost_blast);
|
||||
// RegisterSpellScript(spell_kelthuzad_detonate_mana_aura);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "naxxramas.h"
|
||||
|
||||
enum Spells
|
||||
{
|
||||
// SPELL_CORRUPTED_MIND = 29201, // this triggers the following spells on targets (based on class): 29185, 29194, 29196, 29198
|
||||
SPELL_NECROTIC_AURA = 55593,
|
||||
// SPELL_SUMMON_SPORE = 29234,
|
||||
// SPELL_DEATHBLOOM_10 = 29865, // does 200 dmg every second for 6 seconds with 1200 extra damage at the end. should do 196 dmg every 6 seconds. no extra damage at the end.
|
||||
SPELL_POISON_SHOCK = 22595, // does 180-220 aoe poison damage. if Loatheb recasts this every 6 seconds it's a possible fix for poison aura.
|
||||
// SPELL_DEATHBLOOM_25 = 55053,
|
||||
SPELL_INEVITABLE_DOOM = 29204,
|
||||
//SPELL_INEVITABLE_DOOM_25 = 55052,
|
||||
// SPELL_BERSERK = 26662, // he doesn't cast berserk in Naxx40
|
||||
SPELL_REMOVE_CURSE = 30281 // He periodically removes all curses on himself
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
// EVENT_CORRUPTED_MIND = 1, // Loatheb should cast Corrupted Mind instead of Necrotic Aura
|
||||
EVENT_NECROTIC_AURA = 1,
|
||||
// EVENT_DEATHBLOOM = 2,
|
||||
EVENT_POISON_SHOCK = 2,
|
||||
EVENT_INEVITABLE_DOOM = 3,
|
||||
// EVENT_BERSERK = 4,
|
||||
EVENT_REMOVE_CURSE = 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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_loatheb_40() : CreatureScript("boss_loatheb_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_loatheb_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_loatheb_40AI : public BossAI
|
||||
{
|
||||
explicit boss_loatheb_40AI(Creature* c) : BossAI(c, BOSS_LOATHEB), summons(me)
|
||||
{
|
||||
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
|
||||
}
|
||||
|
||||
uint8 doomCounter;
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
events.Reset();
|
||||
summons.DespawnAll();
|
||||
doomCounter = 0;
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* cr) override
|
||||
{
|
||||
cr->SetInCombatWithZone();
|
||||
summons.Summon(cr);
|
||||
}
|
||||
|
||||
void SummonedCreatureDies(Creature* /*cr*/, Unit*) override
|
||||
{
|
||||
instance->SetData(DATA_SPORE_KILLED, 0);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (who->IsPlayer())
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
BossAI::JustEngagedWith(who);
|
||||
me->SetInCombatWithZone();
|
||||
// events.ScheduleEvent(EVENT_CORRUPTED_MIND, 5s);
|
||||
events.ScheduleEvent(EVENT_NECROTIC_AURA, 10s);
|
||||
// events.ScheduleEvent(EVENT_DEATHBLOOM, 5s);
|
||||
events.ScheduleEvent(EVENT_POISON_SHOCK, 5s);
|
||||
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2min);
|
||||
events.ScheduleEvent(EVENT_SUMMON_SPORE, 15s);
|
||||
// events.ScheduleEvent(EVENT_BERSERK, 12min);
|
||||
events.ScheduleEvent(EVENT_REMOVE_CURSE, 5s);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
summons.DespawnAll();
|
||||
}
|
||||
|
||||
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(13s);
|
||||
break;
|
||||
/*
|
||||
case EVENT_CORRUPTED_MIND:
|
||||
{
|
||||
if (me->CastSpell(me, SPELL_CORRUPTED_MIND, true) == SPELL_CAST_OK)
|
||||
{
|
||||
events.RepeatEvent(10000);
|
||||
}
|
||||
else
|
||||
{
|
||||
events.RepeatEvent(100);
|
||||
}
|
||||
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:
|
||||
{
|
||||
int32 bp0 = 33; // TODO: Amplitude should be 6k, but is 1k. 200 dmg after 6 seconds
|
||||
me->CastCustomSpell(me, SPELL_DEATHBLOOM_10, &bp0, 0, 0, false);
|
||||
events.Repeat(30s);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
case EVENT_POISON_SHOCK:
|
||||
if (me->CastSpell(me, SPELL_POISON_SHOCK, true) == SPELL_CAST_OK)
|
||||
events.RepeatEvent(6000);
|
||||
else
|
||||
events.RepeatEvent(100);
|
||||
break;
|
||||
case EVENT_INEVITABLE_DOOM:
|
||||
{
|
||||
int32 bp0 = 2549;
|
||||
|
||||
if (me->CastCustomSpell(me, SPELL_INEVITABLE_DOOM, &bp0, 0, 0, false) == SPELL_CAST_OK)
|
||||
{
|
||||
doomCounter++;
|
||||
events.Repeat(doomCounter < 6 ? 30s : 15s);
|
||||
}
|
||||
else
|
||||
events.RepeatEvent(100);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
case EVENT_BERSERK:
|
||||
me->CastSpell(me, SPELL_BERSERK, true);
|
||||
break;
|
||||
*/
|
||||
case EVENT_REMOVE_CURSE:
|
||||
me->CastSpell(me, SPELL_REMOVE_CURSE, true);
|
||||
events.Repeat(30s);
|
||||
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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_boss_loatheb_40()
|
||||
{
|
||||
new boss_loatheb_40();
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.h"
|
||||
#include "PassiveAI.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellScript.h"
|
||||
#include "SpellScriptLoader.h"
|
||||
#include "naxxramas.h"
|
||||
|
||||
enum Spells
|
||||
{
|
||||
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,
|
||||
SPELL_WEB_WRAP_STUN = 28622,
|
||||
SPELL_WEB_WRAP_SUMMON = 28627,
|
||||
SPELL_WEB_WRAP_KILL_WEBS = 52512,
|
||||
SPELL_WEB_WRAP_PACIFY_5 = 28618 // 5 seconds pacify silence
|
||||
};
|
||||
|
||||
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,
|
||||
EVENT_WEB_WRAP_APPLY_STUN = 7
|
||||
};
|
||||
|
||||
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[7] =
|
||||
{
|
||||
{3496.615f, -3834.182f, 320.7863f},
|
||||
{3509.108f, -3833.922f, 320.4750f},
|
||||
{3523.644f, -3838.309f, 320.5775f},
|
||||
{3538.152f, -3846.353f, 320.5188f},
|
||||
{3546.219f, -3856.167f, 320.9324f},
|
||||
{3555.135f, -3869.507f, 320.8307f},
|
||||
{3560.282f, -3886.143f, 321.2827f}
|
||||
};
|
||||
|
||||
struct WebTargetSelector
|
||||
{
|
||||
WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {}
|
||||
bool operator()(Unit const* target) const
|
||||
{
|
||||
if (!target->IsPlayer()) // never web nonplayers (pets, guardians, etc.)
|
||||
return false;
|
||||
if (_maexxna->GetVictim() == target) // never target tank
|
||||
return false;
|
||||
if (target->HasAura(SPELL_WEB_WRAP_STUN)) // never target targets that are already webbed
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Unit const* _maexxna;
|
||||
};
|
||||
|
||||
class boss_maexxna_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_maexxna_40() : CreatureScript("boss_maexxna_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_maexxna_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_maexxna_40AI : public BossAI
|
||||
{
|
||||
explicit boss_maexxna_40AI(Creature* c) : BossAI(c, BOSS_MAEXXNA), summons(me)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
|
||||
GuidList wraps;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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->IsPlayer())
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
}
|
||||
|
||||
void DoCastWebWrap()
|
||||
{
|
||||
std::list<Unit*> candidates;
|
||||
SelectTargetList(candidates, RAID_MODE(1, 2, 2, 2), SelectTargetMethod::Random, 0, WebTargetSelector(me));
|
||||
|
||||
std::vector<uint32> positions {0, 1, 2, 3, 4, 5, 6};
|
||||
Acore::Containers::RandomShuffle(positions);
|
||||
|
||||
if (candidates.empty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < RAID_MODE(1, 2, 2, 2); i++)
|
||||
{
|
||||
if (candidates.empty())
|
||||
break;
|
||||
const Position &randomPos = PosWrap[positions[i]];
|
||||
|
||||
auto itr = candidates.begin();
|
||||
|
||||
if (candidates.size() > 1)
|
||||
std::advance(itr, urand(0, candidates.size() - 1));
|
||||
|
||||
Unit *target = *itr;
|
||||
candidates.erase(itr);
|
||||
|
||||
float dx = randomPos.GetPositionX() - target->GetPositionX();
|
||||
float dy = randomPos.GetPositionY() - target->GetPositionY();
|
||||
float distXY = std::hypotf(dx, dy);
|
||||
|
||||
// smooth knockback arc that avoids the ceiling
|
||||
float horizontalSpeed = distXY / 1.5f;
|
||||
float verticalSpeed = 28.0f;
|
||||
if (distXY <= 10.0f)
|
||||
verticalSpeed = 12.0f;
|
||||
else if (distXY <= 20.0f)
|
||||
verticalSpeed = 16.0f;
|
||||
else if (distXY <= 30.0f)
|
||||
verticalSpeed = 20.0f;
|
||||
else if (distXY <= 40.0f)
|
||||
verticalSpeed = 24.0f;
|
||||
|
||||
target->KnockbackFrom(randomPos.GetPositionX(), randomPos.GetPositionY(), -horizontalSpeed, verticalSpeed);
|
||||
me->CastSpell(target, SPELL_WEB_WRAP_PACIFY_5, true); // pacify silence for 5 seconds
|
||||
|
||||
wraps.push_back(target->GetGUID());
|
||||
}
|
||||
events.ScheduleEvent(EVENT_WEB_WRAP_APPLY_STUN, 2s);
|
||||
}
|
||||
|
||||
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, 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, 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, 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, SPELL_FRENZY_10, SPELL_FRENZY_25), true);
|
||||
break;
|
||||
}
|
||||
events.Repeat(1s);
|
||||
break;
|
||||
case EVENT_WEB_WRAP:
|
||||
Talk(EMOTE_WEB_WRAP);
|
||||
DoCastWebWrap();
|
||||
events.Repeat(40s);
|
||||
break;
|
||||
case EVENT_WEB_WRAP_APPLY_STUN:
|
||||
{
|
||||
for (auto& p : wraps)
|
||||
{
|
||||
if (Player* player = ObjectAccessor::GetPlayer(*me, p))
|
||||
{
|
||||
player->CastSpell(player, SPELL_WEB_WRAP_STUN, true);
|
||||
}
|
||||
}
|
||||
wraps.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class boss_maexxna_webwrap_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_maexxna_webwrap_40() : CreatureScript("boss_maexxna_webwrap_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_maexxna_webwrap_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_maexxna_webwrap_40AI : public NullCreatureAI
|
||||
{
|
||||
explicit boss_maexxna_webwrap_40AI(Creature* c) : NullCreatureAI(c) { }
|
||||
|
||||
ObjectGuid victimGUID;
|
||||
|
||||
void IsSummonedBy(WorldObject* summoner) override
|
||||
{
|
||||
if (!summoner)
|
||||
return;
|
||||
victimGUID = summoner->GetGUID();
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (victimGUID)
|
||||
{
|
||||
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
|
||||
{
|
||||
if (victim->IsAlive())
|
||||
{
|
||||
victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP_STUN);
|
||||
victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP_SUMMON_40);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 /*diff*/) override
|
||||
{
|
||||
if (victimGUID)
|
||||
{
|
||||
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
|
||||
{
|
||||
if (!victim->IsAlive())
|
||||
{
|
||||
me->CastSpell(me, SPELL_WEB_WRAP_KILL_WEBS, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class spell_web_wrap_damage : public AuraScript
|
||||
{
|
||||
public:
|
||||
PrepareAuraScript(spell_web_wrap_damage);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_WEB_WRAP_SUMMON_40 });
|
||||
}
|
||||
|
||||
void OnPeriodic(AuraEffect const* aurEff)
|
||||
{
|
||||
if (aurEff->GetTickNumber() == 2)
|
||||
{
|
||||
if (GetCaster()->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
GetTarget()->CastSpell(GetTarget(), SPELL_WEB_WRAP_SUMMON_40, true);
|
||||
else
|
||||
GetTarget()->CastSpell(GetTarget(), SPELL_WEB_WRAP_SUMMON, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectPeriodic += AuraEffectPeriodicFn(spell_web_wrap_damage::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_maexxna_40()
|
||||
{
|
||||
new boss_maexxna_40();
|
||||
new boss_maexxna_webwrap_40();
|
||||
RegisterSpellScript(spell_web_wrap_damage);
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_noth_40() : CreatureScript("boss_noth_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_noth_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_noth_40AI : public BossAI
|
||||
{
|
||||
explicit boss_noth_40AI(Creature* c) : BossAI(c, BOSS_NOTH), summons(me)
|
||||
{}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
Talk(SAY_SLAY);
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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(SPELL_CURSE_OF_THE_PLAGUEBRINGER_10, SPELLVALUE_MAX_TARGETS, 10, me, false); // TODO: Increase to 20 on 40man
|
||||
}
|
||||
events.Repeat(25s); // 50-60 seconds in 40man
|
||||
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, 3, 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, 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, 4, 4));
|
||||
break;
|
||||
case 1:
|
||||
SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(1, 2, 2, 2));
|
||||
SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(1, 2, 2, 2));
|
||||
break;
|
||||
default:
|
||||
SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(2, 4, 4, 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();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_boss_noth_40()
|
||||
{
|
||||
new boss_noth_40();
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_patchwerk_40() : CreatureScript("boss_patchwerk_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_patchwerk_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_patchwerk_40AI : public BossAI
|
||||
{
|
||||
explicit boss_patchwerk_40AI(Creature* c) : BossAI(c, BOSS_PATCHWERK)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
events.Reset();
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
if (!urand(0, 3))
|
||||
Talk(SAY_SLAY);
|
||||
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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, 1200ms);
|
||||
events.ScheduleEvent(EVENT_BERSERK, 7min); // 7 minutes enrange
|
||||
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s);
|
||||
// instance->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 < 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)
|
||||
{
|
||||
int32 dmg = urand(22100,22850);
|
||||
me->CastCustomSpell(finalTarget, SPELL_HATEFUL_STRIKE_10, &dmg, 0, 0, false);
|
||||
}
|
||||
events.Repeat(1200ms);
|
||||
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();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_boss_patchwerk_40()
|
||||
{
|
||||
new boss_patchwerk_40();
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureScript.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "naxxramas.h"
|
||||
#include "SpellInfo.h"
|
||||
|
||||
enum Says
|
||||
{
|
||||
SAY_AGGRO = 0,
|
||||
SAY_SLAY = 1,
|
||||
SAY_TAUNTED = 2,
|
||||
SAY_DEATH = 3,
|
||||
SAY_PATHETIC = 4,
|
||||
SAY_TARGET_DUMMY = 5,
|
||||
SAY_DEATH_KNIGHT_UNDERSTUDY = 0,
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_UNBALANCING_STRIKE = 26613,
|
||||
// SPELL_DISRUPTING_SHOUT = 29107, // 55543
|
||||
SPELL_MANA_BURN = 26046, // Alternative for Disrupting shout.
|
||||
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 NPCs
|
||||
{
|
||||
// NPC_DEATH_KNIGHT_UNDERSTUDY = 351084,
|
||||
NPC_TARGET_DUMMY = 16211,
|
||||
};
|
||||
|
||||
enum Actions
|
||||
{
|
||||
ACTION_FACE_ME = 0,
|
||||
ACTION_TALK = 1,
|
||||
ACTION_EMOTE = 2,
|
||||
ACTION_SALUTE = 3,
|
||||
ACTION_BACK_TO_TRAINING = 4,
|
||||
};
|
||||
|
||||
enum Misc
|
||||
{
|
||||
GROUP_OOC_RP = 0,
|
||||
POINT_DEATH_KNIGHT = 0,
|
||||
};
|
||||
|
||||
class boss_razuvious_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_razuvious_40() : CreatureScript("boss_razuvious_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_razuvious_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_razuvious_40AI : public BossAI
|
||||
{
|
||||
explicit boss_razuvious_40AI(Creature* c) : BossAI(c, BOSS_RAZUVIOUS), summons(me)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
SummonList summons;
|
||||
|
||||
void SpawnHelpers()
|
||||
{
|
||||
// 10man
|
||||
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);
|
||||
// 25man
|
||||
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();
|
||||
ScheduleRP();
|
||||
}
|
||||
|
||||
void ScheduleInteractWithDeathKnight()
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
me->SetFacingToObject(understudy);
|
||||
|
||||
scheduler.Schedule(2s, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (roll_chance_i(75))
|
||||
{
|
||||
bool longText = roll_chance_i(50);
|
||||
Talk(longText ? SAY_TARGET_DUMMY : SAY_PATHETIC);
|
||||
scheduler.Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
understudy->AI()->DoAction(ACTION_TALK);
|
||||
});
|
||||
if (longText)
|
||||
scheduler.DelayGroup(GROUP_OOC_RP, 5s);
|
||||
}
|
||||
else
|
||||
{
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION);
|
||||
scheduler.Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
{
|
||||
if (roll_chance_i(25))
|
||||
understudy->AI()->DoAction(ACTION_EMOTE);
|
||||
else
|
||||
understudy->AI()->DoAction(ACTION_TALK);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
understudy->AI()->DoAction(ACTION_FACE_ME);
|
||||
}).Schedule(10s, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
understudy->AI()->DoAction(ACTION_SALUTE);
|
||||
}).Schedule(13s, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
me->ResumeMovement();
|
||||
}).Schedule(16s, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
understudy->AI()->DoAction(ACTION_BACK_TO_TRAINING);
|
||||
ScheduleRP();
|
||||
});
|
||||
}
|
||||
|
||||
void MovementInform(uint32 type, uint32 id) override
|
||||
{
|
||||
if (type == POINT_MOTION_TYPE && id == POINT_DEATH_KNIGHT)
|
||||
{
|
||||
ScheduleInteractWithDeathKnight();
|
||||
}
|
||||
}
|
||||
|
||||
void ScheduleRP()
|
||||
{
|
||||
_rpBuddyGUID = Acore::Containers::SelectRandomContainerElement(summons);
|
||||
scheduler.Schedule(60s, 80s, GROUP_OOC_RP, [this](TaskContext context)
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
{
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
{
|
||||
if (me->GetDistance2d(understudy) <= 6.0f)
|
||||
{
|
||||
me->PauseMovement();
|
||||
scheduler.Schedule(500ms, GROUP_OOC_RP, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (_rpBuddyGUID)
|
||||
if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID))
|
||||
me->GetMotionMaster()->MovePoint(POINT_DEATH_KNIGHT, understudy->GetNearPosition(3.2f, understudy->GetRelativeAngle(me)));
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
context.Repeat(2s);
|
||||
});
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (roll_chance_i(30))
|
||||
Talk(SAY_SLAY);
|
||||
|
||||
if (who->IsPlayer())
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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->IsCreature() && 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);
|
||||
scheduler.CancelGroup(GROUP_OOC_RP);
|
||||
Talk(SAY_AGGRO);
|
||||
events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, 20s); // TODO: This can be 30 seconds to match vanilla
|
||||
events.ScheduleEvent(EVENT_DISRUPTING_SHOUT, 15s);
|
||||
// events.ScheduleEvent(EVENT_JAGGED_KNIFE, 10s); // wrath only
|
||||
summons.DoZoneInCombat();
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!me->IsInCombat())
|
||||
scheduler.Update(diff);
|
||||
|
||||
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, SPELL_MANA_BURN, false);
|
||||
events.Repeat(10s);
|
||||
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();
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectGuid _rpBuddyGUID;
|
||||
};
|
||||
};
|
||||
|
||||
class boss_razuvious_minion_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_razuvious_minion_40() : CreatureScript("boss_razuvious_minion_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_razuvious_minion_40AI>(creature);
|
||||
}
|
||||
|
||||
struct boss_razuvious_minion_40AI : public ScriptedAI
|
||||
{
|
||||
explicit boss_razuvious_minion_40AI(Creature* creature) : ScriptedAI(creature) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
scheduler.CancelAll();
|
||||
ScheduleAttackDummy();
|
||||
}
|
||||
|
||||
void ScheduleAttackDummy()
|
||||
{
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H);
|
||||
if (Creature* targetDummy = me->FindNearestCreature(NPC_TARGET_DUMMY, 10.0f))
|
||||
{
|
||||
me->SetFacingToObject(targetDummy);
|
||||
}
|
||||
scheduler.Schedule(6s, 9s, GROUP_OOC_RP, [this](TaskContext context)
|
||||
{
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H);
|
||||
context.Repeat(6s, 9s);
|
||||
});
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case ACTION_FACE_ME:
|
||||
scheduler.CancelGroup(GROUP_OOC_RP);
|
||||
me->SetSheath(SHEATH_STATE_UNARMED);
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
|
||||
if (Creature* creature = me->GetInstanceScript()->GetCreature(DATA_RAZUVIOUS_BOSS))
|
||||
me->SetFacingToObject(creature);
|
||||
break;
|
||||
case ACTION_TALK:
|
||||
Talk(SAY_DEATH_KNIGHT_UNDERSTUDY);
|
||||
break;
|
||||
case ACTION_EMOTE:
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_TALK);
|
||||
break;
|
||||
case ACTION_SALUTE:
|
||||
me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE);
|
||||
break;
|
||||
case ACTION_BACK_TO_TRAINING:
|
||||
me->SetSheath(SHEATH_STATE_MELEE);
|
||||
ScheduleAttackDummy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (who->IsPlayer())
|
||||
me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
scheduler.CancelGroup(GROUP_OOC_RP);
|
||||
|
||||
if (Creature* creature = me->GetInstanceScript()->GetCreature(DATA_RAZUVIOUS_BOSS))
|
||||
{
|
||||
creature->SetInCombatWithZone();
|
||||
creature->AI()->AttackStart(who);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
scheduler.Update(diff);
|
||||
|
||||
if (UpdateVictim())
|
||||
{
|
||||
if (!me->HasUnitState(UNIT_STATE_CASTING) || !me->IsCharmed())
|
||||
{
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_boss_razuvious_40()
|
||||
{
|
||||
new boss_razuvious_40();
|
||||
new boss_razuvious_minion_40();
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* 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 = 28531,
|
||||
SPELL_CLEAVE = 19983,
|
||||
SPELL_TAIL_SWEEP = 15847,
|
||||
SPELL_SUMMON_BLIZZARD = 28560,
|
||||
SPELL_LIFE_DRAIN = 28542,
|
||||
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,
|
||||
|
||||
// 10 and 25 Man Spells
|
||||
SPELL_FROST_AURA_10 = 28531,
|
||||
SPELL_FROST_AURA_25 = 55799,
|
||||
SPELL_TAIL_SWEEP_10 = 55697,
|
||||
SPELL_TAIL_SWEEP_25 = 55696,
|
||||
SPELL_LIFE_DRAIN_10 = 28542,
|
||||
SPELL_LIFE_DRAIN_25 = 55665,
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// Unlike other Naxx 40 scripts, this overwrites all versions of the UI
|
||||
// This is due to AI casting used in the spell script
|
||||
|
||||
class boss_sapphiron_40 : public CreatureScript
|
||||
{
|
||||
private:
|
||||
static bool isNaxx40Sapp(uint32 entry)
|
||||
{
|
||||
return (entry == NPC_SAPPHIRON_40);
|
||||
}
|
||||
public:
|
||||
boss_sapphiron_40() : CreatureScript("boss_sapphiron") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_sapphiron_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_sapphiron_40AI : public BossAI
|
||||
{
|
||||
explicit boss_sapphiron_40AI(Creature* c) : BossAI(c, BOSS_SAPPHIRON)
|
||||
{}
|
||||
|
||||
EventMap events;
|
||||
uint8 iceboltCount{};
|
||||
uint32 spawnTimer{};
|
||||
GuidList blockList;
|
||||
ObjectGuid currentTarget;
|
||||
|
||||
void InitializeAI() override
|
||||
{
|
||||
if (instance->GetBossState(BOSS_SAPPHIRON) != DONE)
|
||||
{
|
||||
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 (auto const& 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();
|
||||
if (isNaxx40Sapp(me->GetEntry()))
|
||||
me->CastSpell(me, SPELL_FROST_AURA, true);
|
||||
else
|
||||
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->IsPlayer())
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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, SPELL_TAIL_SWEEP, SPELL_TAIL_SWEEP_25), false);
|
||||
events.Repeat(10s);
|
||||
return;
|
||||
case EVENT_LIFE_DRAIN:
|
||||
if (isNaxx40Sapp(me->GetEntry()))
|
||||
{
|
||||
CustomSpellValues values;
|
||||
int32 bp0 = 1700;
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, bp0);
|
||||
values.AddSpellMod(SPELLVALUE_MAX_TARGETS, 5);
|
||||
me->CastCustomSpell(SPELL_LIFE_DRAIN, values, me, TRIGGERED_NONE, nullptr, nullptr, ObjectGuid::Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
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, 6500, 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, 3, 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()->IsPlayer())
|
||||
{
|
||||
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 (auto const& itr : pList)
|
||||
{
|
||||
if (itr.GetSource()->GetResistance(SPELL_SCHOOL_FROST) > 100)
|
||||
{
|
||||
instance->SetData(DATA_HUNDRED_CLUB, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
events.Repeat(5s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// This will overwrite the declared 10 and 25 man frost explosion to handle all versions of the spell script
|
||||
class spell_sapphiron_frost_explosion : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_sapphiron_frost_explosion);
|
||||
|
||||
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_40::boss_sapphiron_40AI, 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::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_sapphiron_40()
|
||||
{
|
||||
new boss_sapphiron_40();
|
||||
RegisterSpellScript(spell_sapphiron_frost_explosion);
|
||||
}
|
||||
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AreaTriggerScript.h"
|
||||
#include "CreatureScript.h"
|
||||
#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 = 28134,
|
||||
SPELL_STALAGG_CHAIN = 28096,
|
||||
|
||||
// Feugen
|
||||
SPELL_STATIC_FIELD = 28135,
|
||||
SPELL_FEUGEN_CHAIN = 28111,
|
||||
|
||||
// Thaddius
|
||||
SPELL_POLARITY_SHIFT = 28089,
|
||||
SPELL_BALL_LIGHTNING = 28299,
|
||||
SPELL_CHAIN_LIGHTNING = 28167,
|
||||
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_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_thaddius_40() : CreatureScript("boss_thaddius_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_thaddius_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_thaddius_40AI : public BossAI
|
||||
{
|
||||
explicit boss_thaddius_40AI(Creature* c) : BossAI(c, BOSS_THADDIUS), summons(me), ballLightningEnabled(false)
|
||||
{}
|
||||
|
||||
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_40, 3450.45f, -2931.42f, 312.091f, 5.49779f);
|
||||
me->SummonCreature(NPC_FEUGEN_40, 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);
|
||||
}
|
||||
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
Talk(SAY_SLAY);
|
||||
instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
BossAI::JustDied(killer);
|
||||
Talk(SAY_DEATH);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* cr) override
|
||||
{
|
||||
summons.Summon(cr);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* who) override
|
||||
{
|
||||
BossAI::JustEngagedWith(who);
|
||||
me->SetInCombatWithZone();
|
||||
summons.DoZoneInCombat(NPC_FEUGEN_40);
|
||||
summons.DoZoneInCombat(NPC_STALAGG_40);
|
||||
}
|
||||
|
||||
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, 750);
|
||||
}
|
||||
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:
|
||||
{
|
||||
CustomSpellValues values;
|
||||
int32 customChainLightningDamage = 1850; // (1850, 2150), die 675
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, customChainLightningDamage);
|
||||
values.AddSpellMod(SPELLVALUE_MAX_TARGETS, 15);
|
||||
me->CastCustomSpell(SPELL_CHAIN_LIGHTNING, values, me->GetVictim(), TRIGGERED_NONE, nullptr, nullptr, ObjectGuid::Empty);
|
||||
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))
|
||||
{
|
||||
int32 customBallLightningDamage = 6000;
|
||||
me->CastCustomSpell(target, SPELL_BALL_LIGHTNING, &customBallLightningDamage, 0, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class boss_thaddius_summon_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_thaddius_summon_40() : CreatureScript("boss_thaddius_summon_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* pCreature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_thaddius_summon_40AI>(pCreature);
|
||||
}
|
||||
|
||||
struct boss_thaddius_summon_40AI : public ScriptedAI
|
||||
{
|
||||
explicit boss_thaddius_summon_40AI(Creature* c) : ScriptedAI(c)
|
||||
{
|
||||
overload = false;
|
||||
}
|
||||
|
||||
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_40 ? 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_40)
|
||||
{
|
||||
events.ScheduleEvent(EVENT_MINION_POWER_SURGE, 10000);
|
||||
Talk(SAY_STAL_AGGRO);
|
||||
}
|
||||
else
|
||||
{
|
||||
events.ScheduleEvent(EVENT_MINION_STATIC_FIELD, 5000);
|
||||
Talk(SAY_FEUG_AGGRO);
|
||||
}
|
||||
events.ScheduleEvent(EVENT_MINION_CHECK_DISTANCE, 5000);
|
||||
|
||||
if (me->GetEntry() == NPC_STALAGG_40) // This event needs synchronisation, called for stalagg only
|
||||
{
|
||||
events.ScheduleEvent(EVENT_MINION_MAGNETIC_PULL, 20000);
|
||||
}
|
||||
|
||||
if (Creature* cr = me->GetInstanceScript()->GetCreature(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_40 ? EMOTE_STAL_REVIVE : EMOTE_FEUG_REVIVE);
|
||||
}
|
||||
else
|
||||
{
|
||||
me->SetHealth(me->GetMaxHealth());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
Talk(me->GetEntry() == NPC_STALAGG_40 ? SAY_STAL_DEATH : SAY_FEUG_DEATH);
|
||||
Talk(me->GetEntry() == NPC_STALAGG_40 ? EMOTE_STAL_DEATH : EMOTE_FEUG_DEATH);
|
||||
|
||||
if (Creature* cr = me->GetInstanceScript()->GetCreature(DATA_THADDIUS_BOSS))
|
||||
cr->AI()->DoAction(ACTION_SUMMON_DIED);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (!who->IsPlayer())
|
||||
return;
|
||||
|
||||
if (!urand(0, 2))
|
||||
Talk(me->GetEntry() == NPC_STALAGG_40 ? SAY_STAL_SLAY : SAY_FEUG_SLAY);
|
||||
|
||||
me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
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_40 ? 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, SPELL_POWER_SURGE, false);
|
||||
events.Repeat(19s);
|
||||
break;
|
||||
case EVENT_MINION_STATIC_FIELD:
|
||||
me->CastSpell(me, SPELL_STATIC_FIELD, false);
|
||||
events.Repeat(3s);
|
||||
break;
|
||||
case EVENT_MINION_MAGNETIC_PULL:
|
||||
{
|
||||
events.Repeat(20s);
|
||||
if (Creature* feugen = me->GetInstanceScript()->GetCreature(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_40 ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN);
|
||||
cr->InterruptNonMeleeSpells(true);
|
||||
}
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 1000.f, true))
|
||||
{
|
||||
cr->CastStop(SPELL_TESLA_SHOCK);
|
||||
int32 customTeslaShockDamage = 4374;
|
||||
cr->CastCustomSpell(target, SPELL_TESLA_SHOCK, &customTeslaShockDamage, 0, 0, true);
|
||||
}
|
||||
events.Repeat(1500ms);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
overload = false;
|
||||
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG_40 ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
|
||||
}
|
||||
}
|
||||
events.Repeat(5s);
|
||||
break;
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// This will overwrite the declared 10 and 25 man pos_neg_charge to handle all versions of the spell script
|
||||
class spell_thaddius_pos_neg_charge : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_thaddius_pos_neg_charge);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_POSITIVE_CHARGE, SPELL_POSITIVE_CHARGE_STACK });
|
||||
}
|
||||
|
||||
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->IsPlayer())
|
||||
{
|
||||
SetHitDamage(0);
|
||||
}
|
||||
else if (target->GetInstanceScript())
|
||||
{
|
||||
target->GetInstanceScript()->SetData(DATA_CHARGES_CROSSED, 0);
|
||||
}
|
||||
// Adjust damage to 2000 from 4500 for naxx40
|
||||
if (target->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
{
|
||||
SetHitDamage(2000);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
|
||||
}
|
||||
};
|
||||
|
||||
class spell_thaddius_polarity_shift : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_thaddius_polarity_shift);
|
||||
|
||||
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_40)
|
||||
{
|
||||
caster->AI()->Talk(SAY_ELECT);
|
||||
caster->AI()->Talk(EMOTE_POLARITY_SHIFTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_thaddius_polarity_shift::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
|
||||
AfterCast += SpellCastFn(spell_thaddius_polarity_shift::HandleAfterCast);
|
||||
}
|
||||
};
|
||||
|
||||
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 _thaddiusIntro = false;
|
||||
|
||||
bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override
|
||||
{
|
||||
InstanceScript* instance = player->GetInstanceScript();
|
||||
if (!instance || _thaddiusIntro == true || instance->GetBossState(BOSS_THADDIUS) == DONE)
|
||||
return true;
|
||||
|
||||
if (Creature* thaddius = instance->GetCreature(DATA_THADDIUS_BOSS))
|
||||
{
|
||||
thaddius->AI()->Talk(SAY_GREET);
|
||||
_thaddiusIntro = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_thaddius_40()
|
||||
{
|
||||
new boss_thaddius_40();
|
||||
new boss_thaddius_summon_40();
|
||||
// new npc_tesla();
|
||||
RegisterSpellScript(spell_thaddius_pos_neg_charge);
|
||||
// RegisterSpellScript(spell_thaddius_polarity_shift);
|
||||
// new at_thaddius_entrance();
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellScript.h"
|
||||
#include "Player.h"
|
||||
#include "naxxramas.h"
|
||||
|
||||
class npc_naxx40_area_trigger : public CreatureScript
|
||||
{
|
||||
private:
|
||||
static bool isAttuned(Player* player)
|
||||
{
|
||||
if (player->GetQuestStatus(NAXX40_ATTUNEMENT_1) == QUEST_STATUS_REWARDED)
|
||||
return true;
|
||||
if (player->GetQuestStatus(NAXX40_ATTUNEMENT_2) == QUEST_STATUS_REWARDED)
|
||||
return true;
|
||||
if (player->GetQuestStatus(NAXX40_ATTUNEMENT_3) == QUEST_STATUS_REWARDED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
npc_naxx40_area_trigger() : CreatureScript("npc_naxx40_area_trigger") {}
|
||||
|
||||
struct npc_naxx40_area_triggerAI: public ScriptedAI
|
||||
{
|
||||
npc_naxx40_area_triggerAI(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
me->SetDisplayId(11686); // Invisible
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (who && me->GetDistance2d(who) < 5.0f)
|
||||
{
|
||||
if (Player* player = who->ToPlayer())
|
||||
{
|
||||
if (isAttuned(player))
|
||||
{
|
||||
player->SetRaidDifficulty(RAID_DIFFICULTY_10MAN_HEROIC);
|
||||
player->TeleportTo(533, 3005.51f, -3434.64f, 304.195f, 6.2831f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (who && me->GetDistance2d(who) < 20.0f)
|
||||
{
|
||||
if (Player* player = who->ToPlayer())
|
||||
{
|
||||
if (isAttuned(player))
|
||||
{
|
||||
GameObject* door = me->FindNearestGameObject(GO_STRATH_GATE_40, 100.0f);
|
||||
if (door)
|
||||
{
|
||||
door->SetGoState(GO_STATE_ACTIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new npc_naxx40_area_triggerAI(creature);
|
||||
}
|
||||
};
|
||||
|
||||
class boss_heigan_eye_stalk_40 : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_heigan_eye_stalk_40() : CreatureScript("boss_heigan_eye_stalk_40") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetNaxxramasAI<boss_heigan_eye_stalk_40AI>(creature);
|
||||
}
|
||||
|
||||
struct boss_heigan_eye_stalk_40AI : public ScriptedAI
|
||||
{
|
||||
explicit boss_heigan_eye_stalk_40AI(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
timeSinceSpawn = 0;
|
||||
haveSubmerged = false;
|
||||
haveCastSubmerge = false;
|
||||
}
|
||||
|
||||
uint32 timeSinceSpawn;
|
||||
bool haveSubmerged;
|
||||
bool haveCastSubmerge;
|
||||
const uint32 SPELL_MIND_FLAY = 29407;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
me->SetNoCallAssistance(true);
|
||||
me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* who) override
|
||||
{
|
||||
if (who->IsPlayer() && me->GetInstanceScript())
|
||||
me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (timeSinceSpawn < 3000)
|
||||
return;
|
||||
|
||||
if (!who || !(who->GetDistance2d(me) <= 19.0f))
|
||||
return;
|
||||
|
||||
if (me->HasReactState(REACT_AGGRESSIVE) && me->CanStartAttack(who))
|
||||
{
|
||||
if (!me->IsWithinLOSInMap(who))
|
||||
{
|
||||
return;
|
||||
}
|
||||
me->SetNoCallAssistance(true);
|
||||
if (!me->GetVictim())
|
||||
{
|
||||
AttackStart(who);
|
||||
}
|
||||
else if (me->GetMap()->IsDungeon())
|
||||
{
|
||||
who->SetInCombatWith(me);
|
||||
me->AddThreat(who, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
me->SetNoCallAssistance(true);
|
||||
timeSinceSpawn += std::min(diff, std::numeric_limits<uint32>::max() - timeSinceSpawn);
|
||||
|
||||
if (haveSubmerged)
|
||||
{
|
||||
if (!haveCastSubmerge)
|
||||
{
|
||||
haveCastSubmerge = true;
|
||||
me->CastSpell(me, 26234, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
if (!me->IsNonMeleeSpellCast(false))
|
||||
{
|
||||
if (me->GetDistance(me->GetVictim()) < 35.0f)
|
||||
{
|
||||
int32 bp0 = 750; // damage
|
||||
int32 bp1 = -20; // movement speed
|
||||
me->CastCustomSpell(me->GetVictim(), SPELL_MIND_FLAY, &bp0, &bp1, 0, false, nullptr, nullptr, ObjectGuid::Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoStopAttack();
|
||||
}
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void AddSC_custom_creatures_40()
|
||||
{
|
||||
new npc_naxx40_area_trigger();
|
||||
new boss_heigan_eye_stalk_40();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellScript.h"
|
||||
#include "GameObjectAI.h"
|
||||
#include "Player.h"
|
||||
#include "naxxramas.h"
|
||||
#include "IndividualProgression.h"
|
||||
|
||||
class gobject_naxx40_tele : public GameObjectScript
|
||||
{
|
||||
private:
|
||||
static bool isAttuned(Player* player)
|
||||
{
|
||||
if ((player->GetQuestStatus(NAXX40_ATTUNEMENT_1) == QUEST_STATUS_REWARDED) ||
|
||||
(player->GetQuestStatus(NAXX40_ATTUNEMENT_2) == QUEST_STATUS_REWARDED) ||
|
||||
(player->GetQuestStatus(NAXX40_ATTUNEMENT_3) == QUEST_STATUS_REWARDED))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
gobject_naxx40_tele() : GameObjectScript("gobject_naxx40_tele") { }
|
||||
|
||||
struct gobject_naxx40_teleAI: GameObjectAI
|
||||
{
|
||||
explicit gobject_naxx40_teleAI(GameObject* object) : GameObjectAI(object) { };
|
||||
|
||||
};
|
||||
|
||||
GameObjectAI* GetAI(GameObject* object) const override
|
||||
{
|
||||
return new gobject_naxx40_teleAI(object);
|
||||
}
|
||||
|
||||
bool isExcludedFromProgression(Player* player)
|
||||
{
|
||||
if(!sIndividualProgression->excludeAccounts) {
|
||||
return false;
|
||||
}
|
||||
std::string accountName;
|
||||
bool accountNameFound = AccountMgr::GetName(player->GetSession()->GetAccountId(), accountName);
|
||||
std::regex excludedAccountsRegex (sIndividualProgression->excludedAccountsRegex);
|
||||
return (accountNameFound && std::regex_match(accountName, excludedAccountsRegex));
|
||||
}
|
||||
|
||||
bool OnGossipHello(Player* player, GameObject* /*go*/) override
|
||||
{
|
||||
if ((isExcludedFromProgression(player) && (player->GetLevel() <= IP_LEVEL_TBC)) ||
|
||||
((!sIndividualProgression->requireNaxxStrath || player->GetQuestStatus(NAXX40_ENTRANCE_FLAG) == QUEST_STATUS_REWARDED) && (!sIndividualProgression->hasPassedProgression(player, PROGRESSION_TBC_TIER_5))))
|
||||
{
|
||||
player->SetRaidDifficulty(RAID_DIFFICULTY_10MAN_HEROIC);
|
||||
|
||||
if (isAttuned(player) || isExcludedFromProgression(player))
|
||||
player->TeleportTo(533, 3005.51f, -3434.64f, 304.195f, 6.2831f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_custom_gameobjects_40()
|
||||
{
|
||||
new gobject_naxx40_tele();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellScript.h"
|
||||
#include "naxxramas.h"
|
||||
|
||||
class NaxxPlayerScript : public PlayerScript
|
||||
{
|
||||
public:
|
||||
NaxxPlayerScript() : PlayerScript("NaxxPlayerScript") { }
|
||||
|
||||
void OnPlayerBeforeChooseGraveyard(Player* player, TeamId /*teamId*/, bool /*nearCorpse*/, uint32& graveyardOverride) override
|
||||
{
|
||||
if (player->GetMapId() == MAP_NAXX && player->GetMap()->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
{
|
||||
graveyardOverride = NAXX40_GRAVEYARD;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class naxx_northrend_entrance : public AreaTriggerScript
|
||||
{
|
||||
public:
|
||||
naxx_northrend_entrance() : AreaTriggerScript("naxx_northrend_entrance") { }
|
||||
|
||||
bool OnTrigger(Player* player, AreaTrigger const* areaTrigger) override
|
||||
{
|
||||
// Do not allow entrance to Naxx 40 from Northrend
|
||||
// Change 10 man heroic to regular 10 man, as when 10 man heroic is not available
|
||||
Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(true) : player->GetDifficulty(true);
|
||||
if (diff == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
{
|
||||
player->SetRaidDifficulty(RAID_DIFFICULTY_10MAN_NORMAL);
|
||||
}
|
||||
switch (areaTrigger->entry)
|
||||
{
|
||||
// Naxx 10 and 25 entrances
|
||||
case 5191:
|
||||
player->TeleportTo(533, 3005.68f, -3447.77f, 293.93f, 4.65f);
|
||||
break;
|
||||
case 5192:
|
||||
player->TeleportTo(533, 3019.34f, -3434.36f, 293.99f, 6.27f);
|
||||
break;
|
||||
case 5193:
|
||||
player->TeleportTo(533, 3005.9f, -3420.58f, 294.11f, 1.58f);
|
||||
break;
|
||||
case 5194:
|
||||
player->TeleportTo(533, 2992.5f, -3434.42f, 293.94f, 3.13f);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class naxx_exit_trigger : public AreaTriggerScript
|
||||
{
|
||||
public:
|
||||
naxx_exit_trigger() : AreaTriggerScript("naxx_exit_trigger") { }
|
||||
|
||||
bool OnTrigger(Player* player, AreaTrigger const* areaTrigger) override
|
||||
{
|
||||
if (player->GetMap()->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
{
|
||||
// Naxx 40 cannot be exited via portals, as in Classic
|
||||
return false;
|
||||
}
|
||||
switch (areaTrigger->entry)
|
||||
{
|
||||
// Naxx 10 and 25 exits
|
||||
case 5196:
|
||||
player->TeleportTo(571, 3679.25f, -1278.58f, 243.55f, 2.39f);
|
||||
break;
|
||||
case 5197:
|
||||
player->TeleportTo(571, 3679.03f, -1259.68f, 243.55f, 3.98f);
|
||||
break;
|
||||
case 5198:
|
||||
player->TeleportTo(571, 3661.14f, -1279.55f, 243.55f, 0.82f);
|
||||
break;
|
||||
case 5199:
|
||||
player->TeleportTo(571, 3660.01f, -1260.99f, 243.55f, 5.51f);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class NaxxEntryFlag_AllMapScript : public AllMapScript
|
||||
{
|
||||
public:
|
||||
NaxxEntryFlag_AllMapScript() : AllMapScript("NaxxEntryFlag_AllMapScript") { }
|
||||
|
||||
void OnPlayerEnterAll(Map* map, Player* player) override
|
||||
{
|
||||
if (player->IsGameMaster())
|
||||
return;
|
||||
|
||||
// Check if mapId equals to Naxxramas (mapId: 533)
|
||||
if (map->GetId() != 533)
|
||||
return;
|
||||
|
||||
// Cast on player Naxxramas Entry Flag Trigger DND - Classic (spellID: 29296)
|
||||
if (player->GetQuestStatus(NAXX40_ENTRANCE_FLAG) != QUEST_STATUS_REWARDED)
|
||||
{
|
||||
// Mark player as having entered
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(NAXX40_ENTRANCE_FLAG);
|
||||
player->AddQuest(quest, nullptr);
|
||||
player->CompleteQuest(NAXX40_ENTRANCE_FLAG);
|
||||
player->RewardQuest(quest, 0, player, false, false);
|
||||
// Cast on player Naxxramas Entry Flag Trigger DND - Classic (spellID: 29296)
|
||||
player->CastSpell(player, 29296, true); // for visual effect only, possible crash if cast on login
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_custom_scripts_40()
|
||||
{
|
||||
new NaxxPlayerScript();
|
||||
new naxx_exit_trigger();
|
||||
new naxx_northrend_entrance();
|
||||
new NaxxEntryFlag_AllMapScript();
|
||||
}
|
||||
@@ -0,0 +1,449 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellAuraDefines.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellScript.h"
|
||||
#include "naxxramas.h"
|
||||
|
||||
// 28785 - Locust Swarm
|
||||
// Locust Swarm: Reduce damage ~1500 to ~1000, increase radius 25yd to 30yd
|
||||
enum LocustSwarm
|
||||
{
|
||||
SPELL_LOCUST_SWARM = 28785,
|
||||
SPELL_LOCUST_SWARM_TRIGGER = 28786, // periodic effect
|
||||
};
|
||||
|
||||
class spell_anub_locust_swarm_aura_40 : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_anub_locust_swarm_aura_40);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_LOCUST_SWARM_TRIGGER });
|
||||
}
|
||||
|
||||
void HandleTriggerSpell(AuraEffect const* /*aurEff*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
PreventDefaultAction();
|
||||
int32 modifiedLocustSwarmDamage = 812;
|
||||
CustomSpellValues values;
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, modifiedLocustSwarmDamage);
|
||||
values.AddSpellMod(SPELLVALUE_RADIUS_MOD, 3000); // 30yd
|
||||
caster->CastCustomSpell(SPELL_LOCUST_SWARM_TRIGGER, values, caster, TRIGGERED_FULL_MASK, nullptr, nullptr, GetCasterGUID());
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectPeriodic += AuraEffectPeriodicFn(spell_anub_locust_swarm_aura_40::HandleTriggerSpell, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||
}
|
||||
};
|
||||
|
||||
// 28865 - Consumption
|
||||
// add Naxx10HC damage
|
||||
class spell_gen_consumption : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_gen_consumption);
|
||||
|
||||
void CalculateDamage(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Map* map = GetCaster()->GetMap();
|
||||
if (!map)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int32 value = 0;
|
||||
if (map->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) // NAXX25 N
|
||||
{
|
||||
value = urand(4500, 4700);
|
||||
}
|
||||
else if (map->GetId() == 533) // NAXX10 N
|
||||
{
|
||||
if (map->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL)
|
||||
{
|
||||
value = urand(3000, 3200);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = urand(3960, 4840); // NAXX40
|
||||
}
|
||||
}
|
||||
else if (map->GetId() == 532) // Karazhan
|
||||
{
|
||||
value = urand(1110, 1310);
|
||||
}
|
||||
if (value)
|
||||
{
|
||||
SetEffectValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_gen_consumption::CalculateDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 28241 - Poison Cloud
|
||||
// poison damage by Poison Cloud ~3k to ~1k
|
||||
class spell_grobbulus_poison_cloud_poison_damage_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_grobbulus_poison_cloud_poison_damage_40);
|
||||
|
||||
void HandleDamageCalc(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetEffectValue(urand(1110, 1290));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_grobbulus_poison_cloud_poison_damage_40::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 29350 - Plague Cloud
|
||||
enum PlagueCloud
|
||||
{
|
||||
SPELL_PLAGUE_CLOUD_TRIGGER = 30122,
|
||||
};
|
||||
|
||||
class spell_heigan_plague_cloud_aura_40 : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_heigan_plague_cloud_aura_40);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_PLAGUE_CLOUD_TRIGGER });
|
||||
}
|
||||
|
||||
void HandleTriggerSpell(AuraEffect const* /*aurEff*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
PreventDefaultAction();
|
||||
int32 bp0 = 4000;
|
||||
caster->CastCustomSpell(caster, SPELL_PLAGUE_CLOUD_TRIGGER, &bp0, 0, 0, true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectPeriodic += AuraEffectPeriodicFn(spell_heigan_plague_cloud_aura_40::HandleTriggerSpell, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||
}
|
||||
};
|
||||
|
||||
// 29371 - Eruption
|
||||
class spell_heigan_eruption_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_heigan_eruption_40);
|
||||
|
||||
void HandleDamageCalc(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetEffectValue(urand(3500, 4500));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_heigan_eruption_40::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 28819 - Submerge Visual
|
||||
class spell_submerge_visual_aura : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_submerge_visual_aura);
|
||||
|
||||
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
GetTarget()->SetStandState(UNIT_STAND_STATE_SUBMERGED);
|
||||
}
|
||||
|
||||
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
GetTarget()->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectApply += AuraEffectApplyFn(spell_submerge_visual_aura::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
OnEffectRemove += AuraEffectRemoveFn(spell_submerge_visual_aura::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
// 28457 - Dark Blast
|
||||
class spell_kelthuzad_dark_blast_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_kelthuzad_dark_blast_40);
|
||||
|
||||
void CalculateDamage(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetEffectValue(urand(1750,2250));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_kelthuzad_dark_blast_40::CalculateDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 28479 - Frostbolt
|
||||
class spell_kelthuzad_frostbolt_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_kelthuzad_frostbolt_40);
|
||||
|
||||
void CalculateDamage(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetEffectValue(urand(2550,3450));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_kelthuzad_frostbolt_40::CalculateDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 28522 - Icebolt
|
||||
class spell_sapphiron_icebolt_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_sapphiron_icebolt_40);
|
||||
|
||||
void CalculateDamage(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetEffectValue(urand(2625,3375));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_sapphiron_icebolt_40::CalculateDamage, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 28531 - Frost Aura
|
||||
class spell_sapphiron_frost_aura_40 : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_sapphiron_frost_aura_40);
|
||||
|
||||
void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
return;
|
||||
if (urand(0, 99) == 0) // 1% chance to receive extra Frost Aura tick
|
||||
return;
|
||||
amount *= 0.5; // Reduce damage by 50% (1200bp -> 600bp)
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_sapphiron_frost_aura_40::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 60960 - War Stomp - Patchwork Golem
|
||||
class spell_patchwork_golem_war_stomp_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_patchwork_golem_war_stomp_40);
|
||||
|
||||
void CalculateDamage(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetHitDamage(urand(936,1064));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_patchwork_golem_war_stomp_40::CalculateDamage, EFFECT_2, SPELL_EFFECT_WEAPON_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 29213 - Curse of the Plaguebringer
|
||||
enum CurseOfThePlaguebringer
|
||||
{
|
||||
SPELL_REVENGE_OF_THE_PLAGUEBRINGER = 29214,
|
||||
};
|
||||
|
||||
class spell_noth_curse_of_the_plaguebringer_aura_40 : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_noth_curse_of_the_plaguebringer_aura_40);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_REVENGE_OF_THE_PLAGUEBRINGER }); // Revenge of the Plaguebringer
|
||||
}
|
||||
|
||||
void HandleTriggerSpell(AuraEffect const* /*aurEff*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
PreventDefaultAction();
|
||||
CustomSpellValues values;
|
||||
int32 bp0 = 1757; // instant damage
|
||||
int32 bp1 = 874; // periodic damage
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT0, bp0);
|
||||
values.AddSpellMod(SPELLVALUE_BASE_POINT1, bp1);
|
||||
values.AddSpellMod(SPELLVALUE_RADIUS_MOD, 3500); // 35yd
|
||||
GetTarget()->CastCustomSpell(SPELL_REVENGE_OF_THE_PLAGUEBRINGER, values, GetTarget(), TRIGGERED_NONE, nullptr, nullptr, GetCasterGUID());
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectPeriodic += AuraEffectPeriodicFn(spell_noth_curse_of_the_plaguebringer_aura_40::HandleTriggerSpell, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||
}
|
||||
};
|
||||
|
||||
// 26046 - Razuvious - Mana Burn - Alternative for Disrupting Shout
|
||||
class spell_razuvious_disrupting_shout_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_razuvious_disrupting_shout_40);
|
||||
|
||||
void CalculateDamage(SpellEffIndex effIndex)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetEffectValue(urand(4050,4950));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_razuvious_disrupting_shout_40::CalculateDamage, EFFECT_0, SPELL_EFFECT_POWER_BURN);
|
||||
}
|
||||
};
|
||||
|
||||
// 28450 Unholy Staff
|
||||
class spell_unholy_staff_arcane_explosion_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_unholy_staff_arcane_explosion_40);
|
||||
|
||||
void PreventLaunchHit(SpellEffIndex effIndex)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Unit* target = GetHitUnit())
|
||||
{
|
||||
if (target->IsWithinDist2d(caster, 20.0f))
|
||||
{
|
||||
SetEffectValue(urand(1838, 2361));
|
||||
}
|
||||
else
|
||||
{
|
||||
PreventHitDefaultEffect(effIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_unholy_staff_arcane_explosion_40::PreventLaunchHit, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 28153 Disease cloud, Sewage Slime
|
||||
class spell_disease_cloud_damage_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_disease_cloud_damage_40);
|
||||
|
||||
void HandleDamageCalc(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SetEffectValue(urand(278,322));
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_disease_cloud_damage_40::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
// 28135 Static Field
|
||||
class spell_feugen_static_field_40 : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_feugen_static_field_40);
|
||||
|
||||
void HandleDamageCalc(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster || (caster->GetMap()->GetDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Unit* target = GetHitUnit())
|
||||
{
|
||||
Powers PowerType = POWER_MANA;
|
||||
int32 drainedAmount = -target->ModifyPower(PowerType, -500);
|
||||
SetEffectValue(drainedAmount);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectLaunchTarget += SpellEffectFn(spell_feugen_static_field_40::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_custom_spells_40()
|
||||
{
|
||||
RegisterSpellScript(spell_anub_locust_swarm_aura_40);
|
||||
RegisterSpellScript(spell_gen_consumption);
|
||||
RegisterSpellScript(spell_grobbulus_poison_cloud_poison_damage_40);
|
||||
RegisterSpellScript(spell_heigan_plague_cloud_aura_40);
|
||||
RegisterSpellScript(spell_heigan_eruption_40);
|
||||
RegisterSpellScript(spell_submerge_visual_aura);
|
||||
RegisterSpellScript(spell_kelthuzad_dark_blast_40);
|
||||
RegisterSpellScript(spell_kelthuzad_frostbolt_40);
|
||||
RegisterSpellScript(spell_sapphiron_icebolt_40);
|
||||
RegisterSpellScript(spell_sapphiron_frost_aura_40);
|
||||
RegisterSpellScript(spell_patchwork_golem_war_stomp_40);
|
||||
RegisterSpellScript(spell_noth_curse_of_the_plaguebringer_aura_40);
|
||||
RegisterSpellScript(spell_razuvious_disrupting_shout_40);
|
||||
RegisterSpellScript(spell_unholy_staff_arcane_explosion_40);
|
||||
RegisterSpellScript(spell_disease_cloud_damage_40);
|
||||
RegisterSpellScript(spell_feugen_static_field_40);
|
||||
}
|
||||
@@ -0,0 +1,902 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AreaTriggerScript.h"
|
||||
#include "CellImpl.h"
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "CreatureScript.h"
|
||||
#include "InstanceMapScript.h"
|
||||
#include "InstanceScript.h"
|
||||
#include "PassiveAI.h"
|
||||
#include "Player.h"
|
||||
#include "naxxramas.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Map.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
struct LivingPoisonData
|
||||
{
|
||||
Position Start {};
|
||||
Position End {};
|
||||
uint32 DespawnTime {};
|
||||
};
|
||||
|
||||
static const LivingPoisonData LivingPoisonDataList[3]
|
||||
{
|
||||
{ Position { 3128.59, -3118.81, 293.346, 4.76754 }, Position { 3130.322, -3156.51, 293.324 }, 15200 },
|
||||
{ Position { 3154.25, -3125.7, 293.43, 4.47694 }, Position { 3144.779, -3158.416, 293.324 }, 14800 },
|
||||
{ Position { 3175.42, -3134.86, 293.34, 4.284 }, Position { 3158.778, -3164.201, 293.312 }, 14800 }
|
||||
};
|
||||
|
||||
static const float HeiganPos[2]
|
||||
{
|
||||
2796, -3707
|
||||
};
|
||||
|
||||
static const float HeiganEruptionSlope[3]
|
||||
{
|
||||
(-3685 - HeiganPos[1]) / (2724 - HeiganPos[0]),
|
||||
(-3647 - HeiganPos[1]) / (2749 - HeiganPos[0]),
|
||||
(-3637 - HeiganPos[1]) / (2771 - HeiganPos[0]),
|
||||
};
|
||||
|
||||
static constexpr std::array<uint32, HorsemanCount> HorsemanDataGroup
|
||||
{
|
||||
DATA_BARON_RIVENDARE_BOSS,
|
||||
DATA_SIR_ZELIEK_BOSS,
|
||||
DATA_LADY_BLAUMEUX_BOSS,
|
||||
DATA_THANE_KORTHAZZ_BOSS
|
||||
};
|
||||
|
||||
static WorldLocation const SapphironTeleportPos
|
||||
{
|
||||
NaxxramasMapId, 3498.300049f, -5349.490234f, 144.968002f, 1.3698910f
|
||||
};
|
||||
|
||||
static DoorData const doorData[]
|
||||
{
|
||||
{ GO_PATCHWERK_GATE, BOSS_PATCHWERK, DOOR_TYPE_PASSAGE },
|
||||
{ GO_PATCHWERK_GATE, BOSS_GROBBULUS, DOOR_TYPE_ROOM },
|
||||
{ GO_GLUTH_GATE, BOSS_GLUTH, DOOR_TYPE_PASSAGE },
|
||||
{ GO_THADDIUS_GATE, BOSS_GLUTH, DOOR_TYPE_PASSAGE },
|
||||
{ GO_NOTH_ENTRY_GATE, BOSS_NOTH, DOOR_TYPE_ROOM },
|
||||
{ GO_NOTH_EXIT_GATE, BOSS_NOTH, DOOR_TYPE_PASSAGE },
|
||||
{ GO_HEIGAN_ENTRY_GATE, BOSS_NOTH, DOOR_TYPE_PASSAGE },
|
||||
{ GO_HEIGAN_ENTRY_GATE, BOSS_HEIGAN, DOOR_TYPE_ROOM },
|
||||
{ GO_HEIGAN_EXIT_GATE, BOSS_HEIGAN, DOOR_TYPE_PASSAGE },
|
||||
{ GO_HEIGAN_EXIT_GATE_40, BOSS_HEIGAN, DOOR_TYPE_PASSAGE },
|
||||
{ GO_LOATHEB_GATE, BOSS_HEIGAN, DOOR_TYPE_PASSAGE },
|
||||
{ GO_LOATHEB_GATE, BOSS_LOATHEB, DOOR_TYPE_ROOM },
|
||||
{ GO_PLAGUE_EYE_PORTAL, BOSS_LOATHEB, DOOR_TYPE_PASSAGE },
|
||||
{ GO_PLAG_EYE_RAMP_BOSS, BOSS_LOATHEB, DOOR_TYPE_PASSAGE },
|
||||
{ GO_ANUB_GATE, BOSS_ANUB, DOOR_TYPE_ROOM },
|
||||
{ GO_ANUB_NEXT_GATE, BOSS_ANUB, DOOR_TYPE_PASSAGE },
|
||||
{ GO_FAERLINA_WEB, BOSS_FAERLINA, DOOR_TYPE_ROOM },
|
||||
{ GO_FAERLINA_GATE, BOSS_FAERLINA, DOOR_TYPE_PASSAGE },
|
||||
{ GO_MAEXXNA_GATE, BOSS_FAERLINA, DOOR_TYPE_PASSAGE },
|
||||
{ GO_MAEXXNA_GATE, BOSS_MAEXXNA, DOOR_TYPE_ROOM },
|
||||
{ GO_SPIDER_EYE_PORTAL, BOSS_MAEXXNA, DOOR_TYPE_PASSAGE },
|
||||
{ GO_ARAC_EYE_RAMP_BOSS, BOSS_MAEXXNA, DOOR_TYPE_PASSAGE },
|
||||
{ GO_THADDIUS_GATE, BOSS_THADDIUS, DOOR_TYPE_ROOM },
|
||||
{ GO_ABOM_EYE_PORTAL, BOSS_THADDIUS, DOOR_TYPE_PASSAGE },
|
||||
{ GO_CONS_EYE_RAMP_BOSS, BOSS_THADDIUS, DOOR_TYPE_PASSAGE },
|
||||
{ GO_GOTHIK_ENTER_GATE, BOSS_GOTHIK, DOOR_TYPE_ROOM },
|
||||
{ GO_GOTHIK_INNER_GATE, BOSS_GOTHIK, DOOR_TYPE_ROOM },
|
||||
{ GO_GOTHIK_EXIT_GATE, BOSS_GOTHIK, DOOR_TYPE_PASSAGE },
|
||||
{ GO_HORSEMEN_GATE, BOSS_GOTHIK, DOOR_TYPE_PASSAGE },
|
||||
{ GO_HORSEMEN_GATE, BOSS_HORSEMAN, DOOR_TYPE_ROOM },
|
||||
{ GO_DEATHKNIGHT_EYE_PORTAL, BOSS_HORSEMAN, DOOR_TYPE_PASSAGE },
|
||||
{ GO_MILI_EYE_RAMP_BOSS, BOSS_HORSEMAN, DOOR_TYPE_PASSAGE },
|
||||
{ GO_KELTHUZAD_GATE, BOSS_KELTHUZAD, DOOR_TYPE_ROOM },
|
||||
{ 0, 0, DOOR_TYPE_ROOM }
|
||||
};
|
||||
|
||||
static ObjectData const creatureData[]
|
||||
{
|
||||
{ NPC_PATCHWERK, DATA_PATCHWERK_BOSS },
|
||||
{ NPC_STALAGG, DATA_STALAGG_BOSS },
|
||||
{ NPC_FEUGEN, DATA_FEUGEN_BOSS },
|
||||
{ NPC_THADDIUS, DATA_THADDIUS_BOSS },
|
||||
{ NPC_RAZUVIOUS, DATA_RAZUVIOUS_BOSS },
|
||||
{ NPC_GOTHIK, DATA_GOTHIK_BOSS },
|
||||
{ NPC_BARON_RIVENDARE, DATA_BARON_RIVENDARE_BOSS },
|
||||
{ NPC_SIR_ZELIEK, DATA_SIR_ZELIEK_BOSS },
|
||||
{ NPC_LADY_BLAUMEUX, DATA_LADY_BLAUMEUX_BOSS },
|
||||
{ NPC_THANE_KORTHAZZ, DATA_THANE_KORTHAZZ_BOSS },
|
||||
{ NPC_SAPPHIRON, DATA_SAPPHIRON_BOSS },
|
||||
{ NPC_KELTHUZAD, DATA_KELTHUZAD_BOSS },
|
||||
{ NPC_LICH_KING, DATA_LICH_KING_BOSS },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
// overwrite the default ObjectData if Naxx40
|
||||
static ObjectData const creatureDataNX40[]
|
||||
{
|
||||
{ NPC_PATCHWERK_40, DATA_PATCHWERK_BOSS },
|
||||
{ NPC_STALAGG_40, DATA_STALAGG_BOSS },
|
||||
{ NPC_FEUGEN_40, DATA_FEUGEN_BOSS },
|
||||
{ NPC_THADDIUS_40, DATA_THADDIUS_BOSS },
|
||||
{ NPC_RAZUVIOUS_40, DATA_RAZUVIOUS_BOSS },
|
||||
{ NPC_GOTHIK_40, DATA_GOTHIK_BOSS },
|
||||
{ NPC_HIGHLORD_MOGRAINE_40, DATA_BARON_RIVENDARE_BOSS },
|
||||
{ NPC_SIR_ZELIEK_40, DATA_SIR_ZELIEK_BOSS },
|
||||
{ NPC_LADY_BLAUMEUX_40, DATA_LADY_BLAUMEUX_BOSS },
|
||||
{ NPC_THANE_KORTHAZZ_40, DATA_THANE_KORTHAZZ_BOSS },
|
||||
{ NPC_SAPPHIRON_40, DATA_SAPPHIRON_BOSS },
|
||||
{ NPC_KELTHUZAD_40, DATA_KELTHUZAD_BOSS },
|
||||
// { NPC_LICH_KING, DATA_LICH_KING_BOSS },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static ObjectData const gameObjectData[]
|
||||
{
|
||||
{ GO_GOTHIK_INNER_GATE, DATA_GOTHIK_INNER_GATE },
|
||||
{ GO_LOATHEB_PORTAL, DATA_LOATHEB_PORTAL },
|
||||
{ GO_MAEXXNA_PORTAL, DATA_MAEXXNA_PORTAL },
|
||||
{ GO_THADDIUS_PORTAL, DATA_THADDIUS_PORTAL },
|
||||
{ GO_HORSEMAN_PORTAL, DATA_HORSEMAN_PORTAL },
|
||||
{ GO_SAPPHIRON_GATE, DATA_SAPPHIRON_GATE },
|
||||
{ GO_KELTHUZAD_FLOOR, DATA_KELTHUZAD_FLOOR },
|
||||
{ GO_KELTHUZAD_GATE, DATA_KELTHUZAD_GATE },
|
||||
{ GO_KELTHUZAD_PORTAL_1, DATA_KELTHUZAD_PORTAL_1 },
|
||||
{ GO_KELTHUZAD_PORTAL_2, DATA_KELTHUZAD_PORTAL_2 },
|
||||
{ GO_KELTHUZAD_PORTAL_3, DATA_KELTHUZAD_PORTAL_3 },
|
||||
{ GO_KELTHUZAD_PORTAL_4, DATA_KELTHUZAD_PORTAL_4 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
class instance_naxxramas : public InstanceScript
|
||||
{
|
||||
public:
|
||||
instance_naxxramas(Map* map) : InstanceScript(map)
|
||||
{
|
||||
SetHeaders(DataHeader);
|
||||
SetBossNumber(MAX_ENCOUNTERS);
|
||||
SetPersistentDataCount(PERSISTENT_DATA_COUNT);
|
||||
LoadDoorData(doorData);
|
||||
LoadObjectData(creatureData, gameObjectData);
|
||||
|
||||
// NX40 specific data
|
||||
if (instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
LoadObjectData(creatureDataNX40, gameObjectData);
|
||||
|
||||
// GameObjects
|
||||
for (auto& i : _heiganEruption)
|
||||
i.clear();
|
||||
|
||||
// NPCs
|
||||
_patchwerkRoomTrash.clear();
|
||||
_heiganBackRoomTrash.clear();
|
||||
|
||||
// Controls
|
||||
_events.Reset();
|
||||
_currentWingTaunt = SAY_FIRST_WING_TAUNT;
|
||||
_horsemanLoaded = 0;
|
||||
_thaddiusScreams = false;
|
||||
|
||||
// Achievements
|
||||
_abominationsKilled = 0;
|
||||
_faerlinaAchievement = true;
|
||||
_thaddiusAchievement = true;
|
||||
_loathebAchievement = true;
|
||||
_heiganAchievement = true;
|
||||
_sapphironAchievement = true;
|
||||
_horsemanAchievement = true;
|
||||
}
|
||||
|
||||
inline void CreatureTalk(uint32 dataCreature, uint8 dialog)
|
||||
{
|
||||
if (Creature* creature = GetCreature(dataCreature))
|
||||
creature->AI()->Talk(dialog);
|
||||
}
|
||||
|
||||
inline void SetGoState(uint32 dataGameObject, GOState state)
|
||||
{
|
||||
if (GameObject* go = GetGameObject(dataGameObject))
|
||||
go->SetGoState(state);
|
||||
}
|
||||
|
||||
inline void ActivateWingPortal(GameObject* go, EncounterState state)
|
||||
{
|
||||
if (!go || state != DONE)
|
||||
return;
|
||||
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
|
||||
inline void ActivateWingPortal(uint32 wingPortal)
|
||||
{
|
||||
ActivateWingPortal(GetGameObject(wingPortal), DONE);
|
||||
_events.RescheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6s);
|
||||
}
|
||||
|
||||
static inline uint8 GetEruptionSection(float x, float y)
|
||||
{
|
||||
y -= HeiganPos[1];
|
||||
if (y < 1.0f)
|
||||
return 0;
|
||||
|
||||
x -= HeiganPos[0];
|
||||
if (x > -1.0f)
|
||||
return 3;
|
||||
|
||||
float slope = y / x;
|
||||
for (uint32 i = 0; i < 3; ++i)
|
||||
if (slope > HeiganEruptionSlope[i])
|
||||
return i;
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
inline void HeiganEruptSections(uint32 section)
|
||||
{
|
||||
for (uint8 i = 0; i < HeiganEruptSectionCount; ++i)
|
||||
{
|
||||
if (i == section)
|
||||
continue;
|
||||
|
||||
for (GameObject* go : _heiganEruption[i])
|
||||
{
|
||||
go->SendCustomAnim(go->GetGoAnimProgress());
|
||||
go->CastSpell(nullptr, SPELL_ERUPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerEnter(Player* player) override
|
||||
{
|
||||
InstanceScript::OnPlayerEnter(player);
|
||||
if (_thaddiusScreams == false)
|
||||
{
|
||||
_events.ScheduleEvent(EVENT_THADDIUS_SCREAMS, 2min, 2min + 30s);
|
||||
}
|
||||
|
||||
SetData(DATA_THADDIUS_SCREAMS, 0);
|
||||
}
|
||||
|
||||
void OnCreatureCreate(Creature* creature) override
|
||||
{
|
||||
switch (creature->GetEntry())
|
||||
{
|
||||
case NPC_ROTTING_MAGGOT_40:
|
||||
case NPC_DISEASED_MAGGOT_40:
|
||||
case NPC_EYE_STALK_40:
|
||||
_heiganBackRoomTrash.push_back(creature->GetGUID());
|
||||
return;
|
||||
case NPC_LIVING_MONSTROSITY:
|
||||
case NPC_MAD_SCIENTIST:
|
||||
case NPC_PATCHWORK_GOLEM:
|
||||
case NPC_SURGICAL_ASSIST:
|
||||
case NPC_LIVING_MONSTROSITY_40:
|
||||
case NPC_MAD_SCIENTIST_40:
|
||||
case NPC_PATCHWORK_GOLEM_40:
|
||||
case NPC_SURGICAL_ASSIST_40:
|
||||
_patchwerkRoomTrash.push_back(creature->GetGUID());
|
||||
return;
|
||||
case NPC_BILE_RETCHER:
|
||||
case NPC_SLUDGE_BELCHER:
|
||||
case NPC_BILE_RETCHER_40:
|
||||
case NPC_SLUDGE_BELCHER_40:
|
||||
if (creature->GetPositionY() > -3258.0f) // we want only those inside the room, not before
|
||||
_patchwerkRoomTrash.push_back(creature->GetGUID());
|
||||
return;
|
||||
case NPC_BARON_RIVENDARE:
|
||||
case NPC_SIR_ZELIEK:
|
||||
case NPC_LADY_BLAUMEUX:
|
||||
case NPC_THANE_KORTHAZZ:
|
||||
case NPC_HIGHLORD_MOGRAINE_40:
|
||||
case NPC_SIR_ZELIEK_40:
|
||||
case NPC_LADY_BLAUMEUX_40:
|
||||
case NPC_THANE_KORTHAZZ_40:
|
||||
if (++_horsemanLoaded == HorsemanCount)
|
||||
SetBossState(BOSS_HORSEMAN, GetBossState(BOSS_HORSEMAN));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
InstanceScript::OnCreatureCreate(creature);
|
||||
}
|
||||
|
||||
void OnGameObjectCreate(GameObject* go) override
|
||||
{
|
||||
switch (go->GetGOInfo()->displayId)
|
||||
{
|
||||
case GO_DISPLAY_ID_HEIGAN_ERUPTION1:
|
||||
case GO_DISPLAY_ID_HEIGAN_ERUPTION2:
|
||||
_heiganEruption[GetEruptionSection(go->GetPositionX(), go->GetPositionY())].insert(go);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (go->GetEntry())
|
||||
{
|
||||
case GO_SAPPHIRON_GATE:
|
||||
if (GetBossState(BOSS_SAPPHIRON) == DONE)
|
||||
go->SetGoState(GO_STATE_ACTIVE);
|
||||
break;
|
||||
case GO_LOATHEB_PORTAL:
|
||||
ActivateWingPortal(go, GetBossState(BOSS_LOATHEB));
|
||||
break;
|
||||
case GO_THADDIUS_PORTAL:
|
||||
ActivateWingPortal(go, GetBossState(BOSS_THADDIUS));
|
||||
break;
|
||||
case GO_MAEXXNA_PORTAL:
|
||||
ActivateWingPortal(go, GetBossState(BOSS_MAEXXNA));
|
||||
break;
|
||||
case GO_HORSEMAN_PORTAL:
|
||||
ActivateWingPortal(go, GetBossState(BOSS_HORSEMAN));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
InstanceScript::OnGameObjectCreate(go);
|
||||
}
|
||||
|
||||
void OnGameObjectRemove(GameObject* go) override
|
||||
{
|
||||
switch (go->GetGOInfo()->displayId)
|
||||
{
|
||||
case GO_DISPLAY_ID_HEIGAN_ERUPTION1:
|
||||
case GO_DISPLAY_ID_HEIGAN_ERUPTION2:
|
||||
_heiganEruption[GetEruptionSection(go->GetPositionX(), go->GetPositionY())].erase(go);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (go->GetEntry())
|
||||
{
|
||||
case GO_SAPPHIRON_BIRTH:
|
||||
if (Creature* cr = GetCreature(DATA_SAPPHIRON_BOSS))
|
||||
cr->AI()->DoAction(ACTION_SAPPHIRON_BIRTH);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
InstanceScript::OnGameObjectRemove(go);
|
||||
}
|
||||
|
||||
bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override
|
||||
{
|
||||
if (instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
return false; // No achievements in Naxx 40
|
||||
|
||||
switch (criteria_id)
|
||||
{
|
||||
case ACHIEV_CRITERIA_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER_25_PLAYER:
|
||||
return _horsemanAchievement;
|
||||
case ACHIEV_CRITERIA_JUST_CANT_GET_ENOUGH_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_JUST_CANT_GET_ENOUGH_25_PLAYER:
|
||||
return _abominationsKilled >= AbominationKillCountReq;
|
||||
case ACHIEV_CRITERIA_MOMMA_SAID_KNOCK_YOU_OUT_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_MOMMA_SAID_KNOCK_YOU_OUT_25_PLAYER:
|
||||
return _faerlinaAchievement;
|
||||
case ACHIEV_CRITERIA_SHOKING_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_SHOKING_25_PLAYER:
|
||||
return _thaddiusAchievement;
|
||||
case ACHIEV_CRITERIA_SPORE_LOSER_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_SPORE_LOSER_25_PLAYER:
|
||||
return _loathebAchievement;
|
||||
case ACHIEV_CRITERIA_THE_SAFETY_DANCE_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_SAFETY_DANCE_25_PLAYER:
|
||||
return _heiganAchievement;
|
||||
case ACHIEV_CRITERIA_SUBTRACTION_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_KELTHUZAD_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GOTHIK_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_ANUB_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GROBBULUS_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_HEIGAN_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_FAERLINA_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_MAEXXNA_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_SAPPHIRON_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_LOATHEB_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GLUTH_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_THADDIUS_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_PATCHWERK_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_RAZUVIOUS_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_NOTH_10_PLAYER:
|
||||
return instance->GetPlayersCountExceptGMs() < TheDedicatedFew10PlayerReq;
|
||||
case ACHIEV_CRITERIA_SUBTRACTION_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_ANUB_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_FAERLINA_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_MAEXXNA_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_PATCHWERK_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GROBBULUS_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GLUTH_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_THADDIUS_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_NOTH_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_HEIGAN_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_LOATHEB_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_RAZUVIOUS_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GOTHIK_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_SAPPHIRON_25_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_DEDICATED_FEW_KELTHUZAD_25_PLAYER:
|
||||
return instance->GetPlayersCountExceptGMs() < TheDedicatedFew25PlayerReq;
|
||||
case ACHIEV_CRITERIA_THE_HUNDRED_CLUB_10_PLAYER:
|
||||
case ACHIEV_CRITERIA_THE_HUNDRED_CLUB_25_PLAYER:
|
||||
return _sapphironAchievement;
|
||||
case ACHIEV_CRITERIA_THE_UNDYING_KELTHUZAD:
|
||||
case ACHIEV_CRITERIA_THE_UNDYING_THE_FOUR_HORSEMEN:
|
||||
case ACHIEV_CRITERIA_THE_UNDYING_MAEXXNA:
|
||||
case ACHIEV_CRITERIA_THE_UNDYING_LOATHEB:
|
||||
case ACHIEV_CRITERIA_THE_UNDYING_THADDIUS:
|
||||
case ACHIEV_CRITERIA_THE_IMMORTAL_KELTHUZAD:
|
||||
case ACHIEV_CRITERIA_THE_IMMORTAL_THE_FOUR_HORSEMEN:
|
||||
case ACHIEV_CRITERIA_THE_IMMORTAL_MAEXXNA:
|
||||
case ACHIEV_CRITERIA_THE_IMMORTAL_LOATHEB:
|
||||
case ACHIEV_CRITERIA_THE_IMMORTAL_THADDIUS:
|
||||
for (int i = 0; i < MAX_ENCOUNTERS; ++i)
|
||||
if (GetBossState(i) != DONE)
|
||||
return false;
|
||||
|
||||
return !GetPersistentData(PERSISTENT_DATA_IMMORTAL_FAIL);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetData(uint32 id, uint32 data) override
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case DATA_ABOMINATION_KILLED:
|
||||
++_abominationsKilled;
|
||||
return;
|
||||
case DATA_THADDIUS_SCREAMS:
|
||||
_thaddiusScreams = true;
|
||||
return;
|
||||
case DATA_FRENZY_REMOVED:
|
||||
_faerlinaAchievement = false;
|
||||
return;
|
||||
case DATA_CHARGES_CROSSED:
|
||||
_thaddiusAchievement = false;
|
||||
return;
|
||||
case DATA_SPORE_KILLED:
|
||||
_loathebAchievement = false;
|
||||
return;
|
||||
case DATA_HUNDRED_CLUB:
|
||||
_sapphironAchievement = false;
|
||||
return;
|
||||
case DATA_DANCE_FAIL:
|
||||
_heiganAchievement = false;
|
||||
return;
|
||||
case DATA_HEIGAN_ERUPTION:
|
||||
HeiganEruptSections(data);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool SetBossState(uint32 bossId, EncounterState state) override
|
||||
{
|
||||
switch (bossId)
|
||||
{
|
||||
case BOSS_PATCHWERK:
|
||||
{
|
||||
if (state != IN_PROGRESS)
|
||||
break;
|
||||
|
||||
// pull all the trash if not killed
|
||||
if (Creature* patchwerk = GetCreature(DATA_PATCHWERK_BOSS))
|
||||
{
|
||||
for (auto& itr : _patchwerkRoomTrash)
|
||||
{
|
||||
Creature* trash = ObjectAccessor::GetCreature(*patchwerk, itr);
|
||||
if (trash && trash->IsAlive() && !trash->IsInCombat())
|
||||
trash->AI()->AttackStart(patchwerk->GetVictim());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case BOSS_HEIGAN:
|
||||
{
|
||||
if (state == NOT_STARTED)
|
||||
_heiganAchievement = true;
|
||||
|
||||
if (state == DONE)
|
||||
for (auto const& guid : _heiganBackRoomTrash)
|
||||
if (Creature* creature = instance->GetCreature(guid))
|
||||
creature->DespawnOrUnsummon();
|
||||
break;
|
||||
}
|
||||
case BOSS_LOATHEB:
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case NOT_STARTED:
|
||||
_loathebAchievement = true;
|
||||
break;
|
||||
case DONE:
|
||||
ActivateWingPortal(DATA_LOATHEB_PORTAL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case BOSS_FAERLINA:
|
||||
{
|
||||
if (state == NOT_STARTED)
|
||||
_faerlinaAchievement = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case BOSS_MAEXXNA:
|
||||
{
|
||||
if (state == DONE)
|
||||
ActivateWingPortal(DATA_MAEXXNA_PORTAL);
|
||||
|
||||
break;
|
||||
}
|
||||
case BOSS_THADDIUS:
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case NOT_STARTED:
|
||||
_thaddiusAchievement = true;
|
||||
break;
|
||||
case DONE:
|
||||
ActivateWingPortal(DATA_THADDIUS_PORTAL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case BOSS_HORSEMAN:
|
||||
{
|
||||
uint32 horsemanKilled = std::count_if(HorsemanDataGroup.begin(), HorsemanDataGroup.end(), [this](auto&& entry)
|
||||
{
|
||||
Creature* cr = GetCreature(entry);
|
||||
return cr && !cr->IsAlive();
|
||||
});
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case NOT_STARTED:
|
||||
{
|
||||
_horsemanAchievement = true;
|
||||
|
||||
if (!horsemanKilled)
|
||||
break;
|
||||
|
||||
for (auto&& entry : HorsemanDataGroup)
|
||||
{
|
||||
if (Creature* cr = GetCreature(entry))
|
||||
{
|
||||
cr->SetPosition(cr->GetHomePosition());
|
||||
cr->Respawn();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case IN_PROGRESS:
|
||||
{
|
||||
for (auto&& entry : HorsemanDataGroup)
|
||||
if (Creature* cr = GetCreature(entry))
|
||||
cr->SetInCombatWithZone();
|
||||
|
||||
break;
|
||||
}
|
||||
case DONE:
|
||||
{
|
||||
_events.RescheduleEvent(EVENT_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER, 15s);
|
||||
|
||||
if (horsemanKilled != HorsemanCount)
|
||||
return false;
|
||||
|
||||
// all horsemans are killed
|
||||
if (Creature* cr = GetCreature(DATA_BARON_RIVENDARE_BOSS))
|
||||
cr->CastSpell(cr, SPELL_THE_FOUR_HORSEMAN_CREDIT, true);
|
||||
|
||||
ActivateWingPortal(DATA_HORSEMAN_PORTAL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case BOSS_SAPPHIRON:
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case NOT_STARTED:
|
||||
_sapphironAchievement = true;
|
||||
break;
|
||||
case DONE:
|
||||
{
|
||||
if (GetPersistentData(PERSISTENT_DATA_KELTHUZAD_DIALOG))
|
||||
break;
|
||||
|
||||
StorePersistentData(PERSISTENT_DATA_KELTHUZAD_DIALOG, 1);
|
||||
SetGoState(DATA_KELTHUZAD_GATE, GO_STATE_READY);
|
||||
_events.ScheduleEvent(EVENT_FROSTWYRM_WATERFALL_DOOR, 5s);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case BOSS_KELTHUZAD:
|
||||
{
|
||||
if (state == NOT_STARTED)
|
||||
_abominationsKilled = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return InstanceScript::SetBossState(bossId, state);
|
||||
}
|
||||
|
||||
void Update(uint32 diff) override
|
||||
{
|
||||
_events.Update(diff);
|
||||
|
||||
switch (_events.ExecuteEvent())
|
||||
{
|
||||
case EVENT_THADDIUS_SCREAMS:
|
||||
{
|
||||
if (GetBossState(BOSS_THADDIUS) == DONE)
|
||||
break;
|
||||
|
||||
instance->PlayDirectSoundToMap(SOUND_SCREAM + urand(0, 3));
|
||||
return _events.ScheduleEvent(EVENT_THADDIUS_SCREAMS, 2min, 2min + 30s);
|
||||
}
|
||||
case EVENT_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER:
|
||||
_horsemanAchievement = false;
|
||||
break;
|
||||
case EVENT_KELTHUZAD_WING_TAUNT:
|
||||
return CreatureTalk(DATA_KELTHUZAD_BOSS, _currentWingTaunt++);
|
||||
case EVENT_HORSEMEN_INTRO1:
|
||||
CreatureTalk(DATA_THANE_KORTHAZZ_BOSS, SAY_HORSEMEN_DIALOG1);
|
||||
return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO2, 4500ms);
|
||||
case EVENT_HORSEMEN_INTRO2:
|
||||
CreatureTalk(DATA_SIR_ZELIEK_BOSS, SAY_HORSEMEN_DIALOG1);
|
||||
return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO3, 6500ms);
|
||||
case EVENT_HORSEMEN_INTRO3:
|
||||
CreatureTalk(DATA_LADY_BLAUMEUX_BOSS, SAY_HORSEMEN_DIALOG1);
|
||||
return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO4, 6500ms);
|
||||
case EVENT_HORSEMEN_INTRO4:
|
||||
CreatureTalk(DATA_BARON_RIVENDARE_BOSS, SAY_HORSEMEN_DIALOG1);
|
||||
return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO5, 6500ms);
|
||||
case EVENT_HORSEMEN_INTRO5:
|
||||
CreatureTalk(DATA_LADY_BLAUMEUX_BOSS, SAY_HORSEMEN_DIALOG2);
|
||||
return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO6, 6500ms);
|
||||
case EVENT_HORSEMEN_INTRO6:
|
||||
CreatureTalk(DATA_SIR_ZELIEK_BOSS, SAY_HORSEMEN_DIALOG2);
|
||||
return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO7, 6500ms);
|
||||
case EVENT_HORSEMEN_INTRO7:
|
||||
CreatureTalk(DATA_THANE_KORTHAZZ_BOSS, SAY_HORSEMEN_DIALOG2);
|
||||
return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO8, 6500ms);
|
||||
case EVENT_HORSEMEN_INTRO8:
|
||||
return CreatureTalk(DATA_BARON_RIVENDARE_BOSS, SAY_HORSEMEN_DIALOG2);
|
||||
case EVENT_FROSTWYRM_WATERFALL_DOOR:
|
||||
SetGoState(DATA_SAPPHIRON_GATE, GO_STATE_ACTIVE);
|
||||
return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK1, 5s);
|
||||
case EVENT_KELTHUZAD_LICH_KING_TALK1:
|
||||
{
|
||||
if (GetBossState(BOSS_KELTHUZAD) == DONE)
|
||||
break;
|
||||
|
||||
CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG1);
|
||||
return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK2, 10s);
|
||||
}
|
||||
case EVENT_KELTHUZAD_LICH_KING_TALK2:
|
||||
CreatureTalk(DATA_LICH_KING_BOSS, SAY_SAPP_DIALOG2_LICH);
|
||||
return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK3, 14s);
|
||||
case EVENT_KELTHUZAD_LICH_KING_TALK3:
|
||||
CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG3);
|
||||
return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK4, 10s);
|
||||
case EVENT_KELTHUZAD_LICH_KING_TALK4:
|
||||
CreatureTalk(DATA_LICH_KING_BOSS, SAY_SAPP_DIALOG4_LICH);
|
||||
return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK5, 12s);
|
||||
case EVENT_KELTHUZAD_LICH_KING_TALK5:
|
||||
CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG5);
|
||||
return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK6, 5s);
|
||||
case EVENT_KELTHUZAD_LICH_KING_TALK6:
|
||||
CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG6);
|
||||
return SetGoState(DATA_KELTHUZAD_GATE, GO_STATE_ACTIVE);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Controls
|
||||
EventMap _events;
|
||||
uint8 _currentWingTaunt;
|
||||
uint8 _horsemanLoaded;
|
||||
bool _thaddiusScreams;
|
||||
|
||||
// GameObjects
|
||||
std::set<GameObject*> _heiganEruption[HeiganEruptSectionCount];
|
||||
|
||||
// NPCs
|
||||
GuidList _patchwerkRoomTrash;
|
||||
GuidList _heiganBackRoomTrash;
|
||||
|
||||
// Achievements
|
||||
uint8 _abominationsKilled;
|
||||
bool _faerlinaAchievement;
|
||||
bool _thaddiusAchievement;
|
||||
bool _loathebAchievement;
|
||||
bool _sapphironAchievement;
|
||||
bool _heiganAchievement;
|
||||
bool _horsemanAchievement;
|
||||
};
|
||||
|
||||
class npc_mr_bigglesworth : public NullCreatureAI
|
||||
{
|
||||
public:
|
||||
npc_mr_bigglesworth(Creature* c) : NullCreatureAI(c) { }
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
InstanceScript* instance = me->GetInstanceScript();
|
||||
if (!instance)
|
||||
return;
|
||||
|
||||
Creature* kelThuzard = instance->GetCreature(DATA_KELTHUZAD_BOSS);
|
||||
if (!kelThuzard)
|
||||
return;
|
||||
|
||||
kelThuzard->AI()->Talk(SAY_CAT_DIED);
|
||||
}
|
||||
};
|
||||
|
||||
class npc_living_poison : public NullCreatureAI
|
||||
{
|
||||
public:
|
||||
npc_living_poison(Creature* c) : NullCreatureAI(c) { }
|
||||
|
||||
void UpdateAI(uint32 /*diff*/) override
|
||||
{
|
||||
if (me->SelectNearestTarget(1.5f, true))
|
||||
me->CastSpell(me, SPELL_EXPLODE, true);
|
||||
}
|
||||
};
|
||||
|
||||
class npc_naxxramas_trigger : public NullCreatureAI
|
||||
{
|
||||
public:
|
||||
npc_naxxramas_trigger(Creature* c) : NullCreatureAI(c) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_events.Reset();
|
||||
_events.ScheduleEvent(EVENT_SUMMON_LIVING_POISON, 5s);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
_events.Update(diff);
|
||||
switch (_events.ExecuteEvent())
|
||||
{
|
||||
case EVENT_SUMMON_LIVING_POISON:
|
||||
{
|
||||
for (LivingPoisonData const& entry : LivingPoisonDataList)
|
||||
if (Creature* cr = me->SummonCreature(NPC_LIVING_POISON, entry.Start, TEMPSUMMON_TIMED_DESPAWN, entry.DespawnTime))
|
||||
{
|
||||
cr->AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
|
||||
cr->GetMotionMaster()->MovePoint(0, entry.End, false);
|
||||
}
|
||||
|
||||
_events.Repeat(5s);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EventMap _events;
|
||||
};
|
||||
|
||||
class at_naxxramas_hub_portal : public AreaTriggerScript
|
||||
{
|
||||
public:
|
||||
at_naxxramas_hub_portal() : AreaTriggerScript("at_naxxramas_hub_portal") { }
|
||||
|
||||
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
|
||||
{
|
||||
if (!player->IsAlive() || player->IsInCombat())
|
||||
return false;
|
||||
|
||||
InstanceScript* instance = player->GetInstanceScript();
|
||||
if (!instance)
|
||||
return false;
|
||||
|
||||
if ((instance->GetBossState(BOSS_MAEXXNA) != DONE) ||
|
||||
(instance->GetBossState(BOSS_LOATHEB) != DONE) ||
|
||||
(instance->GetBossState(BOSS_THADDIUS) != DONE) ||
|
||||
(instance->GetBossState(BOSS_HORSEMAN) != DONE))
|
||||
return false;
|
||||
|
||||
player->TeleportTo(SapphironTeleportPos);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class OnyNaxxLogoutTeleport : public PlayerScript
|
||||
{
|
||||
public:
|
||||
OnyNaxxLogoutTeleport() : PlayerScript("OnyNaxxLogoutTeleport") {}
|
||||
|
||||
void OnPlayerLogin(Player* player) override
|
||||
{
|
||||
TeleportIfNeeded(player);
|
||||
}
|
||||
|
||||
void OnPlayerLogout(Player* player) override
|
||||
{
|
||||
TeleportIfNeeded(player);
|
||||
}
|
||||
|
||||
void OnPlayerBeforeLogout(Player* player) override
|
||||
{
|
||||
TeleportIfNeeded(player);
|
||||
}
|
||||
|
||||
private:
|
||||
void TeleportIfNeeded(Player* player)
|
||||
{
|
||||
int mapId = player->GetMapId();
|
||||
if (player->GetRaidDifficulty() != RAID_DIFFICULTY_10MAN_HEROIC)
|
||||
return;
|
||||
|
||||
switch (mapId)
|
||||
{
|
||||
case 533:
|
||||
player->TeleportTo(0, 3082.641602f, -3725.781250f, 132.418884f, 0.002488f);
|
||||
break;
|
||||
case 249:
|
||||
player->TeleportTo(1, -4737.995f, -3745.33f, 53.68f, 0.002488f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_instance_naxxramas()
|
||||
{
|
||||
RegisterInstanceScript(instance_naxxramas, NaxxramasMapId);
|
||||
RegisterNaxxramasCreatureAI(npc_mr_bigglesworth);
|
||||
RegisterNaxxramasCreatureAI(npc_living_poison);
|
||||
RegisterNaxxramasCreatureAI(npc_naxxramas_trigger);
|
||||
new at_naxxramas_hub_portal();
|
||||
new OnyNaxxLogoutTeleport();
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DEF_NAXXRAMAS_H
|
||||
#define DEF_NAXXRAMAS_H
|
||||
|
||||
#include "naxxramas_40.h"
|
||||
|
||||
#define DataHeader "NAX"
|
||||
|
||||
#define NaxxramasScriptName "instance_naxxramas"
|
||||
|
||||
enum NaxxramasEncouter
|
||||
{
|
||||
BOSS_PATCHWERK = 0,
|
||||
BOSS_GROBBULUS = 1,
|
||||
BOSS_GLUTH = 2,
|
||||
BOSS_NOTH = 3,
|
||||
BOSS_HEIGAN = 4,
|
||||
BOSS_LOATHEB = 5,
|
||||
BOSS_ANUB = 6,
|
||||
BOSS_FAERLINA = 7,
|
||||
BOSS_MAEXXNA = 8,
|
||||
BOSS_THADDIUS = 9,
|
||||
BOSS_RAZUVIOUS = 10,
|
||||
BOSS_GOTHIK = 11,
|
||||
BOSS_HORSEMAN = 12,
|
||||
BOSS_SAPPHIRON = 13,
|
||||
BOSS_KELTHUZAD = 14,
|
||||
MAX_ENCOUNTERS
|
||||
};
|
||||
|
||||
enum NaxxramasData
|
||||
{
|
||||
DATA_PATCHWERK_BOSS = 100,
|
||||
DATA_STALAGG_BOSS = 101,
|
||||
DATA_FEUGEN_BOSS = 102,
|
||||
DATA_THADDIUS_BOSS = 103,
|
||||
DATA_RAZUVIOUS_BOSS = 104,
|
||||
DATA_GOTHIK_BOSS = 105,
|
||||
DATA_BARON_RIVENDARE_BOSS = 106,
|
||||
DATA_SIR_ZELIEK_BOSS = 107,
|
||||
DATA_LADY_BLAUMEUX_BOSS = 108,
|
||||
DATA_THANE_KORTHAZZ_BOSS = 109,
|
||||
DATA_SAPPHIRON_BOSS = 110,
|
||||
DATA_KELTHUZAD_BOSS = 111,
|
||||
DATA_LICH_KING_BOSS = 112,
|
||||
|
||||
DATA_LOATHEB_PORTAL = 200,
|
||||
DATA_MAEXXNA_PORTAL = 201,
|
||||
DATA_THADDIUS_PORTAL = 202,
|
||||
DATA_HORSEMAN_PORTAL = 203,
|
||||
DATA_GOTHIK_INNER_GATE = 204,
|
||||
DATA_SAPPHIRON_GATE = 205,
|
||||
DATA_KELTHUZAD_GATE = 206,
|
||||
DATA_KELTHUZAD_FLOOR = 207,
|
||||
DATA_KELTHUZAD_PORTAL_1 = 208,
|
||||
DATA_KELTHUZAD_PORTAL_2 = 209,
|
||||
DATA_KELTHUZAD_PORTAL_3 = 210,
|
||||
DATA_KELTHUZAD_PORTAL_4 = 211,
|
||||
|
||||
DATA_HEIGAN_ERUPTION = 300,
|
||||
DATA_DANCE_FAIL = 301,
|
||||
DATA_SPORE_KILLED = 302,
|
||||
DATA_FRENZY_REMOVED = 303,
|
||||
DATA_THADDIUS_SCREAMS = 304,
|
||||
DATA_CHARGES_CROSSED = 305,
|
||||
DATA_HUNDRED_CLUB = 306,
|
||||
DATA_ABOMINATION_KILLED = 307,
|
||||
};
|
||||
|
||||
enum NaxxramasPersistentData
|
||||
{
|
||||
PERSISTENT_DATA_KELTHUZAD_DIALOG = 1,
|
||||
PERSISTENT_DATA_IMMORTAL_FAIL = 2,
|
||||
PERSISTENT_DATA_COUNT
|
||||
};
|
||||
|
||||
enum NaxxramasGameObject
|
||||
{
|
||||
GO_PATCHWERK_GATE = 181123,
|
||||
GO_GLUTH_GATE = 181120,
|
||||
GO_NOTH_ENTRY_GATE = 181200,
|
||||
GO_NOTH_EXIT_GATE = 181201,
|
||||
GO_HEIGAN_ENTRY_GATE = 181202,
|
||||
GO_HEIGAN_EXIT_GATE = 181203,
|
||||
GO_LOATHEB_GATE = 181241,
|
||||
GO_ANUB_GATE = 181126,
|
||||
GO_ANUB_NEXT_GATE = 181195,
|
||||
GO_FAERLINA_WEB = 181235,
|
||||
GO_FAERLINA_GATE = 194022,
|
||||
GO_MAEXXNA_GATE = 181209,
|
||||
GO_THADDIUS_GATE = 181121,
|
||||
GO_GOTHIK_ENTER_GATE = 181124,
|
||||
GO_GOTHIK_INNER_GATE = 181170,
|
||||
GO_GOTHIK_EXIT_GATE = 181125,
|
||||
GO_HORSEMEN_GATE = 181119,
|
||||
GO_SAPPHIRON_GATE = 181225,
|
||||
|
||||
GO_HORSEMEN_CHEST_10 = 181366,
|
||||
GO_HORSEMEN_CHEST_25 = 193426,
|
||||
|
||||
GO_SAPPHIRON_BIRTH = 181356,
|
||||
GO_KELTHUZAD_FLOOR = 181444,
|
||||
GO_KELTHUZAD_GATE = 181228,
|
||||
GO_KELTHUZAD_PORTAL_1 = 181402,
|
||||
GO_KELTHUZAD_PORTAL_2 = 181403,
|
||||
GO_KELTHUZAD_PORTAL_3 = 181404,
|
||||
GO_KELTHUZAD_PORTAL_4 = 181405,
|
||||
|
||||
GO_LOATHEB_PORTAL = 181577,
|
||||
GO_THADDIUS_PORTAL = 181576,
|
||||
GO_MAEXXNA_PORTAL = 181575,
|
||||
GO_HORSEMAN_PORTAL = 181578,
|
||||
|
||||
// "Glow" effect on center-side portal
|
||||
GO_DEATHKNIGHT_EYE_PORTAL = 181210,
|
||||
GO_PLAGUE_EYE_PORTAL = 181211,
|
||||
GO_SPIDER_EYE_PORTAL = 181212,
|
||||
GO_ABOM_EYE_PORTAL = 181213,
|
||||
|
||||
// "Glow" effect on boss-side portal
|
||||
GO_ARAC_EYE_RAMP_BOSS = 181233,
|
||||
GO_PLAG_EYE_RAMP_BOSS = 181231,
|
||||
GO_MILI_EYE_RAMP_BOSS = 181230,
|
||||
GO_CONS_EYE_RAMP_BOSS = 181232
|
||||
};
|
||||
|
||||
enum NaxxramasGameObjectsDisplayId
|
||||
{
|
||||
GO_DISPLAY_ID_HEIGAN_ERUPTION1 = 1287,
|
||||
GO_DISPLAY_ID_HEIGAN_ERUPTION2 = 6785
|
||||
};
|
||||
|
||||
enum NaxxramasCreatureId
|
||||
{
|
||||
// Patchwerk
|
||||
NPC_PATCHWERK = 16028,
|
||||
NPC_PATCHWORK_GOLEM = 16017,
|
||||
NPC_BILE_RETCHER = 16018,
|
||||
NPC_MAD_SCIENTIST = 16020,
|
||||
NPC_LIVING_MONSTROSITY = 16021,
|
||||
NPC_SURGICAL_ASSIST = 16022,
|
||||
NPC_SLUDGE_BELCHER = 16029,
|
||||
|
||||
NPC_LIVING_POISON = 16027,
|
||||
|
||||
// Thaddius
|
||||
NPC_THADDIUS = 15928,
|
||||
NPC_STALAGG = 15929,
|
||||
NPC_FEUGEN = 15930,
|
||||
|
||||
// Razuvious
|
||||
NPC_RAZUVIOUS = 16061,
|
||||
|
||||
// Gothik
|
||||
NPC_GOTHIK = 16060,
|
||||
|
||||
// Four horseman
|
||||
NPC_BARON_RIVENDARE = 30549,
|
||||
NPC_SIR_ZELIEK = 16063,
|
||||
NPC_LADY_BLAUMEUX = 16065,
|
||||
NPC_THANE_KORTHAZZ = 16064,
|
||||
|
||||
// Sapphiron
|
||||
NPC_SAPPHIRON = 15989,
|
||||
|
||||
// Kel'Thuzad
|
||||
NPC_KELTHUZAD = 15990,
|
||||
NPC_LICH_KING = 16980
|
||||
};
|
||||
|
||||
enum NaxxramasAchievemmentCriteria
|
||||
{
|
||||
ACHIEV_CRITERIA_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER_10_PLAYER = 7600, // And They Would All Go Down Together (10 player)
|
||||
ACHIEV_CRITERIA_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER_25_PLAYER = 7601, // And They Would All Go Down Together (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_JUST_CANT_GET_ENOUGH_10_PLAYER = 7614, // Just Can't Get Enough (10 player)
|
||||
ACHIEV_CRITERIA_JUST_CANT_GET_ENOUGH_25_PLAYER = 7615, // Just Can't Get Enough (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_MOMMA_SAID_KNOCK_YOU_OUT_10_PLAYER = 7265, // Momma Said Knock You Out (10 player)
|
||||
ACHIEV_CRITERIA_MOMMA_SAID_KNOCK_YOU_OUT_25_PLAYER = 7549, // Momma Said Knock You Out (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_SHOKING_10_PLAYER = 7604, // Shocking! (10 player)
|
||||
ACHIEV_CRITERIA_SHOKING_25_PLAYER = 7605, // Shocking! (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_SPORE_LOSER_10_PLAYER = 7612, // Spore Loser (10 player)
|
||||
ACHIEV_CRITERIA_SPORE_LOSER_25_PLAYER = 7613, // Spore Loser (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_THE_SAFETY_DANCE_10_PLAYER = 7264, // The Safety Dance (10 player)
|
||||
ACHIEV_CRITERIA_THE_SAFETY_DANCE_25_PLAYER = 7548, // The Safety Dance (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_SUBTRACTION_10_PLAYER = 7608, // Subtraction (10 player)
|
||||
ACHIEV_CRITERIA_SUBTRACTION_25_PLAYER = 7609, // Subtraction (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_THE_HUNDRED_CLUB_10_PLAYER = 7567, // The Hundred Club (10 player)
|
||||
ACHIEV_CRITERIA_THE_HUNDRED_CLUB_25_PLAYER = 7568, // The Hundred Club (25 player)
|
||||
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_ANUB_10_PLAYER = 7146, // The Dedicated Few (25 player) - Anub'Rekhan
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_FAERLINA_10_PLAYER = 7147, // The Dedicated Few (25 player) - Grand Widow Faerlina
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_MAEXXNA_10_PLAYER = 7148, // The Dedicated Few (25 player) - Maexxna
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_PATCHWERK_10_PLAYER = 7149, // The Dedicated Few (25 player) - Patchwerk
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_GROBBULUS_10_PLAYER = 7150, // The Dedicated Few (25 player) - Grobbulus
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_GLUTH_10_PLAYER = 7151, // The Dedicated Few (25 player) - Gluth
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_THADDIUS_10_PLAYER = 7152, // The Dedicated Few (25 player) - Thaddius
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_NOTH_10_PLAYER = 7153, // The Dedicated Few (25 player) - Noth the Plaguebringer
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_HEIGAN_10_PLAYER = 7154, // The Dedicated Few (25 player) - Heigan the Unclean
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_LOATHEB_10_PLAYER = 7155, // The Dedicated Few (25 player) - Loatheb
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_RAZUVIOUS_10_PLAYER = 7156, // The Dedicated Few (25 player) - Instructor Razuvious
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_GOTHIK_10_PLAYER = 7157, // The Dedicated Few (25 player) - Gothik the Harvester
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_SAPPHIRON_10_PLAYER = 7158, // The Dedicated Few (25 player) - Sapphiron
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_KELTHUZAD_10_PLAYER = 6802, // The Dedicated Few (25 player) - Kel'Thuzad
|
||||
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_ANUB_25_PLAYER = 7159, // The Dedicated Few (25 player) - Anub'Rekhan
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_FAERLINA_25_PLAYER = 7160, // The Dedicated Few (25 player) - Grand Widow Faerlina
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_MAEXXNA_25_PLAYER = 7161, // The Dedicated Few (25 player) - Maexxna
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_PATCHWERK_25_PLAYER = 7162, // The Dedicated Few (25 player) - Patchwerk
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_GROBBULUS_25_PLAYER = 7163, // The Dedicated Few (25 player) - Grobbulus
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_GLUTH_25_PLAYER = 7164, // The Dedicated Few (25 player) - Gluth
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_THADDIUS_25_PLAYER = 7165, // The Dedicated Few (25 player) - Thaddius
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_NOTH_25_PLAYER = 7166, // The Dedicated Few (25 player) - Noth the Plaguebringer
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_HEIGAN_25_PLAYER = 7167, // The Dedicated Few (25 player) - Heigan the Unclean
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_LOATHEB_25_PLAYER = 7168, // The Dedicated Few (25 player) - Loatheb
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_RAZUVIOUS_25_PLAYER = 7169, // The Dedicated Few (25 player) - Instructor Razuvious
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_GOTHIK_25_PLAYER = 7170, // The Dedicated Few (25 player) - Gothik the Harvester
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_SAPPHIRON_25_PLAYER = 7171, // The Dedicated Few (25 player) - Sapphiron
|
||||
ACHIEV_CRITERIA_THE_DEDICATED_FEW_KELTHUZAD_25_PLAYER = 7172, // The Dedicated Few (25 player) - Kel'Thuzad
|
||||
|
||||
ACHIEV_CRITERIA_THE_UNDYING_KELTHUZAD = 7617, // The Undying - Kel'Thuzad
|
||||
ACHIEV_CRITERIA_THE_UNDYING_THE_FOUR_HORSEMEN = 13237, // The Undying - The Four Horsemen
|
||||
ACHIEV_CRITERIA_THE_UNDYING_MAEXXNA = 13238, // The Undying - Maexxna
|
||||
ACHIEV_CRITERIA_THE_UNDYING_LOATHEB = 13239, // The Undying - Loatheb
|
||||
ACHIEV_CRITERIA_THE_UNDYING_THADDIUS = 13240, // The Undying - Thaddius
|
||||
|
||||
ACHIEV_CRITERIA_THE_IMMORTAL_KELTHUZAD = 7616, // The Immortal - Kel'Thuzad
|
||||
ACHIEV_CRITERIA_THE_IMMORTAL_THE_FOUR_HORSEMEN = 13233, // The Immortal - The Four Horsemen
|
||||
ACHIEV_CRITERIA_THE_IMMORTAL_MAEXXNA = 13234, // The Immortal - Maexxna
|
||||
ACHIEV_CRITERIA_THE_IMMORTAL_LOATHEB = 13235, // The Immortal - Loatheb
|
||||
ACHIEV_CRITERIA_THE_IMMORTAL_THADDIUS = 13236 // The Immortal - Thaddius
|
||||
};
|
||||
|
||||
enum NaxxramasSay
|
||||
{
|
||||
SAY_HORSEMEN_DIALOG1 = 5,
|
||||
SAY_HORSEMEN_DIALOG2 = 6,
|
||||
|
||||
SAY_SAPP_DIALOG1 = 0,
|
||||
SAY_SAPP_DIALOG2_LICH = 1,
|
||||
SAY_SAPP_DIALOG3 = 2,
|
||||
SAY_SAPP_DIALOG4_LICH = 2,
|
||||
SAY_SAPP_DIALOG5 = 4,
|
||||
SAY_SAPP_DIALOG6 = 20,
|
||||
|
||||
SAY_CAT_DIED = 5, // No!!! A curse upon you, interlopers! The armies of the Lich King will hunt you down. You will not escape your fate...
|
||||
SAY_FIRST_WING_TAUNT = 16
|
||||
};
|
||||
|
||||
enum NaxxramasEvent
|
||||
{
|
||||
EVENT_SUMMON_LIVING_POISON = 1,
|
||||
EVENT_THADDIUS_SCREAMS = 2,
|
||||
EVENT_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER = 3,
|
||||
EVENT_KELTHUZAD_WING_TAUNT = 4,
|
||||
|
||||
EVENT_HORSEMEN_INTRO1 = 5, // Thane Korth'azz: To arms, ye roustabouts! We've got company!
|
||||
EVENT_HORSEMEN_INTRO2 = 6, // Sir Zeliek: Invaders, cease this foolish venture at once! Turn away while you still can!
|
||||
EVENT_HORSEMEN_INTRO3 = 7, // Lady Blaumeux: Come, Zeliek, do not drive them out. Not before we've had our fun!
|
||||
EVENT_HORSEMEN_INTRO4 = 8, // Baron Rivendare: Enough prattling. Let them come. We shall grind their bones to dust.
|
||||
EVENT_HORSEMEN_INTRO5 = 9, // Lady Blaumeux: I do hope they stay alive long enough for me to... introduce myself.
|
||||
EVENT_HORSEMEN_INTRO6 = 10, // Sir Zeliek: Perhaps they will come to their senses... and run away as fast as they can.
|
||||
EVENT_HORSEMEN_INTRO7 = 11, // Thane Korth'azz: I've heard about enough a' yer snivelin'! Shut yer flytrap before I shut it for ye'!
|
||||
EVENT_HORSEMEN_INTRO8 = 12, // Baron Rivendare: Conserve your anger. Harness your rage. You will all have outlets for your frustrations soon enough.
|
||||
|
||||
EVENT_FROSTWYRM_WATERFALL_DOOR = 13,
|
||||
EVENT_KELTHUZAD_LICH_KING_TALK1 = 14,
|
||||
EVENT_KELTHUZAD_LICH_KING_TALK2 = 15,
|
||||
EVENT_KELTHUZAD_LICH_KING_TALK3 = 16,
|
||||
EVENT_KELTHUZAD_LICH_KING_TALK4 = 17,
|
||||
EVENT_KELTHUZAD_LICH_KING_TALK5 = 18,
|
||||
EVENT_KELTHUZAD_LICH_KING_TALK6 = 19
|
||||
};
|
||||
|
||||
enum NaxxramasMisc
|
||||
{
|
||||
SPELL_ERUPTION = 29371,
|
||||
SPELL_EXPLODE = 28433,
|
||||
SPELL_THE_FOUR_HORSEMAN_CREDIT = 59450,
|
||||
|
||||
ACTION_SAPPHIRON_BIRTH = 1,
|
||||
|
||||
// Background screams in instance if Thaddius still alive, four of them from 8873 to 8876
|
||||
SOUND_SCREAM = 8873
|
||||
};
|
||||
|
||||
static constexpr uint32 NaxxramasMapId = 533;
|
||||
static constexpr uint8 HeiganEruptSectionCount = 4;
|
||||
static constexpr uint8 HorsemanCount = 4;
|
||||
static constexpr uint8 AbominationKillCountReq = 18;
|
||||
static constexpr uint8 TheDedicatedFew10PlayerReq = 9;
|
||||
static constexpr uint8 TheDedicatedFew25PlayerReq = 21;
|
||||
|
||||
template<typename AI, typename T>
|
||||
inline AI* GetNaxxramasAI(T* obj)
|
||||
{
|
||||
return GetInstanceAI<AI>(obj, NaxxramasScriptName);
|
||||
}
|
||||
|
||||
#define RegisterNaxxramasCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetNaxxramasAI)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DEF_NAXXRAMAS_40_H
|
||||
#define DEF_NAXXRAMAS_40_H
|
||||
|
||||
enum NX40Spells
|
||||
{
|
||||
// Anub'Rekhan
|
||||
SPELL_SUMMON_CORPSE_SCRABS_5 = 90001, // Changed from 29105 to Level 60 Mob ID for summon
|
||||
SPELL_SUMMON_CORPSE_SCRABS_10 = 90002, // Changed from 29105 to Level 60 Mob ID for summon
|
||||
// Grobbulus
|
||||
SPELL_BOMBARD_SLIME = 90003, // update summon entry
|
||||
// Loatheb
|
||||
SPELL_SUMMON_SPORE = 90006,
|
||||
// Maexxna
|
||||
SPELL_WEB_WRAP_SUMMON_40 = 90007, // custom summon entry: 16486 -> 351075
|
||||
|
||||
// Heigan
|
||||
SPELL_DISRUPTION = 55010, // 29310->55010: Mana Burn AoE spell similar to vanilla
|
||||
};
|
||||
|
||||
enum NX40SpellValues
|
||||
{
|
||||
// Anub'Rekhan
|
||||
IMPALE_BP1 = 3937,
|
||||
IMPALE_BP2 = 299
|
||||
};
|
||||
|
||||
enum NX40Data
|
||||
{
|
||||
DATA_HEIGAN_EXIT_GATE_OLD_40 = 4130,
|
||||
DATA_HEIGAN_ERUPTION_TUNNEL_40 = 4132,
|
||||
DATA_RAZUVIOUS_40 = 4133
|
||||
};
|
||||
|
||||
enum NX40GOs
|
||||
{
|
||||
GO_HEIGAN_EXIT_GATE_40 = 181496,
|
||||
GO_HORSEMEN_CHEST_40 = 361000,
|
||||
// Gate to enter Naxx 40 from Strath
|
||||
GO_STRATH_GATE_40 = 176424
|
||||
};
|
||||
|
||||
enum NX40NPCs
|
||||
{
|
||||
// Anub'Rekhan
|
||||
NPC_CORPSE_SCARAB = 351083,
|
||||
NPC_CRYPT_GUARD = 351082,
|
||||
|
||||
// Faerlina
|
||||
NPC_NAXXRAMAS_WORSHIPPER = 351081,
|
||||
NPC_NAXXRAMAS_FOLLOWER = 351080,
|
||||
|
||||
// Gluth
|
||||
NPC_ZOMBIE_CHOW = 351069,
|
||||
|
||||
// Thaddius
|
||||
NPC_THADDIUS_40 = 351000,
|
||||
NPC_STALAGG_40 = 351001,
|
||||
NPC_FEUGEN_40 = 351002,
|
||||
|
||||
// Grobbulus
|
||||
NPC_FALLOUT_SLIME = 351067,
|
||||
NPC_SEWAGE_SLIME = 351071,
|
||||
NPC_STICHED_GIANT = 351027,
|
||||
|
||||
// Noth
|
||||
NPC_PLAGUED_WARRIOR = 351087,
|
||||
NPC_PLAGUED_CHAMPION = 351086,
|
||||
NPC_PLAGUED_GUARDIAN = 351085,
|
||||
|
||||
// Razuvious
|
||||
NPC_RAZUVIOUS_40 = 351036,
|
||||
NPC_DEATH_KNIGHT_UNDERSTUDY = 351084,
|
||||
|
||||
// Gothik
|
||||
NPC_GOTHIK_40 = 351035,
|
||||
NPC_LIVING_TRAINEE = 351043,
|
||||
NPC_LIVING_KNIGHT = 351044,
|
||||
NPC_LIVING_RIDER = 351045,
|
||||
NPC_DEAD_TRAINEE = 351046,
|
||||
NPC_DEAD_KNIGHT = 351050,
|
||||
NPC_DEAD_HORSE = 351051,
|
||||
NPC_DEAD_RIDER = 351052,
|
||||
NPC_TRIGGER = 351047,
|
||||
|
||||
// Maexxna
|
||||
NPC_WEB_WRAP = 351079,
|
||||
NPC_MAEXXNA_SPIDERLING = 351088,
|
||||
|
||||
// Four horseman
|
||||
NPC_HIGHLORD_MOGRAINE_40 = 351037,
|
||||
NPC_SIR_ZELIEK_40 = 351038,
|
||||
NPC_LADY_BLAUMEUX_40 = 351040,
|
||||
NPC_THANE_KORTHAZZ_40 = 351039,
|
||||
|
||||
// Sapphiron
|
||||
NPC_SAPPHIRON_40 = 351018,
|
||||
|
||||
// Kel'Thuzad
|
||||
NPC_KELTHUZAD_40 = 351019,
|
||||
// NPC_LICH_KING_40 = 16980,
|
||||
NPC_SOLDIER_OF_THE_FROZEN_WASTES = 351073,
|
||||
NPC_UNSTOPPABLE_ABOMINATION = 351074,
|
||||
NPC_SOUL_WEAVER = 351075,
|
||||
NPC_GUARDIAN_OF_ICECROWN = 351076,
|
||||
|
||||
// Frogger
|
||||
// NPC_LIVING_POISON_40 = 16027,
|
||||
// NPC_NAXXRAMAS_TRIGGER_40 = 16082,
|
||||
// NPC_MR_BIGGLESWORTH_40 = 16998,
|
||||
|
||||
// Patchwerk
|
||||
NPC_PATCHWERK_40 = 351028,
|
||||
NPC_PATCHWORK_GOLEM_40 = 351021,
|
||||
NPC_BILE_RETCHER_40 = 351022,
|
||||
NPC_MAD_SCIENTIST_40 = 351023,
|
||||
NPC_LIVING_MONSTROSITY_40 = 351024,
|
||||
NPC_SURGICAL_ASSIST_40 = 351025,
|
||||
NPC_SLUDGE_BELCHER_40 = 351029,
|
||||
|
||||
// Heigan
|
||||
NPC_ROTTING_MAGGOT_40 = 351034,
|
||||
NPC_DISEASED_MAGGOT_40 = 351033,
|
||||
NPC_EYE_STALK_40 = 351090,
|
||||
|
||||
NPC_ARCHMAGE_TARSIS = 16381,
|
||||
};
|
||||
|
||||
enum NX40Maps
|
||||
{
|
||||
MAP_NAXX = 533,
|
||||
};
|
||||
|
||||
enum NX40Graveyards
|
||||
{
|
||||
NAXX40_GRAVEYARD = 909
|
||||
};
|
||||
|
||||
enum NX40AttunementQuests
|
||||
{
|
||||
NAXX40_ATTUNEMENT_1 = 9121,
|
||||
NAXX40_ATTUNEMENT_2 = 9122,
|
||||
NAXX40_ATTUNEMENT_3 = 9123,
|
||||
NAXX40_ENTRANCE_FLAG = 9378
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,315 @@
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ScriptedGossip.h"
|
||||
|
||||
enum CraftsSpellIds : uint32
|
||||
{
|
||||
// Tailoring
|
||||
LEARN_GLACIAL_GLOVES = 28261,
|
||||
HAS_GLACIAL_GLOVES = 28205,
|
||||
LEARN_GLACIAL_WRISTS = 28262,
|
||||
HAS_GLACIAL_WRISTS = 28209,
|
||||
LEARN_GLACIAL_VEST = 28260,
|
||||
HAS_GLACIAL_VEST = 28207,
|
||||
LEARN_GLACIAL_CLOAK = 28263,
|
||||
HAS_GLACIAL_CLOAK = 28208,
|
||||
|
||||
// Leatherworking
|
||||
LEARN_POLAR_GLOVES = 28255,
|
||||
HAS_POLAR_GLOVES = 28220,
|
||||
LEARN_POLAR_BRACERS = 28256,
|
||||
HAS_POLAR_BRACERS = 28221,
|
||||
LEARN_POLAR_TUNIC = 28254,
|
||||
HAS_POLAR_TUNIC = 28219,
|
||||
LEARN_ICY_SCALE_GAUNTLETS = 28258,
|
||||
HAS_ICY_SCALE_GAUNTLETS = 28223,
|
||||
LEARN_ICY_SCALE_BRACERS = 28259,
|
||||
HAS_ICY_SCALE_BRACERS = 28224,
|
||||
LEARN_ICY_SCALE_BREASTPLATE = 28257,
|
||||
HAS_ICY_SCALE_BREASTPLATE = 28222,
|
||||
|
||||
// Blacksmithing
|
||||
LEARN_ICEBANE_GAUNTLETS = 28253,
|
||||
HAS_ICEBANE_GAUNTLETS = 28243,
|
||||
LEARN_ICEBANE_BRACERS = 28252,
|
||||
HAS_ICEBANE_BRACERS = 28244,
|
||||
LEARN_ICEBANE_BREASTPLATE = 28251,
|
||||
HAS_ICEBANE_BREASTPLATE = 28242
|
||||
};
|
||||
|
||||
enum Say
|
||||
{
|
||||
BROADCAST_TEXT_SPIT_TARGET = 31673
|
||||
};
|
||||
|
||||
enum Requirements
|
||||
{
|
||||
FACTION_ARGENT_DAWN = 529,
|
||||
BOOK_REQ_RANK = REP_HONORED,
|
||||
CRAFT1_REQ_RANK = REP_REVERED,
|
||||
CRAFT2_REQ_RANK = REP_EXALTED,
|
||||
MASTER_REQ_SKILL = 225,
|
||||
LEARN_REQ_SKILL = 300
|
||||
};
|
||||
|
||||
enum Quests
|
||||
{
|
||||
QUEST_OMARIONS_HANDBOOK = 9233,
|
||||
OMARIONS_HANDBOOK = 22719
|
||||
};
|
||||
|
||||
enum Gossips
|
||||
{
|
||||
// NPC Text IDs
|
||||
NPC_TEXT_INTRO = 8507,
|
||||
NPC_TEXT_NO_CRAFTER = 8516,
|
||||
NPC_TEXT_NEW_ENTRY = 24400,
|
||||
NPC_TEXT_TAILORING = NPC_TEXT_NEW_ENTRY + 1,
|
||||
NPC_TEXT_BLACKSMITHING = NPC_TEXT_NEW_ENTRY + 2,
|
||||
NPC_TEXT_LEATHERWORKING = NPC_TEXT_NEW_ENTRY + 3,
|
||||
|
||||
// Menu IDs
|
||||
MENU_ID_ENTRY = 24400,
|
||||
MENU_ID_NO_CRAFTER = MENU_ID_ENTRY,
|
||||
MENU_ID_TAILORING = MENU_ID_ENTRY + 1,
|
||||
MENU_ID_BLACKSMITHING = MENU_ID_ENTRY + 2,
|
||||
MENU_ID_LEATHERWORKING = MENU_ID_ENTRY + 3,
|
||||
MENU_ID_INTRO = MENU_ID_ENTRY + 4,
|
||||
|
||||
// Intro
|
||||
ITEM_ID_LEATHERWORKING = 1,
|
||||
ITEM_ID_BLACKSMITHING = 2,
|
||||
ITEM_ID_TAILORING = 3,
|
||||
ITEM_ID_NO_CRAFTER = 4,
|
||||
|
||||
// Tailoring
|
||||
ITEM_ID_GLACIAL_CLOAK = 1,
|
||||
ITEM_ID_GLACIAL_GLOVES = 2,
|
||||
ITEM_ID_GLACIAL_WRISTS = 3,
|
||||
ITEM_ID_GLACIAL_VEST = 4,
|
||||
ITEM_ID_GOODBYE_TAILORING = 5,
|
||||
|
||||
// Blacksmithing
|
||||
ITEM_ID_ICEBANE_BRACERS = 1,
|
||||
ITEM_ID_ICEBANE_GAUNTLETS = 2,
|
||||
ITEM_ID_ICEBANE_BREASTPLATE = 3,
|
||||
ITEM_ID_GOODBYE_BLACKSMITHING = 4,
|
||||
|
||||
// Leatherworking
|
||||
ITEM_ID_POLAR_BRACERS = 1,
|
||||
ITEM_ID_POLAR_GLOVES = 2,
|
||||
ITEM_ID_POLAR_TUNIC = 3,
|
||||
ITEM_ID_ICY_SCALE_BRACERS = 4,
|
||||
ITEM_ID_ICY_SCALE_GAUNTLETS = 5,
|
||||
ITEM_ID_ICY_SCALE_BREASTPLATE = 6,
|
||||
ITEM_ID_GOODBYE_LEATHERWORKING = 7,
|
||||
|
||||
// No crafter, book
|
||||
ITEM_ID_GOODBYE_NO_CRAFTER = 1,
|
||||
|
||||
GOSSIP_CLOSE = 100
|
||||
};
|
||||
|
||||
class npc_omarion : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_omarion() : CreatureScript("npc_omarion_gossip") { }
|
||||
|
||||
bool OnGossipHello(Player* player, Creature* creature) override
|
||||
{
|
||||
ClearGossipMenuFor(player);
|
||||
uint32 tailorSkill = player->GetSkillValue(SKILL_TAILORING);
|
||||
uint32 blacksmithSkill = player->GetSkillValue(SKILL_BLACKSMITHING);
|
||||
uint32 leatherworkSkill = player->GetSkillValue(SKILL_LEATHERWORKING);
|
||||
if (tailorSkill >= MASTER_REQ_SKILL)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_INTRO, ITEM_ID_TAILORING, GOSSIP_SENDER_MAIN, MENU_ID_TAILORING);
|
||||
}
|
||||
|
||||
if (blacksmithSkill >= MASTER_REQ_SKILL)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_INTRO, ITEM_ID_BLACKSMITHING, GOSSIP_SENDER_MAIN, MENU_ID_BLACKSMITHING);
|
||||
}
|
||||
|
||||
if (leatherworkSkill >= MASTER_REQ_SKILL)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_INTRO, ITEM_ID_LEATHERWORKING, GOSSIP_SENDER_MAIN, MENU_ID_LEATHERWORKING);
|
||||
}
|
||||
|
||||
AddGossipItemFor(player, MENU_ID_INTRO, ITEM_ID_NO_CRAFTER, GOSSIP_SENDER_MAIN, MENU_ID_NO_CRAFTER);
|
||||
SendGossipMenuFor(player, NPC_TEXT_INTRO, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
void LearnCraftIfNotAlreadyKnown(uint32 learnId, uint32 knowId, Player *player)
|
||||
{
|
||||
if (!player->HasSpell(knowId))
|
||||
{
|
||||
player->CastSpell(player, learnId, false);
|
||||
}
|
||||
}
|
||||
|
||||
void CloseGossipEmoteAndSpitOnPlayer(Player* player, Creature* creature)
|
||||
{
|
||||
CloseGossipMenuFor(player);
|
||||
creature->TextEmote(BROADCAST_TEXT_SPIT_TARGET, player);
|
||||
creature->HandleEmoteCommand(EMOTE_ONESHOT_NONE); // skip animation
|
||||
creature->HandleEmoteCommand(EMOTE_ONESHOT_RUDE);
|
||||
}
|
||||
|
||||
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
|
||||
{
|
||||
uint32 argentDawnRep = player->GetReputationRank(FACTION_ARGENT_DAWN);
|
||||
uint32 tailorSkill = player->GetSkillValue(SKILL_TAILORING);
|
||||
uint32 blacksmithSkill = player->GetSkillValue(SKILL_BLACKSMITHING);
|
||||
uint32 leatherworkSkill = player->GetSkillValue(SKILL_LEATHERWORKING);
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case GOSSIP_CLOSE:
|
||||
CloseGossipMenuFor(player);
|
||||
break;
|
||||
case MENU_ID_TAILORING:
|
||||
ClearGossipMenuFor(player);
|
||||
if (argentDawnRep < CRAFT1_REQ_RANK || tailorSkill < LEARN_REQ_SKILL)
|
||||
{
|
||||
CloseGossipEmoteAndSpitOnPlayer(player, creature);
|
||||
break;
|
||||
}
|
||||
if (argentDawnRep >= CRAFT1_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_TAILORING, ITEM_ID_GLACIAL_WRISTS, GOSSIP_SENDER_MAIN, LEARN_GLACIAL_WRISTS);
|
||||
AddGossipItemFor(player, MENU_ID_TAILORING, ITEM_ID_GLACIAL_GLOVES, GOSSIP_SENDER_MAIN, LEARN_GLACIAL_GLOVES);
|
||||
}
|
||||
if (argentDawnRep >= CRAFT2_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_TAILORING, ITEM_ID_GLACIAL_VEST, GOSSIP_SENDER_MAIN, LEARN_GLACIAL_VEST);
|
||||
AddGossipItemFor(player, MENU_ID_TAILORING, ITEM_ID_GLACIAL_CLOAK, GOSSIP_SENDER_MAIN, LEARN_GLACIAL_CLOAK);
|
||||
}
|
||||
AddGossipItemFor(player, MENU_ID_TAILORING, ITEM_ID_GOODBYE_TAILORING, GOSSIP_SENDER_MAIN, GOSSIP_CLOSE);
|
||||
SendGossipMenuFor(player, NPC_TEXT_TAILORING, creature->GetGUID());
|
||||
break;
|
||||
case MENU_ID_BLACKSMITHING:
|
||||
ClearGossipMenuFor(player);
|
||||
if (argentDawnRep < CRAFT1_REQ_RANK || blacksmithSkill < LEARN_REQ_SKILL)
|
||||
{
|
||||
CloseGossipEmoteAndSpitOnPlayer(player, creature);
|
||||
break;
|
||||
}
|
||||
if (argentDawnRep >= CRAFT1_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_BLACKSMITHING, ITEM_ID_ICEBANE_BRACERS, GOSSIP_SENDER_MAIN, LEARN_ICEBANE_BRACERS);
|
||||
AddGossipItemFor(player, MENU_ID_BLACKSMITHING, ITEM_ID_ICEBANE_GAUNTLETS, GOSSIP_SENDER_MAIN, LEARN_ICEBANE_GAUNTLETS);
|
||||
}
|
||||
if (argentDawnRep >= CRAFT2_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_BLACKSMITHING, ITEM_ID_ICEBANE_BREASTPLATE, GOSSIP_SENDER_MAIN, LEARN_ICEBANE_BREASTPLATE);
|
||||
}
|
||||
AddGossipItemFor(player, MENU_ID_BLACKSMITHING, ITEM_ID_GOODBYE_BLACKSMITHING, GOSSIP_SENDER_MAIN, GOSSIP_CLOSE);
|
||||
SendGossipMenuFor(player, NPC_TEXT_BLACKSMITHING, creature->GetGUID());
|
||||
break;
|
||||
case MENU_ID_LEATHERWORKING:
|
||||
ClearGossipMenuFor(player);
|
||||
if (argentDawnRep < CRAFT1_REQ_RANK || leatherworkSkill < LEARN_REQ_SKILL)
|
||||
{
|
||||
CloseGossipEmoteAndSpitOnPlayer(player, creature);
|
||||
break;
|
||||
}
|
||||
if (argentDawnRep >= CRAFT1_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_LEATHERWORKING, ITEM_ID_POLAR_BRACERS, GOSSIP_SENDER_MAIN, LEARN_POLAR_BRACERS);
|
||||
AddGossipItemFor(player, MENU_ID_LEATHERWORKING, ITEM_ID_POLAR_GLOVES, GOSSIP_SENDER_MAIN, LEARN_POLAR_GLOVES);
|
||||
}
|
||||
if (argentDawnRep >= CRAFT2_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_LEATHERWORKING, ITEM_ID_POLAR_TUNIC, GOSSIP_SENDER_MAIN, LEARN_POLAR_TUNIC);
|
||||
}
|
||||
if (argentDawnRep >= CRAFT1_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_LEATHERWORKING, ITEM_ID_ICY_SCALE_BRACERS, GOSSIP_SENDER_MAIN, LEARN_ICY_SCALE_BRACERS);
|
||||
AddGossipItemFor(player, MENU_ID_LEATHERWORKING, ITEM_ID_ICY_SCALE_GAUNTLETS, GOSSIP_SENDER_MAIN, LEARN_ICY_SCALE_GAUNTLETS);
|
||||
}
|
||||
if (argentDawnRep >= CRAFT2_REQ_RANK)
|
||||
{
|
||||
AddGossipItemFor(player, MENU_ID_LEATHERWORKING, ITEM_ID_ICY_SCALE_BREASTPLATE, GOSSIP_SENDER_MAIN, LEARN_ICY_SCALE_BREASTPLATE);
|
||||
}
|
||||
AddGossipItemFor(player, MENU_ID_LEATHERWORKING, ITEM_ID_GOODBYE_LEATHERWORKING, GOSSIP_SENDER_MAIN, GOSSIP_CLOSE);
|
||||
SendGossipMenuFor(player, NPC_TEXT_LEATHERWORKING, creature->GetGUID());
|
||||
break;
|
||||
case MENU_ID_NO_CRAFTER:
|
||||
ClearGossipMenuFor(player);
|
||||
if (argentDawnRep < BOOK_REQ_RANK)
|
||||
{
|
||||
CloseGossipEmoteAndSpitOnPlayer(player, creature);
|
||||
break;
|
||||
}
|
||||
if (player->GetQuestStatus(QUEST_OMARIONS_HANDBOOK) == QUEST_STATUS_NONE && !player->HasItemCount(OMARIONS_HANDBOOK, 1, true))
|
||||
{
|
||||
player->AddItem(OMARIONS_HANDBOOK, 1);
|
||||
}
|
||||
AddGossipItemFor(player, MENU_ID_NO_CRAFTER, ITEM_ID_GOODBYE_NO_CRAFTER, GOSSIP_SENDER_MAIN, GOSSIP_CLOSE);
|
||||
SendGossipMenuFor(player, NPC_TEXT_NO_CRAFTER, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_GLACIAL_CLOAK:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_GLACIAL_CLOAK, HAS_GLACIAL_CLOAK, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_TAILORING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_GLACIAL_GLOVES:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_GLACIAL_GLOVES, HAS_GLACIAL_GLOVES, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_TAILORING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_GLACIAL_WRISTS:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_GLACIAL_WRISTS, HAS_GLACIAL_WRISTS, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_TAILORING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_GLACIAL_VEST:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_GLACIAL_VEST, HAS_GLACIAL_VEST, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_TAILORING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_ICEBANE_BRACERS:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_ICEBANE_BRACERS, HAS_ICEBANE_BRACERS, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_BLACKSMITHING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_ICEBANE_GAUNTLETS:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_ICEBANE_GAUNTLETS, HAS_ICEBANE_GAUNTLETS, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_BLACKSMITHING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_ICEBANE_BREASTPLATE:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_ICEBANE_BREASTPLATE, HAS_ICEBANE_BREASTPLATE, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_BLACKSMITHING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_POLAR_BRACERS:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_POLAR_BRACERS, HAS_POLAR_BRACERS, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_LEATHERWORKING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_POLAR_GLOVES:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_POLAR_GLOVES, HAS_POLAR_GLOVES, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_LEATHERWORKING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_POLAR_TUNIC:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_POLAR_TUNIC, HAS_POLAR_TUNIC, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_LEATHERWORKING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_ICY_SCALE_BRACERS:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_ICY_SCALE_BRACERS, HAS_ICY_SCALE_BRACERS, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_LEATHERWORKING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_ICY_SCALE_GAUNTLETS:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_ICY_SCALE_GAUNTLETS, HAS_ICY_SCALE_GAUNTLETS, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_LEATHERWORKING, creature->GetGUID());
|
||||
break;
|
||||
case LEARN_ICY_SCALE_BREASTPLATE:
|
||||
LearnCraftIfNotAlreadyKnown(LEARN_ICY_SCALE_BREASTPLATE, HAS_ICY_SCALE_BREASTPLATE, player);
|
||||
SendGossipMenuFor(player, NPC_TEXT_LEATHERWORKING, creature->GetGUID());
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_npc_omarion_40()
|
||||
{
|
||||
new npc_omarion();
|
||||
}
|
||||
Reference in New Issue
Block a user