Naxx40 Progress

This commit is contained in:
郑佩茹
2022-09-01 23:19:21 -06:00
parent fe1455bb75
commit f2d6afbdb3
22 changed files with 11389 additions and 3893 deletions

View File

@@ -5,6 +5,8 @@ DELETE FROM `areatrigger_teleport` WHERE `ID` IN (5196, 5197, 5198, 5199);
UPDATE `creature` SET `spawnMask` = 3 WHERE `spawnMask` = 7 AND `map` = 533; UPDATE `creature` SET `spawnMask` = 3 WHERE `spawnMask` = 7 AND `map` = 533;
UPDATE `gameobject` SET `spawnMask` = 7 WHERE `spawnMask` = 3 AND `map` = 533; UPDATE `gameobject` SET `spawnMask` = 7 WHERE `spawnMask` = 3 AND `map` = 533;
UPDATE `gameobject` SET `spawnMask` = 3 WHERE `id` IN (202278, 202277); # Orb of Naxxramas does not exist in classic
DELETE FROM `dungeon_access_template` WHERE `id`=122; DELETE FROM `dungeon_access_template` WHERE `id`=122;
INSERT INTO `dungeon_access_template` (`id`, `map_id`, `difficulty`, `min_level`, `max_level`, `min_avg_item_level`, `comment`) VALUES (122, 533, 2, 60, 0, 0, 'Naxxramas - 40man'); INSERT INTO `dungeon_access_template` (`id`, `map_id`, `difficulty`, `min_level`, `max_level`, `min_avg_item_level`, `comment`) VALUES (122, 533, 2, 60, 0, 0, 'Naxxramas - 40man');
@@ -16,6 +18,25 @@ DELETE FROM `creature` WHERE `guid` = 352042;
INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`) VALUES INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`) VALUES
(352042, 351097, 0, 0, 329, 0, 0, 1, 1, 0, 3929.06, -3372.12, 119.653, 4.71395, 300, 0, 0, 6986, 0, 0, 0, 0, 0, '', 0); (352042, 351097, 0, 0, 329, 0, 0, 1, 1, 0, 3929.06, -3372.12, 119.653, 4.71395, 300, 0, 0, 6986, 0, 0, 0, 0, 0, '', 0);
# Four horseman chest
DELETE FROM `gameobject_template` WHERE `entry`=361000;
INSERT INTO `gameobject_template` (`entry`, `type`, `displayId`, `name`, `IconName`, `castBarCaption`, `unk1`, `size`, `Data0`, `Data1`, `Data2`, `Data3`, `Data4`, `Data5`, `Data6`, `Data7`, `Data8`, `Data9`, `Data10`, `Data11`, `Data12`, `Data13`, `Data14`, `Data15`, `Data16`, `Data17`, `Data18`, `Data19`, `Data20`, `Data21`, `Data22`, `Data23`, `AIName`, `ScriptName`, `VerifiedBuild`) VALUES
(361000, 3, 1387, 'Four Horsemen Chest', '', '', '', 1, 1634, 361000, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, '', '', 1);
REPLACE INTO `reference_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `GroupId`, `MinCount`, `MaxCount`) VALUES
(314800, 22349, 0, 0.0, 0, 1, 1, 1),
(314800, 22350, 0, 0.0, 0, 1, 1, 1),
(314800, 22351, 0, 0.0, 0, 1, 1, 1);
REPLACE INTO `gameobject_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `GroupId`, `MinCount`, `MaxCount`) VALUES
(361000, 314800, 314800, 100, 0, 0, 2, 2),
(361000, 22691, 0, 0, 0, 2, 1, 1),
(361000, 22726, 0, 30, 0, 0, 1, 1),
(361000, 22809, 0, 0, 0, 2, 1, 1),
(361000, 22811, 0, 0, 0, 2, 1, 1),
(361000, 23025, 0, 0, 0, 2, 1, 1),
(361000, 23027, 0, 0, 0, 2, 1, 1),
(361000, 23071, 0, 0, 0, 2, 1, 1);
DELETE FROM gameobject WHERE guid BETWEEN 351000 AND 351139; DELETE FROM gameobject WHERE guid BETWEEN 351000 AND 351139;
SET @GUID := 351000; SET @GUID := 351000;
INSERT INTO `gameobject` (`guid`, `id`, `map`, `spawnMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`) VALUES INSERT INTO `gameobject` (`guid`, `id`, `map`, `spawnMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`) VALUES

File diff suppressed because it is too large Load Diff

View File

@@ -137,66 +137,6 @@ public:
} }
}; };
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.68f, -3447.77f, 293.93f, 4.65f);
}
}
}
else if (who && me->GetDistance2d(who) < 20.0f)
{
if (Player* player = who->ToPlayer())
{
if (isAttuned(player))
{
GameObject* door = me->FindNearestGameObject(NAXX_STRATH_GATE, 100.0f);
if (door)
{
door->SetGoState(GO_STATE_ACTIVE);
}
}
}
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_naxx40_area_triggerAI(creature);
}
};
class IndividualPlayerProgression_WorldScript : public WorldScript class IndividualPlayerProgression_WorldScript : public WorldScript
{ {
@@ -658,13 +598,8 @@ public:
// NOTE: Because NAXX40 is not currently implemented, defeating C'Thun ends Vanilla content // NOTE: Because NAXX40 is not currently implemented, defeating C'Thun ends Vanilla content
UpdateProgressionState(killer, PROGRESSION_NAXX40); UpdateProgressionState(killer, PROGRESSION_NAXX40);
break; break;
case KELTHUZAD: case KELTHUZAD_40:
if (killed->getLevel() == 80) { UpdateProgressionState(killer, PROGRESSION_NAXX40);
UpdateProgressionState(killer, PROGRESSION_WOTLK_TIER_1);
}
else {
UpdateProgressionState(killer, PROGRESSION_NAXX40);
}
break; break;
case MALCHEZAAR: case MALCHEZAAR:
UpdateProgressionState(killer, PROGRESSION_TBC_TIER_1); UpdateProgressionState(killer, PROGRESSION_TBC_TIER_1);
@@ -681,6 +616,9 @@ public:
case KILJAEDEN: case KILJAEDEN:
UpdateProgressionState(killer, PROGRESSION_TBC_TIER_5); UpdateProgressionState(killer, PROGRESSION_TBC_TIER_5);
break; break;
case KELTHUZAD:
UpdateProgressionState(killer, PROGRESSION_WOTLK_TIER_1);
break;
case YOGGSARON: case YOGGSARON:
UpdateProgressionState(killer, PROGRESSION_WOTLK_TIER_2); UpdateProgressionState(killer, PROGRESSION_WOTLK_TIER_2);
break; break;
@@ -695,18 +633,6 @@ public:
break; break;
} }
} }
// Used in Naxx40, waiting for this AC PR to merge before can be used: https://github.com/azerothcore/azerothcore-wotlk/pull/12860
//
// void OnBeforeChooseGraveyard(Player* player, TeamId /*teamId*/, bool /*nearCorpse*/, uint32& graveyardOverride) override
// {
// if (player->GetMapId() == MAP_NAXX && player->GetMap()->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC)
// {
// graveyardOverride = NAXX40_GRAVEYARD;
// }
// }
}; };
// Add all scripts in one // Add all scripts in one
@@ -718,7 +644,6 @@ void AddSC_mod_individual_progression()
new npc_ipp_aq(); new npc_ipp_aq();
new npc_ipp_tbc(); new npc_ipp_tbc();
new npc_ipp_wotlk(); new npc_ipp_wotlk();
new npc_naxx40_area_trigger();
new gobject_ipp_tbc(); new gobject_ipp_tbc();
new gobject_ipp_wotlk(); new gobject_ipp_wotlk();
} }

View File

@@ -25,6 +25,7 @@ enum ProgressionBossIDs
NEFARIAN = 11583, NEFARIAN = 11583,
CTHUN = 15727, CTHUN = 15727,
KELTHUZAD = 15990, KELTHUZAD = 15990,
KELTHUZAD_40 = 351019,
MALCHEZAAR = 15690, MALCHEZAAR = 15690,
KAELTHAS = 19622, KAELTHAS = 19622,
ILLIDAN = 22917, ILLIDAN = 22917,
@@ -48,15 +49,7 @@ enum BuffSpells
enum ProgressionQuestIDs enum ProgressionQuestIDs
{ {
MIGHT_OF_KALIMDOR = 8742, MIGHT_OF_KALIMDOR = 8742
NAXX40_ATTUNEMENT_1 = 9121,
NAXX40_ATTUNEMENT_2 = 9122,
NAXX40_ATTUNEMENT_3 = 9123,
};
enum NaxxGraveyards
{
NAXX40_GRAVEYARD = 1449
}; };
enum ProgressionMaps enum ProgressionMaps
@@ -65,7 +58,6 @@ enum ProgressionMaps
MAP_AQ_20 = 509, MAP_AQ_20 = 509,
MAP_AQ_40 = 531, MAP_AQ_40 = 531,
MAP_OUTLANDS = 530, MAP_OUTLANDS = 530,
MAP_NAXX = 533,
MAP_ZUL_AMAN = 568, MAP_ZUL_AMAN = 568,
MAP_NORTHREND = 571, MAP_NORTHREND = 571,
MAP_ULDUAR = 603, MAP_ULDUAR = 603,
@@ -113,9 +105,5 @@ enum ProgressionState : uint8
PROGRESSION_WOTLK_TIER_5 = 16 // Ruby Sanctum PROGRESSION_WOTLK_TIER_5 = 16 // Ruby Sanctum
}; };
enum ProgressionObjects
{
NAXX_STRATH_GATE = 176424
};
#endif //AZEROTHCORE_INDIVIDUALPROGRESSION_H #endif //AZEROTHCORE_INDIVIDUALPROGRESSION_H

View File

@@ -13,6 +13,22 @@ void AddSC_quest_the_masquerade_60_2();
void AddSC_boss_lord_kazzak_60_2(); void AddSC_boss_lord_kazzak_60_2();
void AddSC_boss_chromaggus_60_2_A(); void AddSC_boss_chromaggus_60_2_A();
void AddSC_boss_drakkisath_50_59_B(); void AddSC_boss_drakkisath_50_59_B();
void AddSC_instance_naxxramas_combined();
void AddSC_boss_anubrekhan_40();
void AddSC_boss_faerlina_40();
void AddSC_boss_four_horsemen_40();
void AddSC_boss_gluth_40();
void AddSC_boss_gothik_40();
void AddSC_boss_grobbulus_40();
void AddSC_boss_heigan_40();
void AddSC_boss_kelthuzad_40();
void AddSC_boss_loatheb_40();
void AddSC_boss_maexxna_40();
void AddSC_boss_noth_40();
void AddSC_boss_patchwerk_40();
void AddSC_boss_razuvious_40();
void AddSC_boss_sapphiron_40();
void AddSC_boss_thaddius_40();
void Addmod_individual_progressionScripts() void Addmod_individual_progressionScripts()
@@ -27,5 +43,21 @@ void Addmod_individual_progressionScripts()
AddSC_boss_lord_kazzak_60_2(); AddSC_boss_lord_kazzak_60_2();
AddSC_boss_chromaggus_60_2_A(); AddSC_boss_chromaggus_60_2_A();
AddSC_boss_drakkisath_50_59_B(); AddSC_boss_drakkisath_50_59_B();
AddSC_instance_naxxramas_combined();
AddSC_boss_anubrekhan_40();
AddSC_boss_faerlina_40();
AddSC_boss_four_horsemen_40();
AddSC_boss_gluth_40();
AddSC_boss_gothik_40();
AddSC_boss_grobbulus_40();
AddSC_boss_heigan_40();
AddSC_boss_kelthuzad_40();
AddSC_boss_loatheb_40();
AddSC_boss_maexxna_40();
AddSC_boss_noth_40();
AddSC_boss_patchwerk_40();
AddSC_boss_razuvious_40();
AddSC_boss_sapphiron_40();
AddSC_boss_thaddius_40();
} }

