diff options
author | Ovah <dreadkiller@gmx.de> | 2022-11-04 13:08:34 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2023-01-12 22:55:17 +0100 |
commit | 298febaaf9617664e89ac5e1af9ae7794e47e28c (patch) | |
tree | e1a5050a573e9f15c9467adabfb9c6d051c021ff | |
parent | ad74adc71db5b5b811de0ddeb4ceb3c05ebbdce4 (diff) |
Scripts/Halls of Lightning: reworked General Bjarngrim Encounter (#28457)
* handle missing visuals
* reworked all AI scripts
* handle missing mechanic of Arc Weld
* reworked the whole waypoint and tempoary electrical charge handling
* moved Stormforged Lieutenants to their own spawn group to handle their spawning/despawning via boss
* moved the Tempoary Electrical Charge mechanic from spell_linked_spell to spell script instead
* replaced hacky virtual items for Bjarngrim's stances with sniffed ones
(cherry picked from commit f2fcd6746c72930dc0a951521e2f0fe78ce02690)
6 files changed, 602 insertions, 447 deletions
diff --git a/sql/updates/world/master/2023_01_12_19_world_2022_11_04_05_world.sql b/sql/updates/world/master/2023_01_12_19_world_2022_11_04_05_world.sql new file mode 100644 index 00000000000..80eb5a4a0d6 --- /dev/null +++ b/sql/updates/world/master/2023_01_12_19_world_2022_11_04_05_world.sql @@ -0,0 +1,78 @@ +-- +UPDATE `creature_template` SET `ScriptName`= 'boss_general_bjarngrim' WHERE `entry`= 28586; +UPDATE `creature_template` SET `ScriptName`= 'npc_bjarngrim_stormforged_lieutenant' WHERE `entry`= 29240; + +DELETE FROM `spell_script_names` WHERE `ScriptName` IN +('spell_bjarngrim_defensive_stance_dummy', +'spell_bjarngrim_battle_stance_dummy', +'spell_bjarngrim_berserker_stance_dummy', +'spell_bjarngrim_charge_up', +'spell_bjarngrim_arc_weld'); + +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(53790, 'spell_bjarngrim_defensive_stance_dummy'), +(53791, 'spell_bjarngrim_berserker_stance_dummy'), +(53792, 'spell_bjarngrim_battle_stance_dummy'), +(52098, 'spell_bjarngrim_charge_up'), +(59085, 'spell_bjarngrim_arc_weld'); + +DELETE FROM `creature_text` WHERE `CreatureID`= 28586; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(28586, 0, 0, 'I am the greatest of my father\'s sons! Your end has come!', 14, 0, 100, 0, 0, 14149, 31407, 0, 'General Bjarngrim - Aggro'), +(28586, 1, 0, '%s switches to Defensive Stance!', 41, 0, 100, 0, 0, 0, 29834, 0, 'General Bjarngrim - Announce Defensive Stance'), +(28586, 2, 0, 'Give me your worst!', 14, 0, 100, 0, 0, 14150, 31408, 0, 'General Bjarngrim - Defensive Stance'), +(28586, 3, 0, '%s switches to Berserker Stance!', 41, 0, 100, 0, 0, 0, 29833, 0, 'General Bjarngrim - Announce Berserker Stance'), +(28586, 4, 0, 'GRAAAAAH! Behold the fury of iron and steel!', 14, 0, 100, 0, 0, 14152, 31410, 0, 'General Bjarngrim - Berserker Stance'), +(28586, 5, 0, '%s switches to Battle Stance!', 41, 0, 100, 0, 0, 0, 29832, 0, 'General Bjarngrim - Announce Battle Stance'), +(28586, 6, 0, 'Defend yourself, for all the good it will do!', 14, 0, 100, 0, 0, 14151, 31409, 0, 'General Bjarngrim - Battle Stance'), +(28586, 7, 0, 'So ends your curse.', 14, 0, 100, 0, 0, 14153, 31411, 0, 'General Bjarngrim - Slay 1'), +(28586, 7, 1, 'Flesh... is... weak!', 14, 0, 100, 0, 0, 14154, 31412, 0, 'General Bjarngrim - Slay 2'), +(28586, 7, 2, 'Bolvin umyol marnjar.', 14, 0, 100, 0, 0, 14155, 31413, 0, 'General Bjarngrim - Slay 3'), +(28586, 8, 0, 'How can it be...? Flesh is not... stronger!', 14, 0, 100, 0, 0, 14156, 31414, 0, 'General Bjarngrim - Death'); + +DELETE FROM `conditions` WHERE `SourceEntry` IN (56458) AND `SourceTypeOrReferenceId`= 13; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ScriptName`, `Comment`) VALUES +(13, 1, 56458, 0, 0, 31, 0, 3, 28586, 0, 0, 0, '', 'Charge Up - Target General Bjarngrim'); + +SET @CGUID := 126981; +SET @PATH := @CGUID * 10; +DELETE FROM `waypoint_data` WHERE `id`= @PATH; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`) VALUES +(@PATH, 0, 1262.023, 9.344401, 33.21593, NULL, 0), +(@PATH, 1, 1262.031, 53.14377, 33.17394, NULL, 0), +(@PATH, 2, 1261.928, 98.94911, 33.50209, NULL, 6000), +(@PATH, 3, 1261.893, 60.51574, 33.17393, NULL, 0), +(@PATH, 4, 1261.886, 21.39475, 33.17399, NULL, 0), +(@PATH, 5, 1262.132, -26.1173, 33.50208, NULL, 6000), +(@PATH, 6, 1298.901, -26.74544, 37.24462, NULL, 0), +(@PATH, 7, 1331.648, -27.02919, 40.17395, NULL, 10000), +(@PATH, 8, 1354.665, -4.239692, 41.1354, NULL, 0), +(@PATH, 9, 1371.601, 12.65864, 48.57853, NULL, 0), +(@PATH, 10, 1394.671, 35.86361, 50.03335, NULL, 6000), +(@PATH, 11, 1370.748, 12.2143, 48.29315, NULL, 0), +(@PATH, 12, 1355.246, -3.361762, 41.45641, NULL, 0), +(@PATH, 13, 1332.481, -26.59397, 40.17395, NULL, 0), +(@PATH, 14, 1295.831, -26.50022, 36.55395, NULL, 0), +(@PATH, 15, 1262.658, -26.88303, 33.50208, NULL, 10000); + +DELETE FROM `waypoint_scripts` WHERE `id` IN (12698102, 12698101); + +UPDATE `creature` SET `position_x`= 1262.2057, `position_y`= -1.0628986, `position_z`= 33.50208, `orientation`= 5.140983, `wander_distance`= 0, `MovementType`= 2 WHERE `guid`= @CGUID; +DELETE FROM `creature_addon` WHERE `guid`= @CGUID; +INSERT INTO `creature_addon` (`guid`, `path_id`, `bytes2`) VALUES +(@CGUID, @PATH, 1); + +UPDATE `creature` SET `position_x`= 1265.8042, `position_y`= -4.6208167, `position_z`= 33.502056, `orientation`= 5.258189 WHERE `guid`= 126863; +UPDATE `creature` SET `position_x`= 1258.7352, `position_y`= -4.746479, `position_z`= 33.50209, `orientation`= 5.122859 WHERE `guid`= 126864; + +DELETE FROM `spell_linked_spell` WHERE `spell_trigger`= -52098; + +DELETE FROM `spawn_group_template` WHERE `groupId`= 325; +INSERT INTO `spawn_group_template` (`groupId`, `groupName`, `groupFlags`) VALUES +(325, 'Halls of Lightning - General Bjarngrim - Stormforged Lieutenants', 4); + +DELETE FROM `spawn_group` WHERE `spawnId` IN (126863, 126864); +DELETE FROM `spawn_group` WHERE `groupId`= 325; +INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`) VALUES +(325, 0, 126863), +(325, 0, 126864); diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp deleted file mode 100644 index 6dd2c9d5e57..00000000000 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* - * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* ScriptData -SDName: Boss General Bjarngrim -SD%Complete: 70% -SDComment: Waypoint needed, we expect boss to always have 2x Stormforged Lieutenant following -SDCategory: Halls of Lightning -EndScriptData */ - -#include "ScriptMgr.h" -#include "halls_of_lightning.h" -#include "InstanceScript.h" -#include "ObjectAccessor.h" -#include "ScriptedCreature.h" - -enum Yells -{ - SAY_AGGRO = 0, - SAY_DEFENSIVE_STANCE = 1, - SAY_BATTLE_STANCE = 2, - SAY_BERSEKER_STANCE = 3, - SAY_SLAY = 4, - SAY_DEATH = 5, - EMOTE_DEFENSIVE_STANCE = 6, - EMOTE_BATTLE_STANCE = 7, - EMOTE_BERSEKER_STANCE = 8 -}; - -enum Spells -{ - SPELL_DEFENSIVE_STANCE = 53790, - //SPELL_DEFENSIVE_AURA = 41105, - SPELL_SPELL_REFLECTION = 36096, - SPELL_PUMMEL = 12555, - SPELL_KNOCK_AWAY = 52029, - SPELL_IRONFORM = 52022, - - SPELL_BERSEKER_STANCE = 53791, - //SPELL_BERSEKER_AURA = 41107, - SPELL_INTERCEPT = 58769, - SPELL_WHIRLWIND = 52027, - SPELL_CLEAVE = 15284, - - SPELL_BATTLE_STANCE = 53792, - //SPELL_BATTLE_AURA = 41106, - SPELL_MORTAL_STRIKE = 16856, - SPELL_SLAM = 52026, - - //OTHER SPELLS - //SPELL_CHARGE_UP = 52098, // only used when starting walk from one platform to the other - SPELL_TEMPORARY_ELECTRICAL_CHARGE = 52092, // triggered part of above - - SPELL_ARC_WELD = 59085, - SPELL_RENEW_STEEL_N = 52774, - SPELL_RENEW_STEEL_H = 59160 -}; - -enum Creatures -{ - NPC_STORMFORGED_LIEUTENANT = 29240 -}; - -enum Equips -{ - EQUIP_SWORD = 37871, - EQUIP_SHIELD = 35642, - EQUIP_MACE = 43623 -}; - -enum Stanges -{ - STANCE_DEFENSIVE = 0, - STANCE_BERSERKER = 1, - STANCE_BATTLE = 2 -}; - -/*###### -## boss_bjarngrim -######*/ - -struct boss_bjarngrim : public BossAI -{ - boss_bjarngrim(Creature* creature) : BossAI(creature, DATA_GENERAL_BJARNGRIM) - { - Initialize(); - m_uiStance = STANCE_DEFENSIVE; - canBuff = true; - } - - void Initialize() - { - m_bIsChangingStance = false; - - m_uiChargingStatus = 0; - m_uiCharge_Timer = 1000; - - m_uiChangeStance_Timer = urand(20000, 25000); - - m_uiReflection_Timer = 8000; - m_uiKnockAway_Timer = 20000; - m_uiPummel_Timer = 10000; - m_uiIronform_Timer = 25000; - - m_uiIntercept_Timer = 5000; - m_uiWhirlwind_Timer = 10000; - m_uiCleave_Timer = 8000; - - m_uiMortalStrike_Timer = 8000; - m_uiSlam_Timer = 10000; - } - - void Reset() override - { - BossAI::Reset(); - if (canBuff) - if (!me->HasAura(SPELL_TEMPORARY_ELECTRICAL_CHARGE)) - me->AddAura(SPELL_TEMPORARY_ELECTRICAL_CHARGE, me); - - Initialize(); - - for (uint8 i = 0; i < 2; ++i) - { - // @todo: move this to spawn groups. - if (Creature* pStormforgedLieutenant = ObjectAccessor::GetCreature(*me, m_auiStormforgedLieutenantGUID[i])) - if (!pStormforgedLieutenant->IsAlive()) - pStormforgedLieutenant->Respawn(); - } - - if (m_uiStance != STANCE_DEFENSIVE) - { - DoRemoveStanceAura(m_uiStance); - DoCast(me, SPELL_DEFENSIVE_STANCE); - m_uiStance = STANCE_DEFENSIVE; - } - - SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_SHIELD, EQUIP_NO_CHANGE); - - } - - void EnterEvadeMode(EvadeReason why) override - { - if (me->HasAura(SPELL_TEMPORARY_ELECTRICAL_CHARGE)) - canBuff = true; - else - canBuff = false; - - BossAI::EnterEvadeMode(why); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - Talk(SAY_AGGRO); - - //must get both lieutenants here and make sure they are with him - me->CallForHelp(30.0f); - } - - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_SLAY); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - } - - /// @todo remove when removal is done by the core - void DoRemoveStanceAura(uint8 uiStance) - { - switch (uiStance) - { - case STANCE_DEFENSIVE: - me->RemoveAurasDueToSpell(SPELL_DEFENSIVE_STANCE); - break; - case STANCE_BERSERKER: - me->RemoveAurasDueToSpell(SPELL_BERSEKER_STANCE); - break; - case STANCE_BATTLE: - me->RemoveAurasDueToSpell(SPELL_BATTLE_STANCE); - break; - } - } - - void UpdateAI(uint32 uiDiff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - // Change stance - if (m_uiChangeStance_Timer <= uiDiff) - { - //wait for current spell to finish before change stance - if (me->IsNonMeleeSpellCast(false)) - return; - - DoRemoveStanceAura(m_uiStance); - - int uiTempStance = rand32() % (3 - 1); - - if (uiTempStance >= m_uiStance) - ++uiTempStance; - - m_uiStance = uiTempStance; - - switch (m_uiStance) - { - case STANCE_DEFENSIVE: - Talk(SAY_DEFENSIVE_STANCE); - Talk(EMOTE_DEFENSIVE_STANCE); - DoCast(me, SPELL_DEFENSIVE_STANCE); - SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_SHIELD, EQUIP_NO_CHANGE); - break; - case STANCE_BERSERKER: - Talk(SAY_BERSEKER_STANCE); - Talk(EMOTE_BERSEKER_STANCE); - DoCast(me, SPELL_BERSEKER_STANCE); - SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_SWORD, EQUIP_NO_CHANGE); - break; - case STANCE_BATTLE: - Talk(SAY_BATTLE_STANCE); - Talk(EMOTE_BATTLE_STANCE); - DoCast(me, SPELL_BATTLE_STANCE); - SetEquipmentSlots(false, EQUIP_MACE, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); - break; - } - - m_uiChangeStance_Timer = urand(20000, 25000); - return; - } - else - m_uiChangeStance_Timer -= uiDiff; - - switch (m_uiStance) - { - case STANCE_DEFENSIVE: - { - if (m_uiReflection_Timer <= uiDiff) - { - DoCast(me, SPELL_SPELL_REFLECTION); - m_uiReflection_Timer = urand(8000, 9000); - } - else - m_uiReflection_Timer -= uiDiff; - - if (m_uiKnockAway_Timer <= uiDiff) - { - DoCast(me, SPELL_KNOCK_AWAY); - m_uiKnockAway_Timer = urand(20000, 21000); - } - else - m_uiKnockAway_Timer -= uiDiff; - - if (m_uiPummel_Timer <= uiDiff) - { - DoCastVictim(SPELL_PUMMEL); - m_uiPummel_Timer = urand(10000, 11000); - } - else - m_uiPummel_Timer -= uiDiff; - - if (m_uiIronform_Timer <= uiDiff) - { - DoCast(me, SPELL_IRONFORM); - m_uiIronform_Timer = urand(25000, 26000); - } - else - m_uiIronform_Timer -= uiDiff; - - break; - } - case STANCE_BERSERKER: - { - if (m_uiIntercept_Timer <= uiDiff) - { - //not much point is this, better random target and more often? - DoCastVictim(SPELL_INTERCEPT); - m_uiIntercept_Timer = urand(45000, 46000); - } - else - m_uiIntercept_Timer -= uiDiff; - - if (m_uiWhirlwind_Timer <= uiDiff) - { - DoCast(me, SPELL_WHIRLWIND); - m_uiWhirlwind_Timer = urand(10000, 11000); - } - else - m_uiWhirlwind_Timer -= uiDiff; - - if (m_uiCleave_Timer <= uiDiff) - { - DoCastVictim(SPELL_CLEAVE); - m_uiCleave_Timer = urand(8000, 9000); - } - else - m_uiCleave_Timer -= uiDiff; - - break; - } - case STANCE_BATTLE: - { - if (m_uiMortalStrike_Timer <= uiDiff) - { - DoCastVictim(SPELL_MORTAL_STRIKE); - m_uiMortalStrike_Timer = urand(20000, 21000); - } - else - m_uiMortalStrike_Timer -= uiDiff; - - if (m_uiSlam_Timer <= uiDiff) - { - DoCastVictim(SPELL_SLAM); - m_uiSlam_Timer = urand(15000, 16000); - } - else - m_uiSlam_Timer -= uiDiff; - - break; - } - } - - DoMeleeAttackIfReady(); - } - - private: - bool m_bIsChangingStance; - bool canBuff; - - uint8 m_uiChargingStatus; - uint8 m_uiStance; - - uint32 m_uiCharge_Timer; - uint32 m_uiChangeStance_Timer; - - uint32 m_uiReflection_Timer; - uint32 m_uiKnockAway_Timer; - uint32 m_uiPummel_Timer; - uint32 m_uiIronform_Timer; - - uint32 m_uiIntercept_Timer; - uint32 m_uiWhirlwind_Timer; - uint32 m_uiCleave_Timer; - - uint32 m_uiMortalStrike_Timer; - uint32 m_uiSlam_Timer; - - ObjectGuid m_auiStormforgedLieutenantGUID[2]; -}; - -/*###### -## npc_stormforged_lieutenant -######*/ - -struct npc_stormforged_lieutenant : public ScriptedAI -{ - npc_stormforged_lieutenant(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - _instance = creature->GetInstanceScript(); - } - - void Initialize() - { - m_uiArcWeld_Timer = urand(20000, 21000); - m_uiRenewSteel_Timer = urand(10000, 11000); - } - - void Reset() override - { - Initialize(); - } - - void JustEngagedWith(Unit* who) override - { - if (Creature* bjarngrim = _instance->GetCreature(DATA_GENERAL_BJARNGRIM)) - { - if (bjarngrim->IsAlive() && !bjarngrim->IsEngaged()) - bjarngrim->EngageWithTarget(who); - } - } - - void UpdateAI(uint32 uiDiff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - if (m_uiArcWeld_Timer <= uiDiff) - { - DoCastVictim(SPELL_ARC_WELD); - m_uiArcWeld_Timer = urand(20000, 21000); - } - else - m_uiArcWeld_Timer -= uiDiff; - - if (m_uiRenewSteel_Timer <= uiDiff) - { - if (Creature* bjarngrim = _instance->GetCreature(DATA_GENERAL_BJARNGRIM)) - { - if (bjarngrim->IsAlive()) - DoCast(bjarngrim, SPELL_RENEW_STEEL_N); - } - m_uiRenewSteel_Timer = urand(10000, 14000); - } - else - m_uiRenewSteel_Timer -= uiDiff; - - DoMeleeAttackIfReady(); - } -private: - InstanceScript* _instance; - uint32 m_uiArcWeld_Timer; - uint32 m_uiRenewSteel_Timer; -}; - -void AddSC_boss_bjarngrim() -{ - RegisterHallsOfLightningCreatureAI(boss_bjarngrim); - RegisterHallsOfLightningCreatureAI(npc_stormforged_lieutenant); -} diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_general_bjarngrim.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_general_bjarngrim.cpp new file mode 100644 index 00000000000..caefccde989 --- /dev/null +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_general_bjarngrim.cpp @@ -0,0 +1,509 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ScriptMgr.h" +#include "halls_of_lightning.h" +#include "SpellScript.h" +#include "CreatureGroups.h" +#include "InstanceScript.h" +#include "Map.h" +#include "MovementDefines.h" +#include "ObjectAccessor.h" +#include "ScriptedCreature.h" +#include "SpellAuras.h" +#include "SpellHistory.h" +#include "SpellMgr.h" + +enum Spells +{ + // General Bjarngrim + SPELL_CHARGE_UP = 52098, + SPELL_TEMPOARY_ELECTRICAL_CHARGE = 52092, + SPELL_STANCE_COOLDOWN = 41102, + SPELL_DEFENSIVE_STANCE = 53790, + SPELL_DEFENSIVE_AURA = 41105, + SPELL_KNOCK_AWAY = 52029, + SPELL_SPELL_REFLECTION = 36096, + SPELL_BERSERKER_STANCE = 53791, + SPELL_BERSERKER_AURA = 41107, + SPELL_WHIRLWIND = 52027, + SPELL_INTERCEPT = 58769, + SPELL_CLEAVE = 15284, + SPELL_BATTLE_STANCE = 53792, + SPELL_BATTLE_AURA = 41106, + SPELL_MORTAL_STRIKE = 16856, + SPELL_SLAM = 52026, + + // Stormforged Lieutenant + SPELL_ARC_WELD = 59085, + SPELL_ARC_WELD_DAMAGE = 59097, + SPELL_RENEW_STEEL = 52774, + + // Invisible Stalker + SPELL_CHARGE_UP_DUMMY = 56458 +}; + +enum Events +{ + // General Bjarngrim + EVENT_CHARGE_UP = 1, + EVENT_CHECK_STANCE_COOLDOWN, + EVENT_KNOCK_AWAY, + EVENT_SPELL_REFLECTION, + EVENT_WHIRLWIND, + EVENT_INTERCEPT, + EVENT_CLEAVE, + EVENT_MORTAL_STRIKE, + EVENT_SLAM, + + // Stormforged Lieutenant + EVENT_ARC_WELD = 1, + EVENT_CHECK_BJARNGRIMS_HEALTH +}; + +enum EventGroups +{ + EVENT_GROUP_DEFENSIVE_STANCE = 1, + EVENT_GROUP_BERSERKER_STANCE, + EVENT_GROUP_BATTLE_STANCE +}; + +enum Actions +{ + ACTION_SWITCH_STANCE = 0 +}; + +enum Phases +{ + PHASE_OUT_OF_COMBAT = 1 +}; + +enum Texts +{ + // General Bjarngrim + SAY_AGGRO = 0, + SAY_ANNOUNCE_DEFENSIVE_STANCE = 1, + SAY_DEFENSIVE_STANCE = 2, + SAY_ANNOUNCE_BERSERKER_STANCE = 3, + SAY_BERSERKER_STANCE = 4, + SAY_ANNOUNCE_BATTLE_STANCE = 5, + SAY_BATTLE_STANCE = 6, + SAY_SLAY = 7, + SAY_DEATH = 8 +}; + +enum VirtualItemIds +{ + ITEM_ID_AXE = 43625, + ITEM_ID_SHIELD = 39384, + ITEM_ID_GREATAXE = 43623 +}; + +enum Stances +{ + STANCE_DEFENSIVE = 0, + STANCE_BERSERKER = 1, + STANCE_BATTLE = 2, + + MAX_STANCE +}; + +// These values must be sync with the data in waypoint_data. +// Each of these points is going to trigger a Charge Up sequence +static std::array<uint8, 2> const ChargeUpWaypointIds = { 7, 15 }; +// Each of these points is going to remove the Tempoary Electrical Charge buff from General Bjarngrim +static std::array<uint8, 2> const ClearTempoaryChargeWaypointIds = { 5, 13 }; + +// This value must be sync with the data in spawngroup_template +static constexpr uint32 SPAWN_GROUP_ID_STORMFORGED_LIEUTENANTS = 325; + +struct StanceTextInfo +{ + StanceTextInfo(uint32 announceTextId, uint32 sayTextId) : + AnnounceTextId(announceTextId), SayTextId(sayTextId) { } + + uint32 AnnounceTextId; + uint32 SayTextId; +}; + +struct StanceInfo +{ + StanceInfo(StanceTextInfo textInfo, uint32 stanceSpellId) : + TextInfo(textInfo), StanceSpellId(stanceSpellId) { } + + StanceTextInfo TextInfo; + uint32 StanceSpellId; +}; + +static std::array<StanceInfo, MAX_STANCE> const StanceData = +{ + StanceInfo(StanceTextInfo(SAY_ANNOUNCE_DEFENSIVE_STANCE, SAY_DEFENSIVE_STANCE), SPELL_DEFENSIVE_STANCE), + StanceInfo(StanceTextInfo(SAY_ANNOUNCE_BERSERKER_STANCE, SAY_BERSERKER_STANCE), SPELL_BERSERKER_STANCE), + StanceInfo(StanceTextInfo(SAY_ANNOUNCE_BATTLE_STANCE, SAY_BATTLE_STANCE), SPELL_BATTLE_STANCE) +}; + +struct boss_general_bjarngrim : public BossAI +{ + boss_general_bjarngrim(Creature* creature) : BossAI(creature, DATA_GENERAL_BJARNGRIM), _currentStanceId(STANCE_BATTLE) { } + + void JustAppeared() override + { + events.SetPhase(PHASE_OUT_OF_COMBAT); + instance->instance->SpawnGroupSpawn(SPAWN_GROUP_ID_STORMFORGED_LIEUTENANTS, true, true); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO, who); + events.Reset(); + events.ScheduleEvent(EVENT_CHECK_STANCE_COOLDOWN, 1s); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + _DespawnAtEvade(); + instance->instance->SpawnGroupDespawn(SPAWN_GROUP_ID_STORMFORGED_LIEUTENANTS, true); + } + + void KilledUnit(Unit* victim) override + { + if (victim->IsPlayer()) + Talk(SAY_SLAY, victim); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH, killer); + instance->instance->SpawnGroupDespawn(SPAWN_GROUP_ID_STORMFORGED_LIEUTENANTS, true); + } + + void MovementInform(uint32 motionType, uint32 pointId) override + { + if (motionType != WAYPOINT_MOTION_TYPE) + return; + + if (std::find(ChargeUpWaypointIds.begin(), ChargeUpWaypointIds.end(), pointId) != ChargeUpWaypointIds.end()) + events.ScheduleEvent(EVENT_CHARGE_UP, 4s, 0, PHASE_OUT_OF_COMBAT); + else if (std::find(ClearTempoaryChargeWaypointIds.begin(), ClearTempoaryChargeWaypointIds.end(), pointId) != ClearTempoaryChargeWaypointIds.end()) + me->RemoveAurasDueToSpell(SPELL_TEMPOARY_ELECTRICAL_CHARGE); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_SWITCH_STANCE: + { + me->RemoveAurasDueToSpell(StanceData[_currentStanceId].StanceSpellId); + + // @todo: figure out if the stances just cycle or if they are random + _currentStanceId = (_currentStanceId + 1) % StanceData.size(); + + DoCastSelf(StanceData[_currentStanceId].StanceSpellId); + Talk(StanceData[_currentStanceId].TextInfo.AnnounceTextId); + Talk(StanceData[_currentStanceId].TextInfo.SayTextId); + + switch (_currentStanceId) + { + case STANCE_DEFENSIVE: + events.CancelEventGroup(EVENT_GROUP_BERSERKER_STANCE); + events.CancelEventGroup(EVENT_GROUP_BATTLE_STANCE); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 5s, EVENT_GROUP_DEFENSIVE_STANCE); + events.ScheduleEvent(EVENT_SPELL_REFLECTION, 7s, EVENT_GROUP_DEFENSIVE_STANCE); + break; + case STANCE_BERSERKER: + events.CancelEventGroup(EVENT_GROUP_DEFENSIVE_STANCE); + events.CancelEventGroup(EVENT_GROUP_BATTLE_STANCE); + events.ScheduleEvent(EVENT_WHIRLWIND, 7s, EVENT_GROUP_BERSERKER_STANCE); + events.ScheduleEvent(EVENT_INTERCEPT, 16s, EVENT_GROUP_BERSERKER_STANCE); + events.ScheduleEvent(EVENT_CLEAVE, 19s + 500ms, EVENT_GROUP_BERSERKER_STANCE); + break; + case STANCE_BATTLE: + events.CancelEventGroup(EVENT_GROUP_DEFENSIVE_STANCE); + events.CancelEventGroup(EVENT_GROUP_BERSERKER_STANCE); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, 5s, EVENT_GROUP_BATTLE_STANCE); + events.ScheduleEvent(EVENT_SLAM, 9s, EVENT_GROUP_BATTLE_STANCE); + break; + default: + break; + } + + break; + } + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(PHASE_OUT_OF_COMBAT)) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_CHARGE_UP: + DoCastSelf(SPELL_CHARGE_UP); + if (Creature* stalker = instance->GetCreature(DATA_INVISIBLE_STALKER)) + stalker->m_Events.AddEventAtOffset([stalker]() { stalker->CastSpell(nullptr, SPELL_CHARGE_UP_DUMMY); }, 4s); + break; + case EVENT_CHECK_STANCE_COOLDOWN: + // General Bjarngrim uses a category cooldown to handle the stance switching, so we do as well. + if (me->GetSpellHistory()->GetRemainingCooldown(sSpellMgr->AssertSpellInfo(SPELL_STANCE_COOLDOWN, GetDifficulty())) == 0s) + DoAction(ACTION_SWITCH_STANCE); + events.Repeat(1s); + break; + case EVENT_KNOCK_AWAY: + DoCastAOE(SPELL_KNOCK_AWAY); + events.Repeat(11s); + break; + case EVENT_SPELL_REFLECTION: + DoCastSelf(SPELL_SPELL_REFLECTION); + events.Repeat(8s + 500ms); + break; + case EVENT_WHIRLWIND: + DoCastSelf(SPELL_WHIRLWIND); + break; + case EVENT_INTERCEPT: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, -8.f, true, true)) + DoCast(target, SPELL_INTERCEPT); + break; + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + break; + case EVENT_MORTAL_STRIKE: + DoCastVictim(SPELL_MORTAL_STRIKE); + break; + case EVENT_SLAM: + DoCastVictim(SPELL_SLAM); + events.Repeat(5s); + break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + } + + private: + uint8 _currentStanceId; +}; + +struct npc_bjarngrim_stormforged_lieutenant : public ScriptedAI +{ + npc_bjarngrim_stormforged_lieutenant(Creature* creature) : ScriptedAI(creature), _instance(nullptr) { } + + void InitializeAI() override + { + _instance = me->GetInstanceScript(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_ARC_WELD, 5s, 6s); + _events.ScheduleEvent(EVENT_CHECK_BJARNGRIMS_HEALTH, 10s); + } + + void EnterEvadeMode(EvadeReason why) override + { + ScriptedAI::EnterEvadeMode(why); + _events.Reset(); + } + + 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_ARC_WELD: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.f, true)) + DoCast(target, SPELL_ARC_WELD); + _events.Repeat(22s); + break; + case EVENT_CHECK_BJARNGRIMS_HEALTH: + if (Creature* bjarngrim = _instance->GetCreature(DATA_GENERAL_BJARNGRIM)) + if (bjarngrim->GetHealthPct() <= 75.f) // @todo: validate + DoCast(bjarngrim, SPELL_RENEW_STEEL); + + _events.Repeat(10s, 14s); // @todo: these are just taken from the old code. We know that these heals are pct based so we need the cooldown rather than a timer + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } +private: + EventMap _events; + InstanceScript* _instance; +}; + +// 53790 - Defensive Stance +// 53791 - Berserker Stance +// 53792 - Battle Stance +class spell_bjarngrim_stance_dummy : public AuraScript +{ + PrepareAuraScript(spell_bjarngrim_stance_dummy); + +public: + spell_bjarngrim_stance_dummy(uint8 stanceId) : AuraScript(), _stanceId(stanceId) { } + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DEFENSIVE_AURA, SPELL_BERSERKER_AURA, SPELL_BATTLE_AURA, SPELL_STANCE_COOLDOWN }); + } + + void AfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Creature* target = GetTarget()->ToCreature(); + if (!target) + return; + + switch (_stanceId) + { + case STANCE_DEFENSIVE: + target->SetVirtualItem(0, ITEM_ID_AXE); + target->SetVirtualItem(1, ITEM_ID_SHIELD); + target->CastSpell(nullptr, SPELL_DEFENSIVE_AURA); + break; + case STANCE_BERSERKER: + target->SetVirtualItem(0, ITEM_ID_AXE); + target->SetVirtualItem(1, ITEM_ID_AXE); + target->CastSpell(nullptr, SPELL_BERSERKER_AURA); + target->SetCanDualWield(true); + break; + case STANCE_BATTLE: + target->SetVirtualItem(0, ITEM_ID_GREATAXE); + target->SetVirtualItem(1, 0); + target->CastSpell(nullptr, SPELL_BATTLE_AURA); + break; + default: + break; + } + + target->CastSpell(nullptr, SPELL_STANCE_COOLDOWN); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Creature* target = GetTarget()->ToCreature(); + if (!target) + return; + + switch (_stanceId) + { + case STANCE_DEFENSIVE: + target->RemoveAurasDueToSpell(SPELL_DEFENSIVE_AURA); + break; + case STANCE_BERSERKER: + target->RemoveAurasDueToSpell(SPELL_BERSERKER_AURA); + target->SetCanDualWield(false); + break; + case STANCE_BATTLE: + target->RemoveAurasDueToSpell(SPELL_BATTLE_AURA); + break; + default: + break; + } + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_bjarngrim_stance_dummy::AfterApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_bjarngrim_stance_dummy::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +private: + uint8 _stanceId; +}; + +// 52098 - Charge Up +class spell_bjarngrim_charge_up : public AuraScript +{ + PrepareAuraScript(spell_bjarngrim_charge_up); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_TEMPOARY_ELECTRICAL_CHARGE }); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + target->CastSpell(nullptr, SPELL_TEMPOARY_ELECTRICAL_CHARGE); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_bjarngrim_charge_up::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 59085 - Arc Weld +class spell_bjarngrim_arc_weld : public AuraScript +{ + PrepareAuraScript(spell_bjarngrim_arc_weld); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_ARC_WELD_DAMAGE }); + } + + void HandlePeriodic(AuraEffect const* /*aurEff*/) + { + if (GetTarget()->isMoving()) + GetTarget()->CastSpell(nullptr, SPELL_ARC_WELD_DAMAGE, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_bjarngrim_arc_weld::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; + +void AddSC_boss_general_bjarngrim() +{ + RegisterHallsOfLightningCreatureAI(boss_general_bjarngrim); + RegisterHallsOfLightningCreatureAI(npc_bjarngrim_stormforged_lieutenant); + RegisterSpellScriptWithArgs(spell_bjarngrim_stance_dummy, "spell_bjarngrim_defensive_stance_dummy", STANCE_DEFENSIVE); + RegisterSpellScriptWithArgs(spell_bjarngrim_stance_dummy, "spell_bjarngrim_battle_stance_dummy", STANCE_BATTLE); + RegisterSpellScriptWithArgs(spell_bjarngrim_stance_dummy, "spell_bjarngrim_berserker_stance_dummy", STANCE_BERSERKER); + RegisterSpellScript(spell_bjarngrim_charge_up); + RegisterSpellScript(spell_bjarngrim_arc_weld); +} 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 594a3086f25..79c0cd52e11 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/halls_of_lightning.h +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/halls_of_lightning.h @@ -34,6 +34,9 @@ enum HOLDataTypes DATA_LOKEN = 3, // Additional Data + /*General Bjarngrim*/ + DATA_INVISIBLE_STALKER, + /*Volkhan*/ DATA_VOLKHAN_TEMPER_VISUAL, DATA_VOLKHANS_ANVIL, @@ -45,14 +48,17 @@ enum HOLDataTypes enum HOLCreaturesIds { // Bosses - NPC_GENERAL_BJARNGRIM = 28586, - NPC_VOLKHAN = 28587, - NPC_IONAR = 28546, - NPC_LOKEN = 28923, + NPC_GENERAL_BJARNGRIM = 28586, + NPC_VOLKHAN = 28587, + NPC_IONAR = 28546, + NPC_LOKEN = 28923, + + /*General Bjarngrim*/ + NPC_INVISIBLE_STALKER = 30298, /*Volkhan*/ - NPC_VOLKHANS_ANVIL = 28823, - NPC_MOLTEN_GOLEM = 28695 + NPC_VOLKHANS_ANVIL = 28823, + NPC_MOLTEN_GOLEM = 28695 }; enum HOLGameObjectIds 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 bef018139f0..db9350204ab 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 @@ -37,6 +37,7 @@ ObjectData const creatureData[] = { NPC_VOLKHAN, DATA_VOLKHAN }, { NPC_IONAR, DATA_IONAR }, { NPC_LOKEN, DATA_LOKEN }, + { NPC_INVISIBLE_STALKER, DATA_INVISIBLE_STALKER }, { NPC_VOLKHANS_ANVIL, DATA_VOLKHANS_ANVIL }, { 0, 0 } // END }; diff --git a/src/server/scripts/Northrend/northrend_script_loader.cpp b/src/server/scripts/Northrend/northrend_script_loader.cpp index 3b7a84ba5e4..c41abc94806 100644 --- a/src/server/scripts/Northrend/northrend_script_loader.cpp +++ b/src/server/scripts/Northrend/northrend_script_loader.cpp @@ -97,7 +97,7 @@ void AddSC_boss_sartharion(); void AddSC_obsidian_sanctum(); void AddSC_instance_obsidian_sanctum(); // Ulduar: Halls of Lightning -void AddSC_boss_bjarngrim(); +void AddSC_boss_general_bjarngrim(); void AddSC_boss_loken(); void AddSC_boss_ionar(); void AddSC_boss_volkhan(); @@ -295,7 +295,7 @@ void AddNorthrendScripts() AddSC_obsidian_sanctum(); AddSC_instance_obsidian_sanctum(); // Halls of Lightning - AddSC_boss_bjarngrim(); + AddSC_boss_general_bjarngrim(); AddSC_boss_loken(); AddSC_boss_ionar(); AddSC_boss_volkhan(); |