diff options
Diffstat (limited to 'src')
4 files changed, 644 insertions, 320 deletions
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 1e2bc42c17d..f2afcdbc474 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3363,6 +3363,14 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->AttributesEx |= SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY; }); + // Blizzard (Thorim) + ApplySpellFix({ 62576, 62602 }, [](SpellInfo* spellInfo) + { + // DBC data is wrong for EFFECT_0, it's a different dynobject target than EFFECT_1 + // Both effects should be shared by the same DynObject + const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->TargetA = SpellImplicitTargetInfo(TARGET_DEST_CASTER_LEFT); + }); + // Spinning Up (Mimiron) ApplySpellFix({ 63414 }, [](SpellInfo* spellInfo) { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp index d15a5700bd4..f5a8e693922 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp @@ -20,17 +20,23 @@ #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 "GridNotifiersImpl.h" -#include "SpellAuraEffects.h" #include <G3D/Vector3.h> +#include "AreaBoundary.h" +#include "InstanceScript.h" +#include "ObjectAccessor.h" +#include "MotionMaster.h" enum Spells { // Thorim - SPELL_SHEAT_OF_LIGHTNING = 62276, + SPELL_SHEATH_OF_LIGHTNING = 62276, SPELL_STORMHAMMER = 62042, SPELL_STORMHAMMER_SIF = 64767, SPELL_STORMHAMMER_BOOMERANG = 64909, @@ -48,7 +54,8 @@ enum Spells SPELL_LIGHTNING_PILLAR_1 = 63238, // caster high position, target low position SPELL_UNBALANCING_STRIKE = 62130, SPELL_BERSERK_PHASE_1 = 62560, - SPELL_BERSERK_PHASE_2 = 26662, + SPELL_BERSERK_PHASE_2 = 62555, + SPELL_ACTIVATE_LIGHTNING_ORB_PERIODIC = 62184, // Credits SPELL_CREDIT_SIFFED = 64980, @@ -100,10 +107,9 @@ enum Events EVENT_JUMPDOWN, EVENT_UNBALANCING_STRIKE, EVENT_CHAIN_LIGHTNING, - EVENT_TRANSFER_ENERGY, - EVENT_RELEASE_ENERGY, - EVENT_ACTIVATE_LIGHNING_FIELD, - EVENT_CHECK_PLAYER, + EVENT_START_PERIODIC_CHARGE, + EVENT_LIGHTNING_CHARGE, + EVENT_ACTIVATE_LIGHTNING_FIELD, EVENT_OUTRO_1, EVENT_OUTRO_2, EVENT_OUTRO_3, @@ -138,20 +144,18 @@ enum Yells // Thorim SAY_AGGRO_1 = 0, SAY_AGGRO_2 = 1, - //SAY_SPECIAL_1 = 2, - SAY_SPECIAL_2 = 3, - //SAY_SPECIAL_3 = 4, - SAY_JUMPDOWN = 5, - SAY_SLAY = 6, - SAY_BERSERK = 7, - SAY_WIPE = 8, - SAY_DEATH = 9, - SAY_END_NORMAL_1 = 10, - SAY_END_NORMAL_2 = 11, - SAY_END_NORMAL_3 = 12, - SAY_END_HARD_1 = 13, - SAY_END_HARD_2 = 14, - SAY_END_HARD_3 = 15, + 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, @@ -165,27 +169,6 @@ enum Yells SAY_SIF_EVENT = 2 }; -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 -}; - enum PreAddSpells { SPELL_ACID_BREATH = 62315, @@ -204,6 +187,8 @@ enum PreAddSpells SPELL_HOLY_SMITE = 62335, + SPELL_LEAP = 61934, + SPELL_CHARGE = 32323, SPELL_MORTAL_STRIKE = 35054, SPELL_WHIRLWIND = 33500, @@ -226,6 +211,27 @@ enum PreAddSpells 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; @@ -235,40 +241,39 @@ struct ThorimTrashInfo uint32 ThirdAbility; }; -ThorimTrashInfo const StaticThorimTrashInfo[] = +uint8 const ThorimTrashCount = 13; +ThorimTrashInfo const StaticThorimTrashInfo[ThorimTrashCount] = { // Pre Phase - { BEHEMOTH, 32882, SPELL_ACID_BREATH, SPELL_SWEEP, 0 }, - { MERCENARY_CAPTAIN, 32908, SPELL_DEVASTATE, SPELL_HEROIC_SWIPE, SPELL_SUNDER_ARMOR }, // alliance? - { MERCENARY_SOLDIER, 32885, SPELL_BARBED_SHOT, SPELL_SHOOT, 0 }, // alliance? - { DARK_RUNE_ACOLYTE, 32886, SPELL_RENEW, SPELL_GREATER_HEAL, SPELL_CIRCLE_OF_HEALING }, - { MERCENARY_CAPTAIN, 32907, SPELL_DEVASTATE, SPELL_HEROIC_SWIPE, SPELL_SUNDER_ARMOR }, // horde? - { MERCENARY_SOLDIER, 32883, SPELL_BARBED_SHOT, SPELL_SHOOT, 0 }, // horde? + { 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 }, + { 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 }, + { 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, + ACTION_BERSERK }; -#define MAX_HARD_MODE_TIME 180000 // 3 Minutes - -#define IN_ARENA(who) (who->GetPositionX() < 2181.19f && who->GetPositionY() > -299.12f) - struct SummonLocation { Position pos; @@ -277,12 +282,12 @@ struct SummonLocation SummonLocation const PreAddLocations[] = { - { { 2149.68f, -263.477f, 419.679f, 3.120f }, 32882}, - { { 2131.31f, -271.640f, 419.840f, 2.188f }, 32908}, - { { 2127.24f, -259.182f, 419.974f, 5.917f }, 32885}, - { { 2123.32f, -254.770f, 419.840f, 6.170f }, 32885}, - { { 2120.10f, -258.990f, 419.840f, 6.250f }, 32885}, - { { 2129.09f, -277.142f, 419.756f, 1.222f }, 32886} + { { 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[] = @@ -306,6 +311,18 @@ SummonLocation const GiantAddLocations[] = Position const SifSpawnPosition = { 2148.301f, -297.8453f, 438.3308f, 2.687807f }; +enum Data +{ + DATA_CHARGED_PILLAR = 1, + + FACTION_FRIENDLY = 35 +}; + +enum DisplayIds +{ + THORIM_WEAPON_DISPLAY_ID = 45900 +}; + uint32 const LightningOrbPathSize = 8; G3D::Vector3 const LightningOrbPath[LightningOrbPathSize] = { @@ -319,10 +336,37 @@ G3D::Vector3 const LightningOrbPath[LightningOrbPathSize] = { 2182.310059f, -263.233093f, 414.739410f } }; -Position const ArenaCenter = { 2135.0f, -263.0f, 420.0f, 0.0f }; // used for trash jump calculation -Position const LightningFieldCenter = { 2135.0f, -312.5f, 438.0f, 0.0f }; // used for lightning field calculation +// 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; -// p2 start at 5357 + static float const THORIM_BALCONY_Z_CHECK; +}; +float const HeightPositionCheck::THORIM_BALCONY_Z_CHECK = 428.0f; class RunicSmashExplosionEvent : public BasicEvent { @@ -331,7 +375,7 @@ class RunicSmashExplosionEvent : public BasicEvent bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override { - _owner->CastSpell((Unit*)NULL, SPELL_RUNIC_SMASH); + _owner->CastSpell((Unit*)nullptr, SPELL_RUNIC_SMASH); return true; } @@ -344,22 +388,19 @@ class TrashJumpEvent : public BasicEvent public: TrashJumpEvent(Creature* owner) : _owner(owner), _stage(0) { } - bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override + bool Execute(uint64 eventTime, uint32 /*updateTime*/) override { switch (_stage) { case 0: - if (Creature* stalker = _owner->FindNearestCreature(NPC_THORIM_INVISIBLE_STALKER, 30.0f)) - { - _owner->GetMotionMaster()->MoveJump(*stalker, SPEED_CHARGE, 15.0f); - _owner->SetHomePosition(*stalker); - } + _owner->CastSpell((Unit*)nullptr, SPELL_LEAP); ++_stage; - _owner->m_Events.AddEvent(this, _owner->m_Events.CalculateTime(2000)); + _owner->m_Events.AddEvent(this, eventTime + 2000); return false; case 1: _owner->SetReactState(REACT_AGGRESSIVE); - _owner->AI()->DoZoneInCombat(_owner, 200.0f); + _owner->AI()->DoZoneInCombat(_owner); + _owner->AI()->SetBoundary(&ArenaBoundaries); return true; default: break; @@ -378,14 +419,14 @@ class LightningFieldEvent : public BasicEvent public: LightningFieldEvent(Creature* owner) : _owner(owner) { } - bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override + bool Execute(uint64 eventTime, uint32 /*updateTime*/) override { if (InstanceScript* instance = _owner->GetInstanceScript()) { if (instance->GetBossState(BOSS_THORIM) == IN_PROGRESS) { - _owner->CastSpell((Unit*)NULL, SPELL_LIGHTNING_FIELD); - _owner->m_Events.AddEvent(this, _owner->m_Events.CalculateTime(1000)); + _owner->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_FIELD); + _owner->m_Events.AddEvent(this, eventTime + 1000); return false; } } @@ -426,11 +467,13 @@ class boss_thorim : public CreatureScript if (_encounterFinished) return; + SetBoundary(nullptr); _Reset(); Initialize(); me->SetReactState(REACT_PASSIVE); - SetCombatMovement(false); + me->SetDisableGravity(true); + me->SetControlled(true, UNIT_STATE_ROOT); me->AddUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); events.SetPhase(PHASE_NULL); @@ -444,8 +487,38 @@ class boss_thorim : public CreatureScript for (SummonLocation const& s : PreAddLocations) me->SummonCreature(s.entry, s.pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000); - if (GameObject* go = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_THORIM_LEVER))) - go->AddFlag(GO_FLAG_NOT_SELECTABLE); + 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 + { + 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 @@ -458,7 +531,7 @@ class boss_thorim : public CreatureScript { if (spellInfo->Id == SPELL_TOUCH_OF_DOMINION_TRIGGERED) { - if (Creature* sif = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIF))) + if (Creature* sif = instance->GetCreature(DATA_SIF)) { sif->AI()->Talk(SAY_SIF_DESPAWN); sif->DespawnOrUnsummon(6000); @@ -486,12 +559,21 @@ class boss_thorim : public CreatureScript me->InterruptNonMeleeSpells(true); me->RemoveAllAttackers(); me->AttackStop(); - me->SetFaction(35); + me->SetFaction(FACTION_FRIENDLY); + me->AddUnitFlag(UNIT_FLAG_RENAME); - if (Creature* sif = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIF))) + if (Creature* controller = instance->GetCreature(DATA_THORIM_CONTROLLER)) + controller->RemoveAllAuras(); + if (Creature* pillar = ObjectAccessor::GetCreature(*me, _activePillarGUID)) + pillar->RemoveAllAuras(); + + if (_hardMode) { - summons.Despawn(sif); - sif->DespawnOrUnsummon(10000); + if (Creature* sif = instance->GetCreature(DATA_SIF)) + { + summons.Despawn(sif); + sif->DespawnOrUnsummon(10000); + } } _JustDied(); @@ -504,6 +586,15 @@ class boss_thorim : public CreatureScript 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 { _EnterCombat(); @@ -513,21 +604,23 @@ class boss_thorim : public CreatureScript 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, 25000, 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); - events.ScheduleEvent(EVENT_CHECK_PLAYER, 10000); - DoCast(me, SPELL_SHEAT_OF_LIGHTNING); + DoCast(me, SPELL_SHEATH_OF_LIGHTNING); - if (Creature* runic = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RUNIC_COLOSSUS))) - runic->AI()->DoAction(ACTION_ACTIVATE_RUNIC_SMASH); + if (Creature* runicColossus = instance->GetCreature(DATA_RUNIC_COLOSSUS)) + { + runicColossus->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); + runicColossus->AI()->DoAction(ACTION_ACTIVATE_ADDS); + } - if (GameObject* go = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_THORIM_LEVER))) - go->RemoveFlag(GO_FLAG_NOT_SELECTABLE); + if (GameObject* lever = instance->GetGameObject(DATA_THORIM_LEVER)) + lever->RemoveFlag(GO_FLAG_NOT_SELECTABLE); // Summon Sif me->SummonCreature(NPC_SIF, SifSpawnPosition); @@ -542,8 +635,7 @@ class boss_thorim : public CreatureScript summon->SetReactState(REACT_PASSIVE); summon->CastSpell(summon, SPELL_LIGHTNING_DESTRUCTION, true); - Position pos(LightningOrbPath[LightningOrbPathSize - 1].x, LightningOrbPath[LightningOrbPathSize - 1].y, LightningOrbPath[LightningOrbPathSize - 1].z); - summon->GetMotionMaster()->MovePoint(EVENT_CHARGE_PREPATH, pos, false); + 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); @@ -587,68 +679,52 @@ class boss_thorim : public CreatureScript Talk(SAY_AGGRO_2); break; case EVENT_SAY_SIF_START: - if (Creature* sif = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIF))) + if (Creature* sif = instance->GetCreature(DATA_SIF)) sif->AI()->Talk(SAY_SIF_START); break; case EVENT_START_SIF_CHANNEL: - if (Creature* sif = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIF))) + if (Creature* sif = instance->GetCreature(DATA_SIF)) sif->CastSpell(me, SPELL_TOUCH_OF_DOMINION); break; case EVENT_STORMHAMMER: DoCast(SPELL_STORMHAMMER); - events.ScheduleEvent(EVENT_STORMHAMMER, urand(15000, 20000), 0, PHASE_1); + events.Repeat(15000, 20000); break; case EVENT_CHARGE_ORB: DoCastAOE(SPELL_CHARGE_ORB); - events.ScheduleEvent(EVENT_CHARGE_ORB, urand(15000, 20000), 0, PHASE_1); + events.Repeat(15000, 20000); break; case EVENT_SUMMON_ADDS: SummonWave(); - events.ScheduleEvent(EVENT_SUMMON_ADDS, _orbSummoned ? 3000 : 10000, 0, PHASE_1); + events.Repeat(_orbSummoned ? 3000 : 10000); break; case EVENT_JUMPDOWN: if (_hardMode) - if (Creature* sif = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIF))) + if (Creature* sif = instance->GetCreature(DATA_SIF)) sif->AI()->DoAction(ACTION_START_HARD_MODE); - me->RemoveAurasDueToSpell(SPELL_SHEAT_OF_LIGHTNING); + me->RemoveAurasDueToSpell(SPELL_SHEATH_OF_LIGHTNING); me->SetReactState(REACT_AGGRESSIVE); - SetCombatMovement(true); - me->GetMotionMaster()->MoveJump(2134.79f, -263.03f, 419.84f, me->GetOrientation(), 30.0f, 20.0f); + 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); - events.ScheduleEvent(EVENT_TRANSFER_ENERGY, 20000, 0, PHASE_2); break; case EVENT_UNBALANCING_STRIKE: DoCastVictim(SPELL_UNBALANCING_STRIKE); - events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, urand(15000, 20000), 0, PHASE_2); + events.Repeat(15000, 20000); break; case EVENT_CHAIN_LIGHTNING: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) DoCast(target, SPELL_CHAIN_LIGHTNING); - events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, urand(7000, 15000), 0, PHASE_2); + events.Repeat(7000, 15000); break; - case EVENT_TRANSFER_ENERGY: - { - std::list<Creature*> triggers; - me->GetCreatureListWithEntryInGrid(triggers, NPC_THUNDER_ORB, 200.0f); - triggers.remove_if([](Creature* trigger) - { - return trigger->GetPositionZ() < 425.0f; - }); - - if (!triggers.empty()) - { - Creature* pillar = Trinity::Containers::SelectRandomContainerElement(triggers); - _activePillarGUID = pillar->GetGUID(); - pillar->CastSpell(pillar, SPELL_LIGHTNING_ORB_CHARGED, true); - pillar->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_PILLAR_2); - events.ScheduleEvent(EVENT_RELEASE_ENERGY, 8000, 0, PHASE_2); - } - - events.ScheduleEvent(EVENT_TRANSFER_ENERGY, 16000, 0, PHASE_2); + 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_RELEASE_ENERGY: + case EVENT_LIGHTNING_CHARGE: if (Creature* pillar = ObjectAccessor::GetCreature(*me, _activePillarGUID)) DoCast(pillar, SPELL_LIGHTNING_RELEASE); break; @@ -665,15 +741,15 @@ class boss_thorim : public CreatureScript DoCast(me, SPELL_BERSERK_PHASE_2, true); } break; - case EVENT_ACTIVATE_LIGHNING_FIELD: + case EVENT_ACTIVATE_LIGHTNING_FIELD: { std::list<Creature*> triggers; me->GetCreatureListWithEntryInGrid(triggers, NPC_THORIM_EVENT_BUNNY, 100.0f); triggers.remove_if([](Creature* bunny) { - if (bunny->GetPositionZ() < 430.0f) + if (HeightPositionCheck(false)(bunny)) return true; - return LightningFieldCenter.GetExactDist2dSq(bunny) > 1225.0f; + return LightningFieldCenter.GetExactDist2dSq(bunny) > 1296.0f; }); uint64 timer = 1000; @@ -682,7 +758,7 @@ class boss_thorim : public CreatureScript triggers.remove_if([](Creature* bunny) { - return LightningFieldCenter.GetExactDist2dSq(bunny) < 400.0f; + return LightningFieldCenter.GetExactDist2dSq(bunny) < 576.0f; }); triggers.sort([](Creature* a, Creature* b) @@ -690,26 +766,24 @@ class boss_thorim : public CreatureScript return a->GetPositionX() < b->GetPositionX(); }); - for (std::list<Creature*>::const_iterator itr = triggers.begin(); itr != triggers.end();) + for (auto itr = triggers.cbegin(); itr != triggers.cend();) { - std::list<Creature*>::const_iterator prev = itr++; + auto prev = itr++; if (itr != triggers.end()) (*prev)->CastSpell(*itr, SPELL_LIGHTNING_BEAM_CHANNEL); } break; } - case EVENT_CHECK_PLAYER: - if (!me->GetMap()->GetPlayersCountExceptGMs()) - EnterEvadeMode(); - events.ScheduleEvent(EVENT_CHECK_PLAYER, 10000); - break; case EVENT_OUTRO_1: Talk(_hardMode ? SAY_END_HARD_1 : SAY_END_NORMAL_1); - if (Creature* sif = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIF))) - DoCast(sif, SPELL_STORMHAMMER_SIF); + 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); @@ -717,6 +791,9 @@ class boss_thorim : public CreatureScript default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); @@ -741,7 +818,7 @@ class boss_thorim : public CreatureScript { // Event starts me->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); - DoZoneInCombat(me, 250.0f); + DoZoneInCombat(me); } break; default: @@ -754,7 +831,7 @@ class boss_thorim : public CreatureScript me->GetCreatureListWithEntryInGrid(triggerList, NPC_THORIM_EVENT_BUNNY, 100.0f); triggerList.remove_if([](Creature* bunny) { - if (bunny->GetPositionZ() < 430.0f) + if (HeightPositionCheck(false)(bunny)) return true; return ArenaCenter.GetExactDist2dSq(bunny) < 3025.0f; }); @@ -817,8 +894,8 @@ class boss_thorim : public CreatureScript if (actor->GetTypeId() != TYPEID_PLAYER || !me->IsWithinDistInMap(actor, 10.0f)) return false; - Creature* runicColossus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RUNIC_COLOSSUS)); - Creature* runeGiant = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RUNE_GIANT)); + Creature* runicColossus = instance->GetCreature(DATA_RUNIC_COLOSSUS); + Creature* runeGiant = instance->GetCreature(DATA_RUNE_GIANT); return runicColossus && !runicColossus->IsAlive() && runeGiant && !runeGiant->IsAlive(); } @@ -829,14 +906,14 @@ class boss_thorim : public CreatureScript Talk(SAY_JUMPDOWN); events.SetPhase(PHASE_2); events.ScheduleEvent(EVENT_JUMPDOWN, 8000); - events.ScheduleEvent(EVENT_ACTIVATE_LIGHNING_FIELD, 15000); + events.ScheduleEvent(EVENT_ACTIVATE_LIGHTNING_FIELD, 15000); events.RescheduleEvent(EVENT_BERSERK, 300000, 0, PHASE_2); - if (Creature* sif = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SIF))) + if (Creature* sif = instance->GetCreature(DATA_SIF)) sif->InterruptNonMeleeSpells(false); // Hard Mode - if (_hardMode && events.GetTimer() <= MAX_HARD_MODE_TIME) + if (_hardMode) DoCastAOE(SPELL_CREDIT_SIFFED, true); } else if (me->HealthBelowPctDamaged(1, damage)) @@ -867,7 +944,7 @@ struct npc_thorim_trashAI : public ScriptedAI npc_thorim_trashAI(Creature* creature) : ScriptedAI(creature) { _instance = creature->GetInstanceScript(); - for (uint8 i = 0; i < 13; ++i) + for (uint8 i = 0; i < ThorimTrashCount; ++i) if (me->GetEntry() == StaticThorimTrashInfo[i].Entry) _info = &StaticThorimTrashInfo[i]; @@ -891,32 +968,14 @@ struct npc_thorim_trashAI : public ScriptedAI return heal; } - /// returns heal amount of the given spell including hots - static uint32 GetTotalHeal(uint32 spellId, Unit const* caster) - { - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, caster->GetMap()->GetDifficultyID())) - return GetTotalHeal(spellInfo, caster); - return 0; - } - /// returns remaining heal amount on given target static uint32 GetRemainingHealOn(Unit* target) { uint32 heal = 0; - Unit::AuraApplicationMap const& auras = target->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - Aura const* aura = itr->second->GetBase(); + Unit::AuraEffectList const& auras = target->GetAuraEffectsByType(SPELL_AURA_PERIODIC_HEAL); + for (AuraEffect const* aurEff : auras) + heal += aurEff->GetAmount() * (aurEff->GetTotalTicks() - aurEff->GetTickNumber()); - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (AuraEffect const* aurEff = aura->GetEffect(i)) - { - if (aurEff->GetAuraType() == SPELL_AURA_PERIODIC_HEAL) - heal += aurEff->GetAmount() * (aurEff->GetTotalTicks() - aurEff->GetTickNumber()); - } - } - } return heal; } @@ -957,8 +1016,9 @@ struct npc_thorim_trashAI : public ScriptedAI static Unit* GetUnitWithMostMissingHp(SpellInfo const* spellInfo, Unit* caster) { - float range = spellInfo->GetMaxRange(false); - uint32 heal = GetTotalHeal(spellInfo, 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); @@ -968,17 +1028,13 @@ struct npc_thorim_trashAI : public ScriptedAI return target; } - static Unit* GetHealTarget(uint32 spellId, Unit* caster) + static Unit* GetHealTarget(SpellInfo const* spellInfo, Unit* caster) { Unit* healTarget = nullptr; - - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, caster->GetMap()->GetDifficultyID())) - { - 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); - } + 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; } @@ -986,34 +1042,43 @@ struct npc_thorim_trashAI : public ScriptedAI bool UseAbility(uint32 spellId) { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, GetDifficulty()); + if (!spellInfo) + return false; + Unit* target = nullptr; - if (AIHelper::GetTotalHeal(spellId, me)) - target = AIHelper::GetHealTarget(spellId, me); + if (AIHelper::GetTotalHeal(spellInfo, me)) + target = AIHelper::GetHealTarget(spellInfo, me); else target = me->GetVictim(); - if (target) + if (!target) + return false; + + if (_info->Type == MERCENARY_SOLDIER) { - if (_info->Type == MERCENARY_SOLDIER) + bool allowMove = true; + if (me->IsInRange(target, spellInfo->GetMinRange(), spellInfo->GetMaxRange())) + allowMove = false; + + if (IsCombatMovementAllowed() != allowMove) { - bool allowMove = true; - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, GetDifficulty())) - { - if (me->IsInRange(target, spellInfo->GetMinRange(false), spellInfo->GetMaxRange(false)) - && me->IsWithinLOSInMap(target)) - allowMove = false; - } SetCombatMovement(allowMove); - } - DoCast(target, spellId); - return true; + // need relaunch movement + ScriptedAI::AttackStart(target); + + // give some time to allow reposition, try again in a second + if (allowMove) + return false; + } } - return false; + DoCast(target, spellId); + return true; } - void UpdateAI(uint32 diff) override + void UpdateAI(uint32 diff) final override { if (!UpdateVictim()) return; @@ -1026,6 +1091,9 @@ struct npc_thorim_trashAI : public ScriptedAI while (uint32 eventId = _events.ExecuteEvent()) { ExecuteEvent(eventId); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } if (_info->Type == DARK_RUNE_ACOLYTE) @@ -1055,7 +1123,7 @@ class npc_thorim_pre_phase : public CreatureScript me->setActive(true); // prevent grid unload } - void Reset() + void Reset() override { _events.Reset(); if (_info->PrimaryAbility) @@ -1068,14 +1136,20 @@ class npc_thorim_pre_phase : public CreatureScript SetCombatMovement(false); } - void JustDied(Unit* /*victim*/) + 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; } @@ -1131,16 +1205,25 @@ class npc_thorim_arena_phase : public CreatureScript 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; } - //_isInArena = IN_ARENA(me); } bool CanAIAttack(Unit const* who) const override { - return _isInArena == IN_ARENA(who); + // don't try to attack players in balcony + if (_isInArena && HeightPositionCheck(true)(who)) + return false; + + return CheckBoundary(who); } void Reset() override @@ -1156,19 +1239,25 @@ class npc_thorim_arena_phase : public CreatureScript _events.ScheduleEvent(EVENT_ABILITY_CHARGE, 8000); } - void EnterCombat(Unit* /*who*/) + 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 + 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 - // might be called by mind control release or controllers death? if (Creature* thorim = _instance->GetCreature(BOSS_THORIM)) thorim->AI()->DoAction(ACTION_BERSERK); - ScriptedAI::EnterEvadeMode(); + ScriptedAI::EnterEvadeMode(why); } void ExecuteEvent(uint32 eventId) override @@ -1177,27 +1266,30 @@ class npc_thorim_arena_phase : public CreatureScript { case EVENT_PRIMARY_ABILITY: if (UseAbility(_info->PrimaryAbility)) - _events.ScheduleEvent(eventId, urand(3000, 6000)); + _events.Repeat(3000, 6000); else - _events.ScheduleEvent(eventId, 1000); + _events.Repeat(1000); break; case EVENT_SECONDARY_ABILITY: if (UseAbility(_info->SecondaryAbility)) - _events.ScheduleEvent(eventId, urand(12000, 16000)); + _events.Repeat(12000, 16000); else - _events.ScheduleEvent(eventId, 1000); + _events.Repeat(1000); break; case EVENT_THIRD_ABILITY: if (UseAbility(_info->ThirdAbility)) - _events.ScheduleEvent(eventId, urand(6000, 8000)); + _events.Repeat(6000, 8000); else - _events.ScheduleEvent(eventId, 1000); + _events.Repeat(1000); break; case EVENT_ABILITY_CHARGE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, [this](Unit* unit){ return unit->GetTypeId() == TYPEID_PLAYER && unit->IsInRange(me, 8.0f, 25.0f); })) + { + 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; } @@ -1218,20 +1310,35 @@ 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) override + void JustSummoned(Creature* summon) final override { _summons.Summon(summon); - ScriptedAI::JustSummoned(summon); } - void SummonedCreatureDespawn(Creature* summon) override + void SummonedCreatureDespawn(Creature* summon) final override { - ScriptedAI::SummonedCreatureDespawn(summon); _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; @@ -1247,13 +1354,20 @@ class npc_runic_colossus : public CreatureScript { npc_runic_colossusAI(Creature* creature) : npc_thorim_minibossAI(creature) { + Initialize(); + } + + void Initialize() + { + _runicActive = false; } void Reset() override { + Initialize(); _events.Reset(); - // Runed Door closed + // close the Runic Door _instance->HandleGameObject(_instance->GetGuidData(DATA_RUNIC_DOOR), false); // Spawn trashes @@ -1267,19 +1381,33 @@ class npc_runic_colossus : public CreatureScript // 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 { - // Runed Door opened + // open the Runic Door _instance->HandleGameObject(_instance->GetGuidData(DATA_RUNIC_DOOR), true); if (Creature* thorim = _instance->GetCreature(BOSS_THORIM)) - thorim->AI()->Talk(SAY_SPECIAL_2); - } + thorim->AI()->Talk(SAY_SPECIAL); - void DoAction(int32 action) override - { - if (action == ACTION_ACTIVATE_RUNIC_SMASH) - _events.ScheduleEvent(EVENT_RUNIC_SMASH, 12000); + 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 @@ -1289,7 +1417,6 @@ class npc_runic_colossus : public CreatureScript _events.ScheduleEvent(EVENT_RUNIC_BARRIER, urand(12000, 15000)); _events.ScheduleEvent(EVENT_SMASH, urand(15000, 18000)); _events.ScheduleEvent(EVENT_RUNIC_CHARGE, urand(20000, 24000)); - me->InterruptNonMeleeSpells(true); } void UpdateAI(uint32 diff) override @@ -1305,28 +1432,31 @@ class npc_runic_colossus : public CreatureScript { case EVENT_RUNIC_BARRIER: Talk(EMOTE_RUNIC_BARRIER); - DoCast(me, SPELL_RUNIC_BARRIER); - _events.ScheduleEvent(eventId, urand(35000, 45000)); + DoCastAOE(SPELL_RUNIC_BARRIER); + _events.Repeat(35000, 45000); break; case EVENT_SMASH: - DoCast(me, SPELL_SMASH); - _events.ScheduleEvent(eventId, urand(15000, 18000)); + 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.ScheduleEvent(eventId, 20000); + _events.Repeat(20000); break; } case EVENT_RUNIC_SMASH: DoCast(me, RAND(SPELL_RUNIC_SMASH_LEFT, SPELL_RUNIC_SMASH_RIGHT)); - _events.ScheduleEvent(eventId, 6000); + _events.Repeat(6000); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } if (!UpdateVictim()) @@ -1334,6 +1464,9 @@ class npc_runic_colossus : public CreatureScript DoMeleeAttackIfReady(); } + + private: + bool _runicActive; }; CreatureAI* GetAI(Creature* creature) const override @@ -1355,7 +1488,7 @@ class npc_ancient_rune_giant : public CreatureScript { _events.Reset(); - // Stone Door closed + // close the Stone Door _instance->HandleGameObject(_instance->GetGuidData(DATA_STONE_DOOR), false); // Spawn trashes @@ -1375,7 +1508,7 @@ class npc_ancient_rune_giant : public CreatureScript void JustDied(Unit* /*victim*/) override { - // Stone Door opened + // opem the Stone Door _instance->HandleGameObject(_instance->GetGuidData(DATA_STONE_DOOR), true); } @@ -1399,16 +1532,19 @@ class npc_ancient_rune_giant : public CreatureScript break; case EVENT_STOMP: DoCastAOE(SPELL_STOMP); - _events.ScheduleEvent(eventId, urand(10000, 12000)); + _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.ScheduleEvent(eventId, urand(10000, 12000)); + _events.Repeat(10000, 12000); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); @@ -1428,33 +1564,22 @@ class npc_sif : public CreatureScript struct npc_sifAI : public ScriptedAI { - npc_sifAI(Creature* creature) : ScriptedAI(creature), _summons(me) + npc_sifAI(Creature* creature) : ScriptedAI(creature) { SetCombatMovement(false); + _instance = creature->GetInstanceScript(); } void Reset() override { _events.Reset(); - _summons.DespawnAll(); - } - - void JustSummoned(Creature* summon) override - { - _summons.Summon(summon); - if (summon->GetEntry() == NPC_THORIM_EVENT_BUNNY) - summon->GetMotionMaster()->MoveRandom(60.0f); - } - - void SummonedCreatureDespawn(Creature* summon) override - { - _summons.Despawn(summon); } 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(); } @@ -1493,7 +1618,7 @@ class npc_sif : public CreatureScript if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) DoCast(target, SPELL_BLINK); _events.ScheduleEvent(EVENT_FROST_NOVA, 0); - _events.ScheduleEvent(eventId, urand(20000, 25000)); + _events.Repeat(20000, 25000); return; case EVENT_FROST_NOVA: DoCastAOE(SPELL_FROSTNOVA); @@ -1501,21 +1626,22 @@ class npc_sif : public CreatureScript case EVENT_FROSTBOLT: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) DoCast(target, SPELL_FROSTBOLT); - _events.ScheduleEvent(eventId, 2000); + _events.Repeat(2000); return; case EVENT_FROSTBOLT_VOLLEY: DoCastAOE(SPELL_FROSTBOLT_VOLLEY); - _events.ScheduleEvent(eventId, urand(15000, 20000)); + _events.Repeat(15000, 20000); return; case EVENT_BLIZZARD: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true)) - me->SummonCreature(NPC_THORIM_EVENT_BUNNY, *target, TEMPSUMMON_TIMED_DESPAWN, 25000); DoCastAOE(SPELL_BLIZZARD); - _events.ScheduleEvent(eventId, urand(35000, 45000)); + _events.Repeat(35000, 45000); return; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } // no melee attack @@ -1523,7 +1649,7 @@ class npc_sif : public CreatureScript private: EventMap _events; - SummonList _summons; + InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const override @@ -1532,48 +1658,6 @@ class npc_sif : public CreatureScript } }; -class HeightPositionCheck -{ - public: - HeightPositionCheck(bool ret) : _ret(ret) { } - - bool operator()(WorldObject* obj) const - { - return obj->GetPositionZ() > 425.0f == _ret; - } - - private: - bool _ret; -}; - -// 62577 - Blizzard -// 62603 - Blizzard -class spell_thorim_blizzard : public SpellScriptLoader -{ - public: - spell_thorim_blizzard() : SpellScriptLoader("spell_thorim_blizzard") { } - - class spell_thorim_blizzard_SpellScript : public SpellScript - { - PrepareSpellScript(spell_thorim_blizzard_SpellScript); - - void FilterTargets(std::list<WorldObject*>& targets) - { - targets.remove_if(HeightPositionCheck(true)); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thorim_blizzard_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_thorim_blizzard_SpellScript(); - } -}; - // 62576 - Blizzard // 62602 - Blizzard class spell_thorim_blizzard_effect : public SpellScriptLoader @@ -1600,6 +1684,7 @@ class spell_thorim_blizzard_effect : public SpellScriptLoader return false; } } + return true; } @@ -1662,10 +1747,7 @@ class spell_thorim_charge_orb : public SpellScriptLoader void FilterTargets(std::list<WorldObject*>& targets) { - targets.remove_if([](WorldObject* target) - { - return target->GetPositionZ() < 425.0f; - }); + targets.remove_if(HeightPositionCheck(false)); if (targets.empty()) return; @@ -1718,7 +1800,7 @@ class spell_thorim_lightning_charge : public SpellScriptLoader void HandleCharge() { - GetCaster()->CastSpell(GetCaster(), SPELL_LIGHTNING_CHARGE, true); + GetCaster()->CastSpell(GetCaster(), SPELL_LIGHTNING_CHARGE); } void Register() override @@ -1734,6 +1816,47 @@ class spell_thorim_lightning_charge : public SpellScriptLoader } }; +// 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 { @@ -1755,10 +1878,7 @@ class spell_thorim_stormhammer : public SpellScriptLoader void FilterTargets(std::list<WorldObject*>& targets) { - targets.remove_if([](WorldObject* target) - { - return !IN_ARENA(target); - }); + targets.remove_if([](WorldObject* target) -> bool { return HeightPositionCheck(true)(target) || OutOfArenaCheck()(target); }); if (targets.empty()) { @@ -1780,9 +1900,15 @@ class spell_thorim_stormhammer : public SpellScriptLoader } } + 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); } }; @@ -1821,8 +1947,14 @@ class spell_thorim_stormhammer_sif : public SpellScriptLoader } } + 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); } }; @@ -1833,6 +1965,34 @@ class spell_thorim_stormhammer_sif : public SpellScriptLoader } }; +// 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 { @@ -1852,12 +2012,12 @@ class spell_thorim_runic_smash : public SpellScriptLoader { PreventHitDefaultEffect(effIndex); - std::list<Creature*> triggers; + 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* bunny : triggers) + for (Creature* trigger : triggers) { - float dist = GetCaster()->GetExactDist(bunny); - bunny->m_Events.AddEvent(new RunicSmashExplosionEvent(bunny), bunny->m_Events.CalculateTime(uint64(dist * 30))); + float dist = GetCaster()->GetExactDist(trigger); + trigger->m_Events.AddEvent(new RunicSmashExplosionEvent(trigger), trigger->m_Events.CalculateTime(uint64(dist * 30.f))); }; } @@ -1873,6 +2033,122 @@ class spell_thorim_runic_smash : public SpellScriptLoader } }; +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(); @@ -1881,12 +2157,16 @@ void AddSC_boss_thorim() new npc_runic_colossus(); new npc_ancient_rune_giant(); new npc_sif(); - new spell_thorim_blizzard(); 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/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index c07fcbc0a6d..84773bff908 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -40,7 +40,7 @@ static BossBoundaryData const boundaries = { BOSS_ALGALON, new CircleBoundary(Position(1632.668f, -307.7656f), 45.0) }, { BOSS_ALGALON, new ZRangeBoundary(410.0f, 440.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 dinamically { 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) }, @@ -60,6 +60,10 @@ static DoorData const doorData[] = { 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 }, @@ -99,6 +103,7 @@ ObjectData const creatureData[] = { 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 }, @@ -180,6 +185,7 @@ class instance_ulduar : public InstanceMapScript ObjectGuid LeviathanGateGUID; ObjectGuid KologarnChestGUID; ObjectGuid KologarnBridgeGUID; + ObjectGuid ThorimDarkIronPortcullisGUID; ObjectGuid CacheOfStormsGUID; ObjectGuid CacheOfStormsHardmodeGUID; ObjectGuid HodirRareCacheGUID; @@ -338,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(); @@ -455,6 +471,9 @@ class instance_ulduar : public InstanceMapScript if (GetBossState(BOSS_KOLOGARN) == DONE) HandleGameObject(ObjectGuid::Empty, false, gameObject); break; + 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(); @@ -679,8 +698,14 @@ class instance_ulduar : public InstanceMapScript 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 e7279005679..885686e6fc6 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h @@ -159,6 +159,12 @@ enum UlduarNPCs // 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, @@ -174,6 +180,7 @@ enum UlduarNPCs NPC_GOLEM_LEFT_HAND_BUNNY = 33141, NPC_SIF = 33196, NPC_THUNDER_ORB = 33378, + NPC_THORIM_CONTROLLER = 32879, // Yogg-Saron NPC_SARA = 33134, @@ -249,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, @@ -266,7 +275,8 @@ enum UlduarGameObjects GO_THORIM_RUNIC_DOOR = 194557, GO_THORIM_STONE_DOOR = 194558, GO_THORIM_ENCOUNTER_DOOR = 194559, - GO_THORIM_LEVER = 194265, + GO_THORIM_LEVER = 194264, + GO_THORIM_DARK_IRON_PORTCULLIS = 194560, // Mimiron GO_MIMIRON_TRAM = 194675, @@ -439,6 +449,7 @@ enum UlduarData DATA_RUNIC_DOOR, DATA_STONE_DOOR, DATA_THORIM_HARDMODE, + DATA_THORIM_CONTROLLER, // Misc DATA_BRANN_BRONZEBEARD_INTRO, |