View File

@@ -0,0 +1,247 @@
/*
* 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 "ScriptMgr.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_LOCUST_SWARM_10 = 28785,
SPELL_SUMMON_CORPSE_SCRABS_5 = 29105,
SPELL_SUMMON_CORPSE_SCRABS_10 = 28864,
SPELL_BERSERK = 26662
};
enum Events
{
EVENT_IMPALE = 1,
EVENT_LOCUST_SWARM = 2,
EVENT_BERSERK = 3,
EVENT_SPAWN_GUARD = 4
};
enum Misc
{
NPC_CORPSE_SCARAB = 351083,
NPC_CRYPT_GUARD = 351082,
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
{
explicit boss_anubrekhan_40AI(Creature* c) : BossAI(c, BOSS_ANUB), summons(me)
{
pInstance = c->GetInstanceScript();
sayGreet = false;
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool sayGreet;
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();
events.Reset();
summons.DespawnAll();
SummonCryptGuards();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_ANUB_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override
{
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 SummonedCreatureDespawn(Creature* cr) override
{
summons.Despawn(cr);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
if (pInstance)
{
pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
}
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCRABS_5, true, nullptr, nullptr, me->GetGUID());
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
me->CallForHelp(30.0f);
Talk(SAY_AGGRO);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_ANUB_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
events.ScheduleEvent(EVENT_IMPALE, 15000);
events.ScheduleEvent(EVENT_LOCUST_SWARM, urand(70000, 120000));
events.ScheduleEvent(EVENT_BERSERK, 600000);
if (!summons.HasEntry(NPC_CRYPT_GUARD))
{
SummonCryptGuards();
}
if (!Is25ManRaid())
{
events.ScheduleEvent(EVENT_SPAWN_GUARD, urand(15000, 20000));
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
{
Talk(SAY_GREET);
sayGreet = true;
}
ScriptedAI::MoveInLineOfSight(who);
}
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat() && sayGreet)
{
for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (pInstance)
{
if (Creature* cr = pInstance->instance->GetCreature(*itr))
{
if (cr->IsInCombat())
DoZoneInCombat();
}
}
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_IMPALE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->CastSpell(target, SPELL_IMPALE_10, false);
}
events.RepeatEvent(20000);
break;
case EVENT_LOCUST_SWARM:
Talk(EMOTE_LOCUST);
me->CastSpell(me, SPELL_LOCUST_SWARM_10, false);
events.ScheduleEvent(EVENT_SPAWN_GUARD, 3000);
events.RepeatEvent(90000);
break;
case EVENT_SPAWN_GUARD:
me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
break;
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
}
DoMeleeAttackIfReady();
}
};
};
void AddSC_boss_anubrekhan_40()
{
new boss_anubrekhan_40();
}

View File

@@ -0,0 +1,248 @@
/*
* 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 "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "naxxramas.h"
enum Yells
{
SAY_GREET = 0,
SAY_AGGRO = 1,
SAY_SLAY = 2,
SAY_DEATH = 3,
EMOTE_WIDOWS_EMBRACE = 4,
EMOTE_FRENZY = 5,
SAY_FRENZY = 6
};
enum Spells
{
SPELL_POISON_BOLT_VOLLEY_10 = 28796,
SPELL_RAIN_OF_FIRE_10 = 28794,
SPELL_FRENZY_10 = 28798,
SPELL_WIDOWS_EMBRACE = 28732
};
enum Events
{
EVENT_POISON_BOLT = 1,
EVENT_RAIN_OF_FIRE = 2,
EVENT_FRENZY = 3
};
enum Misc
{
NPC_NAXXRAMAS_WORSHIPPER = 351081,
NPC_NAXXRAMAS_FOLLOWER = 351080
};
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), summons(me)
{
pInstance = me->GetInstanceScript();
sayGreet = false;
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool sayGreet;
void SummonHelpers()
{
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3362.66f, -3620.97f, 261.08f, 4.57276f);
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3344.3f, -3618.31f, 261.08f, 4.69494f);
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3356.71f, -3620.05f, 261.08f, 4.57276f);
me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3350.26f, -3619.11f, 261.08f, 4.67748f);
if (Is25ManRaid())
{
me->SummonCreature(NPC_NAXXRAMAS_FOLLOWER, 3347.49f, -3617.59f, 261.0f, 4.49f);
me->SummonCreature(NPC_NAXXRAMAS_FOLLOWER, 3359.64f, -3619.16f, 261.0f, 4.56f);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
SummonHelpers();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
me->CallForHelp(VISIBLE_RANGE);
summons.DoZoneInCombat();
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_POISON_BOLT, urand(7000, 12000));
events.ScheduleEvent(EVENT_RAIN_OF_FIRE, urand(8000, 12000));
events.ScheduleEvent(EVENT_FRENZY, urand(60000, 80000), 1);
events.SetPhase(1);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!sayGreet && who->GetTypeId() == TYPEID_PLAYER)
{
Talk(SAY_GREET);
sayGreet = true;
}
ScriptedAI::MoveInLineOfSight(who);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (!urand(0, 3))
{
Talk(SAY_SLAY);
}
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_FAERLINA_WEB)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat() && sayGreet)
{
for (SummonList::iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
if (pInstance)
{
if (Creature* cr = pInstance->instance->GetCreature(*itr))
{
if (cr->IsInCombat())
DoZoneInCombat();
}
}
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_POISON_BOLT:
if (!me->HasAura(SPELL_WIDOWS_EMBRACE))
{
me->CastCustomSpell(SPELL_POISON_BOLT_VOLLEY_10, SPELLVALUE_MAX_TARGETS, 10, me, false);
}
events.RepeatEvent(urand(7000, 12000));
break;
case EVENT_RAIN_OF_FIRE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->CastSpell(target, SPELL_RAIN_OF_FIRE_10, false);
}
events.RepeatEvent(urand(8000, 12000));
break;
case EVENT_FRENZY:
if (!me->HasAura(SPELL_FRENZY_10))
{
Talk(SAY_FRENZY);
Talk(EMOTE_FRENZY);
me->CastSpell(me, SPELL_FRENZY_10, true);
events.RepeatEvent(60000);
}
else
{
events.RepeatEvent(30000);
}
break;
}
DoMeleeAttackIfReady();
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (spell->Id == SPELL_WIDOWS_EMBRACE)
{
Talk(EMOTE_WIDOWS_EMBRACE);
if (me->HasAura(SPELL_FRENZY_10))
{
me->RemoveAurasDueToSpell(SPELL_FRENZY_10);
events.RescheduleEvent(EVENT_FRENZY, 60000);
}
pInstance->SetData(DATA_FRENZY_REMOVED, 0);
if (Is25ManRaid())
{
Unit::Kill(caster, caster);
}
}
}
};
};
void AddSC_boss_faerlina_40()
{
new boss_faerlina_40();
}

View File

@@ -0,0 +1,458 @@
/*
* 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_10 = 28884,
SPELL_KORTHAZZ_METEOR_25 = 57467,
// Blaumeux
SPELL_BLAUMEUX_SHADOW_BOLT_10 = 57374,
SPELL_BLAUMEUX_SHADOW_BOLT_25 = 57464,
SPELL_BLAUMEUX_VOID_ZONE_10 = 28863,
SPELL_BLAUMEUX_VOID_ZONE_25 = 57463,
SPELL_BLAUMEUX_UNYIELDING_PAIN = 57381,
// Zeliek
SPELL_ZELIEK_HOLY_WRATH_10 = 28883,
SPELL_ZELIEK_HOLY_WRATH_25 = 57466,
SPELL_ZELIEK_HOLY_BOLT_10 = 57376,
SPELL_ZELIEK_HOLY_BOLT_25 = 57465,
SPELL_ZELIEK_CONDEMNATION = 57377,
// Mograine
SPELL_RIVENDARE_UNHOLY_SHADOW_10 = 28882,
SPELL_RIVENDARE_UNHOLY_SHADOW_25 = 57369
};
enum Events
{
EVENT_MARK_CAST = 1,
EVENT_PRIMARY_SPELL = 2,
EVENT_SECONDARY_SPELL = 3,
EVENT_PUNISH = 4,
EVENT_BERSERK = 5
};
enum Misc
{
// Movement
MOVE_PHASE_NONE = 0,
MOVE_PHASE_STARTED = 1,
MOVE_PHASE_FINISHED = 2,
// Horseman
HORSEMAN_ZELIEK = 0,
HORSEMAN_BLAUMEUX = 1,
HORSEMAN_MOGRAINE = 2,
HORSEMAN_KORTHAZZ = 3
};
enum FourHorsemen
{
SAY_AGGRO = 0,
SAY_TAUNT = 1,
SAY_SPECIAL = 2,
SAY_SLAY = 3,
SAY_DEATH = 4,
EMOTE_RAGECAST = 7
};
// MARKS
const uint32 TABLE_SPELL_MARK[4] = {SPELL_MARK_OF_ZELIEK, SPELL_MARK_OF_BLAUMEUX, SPELL_MARK_OF_MOGRAINE, SPELL_MARK_OF_KORTHAZZ};
// PRIMARY SPELL
const uint32 TABLE_SPELL_PRIMARY_10[4] = {SPELL_ZELIEK_HOLY_BOLT_10, SPELL_BLAUMEUX_SHADOW_BOLT_10, SPELL_RIVENDARE_UNHOLY_SHADOW_10, SPELL_KORTHAZZ_METEOR_10};
const uint32 TABLE_SPELL_PRIMARY_25[4] = {SPELL_ZELIEK_HOLY_BOLT_25, SPELL_BLAUMEUX_SHADOW_BOLT_25, SPELL_RIVENDARE_UNHOLY_SHADOW_25, SPELL_KORTHAZZ_METEOR_25};
// PUNISH
const uint32 TABLE_SPELL_PUNISH[4] = {SPELL_ZELIEK_CONDEMNATION, SPELL_BLAUMEUX_UNYIELDING_PAIN, 0, 0};
// SECONDARY SPELL
const uint32 TABLE_SPELL_SECONDARY_10[4] = {SPELL_ZELIEK_HOLY_WRATH_10, SPELL_BLAUMEUX_VOID_ZONE_10, 0, 0};
const uint32 TABLE_SPELL_SECONDARY_25[4] = {SPELL_ZELIEK_HOLY_WRATH_25, SPELL_BLAUMEUX_VOID_ZONE_25, 0, 0};
const Position WaypointPositions[12] =
{
// Thane waypoints
{2542.3f, -2984.1f, 241.49f, 5.362f},
{2547.6f, -2999.4f, 241.34f, 5.049f},
{2542.9f, -3015.0f, 241.35f, 4.654f},
// Lady waypoints
{2498.3f, -2961.8f, 241.28f, 3.267f},
{2487.7f, -2959.2f, 241.28f, 2.890f},
{2469.4f, -2947.6f, 241.28f, 2.576f},
// Mograine waypoints
{2553.8f, -2968.4f, 241.33f, 5.757f},
{2564.3f, -2972.5f, 241.33f, 5.890f},
{2583.9f, -2971.6f, 241.35f, 0.008f},
// Sir waypoints
{2534.5f, -2921.7f, 241.53f, 1.363f},
{2523.5f, -2902.8f, 241.28f, 2.095f},
{2517.8f, -2896.6f, 241.28f, 2.315f}
};
class boss_four_horsemen_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)
{
pInstance = me->GetInstanceScript();
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;
InstanceScript* pInstance;
uint8 currentWaypoint{};
uint8 movementPhase{};
uint8 horsemanId;
void MoveToCorner()
{
switch(me->GetEntry())
{
case NPC_THANE_KORTHAZZ_40:
currentWaypoint = 0;
break;
case NPC_LADY_BLAUMEUX_40:
currentWaypoint = 3;
break;
case NPC_HIGHLORD_MOGRAINE_40:
currentWaypoint = 6;
break;
case NPC_SIR_ZELIEK_40:
currentWaypoint = 9;
break;
}
me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]);
}
bool IsInRoom()
{
if (me->GetExactDist(2535.1f, -2968.7f, 241.3f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
me->SetPosition(me->GetHomePosition());
movementPhase = MOVE_PHASE_NONE;
currentWaypoint = 0;
me->SetReactState(REACT_AGGRESSIVE);
events.Reset();
events.RescheduleEvent(EVENT_MARK_CAST, 24000);
events.RescheduleEvent(EVENT_BERSERK, 600000);
if ((me->GetEntry() != NPC_LADY_BLAUMEUX_40 && me->GetEntry() != NPC_SIR_ZELIEK_40))
{
events.RescheduleEvent(EVENT_PRIMARY_SPELL, 10000 + rand() % 5000);
}
else
{
events.RescheduleEvent(EVENT_PUNISH, 5000);
events.RescheduleEvent(EVENT_SECONDARY_SPELL, 15000);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
if (pInstance->GetBossState(BOSS_GOTHIK) == DONE)
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
// final waypoint
if (id % 3 == 2)
{
movementPhase = MOVE_PHASE_FINISHED;
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
if (!UpdateVictim())
{
EnterEvadeMode();
return;
}
if (me->GetEntry() == NPC_LADY_BLAUMEUX_40 || me->GetEntry() == NPC_SIR_ZELIEK_40)
{
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
}
return;
}
currentWaypoint = id + 1;
}
void AttackStart(Unit* who) override
{
if (movementPhase == MOVE_PHASE_FINISHED)
{
if (me->GetEntry() == NPC_LADY_BLAUMEUX_40 || me->GetEntry() == NPC_SIR_ZELIEK_40)
{
me->Attack(who, false);
}
else
{
ScriptedAI::AttackStart(who);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
if (pInstance)
{
if (pInstance->GetBossState(BOSS_HORSEMAN) == DONE)
{
if (!me->GetMap()->GetPlayers().IsEmpty())
{
if (Player* player = me->GetMap()->GetPlayers().getFirst()->GetSource())
{
if (GameObject* chest = player->SummonGameObject(GO_HORSEMEN_CHEST_40, 2514.8f, -2944.9f, 245.55f, 5.51f, 0, 0, 0, 0, 0))
{
chest->SetLootRecipient(me);
}
}
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
Talk(SAY_DEATH);
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
if (movementPhase == MOVE_PHASE_NONE)
{
Talk(SAY_AGGRO);
me->SetReactState(REACT_PASSIVE);
movementPhase = MOVE_PHASE_STARTED;
me->SetSpeed(MOVE_RUN, me->GetSpeedRate(MOVE_RUN), true);
MoveToCorner();
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HORSEMEN_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void UpdateAI(uint32 diff) override
{
if (movementPhase == MOVE_PHASE_STARTED && currentWaypoint)
{
me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]);
currentWaypoint = 0;
}
if (!IsInRoom())
return;
if (movementPhase < MOVE_PHASE_FINISHED || !UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MARK_CAST:
me->CastSpell(me, TABLE_SPELL_MARK[horsemanId], false);
events.RepeatEvent((me->GetEntry() == NPC_LADY_BLAUMEUX_40 || me->GetEntry() == NPC_SIR_ZELIEK_40) ? 15000 : 12000);
return;
case EVENT_BERSERK:
Talk(SAY_SPECIAL);
me->CastSpell(me, SPELL_BERSERK, true);
return;
case EVENT_PRIMARY_SPELL:
Talk(SAY_TAUNT);
me->CastSpell(me->GetVictim(), RAID_MODE(TABLE_SPELL_PRIMARY_10[horsemanId], TABLE_SPELL_PRIMARY_25[horsemanId]), false);
events.RepeatEvent(15000);
return;
case EVENT_PUNISH:
if (!SelectTarget(SelectTargetMethod::MaxDistance, 0, 45.0f, true))
{
me->CastSpell(me, TABLE_SPELL_PUNISH[horsemanId], false);
Talk(EMOTE_RAGECAST);
}
events.RepeatEvent(2010);
return;
case EVENT_SECONDARY_SPELL:
me->CastSpell(me->GetVictim(), RAID_MODE(TABLE_SPELL_SECONDARY_10[horsemanId], TABLE_SPELL_SECONDARY_25[horsemanId]), false);
events.RepeatEvent(15000);
return;
}
if ((me->GetEntry() == NPC_LADY_BLAUMEUX_40 || me->GetEntry() == NPC_SIR_ZELIEK_40))
{
if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 45.0f, true))
{
me->CastSpell(target, RAID_MODE(TABLE_SPELL_PRIMARY_10[horsemanId], TABLE_SPELL_PRIMARY_25[horsemanId]), false);
}
}
else
{
DoMeleeAttackIfReady();
}
}
};
};
class spell_four_horsemen_mark : public SpellScriptLoader
{
public:
spell_four_horsemen_mark() : SpellScriptLoader("spell_four_horsemen_mark") { }
class spell_four_horsemen_mark_AuraScript : public AuraScript
{
PrepareAuraScript(spell_four_horsemen_mark_AuraScript);
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Unit* caster = GetCaster())
{
int32 damage;
switch (GetStackAmount())
{
case 1:
damage = 0;
break;
case 2:
damage = 500;
break;
case 3:
damage = 1500;
break;
case 4:
damage = 4000;
break;
case 5:
damage = 12000;
break;
case 6:
damage = 20000;
break;
default:
damage = 20000 + 1000 * (GetStackAmount() - 7);
break;
}
if (damage)
{
caster->CastCustomSpell(SPELL_MARK_DAMAGE, SPELLVALUE_BASE_POINT0, damage, GetTarget());
}
}
}
void Register() override
{
AfterEffectApply += AuraEffectApplyFn(spell_four_horsemen_mark_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_four_horsemen_mark_AuraScript();
}
};
class spell_four_horsemen_consumption : public SpellScript
{
PrepareSpellScript(spell_four_horsemen_consumption);
void HandleDamageCalc(SpellEffIndex /*effIndex*/)
{
uint32 damage = GetCaster()->GetMap()->ToInstanceMap()->GetDifficulty() == REGULAR_DIFFICULTY ? 2750 : 4250;
SetHitDamage(damage);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_four_horsemen_consumption::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
}
};
void AddSC_boss_four_horsemen_40()
{
new boss_four_horsemen_40();
// new spell_four_horsemen_mark();
// RegisterSpellScript(spell_four_horsemen_consumption);
}

