diff --git a/sql/updates/world/4.3.4/2022_09_22_00_world.sql b/sql/updates/world/4.3.4/2022_09_22_00_world.sql
new file mode 100644
index 0000000000..3e5c636500
--- /dev/null
+++ b/sql/updates/world/4.3.4/2022_09_22_00_world.sql
@@ -0,0 +1,40 @@
+UPDATE `creature_template` SET `ScriptName`= 'npc_volkhan_molten_golem' WHERE `entry`= 28695;
+UPDATE `creature_template` SET `mechanic_immune_mask`= `mechanic_immune_mask` | 0x2000000 | 0x100 WHERE `entry` IN (28587, 31536);
+
+UPDATE `creature` SET `spawndist`= 0, `MovementType`= 0 WHERE `guid`= 126875;
+DELETE FROM `creature_template_movement` WHERE `CreatureId`= 28823;
+INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`) VALUES
+(28823, 0, 0, 1, 1);
+
+DELETE FROM `creature_text` WHERE `CreatureID`= 28587;
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `comment`) VALUES
+(28587, 0, 0, 'It is you who have destroyed my children? You... shall... pay!', 14, 0, 100, 0, 0, 13960, 31415, 'Volkhan - Aggro'),
+(28587, 1, 0, '%s runs to his anvil!', 41, 0, 100, 0, 0, 0, 32214, 'Volkhan - Announce Run to Anvil'),
+(28587, 2, 0, 'Nothing is wasted in the process. You will see....', 14, 0, 100, 0, 0, 13962, 31417, 'Volkhan - Run to Anvil'),
+(28587, 2, 1, 'Life from lifelessness... death for you.', 14, 0, 100, 0, 0, 13961, 31416, 'Volkhan - Run to Anvil'),
+(28587, 3, 0, 'All my work... undone!', 14, 0, 100, 0, 0, 13964, 31419, 'Volkhan - Shattering Stomp'),
+(28587, 3, 1, 'I will crush you beneath my boots!', 14, 0, 100, 0, 0, 13963, 31418, 'Volkhan - Shattering Stomp'),
+(28587, 4, 0, '%s prepares to shatter his Brittle Golems!', 41, 0, 100, 0, 0, 0, 29823, 'Volkhan - Announce Shattering Stomp'),
+(28587, 5, 0, 'The master was right... to be concerned.', 14, 0, 100, 0, 0, 13968, 31423, 'Volkhan - Death');
+
+DELETE FROM `conditions` WHERE `SourceEntry` IN (52661, 52654, 52238, 52387, 59528) AND `SourceTypeOrReferenceId`= 13;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ScriptName`, `Comment`) VALUES
+(13, 1, 52661, 0, 0, 31, 0, 3, 28823, 0, 0, 0, '', 'Temper - Target Volkhan''s Anvil'),
+(13, 1, 52654, 0, 0, 31, 0, 3, 28823, 0, 0, 0, '', 'Temper - Target Volkhan''s Anvil'),
+(13, 1, 52238, 0, 0, 31, 0, 3, 28823, 0, 0, 0, '', 'Temper - Target Volkhan''s Anvil'),
+(13, 1, 52387, 0, 0, 31, 0, 3, 28695, 0, 0, 0, '', 'Heat - Target Molten Golem'),
+(13, 1, 59528, 0, 0, 31, 0, 3, 28695, 0, 0, 0, '', 'Heat - Target Molten Golem');
+
+DELETE FROM `spell_script_names` WHERE `ScriptName` IN
+('spell_volkhan_temper_dummy',
+'spell_volkhan_cool_down',
+'spell_volkhan_cosmetic_stun_immune_permanent',
+'spell_volkhan_shattering_stomp');
+
+INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
+(52654, 'spell_volkhan_temper_dummy'),
+(52238, 'spell_volkhan_temper_dummy'),
+(52441, 'spell_volkhan_cool_down'),
+(59123, 'spell_volkhan_cosmetic_stun_immune_permanent'),
+(52237, 'spell_volkhan_shattering_stomp'),
+(59529, 'spell_volkhan_shattering_stomp');
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp
index db6531d916..8da8da1957 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp
@@ -15,473 +15,460 @@
* with this program. If not, see .
*/
-/* ScriptData
-SDName: Boss Volkhan
-SD%Complete: 90%
-SDComment: Event should be pretty close minus a few visual flaws
-SDCategory: Halls of Lightning
-EndScriptData */
-
-#include "ScriptMgr.h"
#include "halls_of_lightning.h"
+#include "ScriptMgr.h"
+#include "GameObject.h"
+#include "InstanceScript.h"
+#include "Map.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
+#include "SpellAuraEffects.h"
#include "SpellInfo.h"
-#include "SpellMgr.h"
+#include "SpellScript.h"
enum Texts
{
- SAY_AGGRO = 0,
- SAY_FORGE = 1,
- SAY_STOMP = 2,
- SAY_SLAY = 3,
- SAY_DEATH = 4,
- EMOTE_TO_ANVIL = 5,
- EMOTE_SHATTER = 6,
+ // Volkhan
+ SAY_AGGRO = 0,
+ SAY_ANNOUNCE_RUN_TO_ANVIL = 1,
+ SAY_RUN_TO_ANVIL = 2,
+ SAY_SHATTERING_STOMP = 3,
+ SAY_ANNOUNCE_SHATTERING_STOMP = 4,
+ SAY_DEATH = 5
};
enum Spells
{
- SPELL_HEAT = 52387,
+ // Volkhan
+ SPELL_TEMPER_SUMMON_OBJECT = 52661,
+ SPELL_TEMPER_DUMMY_INTRO = 52654,
+ SPELL_TEMPER_DUMMY_COMBAT = 52238,
SPELL_SHATTERING_STOMP = 52237,
- SPELL_TEMPER = 52238,
- SPELL_TEMPER_DUMMY = 52654,
+ SPELL_HEAT = 52387,
+ SPELL_DAZE_IMMUNITY_CANCEL = 59556,
+
+ // Volkhan's Anvil
SPELL_SUMMON_MOLTEN_GOLEM = 52405,
- SPELL_FORGE_VISUAL = 52654,
// Molten Golem
- SPELL_BLAST_WAVE = 23113,
- SPELL_IMMOLATION_STRIKE = 52433,
+ SPELL_COOL_DOWN = 52441,
+ SPELL_COOL_DOWN_SLOW = 52443,
+ SPELL_STUN_SELF = 47067, // Serverside spell @todo
+ SPELL_COSMETIC_STUN_IMMUNE_FREEZE_AMNIM = 59123,
SPELL_SHATTER = 52429,
+ SPELL_INSTAKILL_SELF = 29878, // Serverside spell
+ SPELL_IMMOLATION_STRIKE = 52433
};
enum Events
{
- EVENT_PAUSE = 1,
- EVENT_SHATTERING_STOMP = 2,
- EVENT_SHATTER = 3,
- EVENT_FORGE_CAST = 4,
+ // Volkhan
+ EVENT_TEMPER_INTRO = 1,
+ EVENT_RUN_TO_ANVIL,
+ EVENT_TEMPER,
+ EVENT_HEAT,
+ EVENT_SHATTERING_STOMP,
// Molten Golem
- EVENT_BLAST = 5,
- EVENT_IMMOLATION = 6
+ EVENT_IMMOLATION_STRIKE
};
-enum Npcs
+enum Actions
{
- NPC_VOLKHAN_ANVIL = 28823,
- NPC_MOLTEN_GOLEM = 28695,
- NPC_BRITTLE_GOLEM = 28681,
- MAX_GOLEM = 2,
- DATA_SHATTER_RESISTANT = 2042
+ // Volkhan
+ ACTION_SHATTER_GOLEMS = 0,
+ ACTION_GOLEMS_TEMPERED = 1,
+
+ // Molten Golem
+ ACTION_SHATTER = 0
};
enum Phases
{
- PHASE_INTRO = 1,
- PHASE_NORMAL
+ // Volkhan
+ PHASE_INTRO = 1,
+ PHASE_COMBAT
};
-/*######
-## Boss Volkhan
-######*/
-class boss_volkhan : public CreatureScript
+enum MovePoints
{
-public:
- boss_volkhan() : CreatureScript("boss_volkhan") { }
+ // Volkhan
+ POINT_ID_ANVIL = 0
+};
- struct boss_volkhanAI : public BossAI
+enum Misc
+{
+ ENTRY_BRITTLE_GOLEM = 28681
+};
+
+enum Data
+{
+ DATA_SHATTER_RESISTANT = 0
+};
+
+static Position const AnvilPosition = { 1333.5901f, -103.67797f, 56.7177f };
+
+struct boss_volkhan : public BossAI
+{
+ boss_volkhan(Creature* creature) : BossAI(creature, DATA_VOLKHAN),
+ _shatteredGolems(false), _temperingGolems(false), _temperCycles(0), _shatteredGolemsCount(0) { }
+
+ void JustAppeared() override
{
- boss_volkhanAI(Creature* creature) : BossAI(creature, DATA_VOLKHAN)
+ events.SetPhase(PHASE_INTRO);
+ events.ScheduleEvent(EVENT_TEMPER_INTRO, 13s, 0, PHASE_INTRO);
+ }
+
+ void JustEngagedWith(Unit* who) override
+ {
+ BossAI::JustEngagedWith(who);
+ Talk(SAY_AGGRO, who);
+ events.SetPhase(PHASE_COMBAT);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ summons.DespawnAll();
+ _DespawnAtEvade();
+ }
+
+ void JustDied(Unit* killer) override
+ {
+ BossAI::JustDied(killer);
+ Talk(SAY_DEATH, killer);
+ }
+
+ void MovementInform(uint32 motionType, uint32 id)
+ {
+ if (motionType != POINT_MOTION_TYPE)
+ return;
+
+ switch (id)
{
- Initialize();
+ case POINT_ID_ANVIL:
+ events.ScheduleEvent(EVENT_TEMPER, 1s, 0, PHASE_COMBAT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void DamageTaken(Unit* /*attacker*/, uint32& damage) override
+ {
+ if (damage >= me->GetHealth() || _shatteredGolems)
+ return;
+
+ if (_temperCycles >= 3 && !_temperingGolems && !_shatteredGolems && me->HealthBelowPctDamaged(25, damage))
+ {
+ events.CancelEvent(EVENT_RUN_TO_ANVIL);
+ events.ScheduleEvent(EVENT_SHATTERING_STOMP, 1ms, 0, PHASE_COMBAT);
+ _shatteredGolems = true;
+ return;
}
- void Initialize()
+ if (_temperCycles < 3 && me->HealthBelowPctDamaged(100.f - (20.f + 20.f * _temperCycles), damage))
{
- m_bIsStriking = false;
- m_bHasTemper = false;
- m_bCanShatterGolem = false;
- m_uiDelay_Timer = 1000;
- m_uiSummonPhase = 0;
- GolemsShattered = 0;
-
- m_uiHealthAmountModifier = 1;
- }
-
- void Reset() override
- {
- Initialize();
- _Reset();
- DespawnGolem();
- m_lGolemGUIDList.clear();
- events.SetPhase(PHASE_INTRO);
- events.ScheduleEvent(EVENT_FORGE_CAST, 2 * IN_MILLISECONDS, 0, PHASE_INTRO);
- }
-
- void JustEngagedWith(Unit* who) override
- {
- Talk(SAY_AGGRO);
- events.SetPhase(PHASE_NORMAL);
- events.ScheduleEvent(EVENT_PAUSE, 3.5 * IN_MILLISECONDS, 0, PHASE_NORMAL);
- events.ScheduleEvent(EVENT_SHATTERING_STOMP, 0 * IN_MILLISECONDS, 0, PHASE_NORMAL);
- events.ScheduleEvent(EVENT_SHATTER, 5 * IN_MILLISECONDS, 0, PHASE_NORMAL);
- BossAI::JustEngagedWith(who);
- }
-
- void AttackStart(Unit* who) override
- {
- if (me->Attack(who, true))
+ if (!_temperingGolems)
{
- AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
-
- if (!m_bHasTemper)
- me->GetMotionMaster()->MoveChase(who);
+ events.ScheduleEvent(EVENT_RUN_TO_ANVIL, 1ms, 0, PHASE_COMBAT);
+ _temperingGolems = true;
}
+ ++_temperCycles;
}
- void JustDied(Unit* /*killer*/) override
+ }
+
+ void DoAction(int32 action) override
+ {
+ switch (action)
{
- Talk(SAY_DEATH);
- DespawnGolem();
-
- _JustDied();
- }
-
- void KilledUnit(Unit* who) override
- {
- if (who->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
- }
-
- void DespawnGolem()
- {
- if (m_lGolemGUIDList.empty())
- return;
-
- for (ObjectGuid guid : m_lGolemGUIDList)
- {
- if (Creature* temp = ObjectAccessor::GetCreature(*me, guid))
- if (temp->IsAlive())
- temp->DespawnOrUnsummon();
- }
-
- m_lGolemGUIDList.clear();
- }
-
- void ShatterGolem()
- {
- if (m_lGolemGUIDList.empty())
- return;
-
- for (ObjectGuid guid : m_lGolemGUIDList)
- {
- if (Creature* temp = ObjectAccessor::GetCreature(*me, guid))
+ case ACTION_SHATTER_GOLEMS:
+ summons.RemoveNotExisting();
+ for (ObjectGuid const& guid : summons)
{
- // Only shatter brittle golems
- if (temp->IsAlive() && temp->GetEntry() == NPC_BRITTLE_GOLEM)
+ if (Creature* golem = ObjectAccessor::GetCreature(*me, guid))
{
- temp->CastSpell(temp, SPELL_SHATTER, false);
- GolemsShattered += 1;
+ if (golem->HasAura(SPELL_COSMETIC_STUN_IMMUNE_FREEZE_AMNIM))
+ {
+ if (CreatureAI* ai = golem->AI())
+ {
+ ai->DoAction(ACTION_SHATTER);
+ ++_shatteredGolemsCount;
+ }
+ }
}
}
- }
+ break;
+ case ACTION_GOLEMS_TEMPERED:
+ _temperingGolems = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ uint32 GetData(uint32 type) const override
+ {
+ switch (type)
+ {
+ case DATA_SHATTER_RESISTANT:
+ return _shatteredGolemsCount;
+ default:
+ return 0;
}
- void JustSummoned(Creature* summoned) override
+ return 0;
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO))
+ return;
+
+ events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
{
- if (summoned->GetEntry() == NPC_MOLTEN_GOLEM)
+ switch (eventId)
{
- m_lGolemGUIDList.push_back(summoned->GetGUID());
-
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- summoned->GetMotionMaster()->MoveFollow(target, 0.0f, 0.0f);
-
- // Why healing when just summoned?
- summoned->CastSpell(summoned, SPELL_HEAT, CastSpellExtraArgs(false).SetOriginalCaster(me->GetGUID()));
+ case EVENT_TEMPER_INTRO:
+ DoCastAOE(SPELL_TEMPER_SUMMON_OBJECT);
+ DoCastAOE(SPELL_TEMPER_DUMMY_INTRO);
+ events.Repeat(13s);
+ break;
+ case EVENT_RUN_TO_ANVIL:
+ me->AttackStop();
+ me->SetReactState(REACT_PASSIVE);
+ me->GetMotionMaster()->MovePoint(POINT_ID_ANVIL, AnvilPosition);
+ events.CancelEvent(EVENT_HEAT);
+ Talk(SAY_ANNOUNCE_RUN_TO_ANVIL);
+ Talk(SAY_RUN_TO_ANVIL);
+ break;
+ case EVENT_TEMPER:
+ if (Creature const* anvil = instance->GetCreature(DATA_VOLKHANS_ANVIL))
+ {
+ me->SetFacingToObject(anvil);
+ DoCastAOE(SPELL_TEMPER_SUMMON_OBJECT);
+ DoCastAOE(SPELL_TEMPER_DUMMY_COMBAT);
+ me->SetReactState(REACT_AGGRESSIVE);
+ events.ScheduleEvent(EVENT_HEAT, 8s + 500ms, 0, PHASE_COMBAT);
+ }
+ break;
+ case EVENT_HEAT:
+ DoCastAOE(SPELL_HEAT);
+ events.Repeat(10s);
+ break;
+ case EVENT_SHATTERING_STOMP:
+ events.CancelEvent(EVENT_HEAT);
+ Talk(SAY_SHATTERING_STOMP);
+ Talk(SAY_ANNOUNCE_SHATTERING_STOMP);
+ DoCastAOE(SPELL_SHATTERING_STOMP);
+ break;
+ default:
+ break;
}
- }
-
- void MovementInform(uint32 type, uint32 data) override
- {
- if (type == POINT_MOTION_TYPE && data == EVENT_FORGE_CAST)
- {
- if (m_uiSummonPhase == 2)
- {
- me->SetOrientation(2.29f);
- m_uiSummonPhase = 3;
- }
- }
- }
-
- uint32 GetData(uint32 data) const override
- {
- if (data == DATA_SHATTER_RESISTANT)
- return GolemsShattered;
-
- return 0;
- }
-
- void UpdateAI(uint32 diff) override
- {
- // Return since we have no target and are in CombatPhase
- if (events.IsInPhase(PHASE_NORMAL) && !UpdateVictim())
- return;
-
- events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
-
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_PAUSE:
- if (m_bIsStriking)
- {
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE)
- if (me->GetVictim())
- me->GetMotionMaster()->MoveChase(me->GetVictim());
-
- m_bHasTemper = false;
- m_bIsStriking = false;
- events.ScheduleEvent(EVENT_PAUSE, 3.5 * IN_MILLISECONDS, 0, PHASE_NORMAL);
- }
- break;
- case EVENT_SHATTERING_STOMP:
- if (!m_bHasTemper && m_uiHealthAmountModifier >= 3)
- {
- // Should he stomp even if he has no brittle golem to shatter?
- Talk(SAY_STOMP);
-
- DoCast(me, SPELL_SHATTERING_STOMP);
-
- Talk(EMOTE_SHATTER);
- events.ScheduleEvent(EVENT_SHATTERING_STOMP, 30 * IN_MILLISECONDS, 0, PHASE_NORMAL);
- m_bCanShatterGolem = true;
- }
- break;
- case EVENT_SHATTER:
- if (m_bCanShatterGolem)
- {
- ShatterGolem();
- events.ScheduleEvent(EVENT_SHATTER, 3 * IN_MILLISECONDS, 0, PHASE_NORMAL);
- m_bCanShatterGolem = false;
- }
- break;
- case EVENT_FORGE_CAST:
- DoCast(me, SPELL_FORGE_VISUAL);
- events.ScheduleEvent(EVENT_FORGE_CAST, 15 * IN_MILLISECONDS, 0, PHASE_INTRO);
- break;
- default:
- break;
- }
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
- }
-
- // Health check
- if (!m_bCanShatterGolem && me->HealthBelowPct(100 - 20 * m_uiHealthAmountModifier))
- {
- ++m_uiHealthAmountModifier;
-
- if (me->IsNonMeleeSpellCast(false))
- me->InterruptNonMeleeSpells(false);
-
- Talk(SAY_FORGE);
-
- m_bHasTemper = true;
-
- m_uiSummonPhase = 1;
- }
-
- switch (m_uiSummonPhase)
- {
- case 1:
- // 1 - Start run to Anvil
- Talk(EMOTE_TO_ANVIL);
- me->GetMotionMaster()->MovePoint(EVENT_FORGE_CAST, me->GetHomePosition());
- m_uiSummonPhase = 2; // Set Next Phase
- break;
- case 2:
- // 2 - Check if reached Anvil
- // This is handled in: void MovementInform(uint32, uint32) override
- break;
- case 3:
- // 3 - Cast Temper on the Anvil
- if (Unit* target = GetClosestCreatureWithEntry(me, NPC_VOLKHAN_ANVIL, 1000.0f, true))
- {
- me->SetOrientation(2.29f);
- DoCast(target, SPELL_TEMPER, false);
- DoCast(target, SPELL_TEMPER_DUMMY, false);
- }
- m_uiDelay_Timer = 1000; // Delay 2 seconds before next phase can begin
- m_uiSummonPhase = 4; // Set Next Phase
- break;
- case 4:
- // 4 - Wait for delay to expire
- if (m_uiDelay_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_MAXTHREAT, 0))
- {
- me->SetReactState(REACT_AGGRESSIVE);
- me->SetInCombatWith(target);
- me->GetMotionMaster()->MoveFollow(target, 0.0f, 0.0f);
- }
- m_uiSummonPhase = 5;
- }
- else
- m_uiDelay_Timer -= diff;
- break;
- case 5:
- // 5 - Spawn the Golems
- if (Creature* creatureTarget = GetClosestCreatureWithEntry(me, NPC_VOLKHAN_ANVIL, 1000.0f, true))
- for (uint8 i = 0; i < MAX_GOLEM; ++i)
- me->CastSpell(creatureTarget, SPELL_SUMMON_MOLTEN_GOLEM, true);
-
- m_bIsStriking = true;
- m_uiSummonPhase = 0; // Reset back to Phase 0 for next time
- break;
- }
-
- DoMeleeAttackIfReady();
}
- private:
- GuidList m_lGolemGUIDList;
- uint32 m_uiHealthAmountModifier;
- uint8 GolemsShattered;
- uint32 m_uiDelay_Timer;
- uint32 m_uiSummonPhase;
+ DoMeleeAttackIfReady();
+ }
- bool m_bHasTemper;
- bool m_bIsStriking;
- bool m_bCanShatterGolem;
- };
+private:
+ bool _shatteredGolems;
+ bool _temperingGolems;
+ uint8 _temperCycles;
+ uint8 _shatteredGolemsCount;
+};
- CreatureAI* GetAI(Creature* creature) const override
+struct npc_volkhan_molten_golem : public ScriptedAI
+{
+ npc_volkhan_molten_golem(Creature* creature) : ScriptedAI(creature) { }
+
+ void JustAppeared() override
{
- return GetHallsOfLightningAI(creature);
+ DoCastSelf(SPELL_COOL_DOWN);
+ //DoCastSelf(SPELL_STUN_SELF);
+ }
+
+ void JustEngagedWith(Unit* /*who*/) override
+ {
+ _events.ScheduleEvent(EVENT_IMMOLATION_STRIKE, 7s, 12s);
+ }
+
+ void EnterEvadeMode(EvadeReason why) override
+ {
+ ScriptedAI::EnterEvadeMode(why);
+ _events.Reset();
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ me->DespawnOrUnsummon(5s);
+ }
+
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_SHATTER:
+ me->RemoveAurasDueToSpell(SPELL_COSMETIC_STUN_IMMUNE_FREEZE_AMNIM);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE);
+ DoCastAOE(SPELL_SHATTER);
+ DoCastSelf(SPELL_INSTAKILL_SELF);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void DamageTaken(Unit* attacker, uint32& damage) override
+ {
+ // Molten Golems cannot die from foreign damage. They will kill themselves via suicide spell when getting shattered
+ if (damage >= me->GetHealth() && attacker != me)
+ damage = me->GetHealth() - 1;
+ }
+
+ 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_IMMOLATION_STRIKE:
+ DoCastVictim(SPELL_IMMOLATION_STRIKE);
+ _events.Repeat(5s);
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+private:
+ EventMap _events;
+};
+
+// 52654, 52238 - Temper
+class spell_volkhan_temper_dummy : public SpellScript
+{
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_TEMPER_DUMMY_COMBAT });
+ }
+
+ void HandleDummyEffect(SpellEffIndex /*effIndex*/)
+ {
+ Unit* target = GetHitUnit();
+ if (InstanceScript* instance = target->GetInstanceScript())
+ if (GameObject* temperVisual = instance->GetGameObject(DATA_VOLKHAN_TEMPER_VISUAL))
+ temperVisual->SendCustomAnim(0);
+
+ if (GetSpellInfo()->Id == SPELL_TEMPER_DUMMY_COMBAT)
+ {
+ for (uint8 i = 0; i < 2; ++i)
+ GetHitUnit()->CastSpell(nullptr, SPELL_SUMMON_MOLTEN_GOLEM);
+
+ if (Unit* caster = GetCaster())
+ if (Creature* creatureCaster = caster->ToCreature())
+ if (CreatureAI* ai = creatureCaster->AI())
+ ai->DoAction(ACTION_GOLEMS_TEMPERED);
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget.Register(&spell_volkhan_temper_dummy::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
-/*######
-## npc_molten_golem
-######*/
-
-class npc_molten_golem : public CreatureScript
+// 52441 - Cool Down
+class spell_volkhan_cool_down : public AuraScript
{
-public:
- npc_molten_golem() : CreatureScript("npc_molten_golem") { }
-
- CreatureAI* GetAI(Creature* creature) const override
+ bool Validate(SpellInfo const* /*spellInfo*/) override
{
- return GetHallsOfLightningAI(creature);
+ return ValidateSpellInfo({ SPELL_COOL_DOWN_SLOW, SPELL_COSMETIC_STUN_IMMUNE_FREEZE_AMNIM });
}
- struct npc_molten_golemAI : public ScriptedAI
+ void HandlePeriodicDummyEffect(AuraEffect const* /*aurEff*/)
{
- npc_molten_golemAI(Creature* creature) : ScriptedAI(creature)
+ Unit* target = GetTarget();
+ if (target->GetHealthPct() > 1.f)
{
- Initialize();
+ // This damage part feels weird but there is no trace of spells in sniffs that could do such thing otherwise.
+ Unit::DealDamage(target, target, CalculatePct(target->GetMaxHealth(), target->GetMap()->IsHeroic() ? 1 : 2));
+ int32 bp = 0 - static_cast(100.f - target->GetHealthPct());
+ target->CastSpell(nullptr, SPELL_COOL_DOWN_SLOW, CastSpellExtraArgs().AddSpellBP0(bp));
}
-
- void Initialize()
+ else
{
- m_bIsFrozen = false;
- events.ScheduleEvent(EVENT_BLAST, 20 * IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_IMMOLATION, 5 * IN_MILLISECONDS);
+ target->CastSpell(nullptr, SPELL_COSMETIC_STUN_IMMUNE_FREEZE_AMNIM);
+ target->RemoveAurasDueToSpell(SPELL_COOL_DOWN_SLOW);
+ GetAura()->Remove();
}
+ }
- bool m_bIsFrozen;
+ void Register() override
+ {
+ OnEffectPeriodic.Register(&spell_volkhan_cool_down::HandlePeriodicDummyEffect, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+};
- void Reset() override
+// 59123 Cosmetic - Stun + Immune Permanent (Freeze Anim)
+class spell_volkhan_cosmetic_stun_immune_permanent : public AuraScript
+{
+ void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Creature* target = GetTarget()->ToCreature())
{
- Initialize();
+ target->UpdateEntry(ENTRY_BRITTLE_GOLEM, nullptr, false);
+ target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE);
}
+ }
- void AttackStart(Unit* who) override
- {
- if (me->Attack(who, true))
- {
- AddThreat(who, 0.0f);
- me->SetInCombatWith(who);
- who->SetInCombatWith(me);
-
- if (!m_bIsFrozen)
- me->GetMotionMaster()->MoveChase(who);
- }
+ void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Creature* target = GetTarget()->ToCreature())
+ target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE);
}
+ }
- void DamageTaken(Unit* /*pDoneBy*/, uint32 &uiDamage) override
- {
- if (uiDamage > me->GetHealth())
- {
- me->UpdateEntry(NPC_BRITTLE_GOLEM);
- me->SetHealth(1);
- uiDamage = 0;
- me->RemoveAllAuras();
- me->AttackStop();
- // me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); //Set in DB
- // me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); //Set in DB
- if (me->IsNonMeleeSpellCast(false))
- me->InterruptNonMeleeSpells(false);
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
- me->GetMotionMaster()->MovementExpired();
- m_bIsFrozen = true;
- }
- }
+ void Register() override
+ {
+ AfterEffectApply.Register(&spell_volkhan_cosmetic_stun_immune_permanent::HandleApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove.Register(&spell_volkhan_cosmetic_stun_immune_permanent::HandleRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
+ }
+};
- void SpellHit(WorldObject* /*pCaster*/, SpellInfo const* pSpell) override
- {
- // This is the dummy effect of the spells
- if (pSpell->Id == sSpellMgr->GetSpellIdForDifficulty(SPELL_SHATTER, me))
- if (me->GetEntry() == NPC_BRITTLE_GOLEM)
- me->DespawnOrUnsummon();
- }
+// 52237, 59529 - Shattering Stomp
+class spell_volkhan_shattering_stomp : public SpellScript
+{
+ void HandleShattering()
+ {
+ if (Creature* caster = GetCaster()->ToCreature())
+ if (CreatureAI* ai = caster->AI())
+ ai->DoAction(ACTION_SHATTER_GOLEMS);
+ }
- void UpdateAI(uint32 diff) override
- {
- // Return since we have no target or if we are frozen
- if (!UpdateVictim() || m_bIsFrozen)
- return;
-
- events.Update(diff);
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
-
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_BLAST:
- DoCast(me, SPELL_BLAST_WAVE);
- events.ScheduleEvent(EVENT_BLAST, 20 * IN_MILLISECONDS);
- break;
- case EVENT_IMMOLATION:
- DoCastVictim(SPELL_IMMOLATION_STRIKE);
- events.ScheduleEvent(EVENT_BLAST, 5 * IN_MILLISECONDS);
- break;
- default:
- break;
- }
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
- }
-
- DoMeleeAttackIfReady();
- }
-
- private:
- EventMap events;
- };
+ void Register() override
+ {
+ AfterCast.Register(&spell_volkhan_shattering_stomp::HandleShattering);
+ }
};
class achievement_shatter_resistant : public AchievementCriteriaScript
@@ -491,13 +478,24 @@ class achievement_shatter_resistant : public AchievementCriteriaScript
bool OnCheck(Player* /*source*/, Unit* target) override
{
- return target && target->GetAI()->GetData(DATA_SHATTER_RESISTANT) < 5;
+ if (!target || !target->IsCreature())
+ return false;
+
+ if (Creature* creature = target->ToCreature())
+ if (CreatureAI* ai = creature->AI())
+ return ai->GetData(DATA_SHATTER_RESISTANT) < 5;
+
+ return false;
}
};
void AddSC_boss_volkhan()
{
- new boss_volkhan();
- new npc_molten_golem();
+ RegisterHallsOfLightningCreatureAI(boss_volkhan);
+ RegisterHallsOfLightningCreatureAI(npc_volkhan_molten_golem);
+ RegisterSpellScript(spell_volkhan_temper_dummy);
+ RegisterSpellScript(spell_volkhan_cool_down);
+ RegisterSpellScript(spell_volkhan_cosmetic_stun_immune_permanent);
+ RegisterSpellScript(spell_volkhan_shattering_stomp);
new achievement_shatter_resistant();
}
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/halls_of_lightning.h b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/halls_of_lightning.h
index 7db29ea302..6d9830c6b1 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/halls_of_lightning.h
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/halls_of_lightning.h
@@ -31,24 +31,36 @@ enum HOLDataTypes
DATA_BJARNGRIM = 0,
DATA_VOLKHAN = 1,
DATA_IONAR = 2,
- DATA_LOKEN = 3
+ DATA_LOKEN = 3,
+
+ /*Volkhan*/
+ DATA_VOLKHAN_TEMPER_VISUAL,
+ DATA_VOLKHANS_ANVIL,
};
enum HOLCreaturesIds
{
+ // Bosses
NPC_BJARNGRIM = 28586,
NPC_VOLKHAN = 28587,
NPC_IONAR = 28546,
- NPC_LOKEN = 28923
+ NPC_LOKEN = 28923,
+
+ /*Volkhan*/
+ NPC_VOLKHANS_ANVIL = 28823,
+ NPC_MOLTEN_GOLEM = 28695,
};
enum HOLGameObjectIds
{
- GO_BJARNGRIM_DOOR = 191416,
- GO_VOLKHAN_DOOR = 191325,
- GO_IONAR_DOOR = 191326,
- GO_LOKEN_DOOR = 191324,
- GO_LOKEN_THRONE = 192654
+ /*Volkhan*/
+ GO_VOLKHAN_TEMPER_VISUAL = 190858,
+
+ GO_BJARNGRIM_DOOR = 191416,
+ GO_VOLKHAN_DOOR = 191325,
+ GO_IONAR_DOOR = 191326,
+ GO_LOKEN_DOOR = 191324,
+ GO_LOKEN_THRONE = 192654
};
template
@@ -57,4 +69,6 @@ inline AI* GetHallsOfLightningAI(T* obj)
return GetInstanceAI(obj, HoLScriptName);
}
+#define RegisterHallsOfLightningCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetHallsOfLightningAI)
+
#endif // HALLS_OF_LIGHTNING_H_
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/instance_halls_of_lightning.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/instance_halls_of_lightning.cpp
index 735d80cdbe..8514606f07 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/instance_halls_of_lightning.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/instance_halls_of_lightning.cpp
@@ -17,6 +17,7 @@
#include "ScriptMgr.h"
#include "Creature.h"
+#include "CreatureAI.h"
#include "GameObject.h"
#include "halls_of_lightning.h"
#include "InstanceScript.h"
@@ -30,6 +31,19 @@ DoorData const doorData[] =
{ 0, 0, DOOR_TYPE_ROOM } // END
};
+ObjectData const creatureData[] =
+{
+ { NPC_VOLKHAN, DATA_VOLKHAN },
+ { NPC_VOLKHANS_ANVIL, DATA_VOLKHANS_ANVIL },
+ { 0, 0 } // END
+};
+
+ObjectData const gameObjectData[] =
+{
+ { GO_VOLKHAN_TEMPER_VISUAL, DATA_VOLKHAN_TEMPER_VISUAL },
+ { 0, 0 } // END
+};
+
class instance_halls_of_lightning : public InstanceMapScript
{
public:
@@ -41,25 +55,35 @@ class instance_halls_of_lightning : public InstanceMapScript
{
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
+ LoadObjectData(creatureData, gameObjectData);
LoadDoorData(doorData);
}
void OnCreatureCreate(Creature* creature) override
{
+ InstanceScript::OnCreatureCreate(creature);
+
switch (creature->GetEntry())
{
case NPC_BJARNGRIM:
GeneralBjarngrimGUID = creature->GetGUID();
break;
- case NPC_VOLKHAN:
- VolkhanGUID = creature->GetGUID();
- break;
case NPC_IONAR:
IonarGUID = creature->GetGUID();
break;
case NPC_LOKEN:
LokenGUID = creature->GetGUID();
break;
+ case NPC_MOLTEN_GOLEM:
+ if (GetBossState(DATA_VOLKHAN) == IN_PROGRESS)
+ {
+ if (Creature* volkhan = GetCreature(DATA_VOLKHAN))
+ if (CreatureAI* ai = volkhan->AI())
+ ai->JustSummoned(creature);
+ }
+ else // These golems are summoned via trigger missile so we have to clean them up if they spawned during a wipe/completion
+ creature->DespawnOrUnsummon();
+ break;
default:
break;
}
@@ -104,8 +128,6 @@ class instance_halls_of_lightning : public InstanceMapScript
{
case DATA_BJARNGRIM:
return GeneralBjarngrimGUID;
- case DATA_VOLKHAN:
- return VolkhanGUID;
case DATA_IONAR:
return IonarGUID;
case DATA_LOKEN:
@@ -113,12 +135,11 @@ class instance_halls_of_lightning : public InstanceMapScript
default:
break;
}
- return ObjectGuid::Empty;
+ return InstanceScript::GetGuidData(type);
}
protected:
ObjectGuid GeneralBjarngrimGUID;
- ObjectGuid VolkhanGUID;
ObjectGuid IonarGUID;
ObjectGuid LokenGUID;