diff options
| author | Shauren <shauren.trinity@gmail.com> | 2020-06-15 00:26:08 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-15 00:26:08 +0200 |
| commit | c715e635cf3feb50ac61d30659e614aaa2cc0c63 (patch) | |
| tree | 5af30c80f8b1df3f60852adde4680951f6f55442 /src/server/scripts/Northrend | |
| parent | abff505a6eaf3e649be506c802b80eed3dd35f3a (diff) | |
| parent | cf88f0a9735f9ba010a4ae46e848c8f1a86e17fa (diff) | |
Merge pull request #24554 from funjoker/cherry-picks
Diffstat (limited to 'src/server/scripts/Northrend')
30 files changed, 2337 insertions, 144 deletions
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp index 81dcf1846a6..895b9e15cc4 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp @@ -195,7 +195,7 @@ public: { damage = 0; EnterEvadeMode(); - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); bDone = true; } } @@ -323,7 +323,7 @@ public: { damage = 0; EnterEvadeMode(); - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); bDone = true; } } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp index 868bb4e07c8..34af2872fdd 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp @@ -226,7 +226,7 @@ class boss_anubarak_trial : public CreatureScript for (int i = 0; i < 10; i++) if (Creature* scarab = me->SummonCreature(NPC_SCARAB, AnubarakLoc[1].GetPositionX()+urand(0, 50)-25, AnubarakLoc[1].GetPositionY()+urand(0, 50)-25, AnubarakLoc[1].GetPositionZ())) { - scarab->SetFaction(31); + scarab->SetFaction(FACTION_PREY); scarab->GetMotionMaster()->MoveRandom(10); } } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index d4976c75255..cdf2feee3e8 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -204,7 +204,6 @@ enum Actions enum Misc { DATA_MADE_A_MESS = 45374613, // 4537, 4613 are achievement IDs - FACTION_SCOURGE = 974, GOSSIP_MENU_MURADIN_BRONZEBEARD = 10934, GOSSIP_MENU_HIGH_OVERLORD_SAURFANG = 10952 @@ -463,7 +462,7 @@ class boss_deathbringer_saurfang : public CreatureScript { case EVENT_INTRO_ALLIANCE_2: me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetFaction(FACTION_SCOURGE); + me->SetFaction(FACTION_UNDEAD_SCOURGE); Talk(SAY_INTRO_ALLIANCE_2); break; case EVENT_INTRO_ALLIANCE_3: @@ -476,7 +475,7 @@ class boss_deathbringer_saurfang : public CreatureScript break; case EVENT_INTRO_HORDE_2: me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetFaction(FACTION_SCOURGE); + me->SetFaction(FACTION_UNDEAD_SCOURGE); Talk(SAY_INTRO_HORDE_2); break; case EVENT_INTRO_HORDE_4: diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index f4e8d4e10b1..638c600905f 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -57,62 +57,68 @@ enum ScriptTexts enum Spells { // Lady Deathwhisper - SPELL_MANA_BARRIER = 70842, - SPELL_SHADOW_BOLT = 71254, - SPELL_DEATH_AND_DECAY = 71001, - SPELL_DOMINATE_MIND = 71289, - SPELL_DOMINATE_MIND_SCALE = 71290, - SPELL_FROSTBOLT = 71420, - SPELL_FROSTBOLT_VOLLEY = 72905, - SPELL_TOUCH_OF_INSIGNIFICANCE = 71204, - SPELL_SUMMON_SHADE = 71363, - SPELL_SHADOW_CHANNELING = 43897, - SPELL_DARK_TRANSFORMATION_T = 70895, - SPELL_DARK_EMPOWERMENT_T = 70896, - SPELL_DARK_MARTYRDOM_T = 70897, - SPELL_SUMMON_SPIRITS = 72478, + SPELL_MANA_BARRIER = 70842, + SPELL_SHADOW_BOLT = 71254, + SPELL_DEATH_AND_DECAY = 71001, + SPELL_DOMINATE_MIND = 71289, + SPELL_DOMINATE_MIND_SCALE = 71290, + SPELL_FROSTBOLT = 71420, + SPELL_FROSTBOLT_VOLLEY = 72905, + SPELL_TOUCH_OF_INSIGNIFICANCE = 71204, + SPELL_SUMMON_SHADE = 71363, + SPELL_SHADOW_CHANNELING = 43897, + SPELL_DARK_TRANSFORMATION_T = 70895, + SPELL_DARK_EMPOWERMENT_T = 70896, + SPELL_DARK_MARTYRDOM_T = 70897, + SPELL_SUMMON_SPIRITS = 72478, // Achievement - SPELL_FULL_HOUSE = 72827, // does not exist in dbc but still can be used for criteria check + SPELL_FULL_HOUSE = 72827, // does not exist in dbc but still can be used for criteria check // Both Adds - SPELL_TELEPORT_VISUAL = 41236, - SPELL_CLEAR_ALL_DEBUFFS = 34098, - SPELL_FULL_HEAL = 17683, - SPELL_PERMANENT_FEIGN_DEATH = 70628, + SPELL_TELEPORT_VISUAL = 41236, + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_FULL_HEAL = 17683, + SPELL_PERMANENT_FEIGN_DEATH = 70628, // Fanatics - SPELL_DARK_TRANSFORMATION = 70900, - SPELL_NECROTIC_STRIKE = 70659, - SPELL_SHADOW_CLEAVE = 70670, - SPELL_VAMPIRIC_MIGHT = 70674, - SPELL_FANATIC_S_DETERMINATION = 71235, - SPELL_DARK_MARTYRDOM_FANATIC = 71236, + SPELL_DARK_TRANSFORMATION = 70900, + SPELL_NECROTIC_STRIKE = 70659, + SPELL_SHADOW_CLEAVE = 70670, + SPELL_VAMPIRIC_MIGHT = 70674, + SPELL_FANATIC_S_DETERMINATION = 71235, + SPELL_DARK_MARTYRDOM_FANATIC = 71236, + SPELL_DARK_MARTYRDOM_FANATIC_25N = 72495, + SPELL_DARK_MARTYRDOM_FANATIC_10H = 72496, + SPELL_DARK_MARTYRDOM_FANATIC_25H = 72497, // Adherents - SPELL_DARK_EMPOWERMENT = 70901, - SPELL_FROST_FEVER = 67767, - SPELL_DEATHCHILL_BOLT = 70594, - SPELL_DEATHCHILL_BLAST = 70906, - SPELL_CURSE_OF_TORPOR = 71237, - SPELL_SHROUD_OF_THE_OCCULT = 70768, - SPELL_ADHERENT_S_DETERMINATION = 71234, - SPELL_DARK_MARTYRDOM_ADHERENT = 70903, + SPELL_DARK_EMPOWERMENT = 70901, + SPELL_FROST_FEVER = 67767, + SPELL_DEATHCHILL_BOLT = 70594, + SPELL_DEATHCHILL_BLAST = 70906, + SPELL_CURSE_OF_TORPOR = 71237, + SPELL_SHROUD_OF_THE_OCCULT = 70768, + SPELL_ADHERENT_S_DETERMINATION = 71234, + SPELL_DARK_MARTYRDOM_ADHERENT = 70903, + SPELL_DARK_MARTYRDOM_ADHERENT_25N = 72498, + SPELL_DARK_MARTYRDOM_ADHERENT_10H = 72499, + SPELL_DARK_MARTYRDOM_ADHERENT_25H = 72500, // Vengeful Shade - SPELL_VENGEFUL_BLAST = 71544, - SPELL_VENGEFUL_BLAST_PASSIVE = 71494, - SPELL_VENGEFUL_BLAST_25N = 72010, - SPELL_VENGEFUL_BLAST_10H = 72011, - SPELL_VENGEFUL_BLAST_25H = 72012, + SPELL_VENGEFUL_BLAST = 71544, + SPELL_VENGEFUL_BLAST_PASSIVE = 71494, + SPELL_VENGEFUL_BLAST_25N = 72010, + SPELL_VENGEFUL_BLAST_10H = 72011, + SPELL_VENGEFUL_BLAST_25H = 72012, // Darnavan - SPELL_BLADESTORM = 65947, - SPELL_CHARGE = 65927, - SPELL_INTIMIDATING_SHOUT = 65930, - SPELL_MORTAL_STRIKE = 65926, - SPELL_SHATTERING_THROW = 65940, - SPELL_SUNDER_ARMOR = 65936, + SPELL_BLADESTORM = 65947, + SPELL_CHARGE = 65927, + SPELL_INTIMIDATING_SHOUT = 65930, + SPELL_MORTAL_STRIKE = 65926, + SPELL_SHATTERING_THROW = 65940, + SPELL_SUNDER_ARMOR = 65936, }; enum EventTypes @@ -359,7 +365,7 @@ class boss_lady_deathwhisper : public CreatureScript { if (darnavan->IsAlive()) { - darnavan->SetFaction(35); + darnavan->SetFaction(FACTION_FRIENDLY); darnavan->CombatStop(true); darnavan->GetMotionMaster()->MoveIdle(); darnavan->SetReactState(REACT_PASSIVE); @@ -640,6 +646,9 @@ class npc_cult_fanatic : public CreatureScript DoCastSelf(SPELL_DARK_MARTYRDOM_FANATIC); break; case SPELL_DARK_MARTYRDOM_FANATIC: + case SPELL_DARK_MARTYRDOM_FANATIC_25N: + case SPELL_DARK_MARTYRDOM_FANATIC_10H: + case SPELL_DARK_MARTYRDOM_FANATIC_25H: _scheduler .Schedule(Seconds(2), [this](TaskContext /*context*/) { @@ -739,6 +748,9 @@ class npc_cult_adherent : public CreatureScript DoCastSelf(SPELL_DARK_MARTYRDOM_ADHERENT); break; case SPELL_DARK_MARTYRDOM_ADHERENT: + case SPELL_DARK_MARTYRDOM_ADHERENT_25N: + case SPELL_DARK_MARTYRDOM_ADHERENT_10H: + case SPELL_DARK_MARTYRDOM_ADHERENT_25H: _scheduler .Schedule(Seconds(2), [this](TaskContext /*context*/) { diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index 0a7857225ad..97ba864358a 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -264,7 +264,7 @@ class ValithriaDespawner : public BasicEvent creature->SetRespawnDelay(10); if (CreatureData const* data = creature->GetCreatureData()) - creature->SetPosition(data->posX, data->posY, data->posZ, data->orientation); + creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation); creature->DespawnOrUnsummon(); creature->SetCorpseDelay(corpseDelay); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 1e857d6164c..1462ebc1df1 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -358,7 +358,7 @@ class FrostwingGauntletRespawner creature->SetRespawnDelay(2); if (CreatureData const* data = creature->GetCreatureData()) - creature->SetPosition(data->posX, data->posY, data->posZ, data->orientation); + creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation); creature->DespawnOrUnsummon(); creature->SetCorpseDelay(corpseDelay); diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 0aa77ef0eb2..cb85a35c884 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -175,12 +175,6 @@ enum Seats SEAT_0 = 0 }; -enum Factions -{ - // Needed for melee hover disks /when Nexus Lords die/ - FACTION_FRIENDLY = 35 -}; - enum Actions { // Malygos diff --git a/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp b/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp index a75650dd6ba..48b1772ea95 100644 --- a/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp +++ b/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp @@ -261,7 +261,7 @@ public: for (uint8 n = 0; n < 3; ++n) time[n] = 0; me->GetMotionMaster()->Clear(); - me->SetPosition(CenterOfRoom.GetPositionX(), CenterOfRoom.GetPositionY(), CenterOfRoom.GetPositionZ(), CenterOfRoom.GetOrientation()); + me->UpdatePosition(CenterOfRoom.GetPositionX(), CenterOfRoom.GetPositionY(), CenterOfRoom.GetPositionZ(), CenterOfRoom.GetOrientation()); DoCast(me, SPELL_TELESTRA_BACK); me->SetVisible(true); if (Phase == 1) diff --git a/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp b/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp index 857ce9c32b4..5b3228e6413 100644 --- a/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp +++ b/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp @@ -23,11 +23,6 @@ #include "nexus.h" #include "Player.h" -enum Factions -{ - FACTION_HOSTILE_FOR_ALL = 16 -}; - class instance_nexus : public InstanceMapScript { public: @@ -61,31 +56,31 @@ class instance_nexus : public InstanceMapScript // Alliance npcs are spawned by default, if you are alliance, you will fight against horde npcs. case NPC_ALLIANCE_BERSERKER: if (ServerAllowsTwoSideGroups()) - creature->SetFaction(FACTION_HOSTILE_FOR_ALL); + creature->SetFaction(FACTION_MONSTER_2); if (_teamInInstance == ALLIANCE) creature->UpdateEntry(NPC_HORDE_BERSERKER); break; case NPC_ALLIANCE_RANGER: if (ServerAllowsTwoSideGroups()) - creature->SetFaction(FACTION_HOSTILE_FOR_ALL); + creature->SetFaction(FACTION_MONSTER_2); if (_teamInInstance == ALLIANCE) creature->UpdateEntry(NPC_HORDE_RANGER); break; case NPC_ALLIANCE_CLERIC: if (ServerAllowsTwoSideGroups()) - creature->SetFaction(FACTION_HOSTILE_FOR_ALL); + creature->SetFaction(FACTION_MONSTER_2); if (_teamInInstance == ALLIANCE) creature->UpdateEntry(NPC_HORDE_CLERIC); break; case NPC_ALLIANCE_COMMANDER: if (ServerAllowsTwoSideGroups()) - creature->SetFaction(FACTION_HOSTILE_FOR_ALL); + creature->SetFaction(FACTION_MONSTER_2); if (_teamInInstance == ALLIANCE) creature->UpdateEntry(NPC_HORDE_COMMANDER); break; case NPC_COMMANDER_STOUTBEARD: if (ServerAllowsTwoSideGroups()) - creature->SetFaction(FACTION_HOSTILE_FOR_ALL); + creature->SetFaction(FACTION_MONSTER_2); if (_teamInInstance == ALLIANCE) creature->UpdateEntry(NPC_COMMANDER_KOLURG); break; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index 3f622a72c83..201655985d1 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -538,7 +538,7 @@ class boss_algalon_the_observer : public CreatureScript damage = 0; me->SetReactState(REACT_PASSIVE); me->AttackStop(); - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE); DoCast(me, SPELL_SELF_STUN); events.Reset(); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index 09240de5649..004760c07ee 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -780,7 +780,7 @@ class boss_flame_leviathan_safety_container : public CreatureScript me->GetPosition(x, y, z); z = me->GetMap()->GetHeight(me->GetPhaseShift(), x, y, z); me->GetMotionMaster()->MovePoint(0, x, y, z); - me->SetPosition(x, y, z, 0); + me->UpdatePosition(x, y, z, 0); } void UpdateAI(uint32 /*diff*/) override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index 33e346e0c94..e0a73d09571 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -226,7 +226,7 @@ class npc_iron_roots : public CreatureScript SetCombatMovement(false); me->ApplySpellImmune(0, IMMUNITY_ID, 49560, true); // Death Grip - me->SetFaction(14); + me->SetFaction(FACTION_MONSTER); me->SetReactState(REACT_PASSIVE); } @@ -270,6 +270,7 @@ class boss_freya : public CreatureScript { boss_freyaAI(Creature* creature) : BossAI(creature, BOSS_FREYA) { + _encounterFinished = false; Initialize(); memset(elementalTimer, 0, sizeof(elementalTimer)); diffTimer = 0; @@ -312,9 +313,13 @@ class boss_freya : public CreatureScript bool checkElementalAlive[2]; bool trioDefeated[2]; bool random[3]; + bool _encounterFinished; void Reset() override { + if (_encounterFinished) + return; + _Reset(); Initialize(); } @@ -595,6 +600,11 @@ class boss_freya : public CreatureScript void JustDied(Unit* /*killer*/) override { + if (_encounterFinished) + return; + + _encounterFinished = true; + //! Freya's chest is dynamically spawned on death by different spells. const uint32 summonSpell[2][4] = { @@ -606,15 +616,15 @@ class boss_freya : public CreatureScript me->CastSpell((Unit*)NULL, summonSpell[me->GetMap()->GetDifficultyID() - DIFFICULTY_10_N][elderCount], true); Talk(SAY_DEATH); + me->SetReactState(REACT_PASSIVE); - _JustDied(); - me->RemoveAllAuras(); + me->InterruptNonMeleeSpells(true); + me->RemoveAllAttackers(); me->AttackStop(); - me->SetFaction(35); - me->DeleteThreatList(); - me->CombatStop(true); + me->SetFaction(FACTION_FRIENDLY); me->DespawnOrUnsummon(7500); me->CastSpell(me, SPELL_KNOCK_ON_WOOD_CREDIT, true); + _JustDied(); for (uint8 n = 0; n < 3; ++n) { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp index a320994dc07..6635ece7ee9 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp @@ -408,7 +408,7 @@ class boss_hodir : public CreatureScript DoCastAOE(SPELL_KILL_CREDIT, true); /// need to be cast before changing boss faction /// spell will target enemies only - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); me->DespawnOrUnsummon(10000); _JustDied(); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp index cfe2d3879d1..76acc9e7c45 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp @@ -180,7 +180,7 @@ class boss_ignis : public CreatureScript { if (summon->GetEntry() == NPC_IRON_CONSTRUCT) { - summon->SetFaction(16); + summon->SetFaction(FACTION_MONSTER_2); summon->SetReactState(REACT_AGGRESSIVE); summon->RemoveUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_IMMUNE_TO_PC)); summon->SetControlled(false, UNIT_STATE_ROOT); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 690a1ecdbfc..b23e296f0df 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -638,7 +638,7 @@ class boss_mimiron : public CreatureScript case EVENT_OUTTRO_1: me->RemoveAurasDueToSpell(SPELL_SLEEP_VISUAL_1); DoCast(me, SPELL_SLEEP_VISUAL_2); - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); events.ScheduleEvent(EVENT_OUTTRO_2, 3000); break; case EVENT_OUTTRO_2: diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp index a833e13aeec..825718bd3ee 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp @@ -15,27 +15,427 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "MoveSplineInit.h" +#include "Player.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" +#include "TypeContainerVisitor.h" +#include "CellImpl.h" +#include "GridNotifiersImpl.h" #include "ulduar.h" +#include "SpellAuras.h" +#include "SpellMgr.h" +#include <G3D/Vector3.h> +#include "AreaBoundary.h" +#include "InstanceScript.h" +#include "ObjectAccessor.h" +#include "MotionMaster.h" + +enum Spells +{ + // Thorim + SPELL_SHEATH_OF_LIGHTNING = 62276, + SPELL_STORMHAMMER = 62042, + SPELL_STORMHAMMER_SIF = 64767, + SPELL_STORMHAMMER_BOOMERANG = 64909, + SPELL_DEAFENING_THUNDER = 62470, + SPELL_CHARGE_ORB = 62016, + SPELL_SUMMON_LIGHTNING_ORB = 62391, + SPELL_LIGHTNING_DESTRUCTION = 62393, + SPELL_TOUCH_OF_DOMINION = 62507, + SPELL_TOUCH_OF_DOMINION_TRIGGERED = 62565, + SPELL_CHAIN_LIGHTNING = 62131, + SPELL_LIGHTNING_ORB_CHARGED = 62186, // wrong duration, triggered spell should handle lightning release + SPELL_LIGHTNING_CHARGE = 62279, + SPELL_LIGHTNING_RELEASE = 62466, + SPELL_LIGHTNING_PILLAR_2 = 62976, // caster high position, target low position + SPELL_LIGHTNING_PILLAR_1 = 63238, // caster high position, target low position + SPELL_UNBALANCING_STRIKE = 62130, + SPELL_BERSERK_PHASE_1 = 62560, + SPELL_BERSERK_PHASE_2 = 62555, + SPELL_ACTIVATE_LIGHTNING_ORB_PERIODIC = 62184, + + // Credits + SPELL_CREDIT_SIFFED = 64980, + SPELL_CREDIT_KILL = 64985, + + // Lightning Field + SPELL_LIGHTNING_FIELD = 64972, + SPELL_LIGHTNING_BEAM_CHANNEL = 45537, + + // Sif + SPELL_BLIZZARD = 62577, + SPELL_BLINK = 62578, + SPELL_FROSTBOLT_VOLLEY = 62580, + SPELL_FROSTBOLT = 62583, + SPELL_FROSTNOVA = 62597, + SPELL_SIF_TRANSFORM = 64778, + + // Runic Colossus + SPELL_SMASH = 62339, + SPELL_RUNIC_BARRIER = 62338, + SPELL_RUNIC_CHARGE = 62613, + SPELL_RUNIC_SMASH = 62465, + SPELL_RUNIC_SMASH_RIGHT = 62057, + SPELL_RUNIC_SMASH_LEFT = 62058, + + // Ancient Rune Giant + SPELL_RUNIC_FORTIFICATION = 62942, + SPELL_RUNE_DETONATION = 62526, + SPELL_STOMP = 62411 +}; + +enum Phases +{ + PHASE_NULL, + PHASE_1, + PHASE_2 +}; + +enum Events +{ + // Thorim + EVENT_SAY_AGGRO_2 = 1, + EVENT_SAY_SIF_START, + EVENT_START_SIF_CHANNEL, + EVENT_STORMHAMMER, + EVENT_CHARGE_ORB, + EVENT_SUMMON_ADDS, + EVENT_BERSERK, + EVENT_JUMPDOWN, + EVENT_UNBALANCING_STRIKE, + EVENT_CHAIN_LIGHTNING, + EVENT_START_PERIODIC_CHARGE, + EVENT_LIGHTNING_CHARGE, + EVENT_ACTIVATE_LIGHTNING_FIELD, + EVENT_OUTRO_1, + EVENT_OUTRO_2, + EVENT_OUTRO_3, + + // Runic Colossus + EVENT_RUNIC_BARRIER, + EVENT_SMASH, + EVENT_RUNIC_CHARGE, + EVENT_RUNIC_SMASH, + + // Ancient Rune Giant + EVENT_RUNIC_FORTIFICATION, + EVENT_STOMP, + EVENT_RUNE_DETONATION, + + // Arena NPC + EVENT_PRIMARY_ABILITY, + EVENT_SECONDARY_ABILITY, + EVENT_THIRD_ABILITY, + EVENT_ABILITY_CHARGE, + + // Sif + EVENT_BLINK, + EVENT_FROST_NOVA, + EVENT_FROSTBOLT, + EVENT_FROSTBOLT_VOLLEY, + EVENT_BLIZZARD +}; enum Yells { - SAY_AGGRO = 0, - SAY_SPECIAL_1 = 1, - SAY_SPECIAL_2 = 2, - SAY_SPECIAL_3 = 3, - SAY_JUMPDOWN = 4, - SAY_SLAY = 5, - SAY_BERSERK = 6, - SAY_WIPE = 7, - SAY_DEATH = 8, - SAY_END_NORMAL_1 = 9, - SAY_END_NORMAL_2 = 10, - SAY_END_NORMAL_3 = 11, - SAY_END_HARD_1 = 12, - SAY_END_HARD_2 = 13, - SAY_END_HARD_3 = 14 + // Thorim + SAY_AGGRO_1 = 0, + SAY_AGGRO_2 = 1, + SAY_SPECIAL = 2, + SAY_JUMPDOWN = 3, + SAY_SLAY = 4, + SAY_BERSERK = 5, + SAY_WIPE = 6, + SAY_DEATH = 7, + SAY_END_NORMAL_1 = 8, + SAY_END_NORMAL_2 = 9, + SAY_END_NORMAL_3 = 10, + SAY_END_HARD_1 = 11, + SAY_END_HARD_2 = 12, + SAY_END_HARD_3 = 13, + + // Runic Colossus + EMOTE_RUNIC_BARRIER = 0, + + // Ancient Rune Giant + EMOTE_RUNIC_MIGHT = 0, + + // Sif + SAY_SIF_START = 0, + SAY_SIF_DESPAWN = 1, + SAY_SIF_EVENT = 2 +}; + +enum PreAddSpells +{ + SPELL_ACID_BREATH = 62315, + SPELL_SWEEP = 62316, + + SPELL_DEVASTATE = 62317, + SPELL_HEROIC_SWIPE = 62444, + SPELL_SUNDER_ARMOR = 57807, + + SPELL_BARBED_SHOT = 62318, + SPELL_SHOOT = 16496, + + SPELL_RENEW = 62333, + SPELL_GREATER_HEAL = 62334, /// 61965 + SPELL_CIRCLE_OF_HEALING = 61964, + + SPELL_HOLY_SMITE = 62335, + + SPELL_LEAP = 61934, + + SPELL_CHARGE = 32323, + SPELL_MORTAL_STRIKE = 35054, + SPELL_WHIRLWIND = 33500, + + SPELL_LOW_BLOW = 62326, + SPELL_PUMMEL = 38313, + + SPELL_RUNIC_LIGHTNING = 62327, + SPELL_RUNIC_MENDING = 62328, + SPELL_RUNIC_SHIELD = 62321, + + SPELL_RUNIC_STRIKE = 62322, + SPELL_AURA_OF_CELERITY = 62320, + + SPELL_IMPALE = 62331, + SPELL_WHIRLING_TRIP = 64151, + + SPELL_CLEAVE = 42724, + SPELL_HAMSTRING = 48639, + SPELL_SHIELD_SMASH = 62332, +}; + +enum TrashTypes +{ + // Pre Phase Trash + BEHEMOTH, + MERCENARY_CAPTAIN, + MERCENARY_SOLDIER, + + // Arena Phase Trash + DARK_RUNE_CHAMPION, + DARK_RUNE_WARBRINGER, + DARK_RUNE_COMMONER, + DARK_RUNE_EVOKER, + + // Hall Way Trash + IRON_RING_GUARD, + IRON_HONOR_GUARD, + + // Shared + DARK_RUNE_ACOLYTE +}; + +struct ThorimTrashInfo +{ + uint32 Type; + uint32 Entry; + uint32 PrimaryAbility; + uint32 SecondaryAbility; + uint32 ThirdAbility; +}; + +uint8 const ThorimTrashCount = 13; +ThorimTrashInfo const StaticThorimTrashInfo[ThorimTrashCount] = +{ + // Pre Phase + { BEHEMOTH, NPC_JORMUNGAR_BEHEMOTH, SPELL_ACID_BREATH, SPELL_SWEEP, 0 }, + { MERCENARY_CAPTAIN, NPC_MERCENARY_CAPTAIN_A, SPELL_DEVASTATE, SPELL_HEROIC_SWIPE, SPELL_SUNDER_ARMOR }, + { MERCENARY_SOLDIER, NPC_MERCENARY_SOLDIER_A, SPELL_BARBED_SHOT, SPELL_SHOOT, 0 }, + { DARK_RUNE_ACOLYTE, NPC_DARK_RUNE_ACOLYTE_PRE, SPELL_RENEW, SPELL_GREATER_HEAL, SPELL_CIRCLE_OF_HEALING }, + { MERCENARY_CAPTAIN, NPC_MERCENARY_CAPTAIN_H, SPELL_DEVASTATE, SPELL_HEROIC_SWIPE, SPELL_SUNDER_ARMOR }, + { MERCENARY_SOLDIER, NPC_MERCENARY_SOLDIER_H, SPELL_BARBED_SHOT, SPELL_SHOOT, 0 }, + + // Arena Phase + { DARK_RUNE_CHAMPION, NPC_DARK_RUNE_CHAMPION, SPELL_MORTAL_STRIKE, SPELL_WHIRLWIND, 0 }, + { DARK_RUNE_WARBRINGER, NPC_DARK_RUNE_WARBRINGER, SPELL_RUNIC_STRIKE, 0, 0 }, + { DARK_RUNE_EVOKER, NPC_DARK_RUNE_EVOKER, SPELL_RUNIC_LIGHTNING, SPELL_RUNIC_SHIELD, SPELL_RUNIC_MENDING }, + { DARK_RUNE_COMMONER, NPC_DARK_RUNE_COMMONER, SPELL_LOW_BLOW, SPELL_PUMMEL, 0 }, + + // Hall Way + { IRON_RING_GUARD, NPC_IRON_RING_GUARD, SPELL_WHIRLING_TRIP, SPELL_IMPALE, 0 }, + { IRON_HONOR_GUARD, NPC_IRON_HONOR_GUARD, SPELL_CLEAVE, SPELL_SHIELD_SMASH, 0 }, + { DARK_RUNE_ACOLYTE, NPC_DARK_RUNE_ACOLYTE, SPELL_RENEW, SPELL_GREATER_HEAL, 0 } +}; + +enum Actions +{ + ACTION_INCREASE_PREADDS_COUNT, + ACTION_ACTIVATE_RUNIC_SMASH, + ACTION_ACTIVATE_ADDS, + ACTION_PILLAR_CHARGED, + ACTION_START_HARD_MODE, + ACTION_BERSERK +}; + +struct SummonLocation +{ + Position pos; + uint32 entry; +}; + +SummonLocation const PreAddLocations[] = +{ + { { 2149.68f, -263.477f, 419.679f, 3.120f }, NPC_JORMUNGAR_BEHEMOTH }, + { { 2131.31f, -271.640f, 419.840f, 2.188f }, NPC_MERCENARY_CAPTAIN_A }, + { { 2127.24f, -259.182f, 419.974f, 5.917f }, NPC_MERCENARY_SOLDIER_A }, + { { 2123.32f, -254.770f, 419.840f, 6.170f }, NPC_MERCENARY_SOLDIER_A }, + { { 2120.10f, -258.990f, 419.840f, 6.250f }, NPC_MERCENARY_SOLDIER_A }, + { { 2129.09f, -277.142f, 419.756f, 1.222f }, NPC_DARK_RUNE_ACOLYTE_PRE } +}; + +SummonLocation const ColossusAddLocations[] = +{ + { { 2218.38f, -297.50f, 412.18f, 1.030f }, NPC_IRON_RING_GUARD }, + { { 2235.07f, -297.98f, 412.18f, 1.613f }, NPC_IRON_RING_GUARD }, + { { 2235.26f, -338.34f, 412.18f, 1.589f }, NPC_IRON_RING_GUARD }, + { { 2217.69f, -337.39f, 412.18f, 1.241f }, NPC_IRON_RING_GUARD }, + { { 2227.58f, -308.30f, 412.18f, 1.591f }, NPC_DARK_RUNE_ACOLYTE }, + { { 2227.47f, -345.37f, 412.18f, 1.566f }, NPC_DARK_RUNE_ACOLYTE } +}; + +SummonLocation const GiantAddLocations[] = +{ + { { 2198.05f, -428.77f, 419.95f, 6.056f }, NPC_IRON_HONOR_GUARD }, + { { 2220.31f, -436.22f, 412.26f, 1.064f }, NPC_IRON_HONOR_GUARD }, + { { 2158.88f, -441.73f, 438.25f, 0.127f }, NPC_IRON_HONOR_GUARD }, + { { 2198.29f, -436.92f, 419.95f, 0.261f }, NPC_DARK_RUNE_ACOLYTE }, + { { 2230.93f, -434.27f, 412.26f, 1.931f }, NPC_DARK_RUNE_ACOLYTE } +}; + +Position const SifSpawnPosition = { 2148.301f, -297.8453f, 438.3308f, 2.687807f }; + +enum Data +{ + DATA_CHARGED_PILLAR = 1 +}; + +enum DisplayIds +{ + THORIM_WEAPON_DISPLAY_ID = 45900 +}; + +uint32 const LightningOrbPathSize = 8; +G3D::Vector3 const LightningOrbPath[LightningOrbPathSize] = +{ + { 2134.889893f, -298.632996f, 438.247467f }, + { 2134.570068f, -440.317993f, 438.247467f }, + { 2167.820312f, -440.330261f, 438.247589f }, + { 2213.394287f, -433.318298f, 412.665863f }, + { 2227.766113f, -433.275818f, 412.177032f }, + { 2227.551270f, -263.081085f, 412.176880f }, + { 2202.208008f, -262.939270f, 412.168976f }, + { 2182.310059f, -263.233093f, 414.739410f } +}; + +// used for trash jump calculation +Position const ArenaCenter = { 2134.77f, -262.307f }; + +// used for lightning field calculation +Position const LightningFieldCenter = { 2135.178f, -321.122f }; + +CircleBoundary const ArenaFloorCircle(ArenaCenter, 45.4); +CircleBoundary const InvertedBalconyCircle(LightningFieldCenter, 32.0, true); + +CreatureBoundary const ArenaBoundaries = +{ + &ArenaFloorCircle, + &InvertedBalconyCircle +}; + +class HeightPositionCheck +{ + public: + HeightPositionCheck(bool ret) : _ret(ret) { } + + bool operator()(Position const* pos) const + { + return pos->GetPositionZ() > THORIM_BALCONY_Z_CHECK == _ret; + } + + private: + bool _ret; + + static float const THORIM_BALCONY_Z_CHECK; +}; +float const HeightPositionCheck::THORIM_BALCONY_Z_CHECK = 428.0f; + +class RunicSmashExplosionEvent : public BasicEvent +{ + public: + RunicSmashExplosionEvent(Creature* owner) : _owner(owner) { } + + bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override + { + _owner->CastSpell((Unit*)nullptr, SPELL_RUNIC_SMASH); + return true; + } + + private: + Creature* _owner; +}; + +class TrashJumpEvent : public BasicEvent +{ + public: + TrashJumpEvent(Creature* owner) : _owner(owner), _stage(0) { } + + bool Execute(uint64 eventTime, uint32 /*updateTime*/) override + { + switch (_stage) + { + case 0: + _owner->CastSpell((Unit*)nullptr, SPELL_LEAP); + ++_stage; + _owner->m_Events.AddEvent(this, eventTime + 2000); + return false; + case 1: + _owner->SetReactState(REACT_AGGRESSIVE); + _owner->AI()->DoZoneInCombat(_owner); + _owner->AI()->SetBoundary(&ArenaBoundaries); + return true; + default: + break; + } + + return true; + } + + private: + Creature* _owner; + uint8 _stage; +}; + +class LightningFieldEvent : public BasicEvent +{ + public: + LightningFieldEvent(Creature* owner) : _owner(owner) { } + + bool Execute(uint64 eventTime, uint32 /*updateTime*/) override + { + if (InstanceScript* instance = _owner->GetInstanceScript()) + { + if (instance->GetBossState(BOSS_THORIM) == IN_PROGRESS) + { + _owner->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_FIELD); + _owner->m_Events.AddEvent(this, eventTime + 1000); + return false; + } + } + + _owner->InterruptNonMeleeSpells(false); + _owner->AI()->EnterEvadeMode(); + return true; + } + + private: + Creature* _owner; }; class boss_thorim : public CreatureScript @@ -47,17 +447,76 @@ class boss_thorim : public CreatureScript { boss_thorimAI(Creature* creature) : BossAI(creature, BOSS_THORIM) { + _encounterFinished = false; + Initialize(); + } + + void Initialize() + { + _killedCount = 0; + _waveType = 0; + _hardMode = true; + _orbSummoned = false; + _dontStandInTheLightning = true; } void Reset() override { + if (_encounterFinished) + return; + + SetBoundary(nullptr); _Reset(); + Initialize(); + + me->SetReactState(REACT_PASSIVE); + me->SetDisableGravity(true); + me->SetControlled(true, UNIT_STATE_ROOT); + me->AddUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); + + events.SetPhase(PHASE_NULL); + + // Respawn Mini Bosses + for (uint8 i = DATA_RUNIC_COLOSSUS; i <= DATA_RUNE_GIANT; ++i) + if (Creature* miniBoss = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i))) + miniBoss->Respawn(true); + + // Spawn Pre Phase Adds + for (SummonLocation const& s : PreAddLocations) + me->SummonCreature(s.entry, s.pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); + + if (GameObject* lever = instance->GetGameObject(DATA_THORIM_LEVER)) + lever->AddFlag(GO_FLAG_NOT_SELECTABLE); + + // Remove trigger auras + if (Creature* pillar = ObjectAccessor::GetCreature(*me, _activePillarGUID)) + pillar->RemoveAllAuras(); + + if (Creature* controller = instance->GetCreature(DATA_THORIM_CONTROLLER)) + controller->RemoveAllAuras(); + + _activePillarGUID.Clear(); } - void EnterEvadeMode(EvadeReason why) override + void EnterEvadeMode(EvadeReason /*why*/) override { - Talk(SAY_WIPE); - _EnterEvadeMode(why); + summons.DespawnAll(); + _DespawnAtEvade(); + } + + void SetGUID(ObjectGuid guid, int32 type) override + { + if (type == DATA_CHARGED_PILLAR) + { + _activePillarGUID = guid; + + if (Creature* pillar = ObjectAccessor::GetCreature(*me, _activePillarGUID)) + { + pillar->CastSpell(pillar, SPELL_LIGHTNING_ORB_CHARGED, true); + pillar->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_PILLAR_2); + events.ScheduleEvent(EVENT_LIGHTNING_CHARGE, 8000, 0, PHASE_2); + } + } } void KilledUnit(Unit* who) override @@ -66,27 +525,410 @@ class boss_thorim : public CreatureScript Talk(SAY_SLAY); } - void JustDied(Unit* /*killer*/) override + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override { - Talk(SAY_DEATH); + if (spellInfo->Id == SPELL_TOUCH_OF_DOMINION_TRIGGERED) + { + if (Creature* sif = instance->GetCreature(DATA_SIF)) + { + sif->AI()->Talk(SAY_SIF_DESPAWN); + sif->DespawnOrUnsummon(6000); + _hardMode = false; + } + } + } + + void SpellHitTarget(Unit* who, SpellInfo const* spellInfo) override + { + if (who->GetTypeId() == TYPEID_PLAYER && spellInfo->Id == SPELL_LIGHTNING_RELEASE) + _dontStandInTheLightning = false; + } + + void FinishEncounter() + { + if (_encounterFinished) + return; + + _encounterFinished = true; + + DoCastAOE(SPELL_CREDIT_KILL, true); // before change faction + + me->SetReactState(REACT_PASSIVE); + me->InterruptNonMeleeSpells(true); + me->RemoveAllAttackers(); + me->AttackStop(); + me->SetFaction(FACTION_FRIENDLY); + me->AddUnitFlag(UNIT_FLAG_RENAME); + + if (Creature* controller = instance->GetCreature(DATA_THORIM_CONTROLLER)) + controller->RemoveAllAuras(); + if (Creature* pillar = ObjectAccessor::GetCreature(*me, _activePillarGUID)) + pillar->RemoveAllAuras(); + + if (_hardMode) + { + if (Creature* sif = instance->GetCreature(DATA_SIF)) + { + summons.Despawn(sif); + sif->DespawnOrUnsummon(10000); + } + } + _JustDied(); + + Talk(SAY_DEATH); + events.ScheduleEvent(EVENT_OUTRO_1, 4000); + events.ScheduleEvent(EVENT_OUTRO_2, _hardMode ? 8000 : 11000); + events.ScheduleEvent(EVENT_OUTRO_3, _hardMode ? 19000 : 21000); + + me->m_Events.AddEvent(new KeeperDespawnEvent(me), me->m_Events.CalculateTime(35000)); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != EFFECT_MOTION_TYPE || id != EVENT_JUMP) + return; + + me->getThreatManager().resetAllAggro(); + SetBoundary(&ArenaBoundaries); } void EnterCombat(Unit* /*who*/) override { - Talk(SAY_AGGRO); _EnterCombat(); + Talk(SAY_AGGRO_1); + + events.SetPhase(PHASE_1); + + events.ScheduleEvent(EVENT_SAY_AGGRO_2, 9000, 0, PHASE_1); + events.ScheduleEvent(EVENT_SAY_SIF_START, 16500, 0, PHASE_1); + events.ScheduleEvent(EVENT_START_SIF_CHANNEL, 22500, 0, PHASE_1); + + events.ScheduleEvent(EVENT_STORMHAMMER, 40000, 0, PHASE_1); + events.ScheduleEvent(EVENT_CHARGE_ORB, 30000, 0, PHASE_1); + events.ScheduleEvent(EVENT_SUMMON_ADDS, 15000, 0, PHASE_1); + events.ScheduleEvent(EVENT_BERSERK, 369000); + + DoCast(me, SPELL_SHEATH_OF_LIGHTNING); + + if (Creature* runicColossus = instance->GetCreature(DATA_RUNIC_COLOSSUS)) + { + runicColossus->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); + runicColossus->AI()->DoAction(ACTION_ACTIVATE_ADDS); + } + + if (GameObject* lever = instance->GetGameObject(DATA_THORIM_LEVER)) + lever->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + + // Summon Sif + me->SummonCreature(NPC_SIF, SifSpawnPosition); + } + + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) + { + case NPC_LIGHTNING_ORB: + { + summon->SetReactState(REACT_PASSIVE); + summon->CastSpell(summon, SPELL_LIGHTNING_DESTRUCTION, true); + + summon->GetMotionMaster()->MovePoint(EVENT_CHARGE_PREPATH, LightningOrbPath[LightningOrbPathSize - 1].x, LightningOrbPath[LightningOrbPathSize - 1].y, LightningOrbPath[LightningOrbPathSize - 1].z, false); + + Movement::PointsArray path(LightningOrbPath, LightningOrbPath + LightningOrbPathSize); + + Movement::MoveSplineInit init(summon); + init.MovebyPath(path); + init.Launch(); + break; + } + case NPC_DARK_RUNE_CHAMPION: + case NPC_DARK_RUNE_WARBRINGER: + case NPC_DARK_RUNE_EVOKER: + case NPC_DARK_RUNE_COMMONER: + summon->SetReactState(REACT_PASSIVE); + summon->m_Events.AddEvent(new TrashJumpEvent(summon), summon->m_Events.CalculateTime(3000)); + break; + case NPC_SIF: + summon->SetReactState(REACT_PASSIVE); + break; + default: + break; + } + + BossAI::JustSummoned(summon); } - void UpdateAI(uint32 /*diff*/) override + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - //SPELLS @todo - // + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SAY_AGGRO_2: + Talk(SAY_AGGRO_2); + break; + case EVENT_SAY_SIF_START: + if (Creature* sif = instance->GetCreature(DATA_SIF)) + sif->AI()->Talk(SAY_SIF_START); + break; + case EVENT_START_SIF_CHANNEL: + if (Creature* sif = instance->GetCreature(DATA_SIF)) + sif->CastSpell(me, SPELL_TOUCH_OF_DOMINION); + break; + case EVENT_STORMHAMMER: + DoCast(SPELL_STORMHAMMER); + events.Repeat(15000, 20000); + break; + case EVENT_CHARGE_ORB: + DoCastAOE(SPELL_CHARGE_ORB); + events.Repeat(15000, 20000); + break; + case EVENT_SUMMON_ADDS: + SummonWave(); + events.Repeat(_orbSummoned ? 3000 : 10000); + break; + case EVENT_JUMPDOWN: + if (_hardMode) + if (Creature* sif = instance->GetCreature(DATA_SIF)) + sif->AI()->DoAction(ACTION_START_HARD_MODE); + me->RemoveAurasDueToSpell(SPELL_SHEATH_OF_LIGHTNING); + me->SetReactState(REACT_AGGRESSIVE); + me->SetDisableGravity(false); + me->SetControlled(false, UNIT_STATE_ROOT); + me->GetMotionMaster()->MoveJump(2134.8f, -263.056f, 419.983f, me->GetOrientation(), 30.0f, 20.0f); + events.ScheduleEvent(EVENT_START_PERIODIC_CHARGE, 2000, 0, PHASE_2); + events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, 15000, 0, PHASE_2); + events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 20000, 0, PHASE_2); + break; + case EVENT_UNBALANCING_STRIKE: + DoCastVictim(SPELL_UNBALANCING_STRIKE); + events.Repeat(15000, 20000); + break; + case EVENT_CHAIN_LIGHTNING: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_CHAIN_LIGHTNING); + events.Repeat(7000, 15000); + break; + case EVENT_START_PERIODIC_CHARGE: + if (Creature* controller = instance->GetCreature(DATA_THORIM_CONTROLLER)) + controller->CastSpell(controller, SPELL_ACTIVATE_LIGHTNING_ORB_PERIODIC, true); + break; + case EVENT_LIGHTNING_CHARGE: + if (Creature* pillar = ObjectAccessor::GetCreature(*me, _activePillarGUID)) + DoCast(pillar, SPELL_LIGHTNING_RELEASE); + break; + case EVENT_BERSERK: + if (events.IsInPhase(PHASE_1)) + { + Talk(SAY_WIPE); + DoCastAOE(SPELL_BERSERK_PHASE_1, true); + DoCast(me, SPELL_SUMMON_LIGHTNING_ORB, true); + } + else + { + Talk(SAY_BERSERK); + DoCast(me, SPELL_BERSERK_PHASE_2, true); + } + break; + case EVENT_ACTIVATE_LIGHTNING_FIELD: + { + std::list<Creature*> triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_THORIM_EVENT_BUNNY, 100.0f); + triggers.remove_if([](Creature* bunny) + { + if (HeightPositionCheck(false)(bunny)) + return true; + return LightningFieldCenter.GetExactDist2dSq(bunny) > 1296.0f; + }); + + uint64 timer = 1000; + for (Creature* bunny : triggers) + bunny->m_Events.AddEvent(new LightningFieldEvent(bunny), bunny->m_Events.CalculateTime(timer += 100)); + + triggers.remove_if([](Creature* bunny) + { + return LightningFieldCenter.GetExactDist2dSq(bunny) < 576.0f; + }); + + triggers.sort([](Creature* a, Creature* b) + { + return a->GetPositionX() < b->GetPositionX(); + }); + + for (auto itr = triggers.cbegin(); itr != triggers.cend();) + { + auto prev = itr++; + if (itr != triggers.end()) + (*prev)->CastSpell(*itr, SPELL_LIGHTNING_BEAM_CHANNEL); + } + break; + } + case EVENT_OUTRO_1: + Talk(_hardMode ? SAY_END_HARD_1 : SAY_END_NORMAL_1); + if (_hardMode) + DoCast(me, SPELL_STORMHAMMER_SIF); + break; + case EVENT_OUTRO_2: + Talk(_hardMode ? SAY_END_HARD_2 : SAY_END_NORMAL_2); + if (_hardMode) + if (Creature* sif = instance->GetCreature(DATA_SIF)) + sif->SetStandState(UNIT_STAND_STATE_DEAD); + break; + case EVENT_OUTRO_3: + Talk(_hardMode ? SAY_END_HARD_3 : SAY_END_NORMAL_3); + break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + DoMeleeAttackIfReady(); } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_BERSERK: + if (events.IsInPhase(PHASE_2)) + return; + + if (!_orbSummoned) + { + _orbSummoned = true; + events.RescheduleEvent(EVENT_BERSERK, 1000); + } + return; + case ACTION_INCREASE_PREADDS_COUNT: + if (++_killedCount >= 6) + { + // Event starts + me->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); + DoZoneInCombat(me); + } + break; + default: + break; + } + } + + void GetTrashSpawnTriggers(std::list<Creature*>& triggerList, uint32 count = 1) + { + me->GetCreatureListWithEntryInGrid(triggerList, NPC_THORIM_EVENT_BUNNY, 100.0f); + triggerList.remove_if([](Creature* bunny) + { + if (HeightPositionCheck(false)(bunny)) + return true; + return ArenaCenter.GetExactDist2dSq(bunny) < 3025.0f; + }); + + if (triggerList.empty()) + return; + + if (count == 1) + { + Creature* bunny = Trinity::Containers::SelectRandomContainerElement(triggerList); + triggerList.clear(); + triggerList.push_back(bunny); + } + else + Trinity::Containers::RandomResize(triggerList, count); + } + + void SummonWave() + { + switch (_waveType) + { + case 0: + { + // Dark Rune Commoner + std::list<Creature*> triggers; + GetTrashSpawnTriggers(triggers, urand(5, 6)); + + for (Creature* bunny : triggers) + me->SummonCreature(StaticThorimTrashInfo[6 + 3].Entry, *bunny, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); + + ++_waveType; + break; + } + case 1: + if (urand(0, 1)) + { + // Dark Rune Champion or Dark Rune Evoker + std::list<Creature*> triggers; + GetTrashSpawnTriggers(triggers, urand(2, 4)); + + for (Creature* bunny : triggers) + me->SummonCreature(StaticThorimTrashInfo[6 + RAND(0, 2)].Entry, *bunny, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); + } + else + { + // Dark Rune Warbringer + std::list<Creature*> triggers; + GetTrashSpawnTriggers(triggers); + + for (Creature* bunny : triggers) + me->SummonCreature(StaticThorimTrashInfo[6 + 1].Entry, *bunny, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); + } + --_waveType; + break; + } + } + + bool CanStartPhase2(Unit* actor) const + { + if (actor->GetTypeId() != TYPEID_PLAYER || !me->IsWithinDistInMap(actor, 10.0f)) + return false; + + Creature* runicColossus = instance->GetCreature(DATA_RUNIC_COLOSSUS); + Creature* runeGiant = instance->GetCreature(DATA_RUNE_GIANT); + return runicColossus && !runicColossus->IsAlive() && runeGiant && !runeGiant->IsAlive(); + } + + void DamageTaken(Unit* attacker, uint32& damage) override + { + if (events.IsInPhase(PHASE_1) && CanStartPhase2(attacker)) + { + Talk(SAY_JUMPDOWN); + events.SetPhase(PHASE_2); + events.ScheduleEvent(EVENT_JUMPDOWN, 8000); + events.ScheduleEvent(EVENT_ACTIVATE_LIGHTNING_FIELD, 15000); + events.RescheduleEvent(EVENT_BERSERK, 300000, 0, PHASE_2); + + if (Creature* sif = instance->GetCreature(DATA_SIF)) + sif->InterruptNonMeleeSpells(false); + + // Hard Mode + if (_hardMode) + DoCastAOE(SPELL_CREDIT_SIFFED, true); + } + else if (me->HealthBelowPctDamaged(1, damage)) + { + damage = 0; + FinishEncounter(); + } + } + + private: + ObjectGuid _activePillarGUID; + uint8 _killedCount; + uint8 _waveType; + bool _hardMode; + bool _encounterFinished; + bool _orbSummoned; + bool _dontStandInTheLightning; }; CreatureAI* GetAI(Creature* creature) const override @@ -95,7 +937,1234 @@ class boss_thorim : public CreatureScript } }; +struct npc_thorim_trashAI : public ScriptedAI +{ + npc_thorim_trashAI(Creature* creature) : ScriptedAI(creature) + { + _instance = creature->GetInstanceScript(); + for (uint8 i = 0; i < ThorimTrashCount; ++i) + if (me->GetEntry() == StaticThorimTrashInfo[i].Entry) + _info = &StaticThorimTrashInfo[i]; + + ASSERT(_info); + } + + struct AIHelper + { + /// returns heal amount of the given spell including hots + static uint32 GetTotalHeal(SpellInfo const* spellInfo, Unit const* caster) + { + uint32 heal = 0; + for (SpellEffectInfo const* effect : spellInfo->GetEffects()) + { + if (effect->IsEffect(SPELL_EFFECT_HEAL)) + heal += effect->CalcValue(caster); + + if (effect->IsEffect(SPELL_EFFECT_APPLY_AURA) && effect->IsAura(SPELL_AURA_PERIODIC_HEAL)) + heal += spellInfo->GetMaxTicks() * effect->CalcValue(caster); + } + return heal; + } + + /// returns remaining heal amount on given target + static uint32 GetRemainingHealOn(Unit* target) + { + uint32 heal = 0; + Unit::AuraEffectList const& auras = target->GetAuraEffectsByType(SPELL_AURA_PERIODIC_HEAL); + for (AuraEffect const* aurEff : auras) + heal += aurEff->GetAmount() * (aurEff->GetTotalTicks() - aurEff->GetTickNumber()); + + return heal; + } + + class MostHPMissingInRange + { + public: + MostHPMissingInRange(Unit const* referer, float range, uint32 hp, uint32 exclAura = 0, bool exclSelf = false) + : _referer(referer), _range(range), _hp(hp), _exclAura(exclAura), _exclSelf(exclSelf) { } + + bool operator()(Unit* u) + { + if (_exclSelf && u == _referer) + return false; + + if (_exclAura && u->HasAura(_exclAura)) + return false; + + if ((u->GetHealth() + GetRemainingHealOn(u) + _hp) > u->GetMaxHealth()) + return false; + + uint32 missingHP = u->GetMaxHealth() - u->GetHealth(); + if (u->IsAlive() && _referer->IsFriendlyTo(u) && _referer->IsWithinDistInMap(u, _range) && missingHP > _hp) + { + _hp = missingHP; + return true; + } + + return false; + } + + private: + Unit const* _referer; + float _range; + uint32 _hp; + uint32 _exclAura; + bool _exclSelf; + }; + + static Unit* GetUnitWithMostMissingHp(SpellInfo const* spellInfo, Unit* caster) + { + // use positive range, it's a healing spell + float const range = spellInfo->GetMaxRange(true); + uint32 const heal = GetTotalHeal(spellInfo, caster); + + Unit* target = nullptr; + Trinity::MostHPMissingInRange checker(caster, range, heal); + Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(caster, target, checker); + Cell::VisitGridObjects(caster, searcher, 60.0f); + + return target; + } + + static Unit* GetHealTarget(SpellInfo const* spellInfo, Unit* caster) + { + Unit* healTarget = nullptr; + if (!spellInfo->HasAttribute(SPELL_ATTR1_CANT_TARGET_SELF) && !roll_chance_f(caster->GetHealthPct()) && ((caster->GetHealth() + GetRemainingHealOn(caster) + GetTotalHeal(spellInfo, caster)) <= caster->GetMaxHealth())) + healTarget = caster; + else + healTarget = GetUnitWithMostMissingHp(spellInfo, caster); + + return healTarget; + } + }; + + bool UseAbility(uint32 spellId) + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, GetDifficulty()); + if (!spellInfo) + return false; + + Unit* target = nullptr; + if (AIHelper::GetTotalHeal(spellInfo, me)) + target = AIHelper::GetHealTarget(spellInfo, me); + else + target = me->GetVictim(); + + if (!target) + return false; + + if (_info->Type == MERCENARY_SOLDIER) + { + bool allowMove = true; + if (me->IsInRange(target, spellInfo->GetMinRange(), spellInfo->GetMaxRange())) + allowMove = false; + + if (IsCombatMovementAllowed() != allowMove) + { + SetCombatMovement(allowMove); + + // need relaunch movement + ScriptedAI::AttackStart(target); + + // give some time to allow reposition, try again in a second + if (allowMove) + return false; + } + } + + DoCast(target, spellId); + return true; + } + + void UpdateAI(uint32 diff) final override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) + { + ExecuteEvent(eventId); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + if (_info->Type == DARK_RUNE_ACOLYTE) + DoSpellAttackIfReady(SPELL_HOLY_SMITE); + else + DoMeleeAttackIfReady(); + } + + virtual void ExecuteEvent(uint32 eventId) = 0; + +protected: + InstanceScript* _instance; + EventMap _events; + + ThorimTrashInfo const* _info = nullptr; +}; + +class npc_thorim_pre_phase : public CreatureScript +{ + public: + npc_thorim_pre_phase() : CreatureScript("npc_thorim_pre_phase") { } + + struct npc_thorim_pre_phaseAI : public npc_thorim_trashAI + { + npc_thorim_pre_phaseAI(Creature* creature) : npc_thorim_trashAI(creature) + { + me->setActive(true); // prevent grid unload + } + + void Reset() override + { + _events.Reset(); + if (_info->PrimaryAbility) + _events.ScheduleEvent(EVENT_PRIMARY_ABILITY, urand(3000, 6000)); + if (_info->SecondaryAbility) + _events.ScheduleEvent(EVENT_SECONDARY_ABILITY, _info->SecondaryAbility == SPELL_SHOOT ? 2000 : urand(12000, 15000)); + if (_info->ThirdAbility) + _events.ScheduleEvent(EVENT_THIRD_ABILITY, urand(6000, 8000)); + if (_info->Type == MERCENARY_SOLDIER) + SetCombatMovement(false); + } + + void JustDied(Unit* /*victim*/) override + { + if (Creature* thorim = _instance->GetCreature(BOSS_THORIM)) + thorim->AI()->DoAction(ACTION_INCREASE_PREADDS_COUNT); + } + + bool ShouldSparWith(Unit const* target) const override + { + return !target->GetAffectingPlayer(); + } + + void DamageTaken(Unit* attacker, uint32& damage) override + { + // nullify spell damage + if (!attacker->GetAffectingPlayer()) + damage = 0; + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_PRIMARY_ABILITY: + if (UseAbility(_info->PrimaryAbility)) + _events.ScheduleEvent(eventId, urand(15000, 20000)); + else + _events.ScheduleEvent(eventId, 1000); + break; + case EVENT_SECONDARY_ABILITY: + if (UseAbility(_info->SecondaryAbility)) + _events.ScheduleEvent(eventId, _info->SecondaryAbility == SPELL_SHOOT ? 2000 : urand(4000, 8000)); + else + _events.ScheduleEvent(eventId, 1000); + break; + case EVENT_THIRD_ABILITY: + if (UseAbility(_info->ThirdAbility)) + _events.ScheduleEvent(eventId, urand(6000, 8000)); + else + _events.ScheduleEvent(eventId, 1000); + break; + default: + break; + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI<npc_thorim_pre_phaseAI>(creature); + } +}; + +class npc_thorim_arena_phase : public CreatureScript +{ + public: + npc_thorim_arena_phase() : CreatureScript("npc_thorim_arena_phase") { } + + struct npc_thorim_arena_phaseAI : public npc_thorim_trashAI + { + npc_thorim_arena_phaseAI(Creature* creature) : npc_thorim_trashAI(creature) + { + switch (_info->Type) + { + case DARK_RUNE_CHAMPION: + case DARK_RUNE_WARBRINGER: + case DARK_RUNE_COMMONER: + case DARK_RUNE_EVOKER: + _isInArena = true; + break; + case DARK_RUNE_ACOLYTE: + { + _isInArena = (_info->Entry == NPC_DARK_RUNE_ACOLYTE_PRE); + SetBoundary(&ArenaBoundaries, !_isInArena); + break; + } + default: + _isInArena = false; + break; + } + } + + bool CanAIAttack(Unit const* who) const override + { + // don't try to attack players in balcony + if (_isInArena && HeightPositionCheck(true)(who)) + return false; + + return CheckBoundary(who); + } + + void Reset() override + { + _events.Reset(); + if (_info->PrimaryAbility) + _events.ScheduleEvent(EVENT_PRIMARY_ABILITY, urand(3000, 6000)); + if (_info->SecondaryAbility) + _events.ScheduleEvent(EVENT_SECONDARY_ABILITY, urand(7000, 9000)); + if (_info->ThirdAbility) + _events.ScheduleEvent(EVENT_THIRD_ABILITY, urand(6000, 8000)); + if (_info->Type == DARK_RUNE_CHAMPION) + _events.ScheduleEvent(EVENT_ABILITY_CHARGE, 8000); + } + + void EnterCombat(Unit* /*who*/) override + { + if (_info->Type == DARK_RUNE_WARBRINGER) + DoCast(me, SPELL_AURA_OF_CELERITY); + + if (!_isInArena) + if (Creature* colossus = _instance->GetCreature(DATA_RUNIC_COLOSSUS)) + colossus->AI()->DoAction(ACTION_ACTIVATE_RUNIC_SMASH); + } + + void EnterEvadeMode(EvadeReason why) override + { + if (why != EVADE_REASON_NO_HOSTILES && why != EVADE_REASON_BOUNDARY) + return; + + // this should only happen if theres no alive player in the arena -> summon orb + if (Creature* thorim = _instance->GetCreature(BOSS_THORIM)) + thorim->AI()->DoAction(ACTION_BERSERK); + ScriptedAI::EnterEvadeMode(why); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_PRIMARY_ABILITY: + if (UseAbility(_info->PrimaryAbility)) + _events.Repeat(3000, 6000); + else + _events.Repeat(1000); + break; + case EVENT_SECONDARY_ABILITY: + if (UseAbility(_info->SecondaryAbility)) + _events.Repeat(12000, 16000); + else + _events.Repeat(1000); + break; + case EVENT_THIRD_ABILITY: + if (UseAbility(_info->ThirdAbility)) + _events.Repeat(6000, 8000); + else + _events.Repeat(1000); + break; + case EVENT_ABILITY_CHARGE: + { + Unit* referer = me; + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, [referer](Unit* unit){ return unit->GetTypeId() == TYPEID_PLAYER && unit->IsInRange(referer, 8.0f, 25.0f); })) + DoCast(target, SPELL_CHARGE); + _events.ScheduleEvent(eventId, 12000); + break; + } + default: + break; + } + } + + private: + bool _isInArena; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI<npc_thorim_arena_phaseAI>(creature); + } +}; + +struct npc_thorim_minibossAI : public ScriptedAI +{ + npc_thorim_minibossAI(Creature* creature) : ScriptedAI(creature), _summons(me) + { + _instance = creature->GetInstanceScript(); + + SetBoundary(&ArenaBoundaries, true); + } + + bool CanAIAttack(Unit const* who) const final override + { + return CheckBoundary(who); + } + + void JustSummoned(Creature* summon) final override + { + _summons.Summon(summon); + } + + void SummonedCreatureDespawn(Creature* summon) final override + { + _summons.Despawn(summon); + } + + void DoAction(int32 action) override + { + if (action == ACTION_ACTIVATE_ADDS) + { + for (ObjectGuid const& guid : _summons) + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + summon->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); + } + } + +protected: + InstanceScript* _instance; + EventMap _events; + SummonList _summons; +}; + +class npc_runic_colossus : public CreatureScript +{ + public: + npc_runic_colossus() : CreatureScript("npc_runic_colossus") { } + + struct npc_runic_colossusAI : public npc_thorim_minibossAI + { + npc_runic_colossusAI(Creature* creature) : npc_thorim_minibossAI(creature) + { + Initialize(); + } + + void Initialize() + { + _runicActive = false; + } + + void Reset() override + { + Initialize(); + _events.Reset(); + + // close the Runic Door + _instance->HandleGameObject(_instance->GetGuidData(DATA_RUNIC_DOOR), false); + + // Spawn trashes + _summons.DespawnAll(); + for (SummonLocation const& s : ColossusAddLocations) + me->SummonCreature(s.entry, s.pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); + } + + void MoveInLineOfSight(Unit* /*who*/) override + { + // don't enter combat + } + + void DoAction(int32 action) override + { + npc_thorim_minibossAI::DoAction(action); + + if (_runicActive) + return; + + if (action == ACTION_ACTIVATE_RUNIC_SMASH) + { + _runicActive = true; + _events.ScheduleEvent(EVENT_RUNIC_SMASH, 7000); + } + } + + void JustDied(Unit* /*victim*/) override + { + // open the Runic Door + _instance->HandleGameObject(_instance->GetGuidData(DATA_RUNIC_DOOR), true); + + if (Creature* thorim = _instance->GetCreature(BOSS_THORIM)) + thorim->AI()->Talk(SAY_SPECIAL); + + if (Creature* giant = _instance->GetCreature(DATA_RUNE_GIANT)) + { + giant->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); + giant->AI()->DoAction(ACTION_ACTIVATE_ADDS); + } + } + + void EnterCombat(Unit* /*who*/) override + { + DoZoneInCombat(); + _events.Reset(); + _events.ScheduleEvent(EVENT_RUNIC_BARRIER, urand(12000, 15000)); + _events.ScheduleEvent(EVENT_SMASH, urand(15000, 18000)); + _events.ScheduleEvent(EVENT_RUNIC_CHARGE, urand(20000, 24000)); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_RUNIC_BARRIER: + Talk(EMOTE_RUNIC_BARRIER); + DoCastAOE(SPELL_RUNIC_BARRIER); + _events.Repeat(35000, 45000); + break; + case EVENT_SMASH: + DoCastAOE(SPELL_SMASH); + _events.Repeat(15000, 18000); + break; + case EVENT_RUNIC_CHARGE: + { + Unit* referer = me; + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, [referer](Unit* unit){ return unit->GetTypeId() == TYPEID_PLAYER && unit->IsInRange(referer, 8.0f, 40.0f); })) + DoCast(target, SPELL_RUNIC_CHARGE); + _events.Repeat(20000); + break; + } + case EVENT_RUNIC_SMASH: + DoCast(me, RAND(SPELL_RUNIC_SMASH_LEFT, SPELL_RUNIC_SMASH_RIGHT)); + _events.Repeat(6000); + break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + + private: + bool _runicActive; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI<npc_runic_colossusAI>(creature); + } +}; + +class npc_ancient_rune_giant : public CreatureScript +{ + public: + npc_ancient_rune_giant() : CreatureScript("npc_ancient_rune_giant") { } + + struct npc_ancient_rune_giantAI : public npc_thorim_minibossAI + { + npc_ancient_rune_giantAI(Creature* creature) : npc_thorim_minibossAI(creature) { } + + void Reset() override + { + _events.Reset(); + + // close the Stone Door + _instance->HandleGameObject(_instance->GetGuidData(DATA_STONE_DOOR), false); + + // Spawn trashes + _summons.DespawnAll(); + for (SummonLocation const& s : GiantAddLocations) + me->SummonCreature(s.entry, s.pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); + } + + void EnterCombat(Unit* /*who*/) override + { + DoZoneInCombat(); + _events.Reset(); + _events.ScheduleEvent(EVENT_RUNIC_FORTIFICATION, 1); + _events.ScheduleEvent(EVENT_STOMP, urand(10000, 12000)); + _events.ScheduleEvent(EVENT_RUNE_DETONATION, 25000); + } + + void JustDied(Unit* /*victim*/) override + { + // opem the Stone Door + _instance->HandleGameObject(_instance->GetGuidData(DATA_STONE_DOOR), true); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_RUNIC_FORTIFICATION: + Talk(EMOTE_RUNIC_MIGHT); + DoCastAOE(SPELL_RUNIC_FORTIFICATION); + break; + case EVENT_STOMP: + DoCastAOE(SPELL_STOMP); + _events.Repeat(10000, 12000); + break; + case EVENT_RUNE_DETONATION: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, true)) + DoCast(target, SPELL_RUNE_DETONATION); + _events.Repeat(10000, 12000); + break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI<npc_ancient_rune_giantAI>(creature); + } +}; + +class npc_sif : public CreatureScript +{ + public: + npc_sif() : CreatureScript("npc_sif") { } + + struct npc_sifAI : public ScriptedAI + { + npc_sifAI(Creature* creature) : ScriptedAI(creature) + { + SetCombatMovement(false); + _instance = creature->GetInstanceScript(); + } + + void Reset() override + { + _events.Reset(); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override + { + if (spellInfo->Id == SPELL_STORMHAMMER_SIF) + { + me->InterruptSpell(CURRENT_GENERIC_SPELL); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_START_HARD_MODE) + { + me->SetReactState(REACT_AGGRESSIVE); + DoZoneInCombat(me, 250.0f); + Talk(SAY_SIF_EVENT); + _events.Reset(); + _events.ScheduleEvent(EVENT_FROSTBOLT, 2000); + _events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 15000); + _events.ScheduleEvent(EVENT_BLINK, urand(20000, 25000)); + _events.ScheduleEvent(EVENT_BLIZZARD, 30000); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BLINK: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_BLINK); + _events.ScheduleEvent(EVENT_FROST_NOVA, 0); + _events.Repeat(20000, 25000); + return; + case EVENT_FROST_NOVA: + DoCastAOE(SPELL_FROSTNOVA); + return; + case EVENT_FROSTBOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_FROSTBOLT); + _events.Repeat(2000); + return; + case EVENT_FROSTBOLT_VOLLEY: + DoCastAOE(SPELL_FROSTBOLT_VOLLEY); + _events.Repeat(15000, 20000); + return; + case EVENT_BLIZZARD: + DoCastAOE(SPELL_BLIZZARD); + _events.Repeat(35000, 45000); + return; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + // no melee attack + } + + private: + EventMap _events; + InstanceScript* _instance; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI<npc_sifAI>(creature); + } +}; + +// 62576 - Blizzard +// 62602 - Blizzard +class spell_thorim_blizzard_effect : public SpellScriptLoader +{ + public: + spell_thorim_blizzard_effect() : SpellScriptLoader("spell_thorim_blizzard_effect") { } + + class spell_thorim_blizzard_effect_AuraScript : public AuraScript + { + PrepareAuraScript(spell_thorim_blizzard_effect_AuraScript); + + bool CheckAreaTarget(Unit* target) + { + /// @todo: fix this for all dynobj auras + if (target != GetOwner()) + { + // check if not stacking aura already on target + // this one prevents overriding auras periodically by 2 near area aura owners + Unit::AuraApplicationMap const& auraMap = target->GetAppliedAuras(); + for (Unit::AuraApplicationMap::const_iterator iter = auraMap.begin(); iter != auraMap.end(); ++iter) + { + Aura const* aura = iter->second->GetBase(); + if (GetId() == aura->GetId() && GetOwner() != aura->GetOwner() /*!GetAura()->CanStackWith(aura)*/) + return false; + } + } + + return true; + } + + void Register() override + { + DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_thorim_blizzard_effect_AuraScript::CheckAreaTarget); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_thorim_blizzard_effect_AuraScript(); + } +}; + +// 62580, 62604 - Frostbolt Volley +class spell_thorim_frostbolt_volley : public SpellScriptLoader +{ + public: + spell_thorim_frostbolt_volley() : SpellScriptLoader("spell_thorim_frostbolt_volley") { } + + class spell_thorim_frostbolt_volley_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_frostbolt_volley_SpellScript); + + void FilterTargets(std::list<WorldObject*>& unitList) + { + unitList.remove_if([](WorldObject* target) + { + return target->GetTypeId() != TYPEID_PLAYER && (target->GetTypeId() != TYPEID_UNIT || !target->ToUnit()->IsPet()); + }); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thorim_frostbolt_volley_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_frostbolt_volley_SpellScript(); + } +}; + +// 62016 - Charge Orb +class spell_thorim_charge_orb : public SpellScriptLoader +{ + public: + spell_thorim_charge_orb() : SpellScriptLoader("spell_thorim_charge_orb") { } + + class spell_thorim_charge_orb_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_charge_orb_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_LIGHTNING_PILLAR_1 }); + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if(HeightPositionCheck(false)); + + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } + + void HandleScript() + { + if (Unit* target = GetHitUnit()) + target->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_PILLAR_1, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thorim_charge_orb_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + AfterHit += SpellHitFn(spell_thorim_charge_orb_SpellScript::HandleScript); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_charge_orb_SpellScript(); + } +}; + +// 62466 - Lightning Charge +class spell_thorim_lightning_charge : public SpellScriptLoader +{ + public: + spell_thorim_lightning_charge() : SpellScriptLoader("spell_thorim_lightning_charge") { } + + class spell_thorim_lightning_charge_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_lightning_charge_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_LIGHTNING_CHARGE }); + } + + void HandleFocus() + { + /// @workaround: focus target is not working because spell is triggered and instant + if (Creature* creature = GetCaster()->ToCreature()) + creature->FocusTarget(GetSpell(), GetExplTargetWorldObject()); + } + + void HandleCharge() + { + GetCaster()->CastSpell(GetCaster(), SPELL_LIGHTNING_CHARGE); + } + + void Register() override + { + BeforeCast += SpellCastFn(spell_thorim_lightning_charge_SpellScript::HandleFocus); + AfterCast += SpellCastFn(spell_thorim_lightning_charge_SpellScript::HandleCharge); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_lightning_charge_SpellScript(); + } +}; + +// 61934 - Leap +class spell_thorim_arena_leap : public SpellScriptLoader +{ + public: + spell_thorim_arena_leap() : SpellScriptLoader("spell_thorim_arena_leap") { } + + class spell_thorim_arena_leap_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_arena_leap_SpellScript); + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Position const* pos = GetHitDest()) + GetCaster()->ToCreature()->SetHomePosition(*pos); + } + + void Register() override + { + OnEffectLaunch += SpellEffectFn(spell_thorim_arena_leap_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_JUMP_DEST); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_arena_leap_SpellScript(); + } +}; + +struct OutOfArenaCheck +{ + bool operator()(Position const* who) const + { + return !CreatureAI::IsInBounds(ArenaBoundaries, who); + } +}; + +// 62042 - Stormhammer +class spell_thorim_stormhammer : public SpellScriptLoader +{ + public: + spell_thorim_stormhammer() : SpellScriptLoader("spell_thorim_stormhammer") { } + + class spell_thorim_stormhammer_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_stormhammer_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_STORMHAMMER_BOOMERANG, + SPELL_DEAFENING_THUNDER + }); + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if([](WorldObject* target) -> bool { return HeightPositionCheck(true)(target) || OutOfArenaCheck()(target); }); + + if (targets.empty()) + { + FinishCast(SPELL_FAILED_NO_VALID_TARGETS); + return; + } + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + { + target->CastSpell(target, SPELL_DEAFENING_THUNDER, true); + target->CastSpell(GetCaster(), SPELL_STORMHAMMER_BOOMERANG, true); + } + } + + void LoseHammer() + { + GetCaster()->SetVirtualItem(0, 0); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thorim_stormhammer_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY); + AfterCast += SpellCastFn(spell_thorim_stormhammer_SpellScript::LoseHammer); + OnEffectHitTarget += SpellEffectFn(spell_thorim_stormhammer_SpellScript::HandleScript, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_stormhammer_SpellScript(); + } +}; + +// 64767 - Stormhammer +class spell_thorim_stormhammer_sif : public SpellScriptLoader +{ + public: + spell_thorim_stormhammer_sif() : SpellScriptLoader("spell_thorim_stormhammer_sif") { } + + class spell_thorim_stormhammer_sif_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_stormhammer_sif_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_STORMHAMMER_BOOMERANG, + SPELL_SIF_TRANSFORM + }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + { + target->CastSpell(GetCaster(), SPELL_STORMHAMMER_BOOMERANG, true); + target->CastSpell(target, SPELL_SIF_TRANSFORM, true); + } + } + + void LoseHammer() + { + GetCaster()->SetVirtualItem(0, 0); + } + + void Register() override + { + AfterCast += SpellCastFn(spell_thorim_stormhammer_sif_SpellScript::LoseHammer); + OnEffectHitTarget += SpellEffectFn(spell_thorim_stormhammer_sif_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_stormhammer_sif_SpellScript(); + } +}; + +// 64909 - Stormhammer +class spell_thorim_stormhammer_boomerang : public SpellScriptLoader +{ + public: + spell_thorim_stormhammer_boomerang() : SpellScriptLoader("spell_thorim_stormhammer_boomerang") { } + + class spell_thorim_stormhammer_boomerang_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_stormhammer_boomerang_SpellScript); + + void RecoverHammer(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + target->SetVirtualItem(0, THORIM_WEAPON_DISPLAY_ID); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_thorim_stormhammer_boomerang_SpellScript::RecoverHammer, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_stormhammer_boomerang_SpellScript(); + } +}; + +// 62057, 62058 - Runic Smash +class spell_thorim_runic_smash : public SpellScriptLoader +{ + public: + spell_thorim_runic_smash() : SpellScriptLoader("spell_thorim_runic_smash") { } + + class spell_thorim_runic_smash_SpellScript : public SpellScript + { + PrepareSpellScript(spell_thorim_runic_smash_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_RUNIC_SMASH }); + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + std::vector<Creature*> triggers; + GetCaster()->GetCreatureListWithEntryInGrid(triggers, GetSpellInfo()->Id == SPELL_RUNIC_SMASH_LEFT ? NPC_GOLEM_LEFT_HAND_BUNNY : NPC_GOLEM_RIGHT_HAND_BUNNY, 150.0f); + for (Creature* trigger : triggers) + { + float dist = GetCaster()->GetExactDist(trigger); + trigger->m_Events.AddEvent(new RunicSmashExplosionEvent(trigger), trigger->m_Events.CalculateTime(uint64(dist * 30.f))); + }; + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_thorim_runic_smash_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_TRIGGER_SPELL); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_thorim_runic_smash_SpellScript(); + } +}; + +class UpperOrbCheck +{ + public: + UpperOrbCheck() : _check(true) { } + + bool operator() (Creature* target) const + { + return target->GetEntry() == NPC_THUNDER_ORB && _check(target); + } + + private: + HeightPositionCheck const _check; +}; + +// 62184 - Activate Lightning Orb Periodic +class spell_thorim_activate_lightning_orb_periodic : public SpellScriptLoader +{ + public: + spell_thorim_activate_lightning_orb_periodic() : SpellScriptLoader("spell_thorim_activate_lightning_orb_periodic") { } + + class spell_thorim_activate_lightning_orb_periodic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_thorim_activate_lightning_orb_periodic_AuraScript); + + InstanceScript* instance = nullptr; + + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + + Unit* caster = GetCaster(); + std::vector<Creature*> triggers; + + UpperOrbCheck check; + Trinity::CreatureListSearcher<UpperOrbCheck> searcher(caster, triggers, check); + Cell::VisitGridObjects(caster, searcher, 100.f); + + if (!triggers.empty()) + { + Creature* target = Trinity::Containers::SelectRandomContainerElement(triggers); + if (Creature* thorim = instance->GetCreature(BOSS_THORIM)) + thorim->AI()->SetGUID(target->GetGUID(), DATA_CHARGED_PILLAR); + } + } + + bool Load() override + { + if (Unit* caster = GetCaster()) + instance = caster->GetInstanceScript(); + + return instance != nullptr; + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_thorim_activate_lightning_orb_periodic_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_thorim_activate_lightning_orb_periodic_AuraScript(); + } +}; + +// 62331, 62418 - Impale +class spell_iron_ring_guard_impale : public SpellScriptLoader +{ + public: + spell_iron_ring_guard_impale() : SpellScriptLoader("spell_iron_ring_guard_impale") { } + + class spell_iron_ring_guard_impale_AuraScript : public AuraScript + { + PrepareAuraScript(spell_iron_ring_guard_impale_AuraScript); + + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + if (GetTarget()->HealthAbovePct(GetSpellInfo()->GetEffect(EFFECT_1)->CalcValue())) + { + Remove(AURA_REMOVE_BY_ENEMY_SPELL); + PreventDefaultAction(); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_iron_ring_guard_impale_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_iron_ring_guard_impale_AuraScript(); + } +}; + +class condition_thorim_arena_leap : public ConditionScript +{ + public: + condition_thorim_arena_leap() : ConditionScript("condition_thorim_arena_leap"), _check(false) { } + + bool OnConditionCheck(Condition const* condition, ConditionSourceInfo& sourceInfo) override + { + WorldObject* target = sourceInfo.mConditionTargets[condition->ConditionTarget]; + InstanceScript* instance = target->GetInstanceScript(); + + if (!instance) + return false; + + return _check(target); + } + + private: + HeightPositionCheck _check; +}; + void AddSC_boss_thorim() { new boss_thorim(); + new npc_thorim_pre_phase(); + new npc_thorim_arena_phase(); + new npc_runic_colossus(); + new npc_ancient_rune_giant(); + new npc_sif(); + new spell_thorim_blizzard_effect(); + new spell_thorim_frostbolt_volley(); + new spell_thorim_charge_orb(); + new spell_thorim_lightning_charge(); + new spell_thorim_stormhammer(); + new spell_thorim_stormhammer_sif(); + new spell_thorim_stormhammer_boomerang(); + new spell_thorim_arena_leap(); + new spell_thorim_runic_smash(); + new spell_thorim_activate_lightning_orb_periodic(); + new spell_iron_ring_guard_impale(); + new condition_thorim_arena_leap(); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index 2eb507db98c..be43dc8c6cb 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -774,7 +774,7 @@ class boss_sara : public CreatureScript { me->RemoveAllAuras(); me->SetReactState(REACT_PASSIVE); - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); _events.Reset(); _events.SetPhase(PHASE_ONE); } @@ -817,7 +817,7 @@ class boss_sara : public CreatureScript case EVENT_TRANSFORM_3: Talk(SAY_SARA_TRANSFORM_4); DoCast(me, SPELL_FULL_HEAL); - me->SetFaction(16); + me->SetFaction(FACTION_MONSTER_2); if (Creature* voice = _instance->GetCreature(DATA_VOICE_OF_YOGG_SARON)) voice->AI()->DoAction(ACTION_PHASE_TWO); if (Creature* mimiron = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MIMIRON_YS))) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index d34a7ac516c..a90efe7bfd8 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -38,9 +38,9 @@ static BossBoundaryData const boundaries = { BOSS_XT002, new RectangleBoundary(755.0f, 940.0f, -125.0f, 95.0f) }, { BOSS_ASSEMBLY_OF_IRON, new CircleBoundary(Position(1587.2f, 121.0f), 90.0) }, { BOSS_ALGALON, new CircleBoundary(Position(1632.668f, -307.7656f), 45.0) }, - { BOSS_ALGALON, new ZRangeBoundary(410.0f, 440.0f) }, + { BOSS_ALGALON, new ZRangeBoundary(410.0f, 470.0f) }, { BOSS_HODIR, new EllipseBoundary(Position(2001.5f, -240.0f), 50.0, 75.0) }, - { BOSS_THORIM, new CircleBoundary(Position(2134.73f, -263.2f), 50.0) }, + // Thorim sets boundaries dynamically { BOSS_FREYA, new RectangleBoundary(2094.6f, 2520.0f, -250.0f, 200.0f) }, { BOSS_MIMIRON, new CircleBoundary(Position(2744.0f, 2569.0f), 70.0) }, { BOSS_VEZAX, new RectangleBoundary(1740.0f, 1930.0f, 31.0f, 228.0f) }, @@ -59,6 +59,11 @@ static DoorData const doorData[] = { GO_MIMIRON_DOOR_1, BOSS_MIMIRON, DOOR_TYPE_ROOM }, { GO_MIMIRON_DOOR_2, BOSS_MIMIRON, DOOR_TYPE_ROOM }, { GO_MIMIRON_DOOR_3, BOSS_MIMIRON, DOOR_TYPE_ROOM }, + { GO_THORIM_ENCOUNTER_DOOR, BOSS_THORIM, DOOR_TYPE_ROOM }, + { GO_ANCIENT_GATE_OF_THE_KEEPERS, BOSS_HODIR, DOOR_TYPE_PASSAGE }, + { GO_ANCIENT_GATE_OF_THE_KEEPERS, BOSS_MIMIRON, DOOR_TYPE_PASSAGE }, + { GO_ANCIENT_GATE_OF_THE_KEEPERS, BOSS_THORIM, DOOR_TYPE_PASSAGE }, + { GO_ANCIENT_GATE_OF_THE_KEEPERS, BOSS_FREYA, DOOR_TYPE_PASSAGE }, { GO_VEZAX_DOOR, BOSS_VEZAX, DOOR_TYPE_PASSAGE }, { GO_YOGG_SARON_DOOR, BOSS_YOGG_SARON, DOOR_TYPE_ROOM }, { GO_DOODAD_UL_SIGILDOOR_03, BOSS_ALGALON, DOOR_TYPE_ROOM }, @@ -95,6 +100,10 @@ ObjectData const creatureData[] = { NPC_EXPEDITION_COMMANDER, DATA_EXPEDITION_COMMANDER }, { NPC_RAZORSCALE_CONTROLLER, DATA_RAZORSCALE_CONTROL }, + { NPC_SIF, DATA_SIF }, + { NPC_RUNIC_COLOSSUS, DATA_RUNIC_COLOSSUS }, + { NPC_RUNE_GIANT, DATA_RUNE_GIANT }, + { NPC_THORIM_CONTROLLER, DATA_THORIM_CONTROLLER }, { NPC_COMPUTER, DATA_COMPUTER }, { NPC_WORLD_TRIGGER_MIMIRON, DATA_MIMIRON_WORLD_TRIGGER }, { NPC_VOICE_OF_YOGG_SARON, DATA_VOICE_OF_YOGG_SARON }, @@ -118,6 +127,9 @@ ObjectData const objectData[] = { GO_RAZOR_HARPOON_2, GO_RAZOR_HARPOON_2 }, { GO_RAZOR_HARPOON_3, GO_RAZOR_HARPOON_3 }, { GO_RAZOR_HARPOON_4, GO_RAZOR_HARPOON_4 }, + { GO_THORIM_LEVER, DATA_THORIM_LEVER }, + { GO_THORIM_STONE_DOOR, DATA_STONE_DOOR }, + { GO_THORIM_RUNIC_DOOR, DATA_RUNIC_DOOR }, { 0, 0 } }; @@ -173,7 +185,9 @@ class instance_ulduar : public InstanceMapScript ObjectGuid LeviathanGateGUID; ObjectGuid KologarnChestGUID; ObjectGuid KologarnBridgeGUID; - ObjectGuid ThorimChestGUID; + ObjectGuid ThorimDarkIronPortcullisGUID; + ObjectGuid CacheOfStormsGUID; + ObjectGuid CacheOfStormsHardmodeGUID; ObjectGuid HodirRareCacheGUID; ObjectGuid HodirChestGUID; ObjectGuid MimironTramGUID; @@ -330,6 +344,16 @@ class instance_ulduar : public InstanceMapScript creature->UpdateEntry(NPC_BATTLE_PRIEST_GINA); break; + // Thorim + case NPC_MERCENARY_CAPTAIN_H: + if (TeamInInstance == HORDE) + creature->UpdateEntry(NPC_MERCENARY_CAPTAIN_A); + break; + case NPC_MERCENARY_SOLDIER_H: + if (TeamInInstance == HORDE) + creature->UpdateEntry(NPC_MERCENARY_SOLDIER_A); + break; + // Freya case NPC_IRONBRANCH: ElderGUIDs[0] = creature->GetGUID(); @@ -447,9 +471,16 @@ class instance_ulduar : public InstanceMapScript if (GetBossState(BOSS_KOLOGARN) == DONE) HandleGameObject(ObjectGuid::Empty, false, gameObject); break; - case GO_THORIM_CHEST_HERO: - case GO_THORIM_CHEST: - ThorimChestGUID = gameObject->GetGUID(); + case GO_THORIM_DARK_IRON_PORTCULLIS: + ThorimDarkIronPortcullisGUID = gameObject->GetGUID(); + break; + case GO_CACHE_OF_STORMS_10: + case GO_CACHE_OF_STORMS_25: + CacheOfStormsGUID = gameObject->GetGUID(); + break; + case GO_CACHE_OF_STORMS_HARDMODE_10: + case GO_CACHE_OF_STORMS_HARDMODE_25: + CacheOfStormsHardmodeGUID = gameObject->GetGUID(); break; case GO_HODIR_RARE_CACHE_OF_WINTER_HERO: case GO_HODIR_RARE_CACHE_OF_WINTER: @@ -656,11 +687,25 @@ class instance_ulduar : public InstanceMapScript case BOSS_THORIM: if (state == DONE) { - if (GameObject* gameObject = instance->GetGameObject(ThorimChestGUID)) - gameObject->SetRespawnTime(gameObject->GetRespawnDelay()); + if (Creature* thorim = GetCreature(BOSS_THORIM)) + { + if (GameObject* cache = instance->GetGameObject(thorim->AI()->GetData(DATA_THORIM_HARDMODE) ? CacheOfStormsHardmodeGUID : CacheOfStormsGUID)) + { + cache->SetLootRecipient(thorim->GetLootRecipient()); + cache->SetRespawnTime(cache->GetRespawnDelay()); + cache->RemoveFlag(GO_FLAG_LOCKED); + cache->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + cache->RemoveFlag(GO_FLAG_NODESPAWN); + } + } instance->SummonCreature(NPC_THORIM_OBSERVATION_RING, ObservationRingKeepersPos[2]); } + else + { + DoCloseDoorOrButton(GetGuidData(DATA_THORIM_LEVER)); + DoCloseDoorOrButton(ThorimDarkIronPortcullisGUID); + } break; case BOSS_ALGALON: if (state == DONE) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h index a26b699b396..885686e6fc6 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h @@ -157,6 +157,31 @@ enum UlduarNPCs // Freya Achievement Trigger NPC_FREYA_ACHIEVE_TRIGGER = 33406, + // Thorim + NPC_THORIM_INVISIBLE_STALKER = 32780, + NPC_JORMUNGAR_BEHEMOTH = 32882, + NPC_MERCENARY_CAPTAIN_A = 32908, + NPC_MERCENARY_CAPTAIN_H = 32907, + NPC_MERCENARY_SOLDIER_A = 32885, + NPC_MERCENARY_SOLDIER_H = 32883, + NPC_DARK_RUNE_ACOLYTE_PRE = 32886, + NPC_RUNIC_COLOSSUS = 32872, + NPC_RUNE_GIANT = 32873, + NPC_IRON_RING_GUARD = 32874, + NPC_IRON_HONOR_GUARD = 32875, + NPC_DARK_RUNE_CHAMPION = 32876, + NPC_DARK_RUNE_WARBRINGER = 32877, + NPC_DARK_RUNE_EVOKER = 32878, + NPC_DARK_RUNE_COMMONER = 32904, + NPC_DARK_RUNE_ACOLYTE = 33110, + NPC_THORIM_EVENT_BUNNY = 32892, + NPC_LIGHTNING_ORB = 33138, + NPC_GOLEM_RIGHT_HAND_BUNNY = 33140, + NPC_GOLEM_LEFT_HAND_BUNNY = 33141, + NPC_SIF = 33196, + NPC_THUNDER_ORB = 33378, + NPC_THORIM_CONTROLLER = 32879, + // Yogg-Saron NPC_SARA = 33134, NPC_GUARDIAN_OF_YOGG_SARON = 33136, @@ -231,6 +256,8 @@ enum UlduarGameObjects GO_KOLOGARN_BRIDGE = 194232, GO_KOLOGARN_DOOR = 194553, + GO_ANCIENT_GATE_OF_THE_KEEPERS = 194255, + // Hodir GO_HODIR_ENTRANCE = 194442, GO_HODIR_DOOR = 194634, @@ -241,8 +268,15 @@ enum UlduarGameObjects GO_HODIR_CHEST = 194307, // Thorim - GO_THORIM_CHEST_HERO = 194315, - GO_THORIM_CHEST = 194314, + GO_CACHE_OF_STORMS_10 = 194312, + GO_CACHE_OF_STORMS_HARDMODE_10 = 194313, + GO_CACHE_OF_STORMS_25 = 194315, + GO_CACHE_OF_STORMS_HARDMODE_25 = 194314, + GO_THORIM_RUNIC_DOOR = 194557, + GO_THORIM_STONE_DOOR = 194558, + GO_THORIM_ENCOUNTER_DOOR = 194559, + GO_THORIM_LEVER = 194264, + GO_THORIM_DARK_IRON_PORTCULLIS = 194560, // Mimiron GO_MIMIRON_TRAM = 194675, @@ -407,6 +441,16 @@ enum UlduarData DATA_ALGALON_TRAPDOOR, DATA_BRANN_BRONZEBEARD_ALG, + // Thorim + DATA_SIF, + DATA_THORIM_LEVER, + DATA_RUNIC_COLOSSUS, + DATA_RUNE_GIANT, + DATA_RUNIC_DOOR, + DATA_STONE_DOOR, + DATA_THORIM_HARDMODE, + DATA_THORIM_CONTROLLER, + // Misc DATA_BRANN_BRONZEBEARD_INTRO, DATA_LORE_KEEPER_OF_NORGANNON, @@ -423,10 +467,15 @@ enum UlduarWorldStates enum UlduarAchievementData { // FL Achievement boolean - DATA_UNBROKEN = 29052906, // 2905, 2906 are achievement IDs, + DATA_UNBROKEN = 29052906, // 2905, 2906 are achievement IDs, MAX_HERALD_ARMOR_ITEMLEVEL = 226, - MAX_HERALD_WEAPON_ITEMLEVEL = 232, - SPELL_LUMBERJACKED_CREDIT = 65296 + MAX_HERALD_WEAPON_ITEMLEVEL = 232 +}; + +enum UlduarSharedSpells +{ + SPELL_LUMBERJACKED_CREDIT = 65296, + SPELL_TELEPORT_KEEPER_VISUAL = 62940 // used by keepers }; enum UlduarEvents @@ -445,6 +494,23 @@ enum YoggSaronIllusions STORMWIND_ILLUSION = 2, }; +class KeeperDespawnEvent : public BasicEvent +{ +public: + KeeperDespawnEvent(Creature* owner, uint32 despawnTimerOffset = 500) : _owner(owner), _despawnTimer(despawnTimerOffset) { } + + bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override + { + _owner->CastSpell(_owner, SPELL_TELEPORT_KEEPER_VISUAL); + _owner->DespawnOrUnsummon(1000 + _despawnTimer); + return true; + } + +private: + Creature* _owner; + uint32 _despawnTimer; +}; + template<typename AI, typename T> inline AI* GetUlduarAI(T* obj) { diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp index 5b9ab19fb86..ae9d06e9e5d 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp @@ -277,7 +277,7 @@ class npc_frozen_orb_stalker : public CreatureScript { Position pos; me->GetNearPoint(toravon, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, 10.0f, 0.0f); - me->SetPosition(pos); + me->UpdatePosition(pos); DoCast(me, SPELL_FROZEN_ORB_SUMMON); } } diff --git a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp index 042b8dad906..8d7231d2ea3 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp @@ -243,7 +243,7 @@ class npc_ichor_globule : public CreatureScript struct npc_ichor_globuleAI : public ScriptedAI { - npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature) + npc_ichor_globuleAI(Creature* creature) : ScriptedAI(creature), _splashTriggered(false) { _instance = creature->GetInstanceScript(); creature->SetReactState(REACT_PASSIVE); @@ -275,14 +275,21 @@ class npc_ichor_globule : public CreatureScript // this feature should be still implemented void DamageTaken(Unit* /*attacker*/, uint32& damage) override { + if (_splashTriggered) + return; + if (damage >= me->GetHealth()) + { + _splashTriggered = true; DoCastAOE(SPELL_SPLASH); + } } void UpdateAI(uint32 /*diff*/) override { } private: InstanceScript* _instance; + bool _splashTriggered; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/northrend_script_loader.cpp b/src/server/scripts/Northrend/northrend_script_loader.cpp index 934d29e61b6..57bd641591c 100644 --- a/src/server/scripts/Northrend/northrend_script_loader.cpp +++ b/src/server/scripts/Northrend/northrend_script_loader.cpp @@ -110,6 +110,7 @@ void AddSC_boss_general_vezax(); void AddSC_boss_mimiron(); void AddSC_boss_hodir(); void AddSC_boss_freya(); +void AddSC_boss_thorim(); void AddSC_boss_yogg_saron(); void AddSC_boss_algalon_the_observer(); void AddSC_instance_ulduar(); @@ -291,6 +292,7 @@ void AddNorthrendScripts() AddSC_boss_mimiron(); AddSC_boss_hodir(); AddSC_boss_freya(); + AddSC_boss_thorim(); AddSC_boss_yogg_saron(); AddSC_boss_algalon_the_observer(); AddSC_instance_ulduar(); diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index b14038431ad..b487d7fc6e1 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -119,7 +119,7 @@ public: DoCast(me, SPELL_EXPLODE_CART, true); DoCast(me, SPELL_SUMMON_CART, true); if (GameObject* cart = me->FindNearestGameObject(GO_EXPLOSIVES_CART, 3.0f)) - cart->SetFaction(14); + cart->SetFaction(FACTION_MONSTER); phaseTimer = 3000; phase = 2; break; @@ -557,9 +557,6 @@ enum Lurgglbr GO_CAGE = 187369, - FACTION_ESCORTEE_A = 774, - FACTION_ESCORTEE_H = 775, - SAY_START_1 = 0, SAY_START_2 = 1, SAY_END_1 = 2, @@ -681,11 +678,11 @@ public: switch (player->GetTeam()) { case ALLIANCE: - me->SetFaction(FACTION_ESCORTEE_A); + me->SetFaction(FACTION_ESCORTEE_A_PASSIVE); break; default: case HORDE: - me->SetFaction(FACTION_ESCORTEE_H); + me->SetFaction(FACTION_ESCORTEE_H_PASSIVE); break; } } @@ -1690,10 +1687,10 @@ public: switch (player->GetTeam()) { case ALLIANCE: - me->SetFaction(FACTION_ESCORTEE_A); + me->SetFaction(FACTION_ESCORTEE_A_PASSIVE); break; case HORDE: - me->SetFaction(FACTION_ESCORTEE_H); + me->SetFaction(FACTION_ESCORTEE_H_PASSIVE); break; } me->SetStandState(UNIT_STAND_STATE_STAND); diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index 5c7df86b6a4..9c3f455bc41 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -379,9 +379,7 @@ enum StrengthenAncientsMisc SPELL_CREATE_ITEM_BARK = 47550, SPELL_CONFUSED = 47044, - NPC_LOTHALOR = 26321, - - FACTION_WALKER_ENEMY = 14, + NPC_LOTHALOR = 26321 }; class spell_q12096_q12092_dummy : public SpellScriptLoader // Strengthen the Ancients: On Interact Dummy to Woodlands Walker @@ -414,7 +412,7 @@ public: else if (roll == 0) // enemy version { tree->AI()->Talk(SAY_WALKER_ENEMY, player); - tree->SetFaction(FACTION_WALKER_ENEMY); + tree->SetFaction(FACTION_MONSTER); tree->Attack(player, true); } } diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp index d5c06855cff..bfb90373474 100644 --- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp +++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp @@ -103,7 +103,7 @@ public: Talk(SAY_WORGRAGGRO3); if (Creature* RWORG = me->SummonCreature(NPC_RAVENOUS_WORG, me->GetPositionX()+10, me->GetPositionY()+8, me->GetPositionZ()+2, 3.229f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000)) { - RWORG->SetFaction(35); + RWORG->SetFaction(FACTION_FRIENDLY); _RavenousworgGUID = RWORG->GetGUID(); } break; @@ -136,7 +136,7 @@ public: { RWORG->Kill(Mrfloppy); Mrfloppy->ExitVehicle(); - RWORG->SetFaction(14); + RWORG->SetFaction(FACTION_MONSTER); RWORG->GetMotionMaster()->MovePoint(0, RWORG->GetPositionX()+10, RWORG->GetPositionY()+80, RWORG->GetPositionZ()); Talk(SAY_VICTORY2); } diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index cab2d03b29c..1989b424679 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -43,7 +43,6 @@ EndContentData */ enum Entries { NPC_APOTHECARY_HANES = 23784, - FACTION_ESCORTEE_H = 775, QUEST_TRAIL_OF_FIRE = 11241, SPELL_HEALING_POTION = 17534, @@ -155,7 +154,7 @@ public: break; case EVENT_START_ESCORT: events.Reset(); - me->SetFaction(FACTION_ESCORTEE_H); + me->SetFaction(FACTION_ESCORTEE_H_PASSIVE); me->SetReactState(REACT_AGGRESSIVE); ENSURE_AI(npc_escortAI, (me->AI()))->Start(true, true, _player); break; diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp index 8adbd37d884..bf4caf3128f 100644 --- a/src/server/scripts/Northrend/zone_icecrown.cpp +++ b/src/server/scripts/Northrend/zone_icecrown.cpp @@ -47,7 +47,7 @@ public: { Initialize(); creature->GetMotionMaster()->MovePoint(0, 8599.258f, 963.951f, 547.553f); - creature->SetFaction(35); //wrong faction in db? + creature->SetFaction(FACTION_FRIENDLY); //wrong faction in db? } void Initialize() @@ -69,7 +69,7 @@ public: if (uiType != POINT_MOTION_TYPE) return; - me->SetFaction(14); + me->SetFaction(FACTION_MONSTER); } void DamageTaken(Unit* pDoneBy, uint32& uiDamage) override @@ -78,7 +78,7 @@ public: { uiDamage = 0; pDoneBy->CastSpell(pDoneBy, SPELL_KILL_CREDIT, true); - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); me->DespawnOrUnsummon(5000); me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); EnterEvadeMode(); diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index 01285e18a61..5ace31b5cdd 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -340,7 +340,7 @@ public: if (quest->GetQuestId() == QUEST_DISASTER) { me->GetMotionMaster()->MoveJumpTo(0, 0.4f, 0.4f); - me->SetFaction(113); + me->SetFaction(FACTION_ESCORTEE_N_NEUTRAL_PASSIVE); Start(false, false, player->GetGUID()); Talk(SAY_WP_1); diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 8fbf23d4a44..5a0e6c5aa99 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -93,7 +93,7 @@ public: if (menuId == GOSSIP_ID && gossipListId == GOSSIP_OPTION_ID) { CloseGossipMenuFor(player); - me->SetFaction(113); + me->SetFaction(FACTION_ESCORTEE_N_NEUTRAL_PASSIVE); Start(true, true, player->GetGUID()); } return false; diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp index 48dc7a1d7e4..4e1079eccc7 100644 --- a/src/server/scripts/Northrend/zone_zuldrak.cpp +++ b/src/server/scripts/Northrend/zone_zuldrak.cpp @@ -139,7 +139,7 @@ public: void Reset() override { - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); DoCast(me, SPELL_KNEEL, true); // Little Hack for kneel - Thanks Illy :P } |