View File

@@ -0,0 +1,273 @@
/*
* 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 Spells
{
SPELL_MORTAL_WOUND = 25646,
SPELL_ENRAGE = 28371,
SPELL_DECIMATE = 28374,
SPELL_BERSERK = 26662,
SPELL_INFECTED_WOUND = 29306,
SPELL_CHOW_SEARCHER = 28404
};
enum Events
{
EVENT_MORTAL_WOUND = 1,
EVENT_ENRAGE = 2,
EVENT_DECIMATE = 3,
EVENT_BERSERK = 4,
EVENT_SUMMON_ZOMBIE = 5,
EVENT_CAN_EAT_ZOMBIE = 6
};
enum Misc
{
NPC_ZOMBIE_CHOW = 351069
};
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)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
void Reset() override
{
BossAI::Reset();
me->ApplySpellImmune(SPELL_INFECTED_WOUND, IMMUNITY_ID, SPELL_INFECTED_WOUND, true);
events.Reset();
summons.DespawnAll();
me->SetReactState(REACT_AGGRESSIVE);
}
void MoveInLineOfSight(Unit* who) override
{
if (!me->GetVictim() || me->GetVictim()->GetEntry() != NPC_ZOMBIE_CHOW)
{
if (who->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinDistInMap(who, 6.5f))
{
SetGazeOn(who);
Talk(EMOTE_SPOTS_ONE);
}
else
{
ScriptedAI::MoveInLineOfSight(who);
}
}
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_MORTAL_WOUND, 10000);
events.ScheduleEvent(EVENT_ENRAGE, 22000);
events.ScheduleEvent(EVENT_DECIMATE, 105000);
events.ScheduleEvent(EVENT_BERSERK, 360000);
events.ScheduleEvent(EVENT_SUMMON_ZOMBIE, 10000);
events.ScheduleEvent(EVENT_CAN_EAT_ZOMBIE, 1000);
}
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() == NPC_ZOMBIE_CHOW)
{
summon->AI()->AttackStart(me);
}
summons.Summon(summon);
}
void SummonedCreatureDies(Creature* cr, Unit*) override { summons.Despawn(cr); }
void KilledUnit(Unit* who) override
{
if (me->IsAlive() && who->GetEntry() == NPC_ZOMBIE_CHOW)
{
me->ModifyHealth(int32(me->GetMaxHealth() * 0.05f));
}
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
bool SelectPlayerInRoom()
{
if (me->IsInCombat())
return false;
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
Player* player = itr.GetSource();
if (!player || !player->IsAlive())
continue;
if (player->GetPositionZ() > 300.0f || me->GetExactDist(player) > 50.0f)
continue;
AttackStart(player);
return true;
}
return false;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictimWithGaze() && !SelectPlayerInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_ENRAGE:
Talk(EMOTE_ENRAGE);
me->CastSpell(me, SPELL_ENRAGE, true);
events.RepeatEvent(22000);
break;
case EVENT_MORTAL_WOUND:
me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false);
events.RepeatEvent(10000);
break;
case EVENT_DECIMATE:
Talk(EMOTE_DECIMATE);
me->CastSpell(me, SPELL_DECIMATE, false);
events.RepeatEvent(105000);
break;
case EVENT_SUMMON_ZOMBIE:
{
uint8 rand = urand(0, 2);
for (int32 i = 0; i < 1; ++i)
{
// In 40 man raid, use all gates
me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[urand(0, 2)]);
(rand == 2 ? rand = 0 : rand++);
}
events.RepeatEvent(10000);
break;
}
case EVENT_CAN_EAT_ZOMBIE:
events.RepeatEvent(1000);
if (me->GetVictim()->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinMeleeRange(me->GetVictim()))
{
me->CastCustomSpell(SPELL_CHOW_SEARCHER, SPELLVALUE_RADIUS_MOD, 20000, me, true);
Talk(EMOTE_DEVOURS_ALL);
return; // leave it to skip DoMeleeAttackIfReady
}
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_gluth_decimate : public SpellScriptLoader
{
public:
spell_gluth_decimate() : SpellScriptLoader("spell_gluth_decimate") { }
class spell_gluth_decimate_SpellScript : public SpellScript
{
PrepareSpellScript(spell_gluth_decimate_SpellScript);
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Unit* unitTarget = GetHitUnit())
{
int32 damage = int32(unitTarget->GetHealth()) - int32(unitTarget->CountPctFromMaxHealth(5));
if (damage <= 0)
return;
if (Creature* cTarget = unitTarget->ToCreature())
{
cTarget->SetWalk(true);
cTarget->GetMotionMaster()->MoveFollow(GetCaster(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED);
cTarget->SetReactState(REACT_PASSIVE);
Unit::DealDamage(GetCaster(), cTarget, damage);
return;
}
GetCaster()->CastCustomSpell(28375, SPELLVALUE_BASE_POINT0, damage, unitTarget);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_gluth_decimate_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_gluth_decimate_SpellScript();
}
};
void AddSC_boss_gluth_40()
{
new boss_gluth_40();
// new spell_gluth_decimate();
}

View File

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

View File

@@ -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 "PassiveAI.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "naxxramas.h"
enum Spells
{
SPELL_POISON_CLOUD = 28240,
SPELL_MUTATING_INJECTION = 28169,
SPELL_MUTATING_EXPLOSION = 28206,
SPELL_SLIME_SPRAY = 28157,
SPELL_POISON_CLOUD_DAMAGE_AURA = 28158,
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 = 351067,
NPC_SEWAGE_SLIME = 351071,
NPC_STICHED_GIANT = 351027
};
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)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
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 EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
PullChamberAdds();
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_POISON_CLOUD, 15000);
events.ScheduleEvent(EVENT_MUTATING_INJECTION, 12000);
events.ScheduleEvent(EVENT_SLIME_SPRAY, 10000);
events.ScheduleEvent(EVENT_BERSERK, 12 * 60 * 1000); // 12 minute enrage
}
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_SLIME_SPRAY && target->GetTypeId() == TYPEID_PLAYER)
{
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->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
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.RepeatEvent(15000);
break;
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_SLIME_SPRAY:
Talk(EMOTE_SLIME);
me->CastSpell(me->GetVictim(), SPELL_SLIME_SPRAY, false);
events.RepeatEvent(20000);
break;
case EVENT_MUTATING_INJECTION:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, 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_40_poison_cloudAI>(pCreature);
}
struct boss_grobbulus_40_poison_cloudAI : public NullCreatureAI
{
explicit boss_grobbulus_40_poison_cloudAI(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->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
if (auraVisualTimer) // this has to be delayed to be visible
{
auraVisualTimer += diff;
if (auraVisualTimer >= 1000)
{
me->CastSpell(me, SPELL_POISON_CLOUD_DAMAGE_AURA, 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 SpellScriptLoader
{
public:
spell_grobbulus_poison() : SpellScriptLoader("spell_grobbulus_poison") { }
class spell_grobbulus_poison_SpellScript : public SpellScript
{
PrepareSpellScript(spell_grobbulus_poison_SpellScript);
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_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_grobbulus_poison_SpellScript();
}
};
class spell_grobbulus_mutating_injection : public SpellScriptLoader
{
public:
spell_grobbulus_mutating_injection() : SpellScriptLoader("spell_grobbulus_mutating_injection") { }
class spell_grobbulus_mutating_injection_AuraScript : public AuraScript
{
PrepareAuraScript(spell_grobbulus_mutating_injection_AuraScript);
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())
{
caster->CastSpell(GetTarget(), SPELL_MUTATING_EXPLOSION, true);
}
break;
default:
return;
}
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_grobbulus_mutating_injection_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_grobbulus_mutating_injection_AuraScript();
}
};
void AddSC_boss_grobbulus_40()
{
new boss_grobbulus_40();
new boss_grobbulus_poison_cloud_40();
// new spell_grobbulus_mutating_injection();
// new spell_grobbulus_poison();
}

View File

@@ -0,0 +1,251 @@
/*
* 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 "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_SPELL_DISRUPTION = 29310,
SPELL_DECREPIT_FEVER = 29998,
SPELL_PLAGUE_CLOUD = 29350,
SPELL_TELEPORT_SELF = 30211
};
enum Events
{
EVENT_DISRUPTION = 1,
EVENT_DECEPIT_FEVER = 2,
EVENT_ERUPT_SECTION = 3,
EVENT_SWITCH_PHASE = 4,
EVENT_SAFETY_DANCE = 5,
EVENT_PLAGUE_CLOUD = 6
};
enum Misc
{
PHASE_SLOW_DANCE = 0,
PHASE_FAST_DANCE = 1
};
class boss_heigan_40 : public CreatureScript
{
public:
boss_heigan_40() : CreatureScript("boss_heigan_40") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_heigan_40AI>(pCreature);
}
struct boss_heigan_40AI : public BossAI
{
explicit boss_heigan_40AI(Creature* c) : BossAI(c, BOSS_HEIGAN)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
uint8 currentPhase{};
uint8 currentSection{};
bool moveRight{};
void Reset() override
{
BossAI::Reset();
events.Reset();
currentPhase = 0;
currentSection = 3;
moveRight = true;
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HEIGAN_ENTER_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(EMOTE_DEATH);
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
me->SetInCombatWithZone();
Talk(SAY_AGGRO);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_HEIGAN_ENTER_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
StartFightPhase(PHASE_SLOW_DANCE);
}
void 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, urand(12000, 15000));
events.ScheduleEvent(EVENT_DECEPIT_FEVER, 17000);
events.ScheduleEvent(EVENT_ERUPT_SECTION, 15000);
events.ScheduleEvent(EVENT_SWITCH_PHASE, 90000);
}
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, 1000);
events.ScheduleEvent(EVENT_ERUPT_SECTION, 7000);
events.ScheduleEvent(EVENT_SWITCH_PHASE, 45000);
}
events.ScheduleEvent(EVENT_SAFETY_DANCE, 5000);
}
bool IsInRoom(Unit* who)
{
if (who->GetPositionX() > 2826 || who->GetPositionX() < 2723 || who->GetPositionY() > -3641 || who->GetPositionY() < -3736)
{
if (who->GetGUID() == me->GetGUID())
EnterEvadeMode();
return false;
}
return true;
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom(me))
return;
if (!UpdateVictim())
return;
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_DISRUPTION:
me->CastSpell(me, SPELL_SPELL_DISRUPTION, false);
events.RepeatEvent(10000);
break;
case EVENT_DECEPIT_FEVER:
me->CastSpell(me, SPELL_DECREPIT_FEVER, false);
events.RepeatEvent(urand(22000, 25000));
break;
case EVENT_PLAGUE_CLOUD:
me->CastSpell(me, SPELL_PLAGUE_CLOUD, false);
break;
case EVENT_SWITCH_PHASE:
if (currentPhase == PHASE_SLOW_DANCE)
{
StartFightPhase(PHASE_FAST_DANCE);
}
else
{
StartFightPhase(PHASE_SLOW_DANCE);
Talk(EMOTE_DANCE_END); // avoid play the emote on aggro
}
break;
case EVENT_ERUPT_SECTION:
if (pInstance)
{
pInstance->SetData(DATA_HEIGAN_ERUPTION, currentSection);
if (currentSection == 3)
{
moveRight = false;
}
else if (currentSection == 0)
{
moveRight = true;
}
moveRight ? currentSection++ : currentSection--;
}
if (currentPhase == PHASE_SLOW_DANCE)
{
Talk(SAY_TAUNT);
}
events.RepeatEvent(currentPhase == PHASE_SLOW_DANCE ? 10000 : 4000);
break;
case EVENT_SAFETY_DANCE:
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
if (IsInRoom(itr.GetSource()) && !itr.GetSource()->IsAlive())
{
pInstance->SetData(DATA_DANCE_FAIL, 0);
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
return;
}
}
events.RepeatEvent(5000);
return;
}
}
DoMeleeAttackIfReady();
}
};
};
void AddSC_boss_heigan_40()
{
new boss_heigan_40();
}

View File

@@ -0,0 +1,750 @@
/*
* 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
{
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 = 28478,
SPELL_FROST_BOLT_MULTI = 28479,
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 = 351073,
NPC_UNSTOPPABLE_ABOMINATION = 351074,
NPC_SOUL_WEAVER = 351075,
NPC_GUARDIAN_OF_ICECROWN = 351076,
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)
{
pInstance = me->GetInstanceScript();
_justSpawned = true;
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
bool _justSpawned;
float NormalizeOrientation(float o)
{
return std::fmod(o, 2.0f * static_cast<float>(M_PI)); // Only positive values will be passed
}
void SpawnHelpers()
{
// spawn at gate
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3656.19f, -5093.78f, 143.33f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo center
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3657.94f, -5087.68f, 143.60f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo left
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3655.48f, -5100.05f, 143.53f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo right
me->SummonCreature(NPC_SOUL_WEAVER, 3651.73f, -5092.62f, 143.38f, 6.05, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // soul behind
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3660.17f, -5092.45f, 143.37f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.39f, -5096.21f, 143.29f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front right
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.29f, -5090.19f, 143.48f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske left left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3657.43f, -5098.03f, 143.41f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3654.36f, -5090.51f, 143.48f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske behind left
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3653.35f, -5095.91f, 143.41f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right
// 6 rooms, 8 soldiers, 3 abominations and 1 weaver in each room | middle positions in table starts from 6
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 0; j < 8; ++j)
{
float angle = M_PI * 2 / 8 * j;
me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 1; j < 4; ++j)
{
float dist = j == 2 ? 0.0f : 8.0f; // second in middle
float angle = SummonGroups[i].GetOrientation() + M_PI * 2 / 4 * j;
me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, SummonGroups[i].GetPositionX() + dist * cos(angle), SummonGroups[i].GetPositionY() + dist * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
for (uint8 i = 6; i < 12; ++i)
{
for (uint8 j = 0; j < 1; ++j)
{
float angle = SummonGroups[i].GetOrientation() + M_PI;
me->SummonCreature(NPC_SOUL_WEAVER, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ() + 0.5f, SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000);
}
}
}
void SummonHelper(uint32 entry, uint32 count)
{
for (uint8 i = 0; i < count; ++i)
{
if (Creature* cr = me->SummonCreature(entry, SpawnPool[urand(0, 6)], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000))
{
if (Unit* target = SelectTargetFromPlayerList(100.0f))
{
cr->AI()->DoAction(ACTION_CALL_HELP_OFF);
cr->AI()->AttackStart(target);
}
}
}
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetReactState(REACT_AGGRESSIVE);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
go->SetPhaseMask(1, true);
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
if(!_justSpawned) // Don't open the door if we just spawned and are still doing the conversation
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
_justSpawned = false;
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_1)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_2)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_3)))
{
go->SetGoState(GO_STATE_READY);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_4)))
{
go->SetGoState(GO_STATE_READY);
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
ScriptedAI::EnterEvadeMode(why);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DoAction(ACTION_GUARDIANS_OFF);
if (Creature* guardian = summons.GetCreatureWithEntry(NPC_GUARDIAN_OF_ICECROWN))
{
guardian->AI()->Talk(EMOTE_GUARDIAN_FLEE);
}
Talk(SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!me->IsInCombat() && who->GetTypeId() == TYPEID_PLAYER && who->IsAlive() && me->GetDistance(who) <= 50.0f)
AttackStart(who);
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(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, 5000);
events.ScheduleEvent(EVENT_SUMMON_SOLDIER, 6400);
events.ScheduleEvent(EVENT_SUMMON_UNSTOPPABLE_ABOMINATION, 10000);
events.ScheduleEvent(EVENT_SUMMON_SOUL_WEAVER, 12000);
events.ScheduleEvent(EVENT_PHASE_2, 228000);
events.ScheduleEvent(EVENT_ENRAGE, 900000);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15000);
go->SetGoState(GO_STATE_ACTIVE);
}
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
if (!cr->IsInCombat())
{
cr->GetMotionMaster()->MoveRandom(5);
}
if (cr->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
cr->SetHomePosition(cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ(), cr->GetOrientation());
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (!me->HasAura(SPELL_KELTHUZAD_CHANNEL))
{
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
switch (events.ExecuteEvent())
{
case EVENT_FLOOR_CHANGE:
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_FLOOR)))
{
events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15000);
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.RepeatEvent(3100);
break;
case EVENT_SUMMON_UNSTOPPABLE_ABOMINATION:
SummonHelper(NPC_UNSTOPPABLE_ABOMINATION, 1);
events.RepeatEvent(18500);
break;
case EVENT_SUMMON_SOUL_WEAVER:
SummonHelper(NPC_SOUL_WEAVER, 1);
events.RepeatEvent(30000);
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, urand(2000, 10000));
events.ScheduleEvent(EVENT_FROST_BOLT_MULTI, urand(15000, 30000));
events.ScheduleEvent(EVENT_DETONATE_MANA, 30000);
events.ScheduleEvent(EVENT_PHASE_3, 1000);
events.ScheduleEvent(EVENT_SHADOW_FISSURE, 25000);
events.ScheduleEvent(EVENT_FROST_BLAST, 45000);
if (Is25ManRaid())
{
events.ScheduleEvent(EVENT_CHAINS, 90000);
}
break;
case EVENT_ENRAGE:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_FROST_BOLT_SINGLE:
me->CastSpell(me->GetVictim(), SPELL_FROST_BOLT_SINGLE, false);
events.RepeatEvent(urand(2000, 10000));
break;
case EVENT_FROST_BOLT_MULTI:
me->CastSpell(me, SPELL_FROST_BOLT_MULTI, false);
events.RepeatEvent(urand(15000, 30000));
break;
case EVENT_SHADOW_FISSURE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
me->CastSpell(target, SPELL_SHADOW_FISURE, false);
}
events.RepeatEvent(25000);
break;
case EVENT_FROST_BLAST:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0, true))
{
me->CastSpell(target, SPELL_FROST_BLAST, false);
}
Talk(SAY_FROST_BLAST);
events.RepeatEvent(45000);
break;
case EVENT_CHAINS:
for (uint8 i = 0; i < 3; ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 200, true, -SPELL_CHAINS_OF_KELTHUZAD))
{
me->CastSpell(target, SPELL_CHAINS_OF_KELTHUZAD, true);
}
}
Talk(SAY_CHAIN);
events.RepeatEvent(90000);
break;
case EVENT_DETONATE_MANA:
{
std::vector<Unit*> unitList;
ThreatContainer::StorageType const& threatList = me->GetThreatMgr().getThreatList();
for (auto itr : threatList)
{
if (itr->getTarget()->GetTypeId() == TYPEID_PLAYER
&& itr->getTarget()->getPowerType() == POWER_MANA
&& itr->getTarget()->GetPower(POWER_MANA))
{
unitList.push_back(itr->getTarget());
}
}
if (!unitList.empty())
{
auto itr = unitList.begin();
advance(itr, urand(0, unitList.size() - 1));
me->CastSpell(*itr, SPELL_DETONATE_MANA, false);
Talk(SAY_SPECIAL);
}
events.RepeatEvent(30000);
break;
}
case EVENT_PHASE_3:
if (me->HealthBelowPct(45))
{
Talk(SAY_REQUEST_AID);
events.DelayEvents(5500);
events.ScheduleEvent(EVENT_P3_LICH_KING_SAY, 5000);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_1)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_2)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_3)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_KELTHUZAD_PORTAL_4)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
break;
}
events.RepeatEvent(1000);
break;
case EVENT_P3_LICH_KING_SAY:
if (pInstance)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_LICH_KING_BOSS)))
{
cr->AI()->Talk(SAY_ANSWER_REQUEST);
}
}
for (uint8 i = 0 ; i < 5; ++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->GetTypeId() != TYPEID_PLAYER && !who->IsPet())
return;
ScriptedAI::MoveInLineOfSight(who);
}
void JustDied(Unit* /*killer*/) override
{
if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_ABOMINATION_KILLED, 0);
}
}
void AttackStart(Unit* who) override
{
ScriptedAI::AttackStart(who);
if (callHelp)
{
std::list<Creature*> targets;
me->GetCreaturesWithEntryInRange(targets, 15.0f, me->GetEntry());
for (std::list<Creature*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
{
if ((*itr)->GetGUID() != me->GetGUID())
{
(*itr)->ToCreature()->AI()->DoAction(ACTION_CALL_HELP_OFF);
(*itr)->ToCreature()->AI()->AttackStart(who);
}
}
}
if (me->GetEntry() != NPC_UNSTOPPABLE_ABOMINATION && me->GetEntry() != NPC_GUARDIAN_OF_ICECROWN)
{
me->AddThreat(who, 1000000.0f);
}
}
void EnterCombat(Unit* /*who*/) override
{
me->SetInCombatWithZone();
if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION)
{
events.ScheduleEvent(EVENT_MINION_FRENZY, 1000);
events.ScheduleEvent(EVENT_MINION_MORTAL_WOUND, 5000);
}
else if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
events.ScheduleEvent(EVENT_MINION_BLOOD_TAP, 15000);
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustReachedHome() override
{
if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN)
{
me->DespawnOrUnsummon();
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_MINION_MORTAL_WOUND:
me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false);
events.RepeatEvent(15000);
break;
case EVENT_MINION_FRENZY:
if (me->HealthBelowPct(35))
{
me->CastSpell(me, SPELL_FRENZY, true);
break;
}
events.RepeatEvent(1000);
break;
case EVENT_MINION_BLOOD_TAP:
me->CastSpell(me->GetVictim(), SPELL_BLOOD_TAP, false);
events.RepeatEvent(15000);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_kelthuzad_frost_blast : public SpellScriptLoader
{
public:
spell_kelthuzad_frost_blast() : SpellScriptLoader("spell_kelthuzad_frost_blast") { }
class spell_kelthuzad_frost_blast_SpellScript : public SpellScript
{
PrepareSpellScript(spell_kelthuzad_frost_blast_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
if (!caster || !caster->ToCreature())
return;
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (!target->ToUnit()->HasAura(SPELL_FROST_BLAST))
{
tmplist.push_back(target);
}
}
targets.clear();
for (auto& itr : tmplist)
{
targets.push_back(itr);
}
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kelthuzad_frost_blast_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_kelthuzad_frost_blast_SpellScript();
}
};
class spell_kelthuzad_detonate_mana : public SpellScriptLoader
{
public:
spell_kelthuzad_detonate_mana() : SpellScriptLoader("spell_kelthuzad_detonate_mana") { }
class spell_kelthuzad_detonate_mana_AuraScript : public AuraScript
{
PrepareAuraScript(spell_kelthuzad_detonate_mana_AuraScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_MANA_DETONATION_DAMAGE });
}
void HandleScript(AuraEffect const* aurEff)
{
PreventDefaultAction();
Unit* target = GetTarget();
if (auto mana = int32(target->GetMaxPower(POWER_MANA) / 10))
{
mana = target->ModifyPower(POWER_MANA, -mana);
target->CastCustomSpell(SPELL_MANA_DETONATION_DAMAGE, SPELLVALUE_BASE_POINT0, -mana * 10, target, true, nullptr, aurEff);
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_kelthuzad_detonate_mana_AuraScript::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_kelthuzad_detonate_mana_AuraScript();
}
};
void AddSC_boss_kelthuzad_40()
{
new boss_kelthuzad_40();
new boss_kelthuzad_minion_40();
// new spell_kelthuzad_frost_blast();
// new spell_kelthuzad_detonate_mana();
}

View File

@@ -0,0 +1,201 @@
/*
* 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 "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Spells
{
SPELL_NECROTIC_AURA = 55593,
SPELL_SUMMON_SPORE = 29234,
SPELL_DEATHBLOOM = 29865,
SPELL_INEVITABLE_DOOM = 29204,
SPELL_BERSERK = 26662
};
enum Events
{
EVENT_NECROTIC_AURA = 1,
EVENT_DEATHBLOOM = 2,
EVENT_INEVITABLE_DOOM = 3,
EVENT_BERSERK = 4,
EVENT_SUMMON_SPORE = 5,
EVENT_NECROTIC_AURA_FADING = 6,
EVENT_NECROTIC_AURA_REMOVED = 7
};
enum Texts
{
SAY_NECROTIC_AURA_APPLIED = 0,
SAY_NECROTIC_AURA_REMOVED = 1,
SAY_NECROTIC_AURA_FADING = 2
};
class boss_loatheb_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)
{
pInstance = me->GetInstanceScript();
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
InstanceScript* pInstance;
uint8 doomCounter;
EventMap events;
SummonList summons;
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
doomCounter = 0;
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, NOT_STARTED);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_LOATHEB_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override
{
cr->SetInCombatWithZone();
summons.Summon(cr);
}
void SummonedCreatureDies(Creature* /*cr*/, Unit*) override
{
if (pInstance)
{
pInstance->SetData(DATA_SPORE_KILLED, 0);
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_NECROTIC_AURA, 10000);
events.ScheduleEvent(EVENT_DEATHBLOOM, 5000);
events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 120000);
events.ScheduleEvent(EVENT_SUMMON_SPORE, 15000);
events.ScheduleEvent(EVENT_BERSERK, 720000);
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, IN_PROGRESS);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_LOATHEB_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
if (pInstance)
{
pInstance->SetData(BOSS_LOATHEB, DONE);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || !IsInRoom())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SUMMON_SPORE:
me->CastSpell(me, SPELL_SUMMON_SPORE, true);
events.RepeatEvent(35000);
break;
case EVENT_NECROTIC_AURA:
me->CastSpell(me, SPELL_NECROTIC_AURA, true);
Talk(SAY_NECROTIC_AURA_APPLIED);
events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14000);
events.ScheduleEvent(EVENT_NECROTIC_AURA_REMOVED, 17000);
events.RepeatEvent(20000);
break;
case EVENT_DEATHBLOOM:
me->CastSpell(me, SPELL_DEATHBLOOM, false);
events.RepeatEvent(30000);
break;
case EVENT_INEVITABLE_DOOM:
me->CastSpell(me, SPELL_INEVITABLE_DOOM, false);
doomCounter++;
events.RepeatEvent(doomCounter < 6 ? 30000 : 15000);
break;
case EVENT_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_NECROTIC_AURA_FADING:
Talk(SAY_NECROTIC_AURA_FADING);
break;
case EVENT_NECROTIC_AURA_REMOVED:
Talk(SAY_NECROTIC_AURA_REMOVED);
break;
}
DoMeleeAttackIfReady();
}
bool IsInRoom()
{
// Calculate the distance between his home position to the gate
if (me->GetExactDist(me->GetHomePosition().GetPositionX(),
me->GetHomePosition().GetPositionY(),
me->GetHomePosition().GetPositionZ()) > 50.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
};
};
void AddSC_boss_loatheb_40()
{
new boss_loatheb_40();
}

View File

@@ -0,0 +1,265 @@
/*
* 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 "PassiveAI.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Spells
{
SPELL_WEB_WRAP = 28622,
SPELL_WEB_SPRAY = 29484,
SPELL_POISON_SHOCK = 28741,
SPELL_NECROTIC_POISON = 54121,
SPELL_FRENZY = 54123,
};
enum Events
{
EVENT_WEB_SPRAY = 1,
EVENT_POISON_SHOCK = 2,
EVENT_NECROTIC_POISON = 3,
EVENT_WEB_WRAP = 4,
EVENT_HEALTH_CHECK = 5,
EVENT_SUMMON_SPIDERLINGS = 6
};
enum Emotes
{
EMOTE_SPIDERS = 0,
EMOTE_WEB_WRAP = 1,
EMOTE_WEB_SPRAY = 2
};
enum Misc
{
NPC_WEB_WRAP = 351079,
NPC_MAEXXNA_SPIDERLING = 351088
};
const Position PosWrap[3] =
{
{3546.796f, -3869.082f, 296.450f, 0.0f},
{3531.271f, -3847.424f, 299.450f, 0.0f},
{3497.067f, -3843.384f, 302.384f, 0.0f}
};
class boss_maexxna_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)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
bool IsInRoom()
{
if (me->GetExactDist(3486.6f, -3890.6f, 291.8f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_MAEXXNA_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_WEB_WRAP, 20000);
events.ScheduleEvent(EVENT_WEB_SPRAY, 40000);
events.ScheduleEvent(EVENT_POISON_SHOCK, 10000);
events.ScheduleEvent(EVENT_NECROTIC_POISON, 5000);
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1000);
events.ScheduleEvent(EVENT_SUMMON_SPIDERLINGS, 30000);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_MAEXXNA_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* cr) override
{
if (cr->GetEntry() == NPC_MAEXXNA_SPIDERLING)
{
cr->SetInCombatWithZone();
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
cr->AI()->AttackStart(target);
}
}
summons.Summon(cr);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
summons.DespawnAll();
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_WEB_SPRAY:
Talk(EMOTE_WEB_SPRAY);
me->CastSpell(me, SPELL_WEB_SPRAY, true);
events.RepeatEvent(40000);
break;
case EVENT_POISON_SHOCK:
me->CastSpell(me->GetVictim(), SPELL_POISON_SHOCK, false);
events.RepeatEvent(10000);
break;
case EVENT_NECROTIC_POISON:
me->CastSpell(me->GetVictim(), SPELL_NECROTIC_POISON, false);
events.RepeatEvent(30000);
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.RepeatEvent(40000);
break;
case EVENT_HEALTH_CHECK:
if (me->GetHealthPct() < 30)
{
me->CastSpell(me, SPELL_FRENZY, true);
break;
}
events.RepeatEvent(1000);
break;
case EVENT_WEB_WRAP:
Talk(EMOTE_WEB_WRAP);
for (uint8 i = 0; i < 2; ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0, true, -SPELL_WEB_WRAP))
{
target->RemoveAura(SPELL_WEB_SPRAY);
uint8 pos = urand(0, 2);
if (Creature* wrap = me->SummonCreature(NPC_WEB_WRAP, PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
wrap->AI()->SetGUID(target->GetGUID());
target->GetMotionMaster()->MoveJump(PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 20, 20);
}
}
}
events.RepeatEvent(40000);
break;
}
DoMeleeAttackIfReady();
}
};
};
class boss_maexxna_webwrap : public CreatureScript
{
public:
boss_maexxna_webwrap() : CreatureScript("boss_maexxna_webwrap") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_maexxna_webwrapAI>(pCreature);
}
struct boss_maexxna_webwrapAI : public NullCreatureAI
{
explicit boss_maexxna_webwrapAI(Creature* c) : NullCreatureAI(c) {}
ObjectGuid victimGUID;
void SetGUID(ObjectGuid guid, int32 /*param*/) override
{
victimGUID = guid;
if (me->m_spells[0] && victimGUID)
{
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
{
victim->CastSpell(victim, me->m_spells[0], true, nullptr, nullptr, me->GetGUID());
}
}
}
void JustDied(Unit* /*killer*/) override
{
if (me->m_spells[0] && victimGUID)
{
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
{
victim->RemoveAurasDueToSpell(me->m_spells[0], me->GetGUID());
}
}
}
};
};
void AddSC_boss_maexxna_40()
{
new boss_maexxna_40();
// new boss_maexxna_webwrap();
}

View File

@@ -0,0 +1,303 @@
/*
* 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 "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Says
{
SAY_AGGRO = 0,
SAY_SUMMON = 1,
SAY_SLAY = 2,
SAY_DEATH = 3,
EMOTE_SUMMON = 4,
EMOTE_SUMMON_WAVE = 5,
EMOTE_TELEPORT_BALCONY = 6,
EMOTE_TELEPORT_BACK = 7,
EMOTE_BLINK = 8
};
enum Spells
{
SPELL_CURSE_OF_THE_PLAGUEBRINGER = 29213,
SPELL_CRIPPLE = 29212,
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 = 351088,
NPC_PLAGUED_CHAMPION = 351087,
NPC_PLAGUED_GUARDIAN = 351086
};
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)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
uint8 timesInBalcony;
EventMap events;
SummonList summons;
void StartGroundPhase()
{
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->SetControlled(false, UNIT_STATE_ROOT);
events.Reset();
events.ScheduleEvent(EVENT_MOVE_TO_BALCONY, 110000);
events.ScheduleEvent(EVENT_CURSE, 15000);
events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE, 10000);
if (Is25ManRaid())
{
events.ScheduleEvent(EVENT_BLINK, 26000);
}
}
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, 4000);
events.ScheduleEvent(EVENT_MOVE_TO_GROUND, 70000);
}
void SummonHelper(uint32 entry, uint32 count)
{
for (uint8 i = 0; i < count; ++i)
{
me->SummonCreature(entry, summoningPosition[urand(0, 4)]);
}
}
bool IsInRoom()
{
if (me->GetExactDist(2684.8f, -3502.5f, 261.3f) > 80.0f)
{
EnterEvadeMode(EVADE_REASON_OTHER);
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->CastSpell(me, SPELL_TELEPORT_BACK, true);
me->SetControlled(false, UNIT_STATE_ROOT);
me->SetReactState(REACT_AGGRESSIVE);
timesInBalcony = 0;
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void EnterEvadeMode(EvadeReason why) override
{
me->SetControlled(false, UNIT_STATE_ROOT);
ScriptedAI::EnterEvadeMode(why);
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
Talk(SAY_AGGRO);
StartGroundPhase();
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->SetInCombatWithZone();
}
void JustDied(Unit* killer) override
{
if (me->GetPositionZ() > 270.27f)
{
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE);
me->NearTeleportTo(nothPosition.GetPositionX(), nothPosition.GetPositionY(), nothPosition.GetPositionZ(), nothPosition.GetOrientation(), true);
}
BossAI::JustDied(killer);
Talk(SAY_DEATH);
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_NOTH_ENTRY_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
// GROUND
case EVENT_CURSE:
if (events.GetPhaseMask() == 0)
{
me->CastCustomSpell(SPELL_CURSE_OF_THE_PLAGUEBRINGER, SPELLVALUE_MAX_TARGETS, 10, me, false);
}
events.RepeatEvent(25000);
break;
case EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE:
Talk(SAY_SUMMON);
Talk(EMOTE_SUMMON);
events.RepeatEvent(30000);
events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_REAL, 4000);
break;
case EVENT_SUMMON_PLAGUED_WARRIOR_REAL:
me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true);
SummonHelper(NPC_PLAGUED_WARRIOR, 3);
break;
case EVENT_MOVE_TO_BALCONY:
Talk(EMOTE_TELEPORT_BALCONY);
me->CastSpell(me, SPELL_TELEPORT, true);
StartBalconyPhase();
break;
case EVENT_BLINK:
DoResetThreat();
me->CastSpell(me, SPELL_CRIPPLE, false);
me->CastSpell(me, SPELL_BLINK, true);
Talk(EMOTE_BLINK);
events.RepeatEvent(30000);
break;
// BALCONY
case EVENT_BALCONY_SUMMON_ANNOUNCE:
Talk(EMOTE_SUMMON_WAVE);
events.RepeatEvent(30000);
events.ScheduleEvent(EVENT_BALCONY_SUMMON_REAL, 4000);
break;
case EVENT_BALCONY_SUMMON_REAL:
me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true); // visual
switch (timesInBalcony)
{
case 0:
SummonHelper(NPC_PLAGUED_CHAMPION, 4);
break;
case 1:
SummonHelper(NPC_PLAGUED_CHAMPION, 2);
SummonHelper(NPC_PLAGUED_GUARDIAN, 2);
break;
default:
SummonHelper(NPC_PLAGUED_GUARDIAN, 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();
}

View File

@@ -0,0 +1,190 @@
/*
* 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 "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Yells
{
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_DEATH = 2,
EMOTE_BERSERK = 3,
EMOTE_ENRAGE = 4
};
enum Spells
{
SPELL_HATEFUL_STRIKE = 28308,
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
};
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)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
InstanceScript* pInstance;
void Reset() override
{
BossAI::Reset();
events.Reset();
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (!urand(0, 3))
{
Talk(SAY_SLAY);
}
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
Talk(SAY_AGGRO);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_HATEFUL_STRIKE, 1200);
events.ScheduleEvent(EVENT_BERSERK, 7 * 60 * 1000); // 7 minutes enrange
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1000);
}
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)
{
me->CastSpell(finalTarget, SPELL_HATEFUL_STRIKE, false);
}
events.RepeatEvent(1200);
break;
}
case EVENT_BERSERK:
Talk(EMOTE_BERSERK);
me->CastSpell(me, SPELL_BERSERK, true);
events.ScheduleEvent(EVENT_SLIME_BOLT, 3000);
break;
case EVENT_SLIME_BOLT:
me->CastSpell(me, SPELL_SLIME_BOLT, false);
events.RepeatEvent(3000);
break;
case EVENT_HEALTH_CHECK:
if (me->GetHealthPct() <= 5)
{
Talk(EMOTE_ENRAGE);
me->CastSpell(me, SPELL_FRENZY, true);
break;
}
events.RepeatEvent(1000);
break;
}
DoMeleeAttackIfReady();
}
};
};
void AddSC_boss_patchwerk_40()
{
new boss_patchwerk_40();
}

View File

@@ -0,0 +1,232 @@
/*
* 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 "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "naxxramas.h"
enum Says
{
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_TAUNTED = 2,
SAY_DEATH = 3
};
enum Spells
{
SPELL_UNBALANCING_STRIKE = 26613,
SPELL_DISRUPTING_SHOUT = 29107,
SPELL_JAGGED_KNIFE = 55550,
SPELL_HOPELESS = 29125,
SPELL_TAUNT = 29060
};
enum Events
{
EVENT_UNBALANCING_STRIKE = 1,
EVENT_DISRUPTING_SHOUT = 2,
EVENT_JAGGED_KNIFE = 3
};
enum Misc
{
NPC_DEATH_KNIGHT_UNDERSTUDY = 351084,
NPC_RAZUVIOUS = 351036
};
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)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
SummonList summons;
InstanceScript* pInstance;
void SpawnHelpers()
{
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2762.23f, -3085.07f, 267.685f, 1.95f);
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2758.24f, -3110.97f, 267.685f, 3.94f);
if (Is25ManRaid())
{
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2782.45f, -3088.03f, 267.685f, 0.75f);
me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2778.56f, -3113.74f, 267.685f, 5.28f);
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void Reset() override
{
BossAI::Reset();
summons.DespawnAll();
events.Reset();
SpawnHelpers();
}
void KilledUnit(Unit* who) override
{
if (roll_chance_i(30))
{
Talk(SAY_SLAY);
}
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
// Damage done by the controlled Death Knight understudies should also count toward damage done by players
if(who && who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_DEATH_KNIGHT_UNDERSTUDY)
{
me->LowerPlayerDamageReq(damage);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
me->CastSpell(me, SPELL_HOPELESS, true);
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (spell->Id == SPELL_TAUNT)
{
Talk(SAY_TAUNTED, caster);
}
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, 20000);
events.ScheduleEvent(EVENT_DISRUPTING_SHOUT, 15000);
events.ScheduleEvent(EVENT_JAGGED_KNIFE, 10000);
summons.DoZoneInCombat();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_UNBALANCING_STRIKE:
me->CastSpell(me->GetVictim(), SPELL_UNBALANCING_STRIKE, false);
events.RepeatEvent(20000);
break;
case EVENT_DISRUPTING_SHOUT:
me->CastSpell(me, SPELL_DISRUPTING_SHOUT, false);
events.RepeatEvent(15000);
break;
case EVENT_JAGGED_KNIFE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f))
{
me->CastSpell(target, SPELL_JAGGED_KNIFE, false);
}
events.RepeatEvent(10000);
break;
}
DoMeleeAttackIfReady();
}
};
};
class boss_razuvious_minion_40 : public CreatureScript
{
public:
boss_razuvious_minion_40() : CreatureScript("boss_razuvious_minion_40") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetNaxxramasAI<boss_razuvious_minion_40AI>(pCreature);
}
struct boss_razuvious_minion_40AI : public ScriptedAI
{
explicit boss_razuvious_minion_40AI(Creature* c) : ScriptedAI(c) { }
EventMap events;
void Reset() override
{
events.Reset();
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && me->GetInstanceScript())
{
me->GetInstanceScript()->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void EnterCombat(Unit* who) override
{
if (Creature* cr = me->FindNearestCreature(NPC_RAZUVIOUS, 100.0f))
{
cr->SetInCombatWithZone();
cr->AI()->AttackStart(who);
}
}
void UpdateAI(uint32 diff) override
{
if (UpdateVictim())
{
events.Update(diff);
if (!me->HasUnitState(UNIT_STATE_CASTING) || !me->IsCharmed())
{
DoMeleeAttackIfReady();
}
}
}
};
};
void AddSC_boss_razuvious_40()
{
new boss_razuvious_40();
new boss_razuvious_minion_40();
}

View File

@@ -0,0 +1,462 @@
/*
* 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
};
enum Misc
{
GO_ICE_BLOCK = 181247,
NPC_BLIZZARD = 16474,
POINT_CENTER = 1
};
enum Events
{
EVENT_BERSERK = 1,
EVENT_CLEAVE = 2,
EVENT_TAIL_SWEEP = 3,
EVENT_LIFE_DRAIN = 4,
EVENT_BLIZZARD = 5,
EVENT_FLIGHT_START = 6,
EVENT_FLIGHT_LIFTOFF = 7,
EVENT_FLIGHT_ICEBOLT = 8,
EVENT_FLIGHT_BREATH = 9,
EVENT_FLIGHT_SPELL_EXPLOSION = 10,
EVENT_FLIGHT_START_LAND = 11,
EVENT_LAND = 12,
EVENT_GROUND = 13,
EVENT_HUNDRED_CLUB = 14
};
class boss_sapphiron_40 : public CreatureScript
{
public:
boss_sapphiron_40() : CreatureScript("boss_sapphiron_40") { }
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)
{
pInstance = me->GetInstanceScript();
}
EventMap events;
InstanceScript* pInstance;
uint8 iceboltCount{};
uint32 spawnTimer{};
GuidList blockList;
ObjectGuid currentTarget;
void InitializeAI() override
{
me->SummonGameObject(GO_SAPPHIRON_BIRTH, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, 0, 0, 0, 0, 0);
me->SetVisible(false);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
ScriptedAI::InitializeAI();
}
bool IsInRoom()
{
if (me->GetExactDist(3523.5f, -5235.3f, 137.6f) > 100.0f)
{
EnterEvadeMode();
return false;
}
return true;
}
void Reset() override
{
BossAI::Reset();
if (me->IsVisible())
{
me->SetReactState(REACT_AGGRESSIVE);
}
events.Reset();
iceboltCount = 0;
spawnTimer = 0;
currentTarget.Clear();
blockList.clear();
}
void EnterCombatSelfFunction()
{
Map::PlayerList const& PlList = me->GetMap()->GetPlayers();
if (PlList.IsEmpty())
return;
for (const auto& i : PlList)
{
if (Player* player = i.GetSource())
{
if (player->IsGameMaster())
continue;
if (player->IsAlive() && me->GetDistance(player) < 80.0f)
{
me->SetInCombatWith(player);
player->SetInCombatWith(me);
me->AddThreat(player, 0.0f);
}
}
}
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(who);
EnterCombatSelfFunction();
me->CastSpell(me, SPELL_FROST_AURA, true);
events.ScheduleEvent(EVENT_BERSERK, 900000);
events.ScheduleEvent(EVENT_CLEAVE, 5000);
events.ScheduleEvent(EVENT_TAIL_SWEEP, 10000);
events.ScheduleEvent(EVENT_LIFE_DRAIN, 17000);
events.ScheduleEvent(EVENT_BLIZZARD, 17000);
events.ScheduleEvent(EVENT_FLIGHT_START, 45000);
events.ScheduleEvent(EVENT_HUNDRED_CLUB, 5000);
}
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, 500);
}
}
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_ICEBOLT_CAST)
{
me->CastSpell(target, SPELL_ICEBOLT_TRIGGER, true);
}
}
bool IsValidExplosionTarget(WorldObject* target)
{
for (ObjectGuid const& guid : blockList)
{
if (target->GetGUID() == guid)
return false;
if (Unit* block = ObjectAccessor::GetUnit(*me, guid))
{
if (block->IsInBetween(me, target, 2.0f) && block->IsWithinDist(target, 10.0f))
return false;
}
}
return true;
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER && pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void UpdateAI(uint32 diff) override
{
if (spawnTimer)
{
spawnTimer += diff;
if (spawnTimer >= 21500)
{
me->SetVisible(true);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_AGGRESSIVE);
spawnTimer = 0;
}
return;
}
if (!IsInRoom())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_BERSERK:
Talk(EMOTE_ENRAGE);
me->CastSpell(me, SPELL_BERSERK, true);
return;
case EVENT_CLEAVE:
me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
events.RepeatEvent(10000);
return;
case EVENT_TAIL_SWEEP:
me->CastSpell(me, SPELL_TAIL_SWEEP, false);
events.RepeatEvent(10000);
return;
case EVENT_LIFE_DRAIN:
me->CastCustomSpell(SPELL_LIFE_DRAIN, SPELLVALUE_MAX_TARGETS, 5, me, false);
events.RepeatEvent(24000);
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(6500);
return;
}
case EVENT_FLIGHT_START:
if (me->HealthBelowPct(11))
{
return;
}
events.RepeatEvent(45000);
events.DelayEvents(35000);
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, 3000);
iceboltCount = 3;
return;
case EVENT_FLIGHT_ICEBOLT:
{
if (currentTarget)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, currentTarget))
{
me->SummonGameObject(GO_ICE_BLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0);
}
}
std::vector<Unit*> targets;
auto i = me->GetThreatMgr().getThreatList().begin();
for (; i != me->GetThreatMgr().getThreatList().end(); ++i)
{
if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER)
{
bool inList = false;
if (!blockList.empty())
{
for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
{
if ((*i)->getTarget()->GetGUID() == *itr)
{
inList = true;
break;
}
}
}
if (!inList)
{
targets.push_back((*i)->getTarget());
}
}
}
if (!targets.empty() && iceboltCount)
{
auto itr = targets.begin();
advance(itr, urand(0, targets.size() - 1));
me->CastSpell(*itr, SPELL_ICEBOLT_CAST, false);
blockList.push_back((*itr)->GetGUID());
currentTarget = (*itr)->GetGUID();
--iceboltCount;
events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, (me->GetExactDist(*itr) / 13.0f)*IN_MILLISECONDS);
}
else
{
events.ScheduleEvent(EVENT_FLIGHT_BREATH, 1000);
}
return;
}
case EVENT_FLIGHT_BREATH:
currentTarget.Clear();
Talk(EMOTE_BREATH);
me->CastSpell(me, SPELL_FROST_MISSILE, false);
events.ScheduleEvent(EVENT_FLIGHT_SPELL_EXPLOSION, 8500);
return;
case EVENT_FLIGHT_SPELL_EXPLOSION:
me->CastSpell(me, SPELL_FROST_EXPLOSION, true);
events.ScheduleEvent(EVENT_FLIGHT_START_LAND, 3000);
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, 1000);
return;
case EVENT_LAND:
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
events.ScheduleEvent(EVENT_GROUND, 1500);
return;
case EVENT_GROUND:
Talk(EMOTE_GROUND_PHASE);
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
return;
case EVENT_HUNDRED_CLUB:
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
for (const auto& itr : pList)
{
if (itr.GetSource()->GetResistance(SPELL_SCHOOL_FROST) > 100 && pInstance)
{
pInstance->SetData(DATA_HUNDRED_CLUB, 0);
return;
}
}
events.RepeatEvent(5000);
return;
}
}
DoMeleeAttackIfReady();
}
};
};
class spell_sapphiron_frost_explosion : public SpellScriptLoader
{
public:
spell_sapphiron_frost_explosion() : SpellScriptLoader("spell_sapphiron_frost_explosion") { }
class spell_sapphiron_frost_explosion_SpellScript : public SpellScript
{
PrepareSpellScript(spell_sapphiron_frost_explosion_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
Unit* caster = GetCaster();
if (!caster || !caster->ToCreature())
return;
std::list<WorldObject*> tmplist;
for (auto& target : targets)
{
if (CAST_AI(boss_sapphiron_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_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_sapphiron_frost_explosion_SpellScript();
}
};
void AddSC_boss_sapphiron_40()
{
new boss_sapphiron_40();
// new spell_sapphiron_frost_explosion();
}

View File

@@ -0,0 +1,772 @@
/*
* 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 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)
{
pInstance = me->GetInstanceScript();
}
InstanceScript* pInstance;
EventMap events;
SummonList summons;
uint32 summonTimer{};
uint32 reviveTimer{};
uint32 resetTimer{};
bool ballLightningEnabled;
void DoAction(int32 param) override
{
if (param == ACTION_SUMMON_DIED)
{
if (summonTimer)
{
summonTimer = 0;
reviveTimer = 1;
return;
}
summonTimer = 1;
}
}
void Reset() override
{
BossAI::Reset();
events.Reset();
summons.DespawnAll();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetControlled(true, UNIT_STATE_ROOT);
summonTimer = 0;
reviveTimer = 0;
resetTimer = 1;
me->SetPosition(me->GetHomePosition());
ballLightningEnabled = false;
me->SummonCreature(NPC_STALAGG_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);
}
if (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
if (pInstance->GetBossState(BOSS_GLUTH) == DONE)
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
Talk(SAY_SLAY);
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(SAY_DEATH);
if (pInstance)
{
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY);
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK);
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
go->SetGoState(GO_STATE_ACTIVE);
}
}
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
}
void EnterCombat(Unit* who) override
{
BossAI::EnterCombat(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, 1000);
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, 14000);
events.ScheduleEvent(EVENT_THADDIUS_BERSERK, 360000);
events.ScheduleEvent(EVENT_THADDIUS_POLARITY_SHIFT, 30000);
events.ScheduleEvent(EVENT_ALLOW_BALL_LIGHTNING, 5000);
return;
case EVENT_THADDIUS_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_THADDIUS_CHAIN_LIGHTNING:
me->CastSpell(me->GetVictim(), SPELL_CHAIN_LIGHTNING, false);
events.RepeatEvent(15000);
break;
case EVENT_THADDIUS_POLARITY_SHIFT:
me->CastSpell(me, SPELL_POLARITY_SHIFT, false);
events.RepeatEvent(30000);
break;
case EVENT_ALLOW_BALL_LIGHTNING:
ballLightningEnabled = true;
break;
}
if (me->IsWithinMeleeRange(me->GetVictim()))
{
DoMeleeAttackIfReady();
}
else if (ballLightningEnabled)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat))
{
me->CastSpell(target, SPELL_BALL_LIGHTNING, false);
}
}
}
};
};
class boss_thaddius_summon_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)
{
pInstance = me->GetInstanceScript();
overload = false;
}
InstanceScript* pInstance;
EventMap events;
uint32 pullTimer{};
uint32 visualTimer{};
bool overload;
ObjectGuid myCoil;
void Reset() override
{
pullTimer = 0;
visualTimer = 1;
overload = false;
events.Reset();
me->SetControlled(false, UNIT_STATE_STUNNED);
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
{
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG_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 EnterCombat(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 (pInstance)
{
if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetGuidData(DATA_THADDIUS_GATE)))
{
go->SetGoState(GO_STATE_READY);
}
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_THADDIUS_BOSS)))
{
cr->AI()->AttackStart(pWho);
cr->AddThreat(pWho, 10.0f);
}
}
}
void DoAction(int32 param) override
{
if (param == ACTION_MAGNETIC_PULL)
{
pullTimer = 1;
me->SetControlled(true, UNIT_STATE_STUNNED);
}
else if (param == ACTION_RESTORE)
{
if (!me->IsAlive())
{
me->Respawn();
me->SetInCombatWithZone();
Talk(me->GetEntry() == NPC_STALAGG_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 (pInstance)
{
if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_THADDIUS_BOSS)))
{
cr->AI()->DoAction(ACTION_SUMMON_DIED);
}
}
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
if (pInstance)
{
pInstance->SetData(DATA_IMMORTAL_FAIL, 0);
}
if (!urand(0, 2))
{
Talk(me->GetEntry() == NPC_STALAGG_40 ? SAY_STAL_SLAY : SAY_FEUG_SLAY);
}
}
void UpdateAI(uint32 diff) override
{
if (visualTimer)
{
visualTimer += diff;
if (visualTimer >= 3000)
{
visualTimer = 0;
if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f))
{
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG_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.RepeatEvent(19000);
break;
case EVENT_MINION_STATIC_FIELD:
me->CastSpell(me, SPELL_STATIC_FIELD, false);
events.RepeatEvent(3000);
break;
case EVENT_MINION_MAGNETIC_PULL:
events.RepeatEvent(20000);
if (pInstance)
{
if (Creature* feugen = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_FEUGEN_BOSS)))
{
if (!feugen->IsAlive() || !feugen->GetVictim() || !me->GetVictim())
return;
float threatFeugen = feugen->GetThreatMgr().getThreat(feugen->GetVictim());
float threatStalagg = me->GetThreatMgr().getThreat(me->GetVictim());
Unit* tankFeugen = feugen->GetVictim();
Unit* tankStalagg = me->GetVictim();
feugen->GetThreatMgr().modifyThreatPercent(tankFeugen, -100);
feugen->AddThreat(tankStalagg, threatFeugen);
feugen->CastSpell(tankStalagg, SPELL_MAGNETIC_PULL, true);
feugen->AI()->DoAction(ACTION_MAGNETIC_PULL);
me->GetThreatMgr().modifyThreatPercent(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);
cr->CastSpell(target, SPELL_TESLA_SHOCK, true);
}
events.RepeatEvent(1500);
break;
}
else
{
overload = false;
cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG_40 ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false);
}
}
events.RepeatEvent(5000);
break;
}
DoMeleeAttackIfReady();
}
};
};
class spell_thaddius_pos_neg_charge : public SpellScriptLoader
{
public:
spell_thaddius_pos_neg_charge() : SpellScriptLoader("spell_thaddius_pos_neg_charge") { }
class spell_thaddius_pos_neg_charge_SpellScript : public SpellScript
{
PrepareSpellScript(spell_thaddius_pos_neg_charge_SpellScript);
void HandleTargets(std::list<WorldObject*>& targets)
{
uint8 count = 0;
for (auto& ihit : targets)
{
if (ihit->GetGUID() != GetCaster()->GetGUID())
{
if (Player* target = ihit->ToPlayer())
{
if (target->HasAura(GetTriggeringSpell()->Id))
{
++count;
}
}
}
}
if (count)
{
uint32 spellId = GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE ? SPELL_POSITIVE_CHARGE_STACK : SPELL_NEGATIVE_CHARGE_STACK;
GetCaster()->SetAuraStack(spellId, GetCaster(), count);
}
}
void HandleDamage(SpellEffIndex /*effIndex*/)
{
if (!GetTriggeringSpell())
return;
Unit* target = GetHitUnit();
if (!target)
return;
if (target->HasAura(GetTriggeringSpell()->Id) || target->GetTypeId() != TYPEID_PLAYER)
{
SetHitDamage(0);
}
else if (target->GetInstanceScript())
{
target->GetInstanceScript()->SetData(DATA_CHARGES_CROSSED, 0);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_thaddius_pos_neg_charge_SpellScript();
}
};
class spell_thaddius_polarity_shift : public SpellScriptLoader
{
public:
spell_thaddius_polarity_shift() : SpellScriptLoader("spell_thaddius_polarity_shift") { }
class spell_thaddius_polarity_shift_SpellScript : public SpellScript
{
PrepareSpellScript(spell_thaddius_polarity_shift_SpellScript);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo({ SPELL_POSITIVE_POLARITY, SPELL_NEGATIVE_POLARITY });
}
void HandleDummy(SpellEffIndex /* effIndex */)
{
Unit* caster = GetCaster();
if (Unit* target = GetHitUnit())
{
target->RemoveAurasDueToSpell(SPELL_POSITIVE_CHARGE_STACK);
target->RemoveAurasDueToSpell(SPELL_NEGATIVE_CHARGE_STACK);
target->CastSpell(target, roll_chance_i(50) ? SPELL_POSITIVE_POLARITY : SPELL_NEGATIVE_POLARITY, true, nullptr, nullptr, caster->GetGUID());
}
}
void HandleAfterCast()
{
if (GetCaster())
{
if (Creature* caster = GetCaster()->ToCreature())
{
if (caster->GetEntry() == NPC_THADDIUS_40)
{
caster->AI()->Talk(SAY_ELECT);
caster->AI()->Talk(EMOTE_POLARITY_SHIFTED);
}
}
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_thaddius_polarity_shift_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
AfterCast += SpellCastFn(spell_thaddius_polarity_shift_SpellScript::HandleAfterCast);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_thaddius_polarity_shift_SpellScript();
}
};
class npc_tesla : public CreatureScript
{
public:
npc_tesla() : CreatureScript("npc_tesla") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetNaxxramasAI<npc_teslaAI>(creature);
}
struct npc_teslaAI : public ScriptedAI
{
public:
npc_teslaAI(Creature* creature) : ScriptedAI(creature) { }
void EnterEvadeMode(EvadeReason /*why*/) override { } // never stop casting due to evade
void UpdateAI(uint32 /*diff*/) override { } // never do anything unless told
void EnterCombat(Unit* /*who*/) override { }
void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType, SpellSchoolMask) override { damage = 0; } // no, you can't kill it
};
};
class at_thaddius_entrance : public AreaTriggerScript
{
public:
at_thaddius_entrance() : AreaTriggerScript("at_thaddius_entrance") { }
bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override
{
InstanceScript* instance = player->GetInstanceScript();
if (!instance || instance->GetData(DATA_HAD_THADDIUS_GREET) || instance->GetBossState(BOSS_THADDIUS) == DONE)
return true;
if (Creature* thaddius = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THADDIUS_BOSS)))
{
thaddius->AI()->Talk(SAY_GREET);
}
instance->SetData(DATA_HAD_THADDIUS_GREET, 1);
return true;
}
};
void AddSC_boss_thaddius_40()
{
new boss_thaddius_40();
new boss_thaddius_summon_40();
// new npc_tesla();
// new spell_thaddius_pos_neg_charge();
// new spell_thaddius_polarity_shift();
// new at_thaddius_entrance();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,257 @@
/*
* 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 "CreatureAIImpl.h"
#include "SpellScript.h"
#define NaxxramasScriptName "instance_naxxramas"
enum Encouters
{
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 NXData
{
DATA_NOTH_ENTRY_GATE = 100,
DATA_HEIGAN_ERUPTION = 101,
DATA_HEIGAN_ENTER_GATE = 102,
DATA_LOATHEB_GATE = 103,
DATA_ANUB_GATE = 104,
DATA_FAERLINA_WEB = 105,
DATA_MAEXXNA_GATE = 106,
DATA_THADDIUS_BOSS = 107,
DATA_STALAGG_BOSS = 108,
DATA_FEUGEN_BOSS = 109,
DATA_THADDIUS_GATE = 110,
DATA_GOTHIK_ENTER_GATE = 111,
DATA_GOTHIK_INNER_GATE = 112,
DATA_GOTHIK_EXIT_GATE = 113,
DATA_HORSEMEN_GATE = 114,
DATA_LICH_KING_BOSS = 115,
DATA_KELTHUZAD_FLOOR = 116,
DATA_ABOMINATION_KILLED = 117,
DATA_FRENZY_REMOVED = 118,
DATA_CHARGES_CROSSED = 119,
DATA_SPORE_KILLED = 120,
DATA_HUNDRED_CLUB = 121,
DATA_DANCE_FAIL = 122,
DATA_IMMORTAL_FAIL = 123,
DATA_KELTHUZAD_GATE = 124,
DATA_HAD_THADDIUS_GREET = 125,
DATA_KELTHUZAD_PORTAL_1 = 126,
DATA_KELTHUZAD_PORTAL_2 = 127,
DATA_KELTHUZAD_PORTAL_3 = 128,
DATA_KELTHUZAD_PORTAL_4 = 129
};
enum NXGOs
{
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_HORSEMEN_CHEST_40 = 361000,
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,
// Gate to enter Naxx 40 from Strath
NAXX_STRATH_GATE = 176424
};
enum NXNPCs
{
// Thaddius
NPC_THADDIUS = 15928,
NPC_STALAGG = 15929,
NPC_FEUGEN = 15930,
// 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,
// Frogger
NPC_LIVING_POISON = 16027,
NPC_NAXXRAMAS_TRIGGER = 16082,
NPC_MR_BIGGLESWORTH = 16998,
// 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,
};
enum NX40NPCs
{
// Thaddius
NPC_THADDIUS_40 = 351000,
NPC_STALAGG_40 = 351001,
NPC_FEUGEN_40 = 351002,
// 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,
// 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,
};
enum NXMisc
{
SPELL_ERUPTION = 29371,
SPELL_FROGGER_EXPLODE = 28433,
ACTION_SAPPHIRON_BIRTH = 1,
// Background screams in instance if Thaddius still alive, four of them from 8873 to 8876
SOUND_SCREAM = 8873
};
enum NXSays
{
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,
SAY_FIRST_WING_TAUNT = 16
};
enum NXEvents
{
EVENT_THADDIUS_SCREAMS = 0,
EVENT_KELTHUZAD_WING_TAUNT = 1,
EVENT_FROSTWYRM_WATERFALL_DOOR = 2
};
enum NXMaps
{
MAP_NAXX = 533,
};
enum NXGraveyards
{
NAXX40_GRAVEYARD = 1449
};
enum NXAttunementQuests
{
NAXX40_ATTUNEMENT_1 = 9121,
NAXX40_ATTUNEMENT_2 = 9122,
NAXX40_ATTUNEMENT_3 = 9123
};
template <class AI, class T>
inline AI* GetNaxxramasAI(T* obj)
{
return GetInstanceAI<AI>(obj, NaxxramasScriptName);
}
#endif