diff options
6 files changed, 1293 insertions, 0 deletions
diff --git a/sql/updates/world/master/2020_04_25_00_world.sql b/sql/updates/world/master/2020_04_25_00_world.sql new file mode 100644 index 00000000000..8910c19b86d --- /dev/null +++ b/sql/updates/world/master/2020_04_25_00_world.sql @@ -0,0 +1,183 @@ +-- Spawns +SET @CGUID := 450000; +DELETE FROM `creature` WHERE `guid`= @CGUID+0; +INSERT INTO `creature` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnDifficulties`, `PhaseId`, `PhaseGroup`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `VerifiedBuild`) VALUES +(@CGUID+0, 122450, 1712, 8638, 9280, "14,15,16,17", 169, 0, 0, 0, -3292.458, 9822.647, -63.49804, 4.729585, 7200, 0, 0, 0, 0, 0, 0, 0, 0, 26365); -- Garothi Worldbreaker + +SET @OGUID := 300000; +DELETE FROM `gameobject` WHERE `guid` BETWEEN @OGUID+0 AND @OGUID+2; +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnDifficulties`, `PhaseId`, `PhaseGroup`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `VerifiedBuild`) VALUES +(@OGUID+0, 278488, 1712, 8638, 8638, '14,15,16,17', 169, 0, -3330.073, 9823.818, -66.76595, 4.92183, 0.02519464, 0.01764011, -0.6292171, 0.7766207, 7200, 255, 1, 30706), -- Rock (Area: -Unknown- - Difficulty: 15) +(@OGUID+1, 253128, 1712, 8638, 8638, '14,15,16,17', 169, 0, -3429.696, 9523.502, 14.88574, 0.254501, 0, 0, 0.1269073, 0.9919146, 7200, 255, 1, 30706), -- Instance Portal (Area: -Unknown- - Difficulty: 15) +(@OGUID+2, 277365, 1712, 8638, 8638, '14,15,16,17', 169, 0, -3324.769, 9814.266, -68.07056, 2.050762, 0, 0, 0.8549118, 0.5187734, 7200, 255, 1, 30706); -- Collision (Area: -Unknown- - Difficulty: 15) + +-- Add Missing Gameobject Template Data +DELETE FROM `gameobject_template` WHERE `entry` IN (278488, 277365, 253128); +INSERT INTO `gameobject_template` (`entry`, `type`, `displayId`, `name`, `IconName`, `castBarCaption`, `unk1`, `size`, `Data0`, `Data1`, `Data2`, `Data3`, `Data4`, `Data5`, `Data6`, `Data7`, `Data8`, `Data9`, `Data10`, `Data11`, `Data12`, `Data13`, `Data14`, `Data15`, `Data16`, `Data17`, `Data18`, `Data19`, `Data20`, `Data21`, `Data22`, `Data23`, `Data24`, `Data25`, `Data26`, `Data27`, `Data28`, `Data29`, `Data30`, `Data31`, `Data32`, `Data33`, `RequiredLevel`, `VerifiedBuild`) VALUES +(278488, 0, 46128, 'Rock', '', '', '', 0.5500001, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30706), -- Rock +(277365, 0, 6391, 'Collision', '', '', '', 3.289998, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30706), -- Collision +(253128, 5, 6450, 'Instance Portal', '', '', '', 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30706); -- Instance Portal + +-- Gameobject Template Addon +DELETE FROM `gameobject_template_addon` WHERE `entry` IN (278488, 277365, 253128); +INSERT INTO `gameobject_template_addon` (`entry`, `faction`, `flags`) VALUES +(277365, 0, 48), -- Collision +(253128, 0, 32), -- Instance Portal +(278488, 0, 48); -- Rock + +-- Instance Script +DELETE FROM `instance_template` WHERE `map`= 1712; +INSERT INTO `instance_template` (`map`, `parent`, `script`, `allowMount`) VALUES +(1712, 0, 'instance_antorus_the_burning_throne', 0); + +-- Instance Encounter +DELETE FROM `instance_encounters` WHERE `entry`= 2076; +INSERT INTO `instance_encounters` (`entry`, `creditType`, `creditEntry`, `lastEncounterDungeon`, `comment`) VALUES +(2076, 0, 122450, 0, 'Garothi Worldbreaker'); + +-- Template Updates +-- Garothi Worldbreaker +UPDATE `creature_template` SET `minlevel`= 113, `maxlevel`= 113, `faction`= 14, `unit_class`= 1, `InhabitType`= 12, `unit_flags2`= 4227072, `VehicleId`= 5430, `mechanic_immune_mask`= 650854271, `ScriptName`= 'boss_garothi_worldbreaker', `flags_extra` = 1 WHERE `entry`= 122450; +-- Annihilator +UPDATE `creature_template` SET `minlevel`= 113, `maxlevel`= 113, `faction`= 14, `unit_class`= 1, `InhabitType`= 12, `unit_flags`= 33554432, `unit_flags2`= 32768, `baseattacktime`= 1500, `VehicleId`= 5430, `mechanic_immune_mask`= 650854271 WHERE `entry`= 122778; +-- Decimator +UPDATE `creature_template` SET `minlevel`= 113, `maxlevel`= 113, `faction`= 14, `unit_class`= 1, `InhabitType`= 12, `unit_flags`= 33554432, `unit_flags2`= 32768, `baseattacktime`= 1500, `VehicleId`= 5430, `mechanic_immune_mask`= 650854271 WHERE `entry`= 122773; +-- Annihilation +UPDATE `creature_template` SET `minlevel`= 113, `maxlevel`= 113, `faction`= 14, `unit_class`= 1, `InhabitType`= 8, `unit_flags`= 33554432, `unit_flags2`= 2048, `flags_extra`= 128 WHERE `entry`= 122818; +-- Garothi Worldbreaker (Surging Fel) +UPDATE `creature_template` SET `minlevel`= 113, `maxlevel`= 113, `faction`= 14, `unit_class`= 1, `InhabitType`= 12, `unit_flags`= 33554432, `unit_flags2`= 34816, `flags_extra`= 128 WHERE `entry`= 124167; + +-- Spells +DELETE FROM `spell_script_names` WHERE `ScriptName` IN +('spell_garothi_apocalypse_drive', +'spell_garothi_fel_bombardment_selector', +'spell_garothi_fel_bombardment_warning', +'spell_garothi_fel_bombardment_periodic', +'spell_garothi_searing_barrage_periodic', +'spell_garothi_searing_barrage_dummy', +'spell_garothi_searing_barrage_selector', +'spell_garothi_decimation_selector', +'spell_garothi_decimation_warning', +'spell_garothi_carnage', +'spell_garothi_annihilation_selector', +'spell_garothi_annihilation_triggered', +'spell_garothi_eradication', +'spell_garothi_surging_fel', +'spell_garothi_cannon_chooser'); + +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(244152, 'spell_garothi_apocalypse_drive'), +(244150, 'spell_garothi_fel_bombardment_selector'), +(246220, 'spell_garothi_fel_bombardment_warning'), +(244536, 'spell_garothi_fel_bombardment_periodic'), +(244398, 'spell_garothi_searing_barrage_dummy'), +(246369, 'spell_garothi_searing_barrage_dummy'), +(246360, 'spell_garothi_searing_barrage_selector'), +(244399, 'spell_garothi_decimation_selector'), +(244410, 'spell_garothi_decimation_warning'), +(244106, 'spell_garothi_carnage'), +(247572, 'spell_garothi_annihilation_selector'), +(244761, 'spell_garothi_annihilation_triggered'), +(244969, 'spell_garothi_eradication'), +(246655, 'spell_garothi_surging_fel'), +(245124, 'spell_garothi_cannon_chooser'); + +-- Addons +DELETE FROM `creature_template_addon` WHERE `entry` IN (122450, 122778, 122773); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `auras`) VALUES +(122450, 0, 0, 0, 0, 0, '246839'), -- 122450 (Garothi Worldbreaker) - Helmet +(122778, 0, 0, 0, 0, 0, '107829'), -- 122778 (Annihilator) - Update Interactions +(122773, 0, 0, 0, 0, 0, '107829'); -- 122773 (Decimator) - Update Interactions + +-- Texts +DELETE FROM `creature_text` WHERE `CreatureID` IN (122450, 122773); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +-- Garothi Worldbreaker +(122450, 0, 0, 'Enemy combatants detected. Threat level nominal.', 14, 0, 100, 0, 0, 86397, 132426, 0, 'Garothi Worldbreaker - Aggro'), +(122450, 1, 0, 'Targets cleared.', 14, 0, 100, 0, 0, 86401, 132427, 0, 'Garothi Worldbreaker - Evade 1'), +(122450, 1, 1, 'Enemy combatants neutralized.', 14, 0, 100, 0, 0, 86974, 135270, 0, 'Garothi Worldbreaker - Evade 2'), +(122450, 1, 2, 'No hostile targets remaining. Continuing patrol.', 14, 0, 100, 0, 0, 86975, 135271, 0, 'Garothi Worldbreaker - Evade 3'), +(122450, 2, 0, '|TInterface\\ICONS\\Ability_BossFelMagnaron_HandEmpowered.blp:20|t %s is powering the |cFFF00000|Hspell:244152|h[Apocalypse Drive]|h|r!', 41, 0, 100, 0, 0, 0, 132631, 0, 'Garothi Worldbreaker - Announce Apocalypse Drive'), +(122450, 3, 0, 'Apocalypse drive engaged.', 14, 0, 100, 0, 0, 86399, 132433, 0, 'Garothi Worldbreaker - Apocalpyse Drive'), +(122450, 4, 0, '|TInterface\\ICONS\\ABILITY_WARRIOR_COLOSSUSSMASH.BLP:20|t The Garothi Worldbreaker rears back for |cFFF00000|Hspell:244969|h[Eradication]|h|r! Run away!', 41, 0, 100, 0, 0, 0, 138536, 0, 'Garothi Worldbreaker - Announce Eradication'), +(122450, 5, 0, 'Systems re-engaged, rerouting power. Weapons primed.', 14, 0, 100, 0, 0, 86400, 132434, 0, 'Garothi Worldbreaker - Finish Apocalypse Drive'), +(122450, 6, 0, 'Targets aquired.', 14, 0, 100, 0, 0, 86971, 135267, 0, 'Garothi Worldbreaker - Decimation 1'), +(122450, 6, 1, 'Targets acquired for decimation.', 14, 0, 100, 0, 0, 86393, 132429, 0, 'Garothi Worldbreaker - Decimation 2'), +(122450, 6, 2, 'Weapon system primed, target locked.', 14, 0, 100, 0, 0, 86395, 132431, 0, 'Garothi Worldbreaker - Decimation 3'), +(122450, 7, 0, 'Leave none alive.', 14, 0, 100, 0, 0, 86396, 132432, 0, 'Garothi Worldbreaker - Annihilation 1'), +(122450, 7, 1, 'Cannon primed for annihilation.', 14, 0, 100, 0, 0, 86394, 132430, 0, 'Garothi Worldbreaker - Annihilation 2'), +(122450, 8, 0, '|TInterface\\ICONS\\ABILITY_HUNTER_SNIPERSHOT.BLP:20|t You are targeted for |cFFF00000|Hspell:244536|h[Fel Bombardment]|h|r!', 42, 0, 100, 0, 0, 0, 132630, 0, 'Garothi Worldbreaker - Announce Fel Bombardment'), +(122450, 9, 0, 'Hostile destroyed.', 14, 0, 100, 0, 0, 86978, 135275, 0, 'Garothi Worldbreaker - Slay 1'), +(122450, 9, 1, 'Threat terminated.', 14, 0, 100, 0, 0, 86977, 135274, 0, 'Garothi Worldbreaker - Slay 2'), +(122450, 9, 2, 'Exterminated.', 14, 0, 100, 0, 0, 86976, 135273, 0, 'Garothi Worldbreaker - Slay 3'), +(122450, 10, 0, 'Critical system failure...', 14, 0, 100, 0, 0, 86398, 132428, 0, 'Garothi Worldbreaker - Death 1'), +(122450, 10, 1, 'Power core... rupturing...', 14, 0, 100, 0, 0, 86972, 135268, 0, 'Garothi Worldbreaker - Death 2'), +(122450, 10, 2, 'Termination imminent...', 14, 0, 100, 0, 0, 86973, 135269, 0, 'Garothi Worldbreaker - Death 3'), +-- Decimator +(122773, 0, 0, '|TInterface\\ICONS\\ABILITY_MAGE_FIRESTARTER.BLP:20|t You are targeted for |cFFF00000|Hspell:244410|h[Decimation]|h|r!', 42, 0, 100, 0, 0, 0, 132901, 0, 'Decimator - Announce Decimation'); + +-- Conditions +DELETE FROM `conditions` WHERE `SourceEntry` IN (247572, 244761, 245237, 246012) AND `SourceTypeOrReferenceId`= 13; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ScriptName`, `Comment`) VALUES +(13, 1, 247572, 0, 0, 51, 0, 5, 122818, 0, 0, 0, '', 'Annihilation - Target Annihilation'), +(13, 2, 245237, 0, 1, 51, 0, 5, 122773, 0, 0, 0, '', 'Empowered - Target Annihilator'), +(13, 2, 245237, 0, 2, 51, 0, 5, 122778, 0, 0, 0, '', 'Empowered - Target Decimator'), +(13, 1, 246012, 0, 0, 51, 0, 5, 122773, 0, 0, 0, '', 'Restore Health - Target Annihilator'), +(13, 1, 246012, 0, 1, 51, 0, 5, 122778, 0, 0, 0, '', 'Restore Health - Target Decimator'); + +-- Summon Groups +DELETE FROM `creature_summon_groups` WHERE `summonerId`= 122450 AND `summonerType`= 0; +INSERT INTO `creature_summon_groups` (`summonerId`, `summonerType`, `groupId`, `entry`, `position_x`, `position_y`, `position_z`, `orientation`, `summonType`, `summonTime`) VALUES +-- SUMMON_GROUP_ID_SURGING_FEL = 0 +(122450, 0, 0, 124167, -3262, 9810.797, -63.93631, 4.577713, 8, 0), +(122450, 0, 0, 124167, -3277, 9810.8, -63.68398, 4.577713, 8, 0), +(122450, 0, 0, 124167, -3307, 9810.8, -63.85229, 4.577713, 8, 0), +(122450, 0, 0, 124167, -3322, 9810.8, -64.84264, 4.577713, 8, 0), +(122450, 0, 0, 124167, -3292, 9810.797, -64.07504, 4.577713, 8, 0); + +-- Vehicle Accessories +DELETE FROM `vehicle_template_accessory` WHERE `entry`= 122450; +INSERT INTO `vehicle_template_accessory` (`entry`, `accessory_entry`, `seat_id`, `minion`, `description`, `summontype`, `summontimer`) VALUES +(122450, 122773, 1, 1, 'Garothi Worldbreaker - Decimator', 6, 200), -- Garothi Worldbreaker - Decimator +(122450, 122778, 0, 1, 'Garothi Worldbreaker - Annihilator', 6, 200); -- Garothi Worldbreaker - Annihilator + +-- Spellclicks +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`= 122450; +INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `cast_flags`, `user_type`) VALUES +(122450, 237873, 1, 1); + +-- Model Data +DELETE FROM `creature_model_info` WHERE `DisplayID` IN (76534, 77168, 77029); +INSERT INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`) VALUES +(76534, 20.02365, 30, 0), +(77168, 3.944656, 75, 0), +(77029, 3.944656, 75, 0); + +-- Areatriggers +DELETE FROM `areatrigger_template` WHERE `Id` IN (15496, 0); +INSERT INTO `areatrigger_template` (`Id`, `Type`, `Flags`, `Data0`, `Data1`, `Data2`, `Data3`, `Data4`, `Data5`, `ScriptName`, `VerifiedBuild`) VALUES +(15496, 0, 4, 4, 4, 0, 0, 0, 0, 'at_garothi_annihilation', 26365), +(0, 3, 4, 0, 0, 0, 0, 0, 0, '', 26365); + +DELETE FROM `spell_areatrigger` WHERE `SpellMiscId` IN (10662, 10876); +INSERT INTO `spell_areatrigger` (`SpellMiscId`, `AreaTriggerId`) VALUES +(10662, 15496), +(10876, 0); + +DELETE FROM `areatrigger_template_polygon_vertices` WHERE `AreaTriggerId`= 0; +INSERT INTO `areatrigger_template_polygon_vertices` (`AreaTriggerId`, `Idx`, `VerticeX`, `VerticeY`, `VerifiedBuild`) VALUES +(0, 0, 0, -15, 26365), +(0, 1, 0, 15, 26365), +(0, 2, 100, -15, 26365), +(0, 3, 100, -15, 26365); + +-- Procs +DELETE FROM `spell_proc` WHERE `SpellId`= 244106; +INSERT INTO `spell_proc` (`SpellId`, `SpellFamilyName`, `SpellFamilyMask0`, `SpellFamilyMask1`, `SpellFamilyMask2`, `ProcFlags`, `SpellTypeMask`, `SpellPhaseMask`, `HitMask`, `AttributesMask`) VALUES +(244106, 0, 0, 0, 0x00000008, 0, 0, 0, 0, 0); + +-- Custom Attributes +DELETE FROM `spell_custom_attr` WHERE `entry` IN (244761, 244410); +INSERT INTO `spell_custom_attr` (`entry`, `attributes`) VALUES +(244761, 0x8), +(244410, 0x1000); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 3c15a374d1b..1dcc18773e1 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3590,6 +3590,20 @@ void SpellMgr::LoadSpellInfoCorrections() }); // ENDOF FIRELANDS SPELLS + // + // ANTORUS THE BURNING THRONE SPELLS + // + + // Decimation + ApplySpellFix({ 244449 }, [](SpellInfo* spellInfo) + { + // For some reason there is a instakill effect that serves absolutely no purpose. + // Until we figure out what it's actually used for we disable it. + const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_2))->Effect = 0; + }); + + // ENDOF ANTORUS THE BURNING THRONE SPELLS + // Summon Master Li Fei ApplySpellFix({ 102445 }, [](SpellInfo* spellInfo) { diff --git a/src/server/scripts/Argus/AntorusTheBurningThrone/antorus_the_burning_throne.h b/src/server/scripts/Argus/AntorusTheBurningThrone/antorus_the_burning_throne.h new file mode 100644 index 00000000000..7d4fa40ee4c --- /dev/null +++ b/src/server/scripts/Argus/AntorusTheBurningThrone/antorus_the_burning_throne.h @@ -0,0 +1,75 @@ +/* + * 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/>. + */ + +#ifndef DEF_ANTORUS_THE_BURNING_THRONE_H_ +#define DEF_ANTORUS_THE_BURNING_THRONE_H_ + +#include "CreatureAIImpl.h" + +#define DataHeader "ABT" +#define ABTScriptName "instance_antorus_the_burning_throne" + +uint32 const EncounterCount = 10; + +enum AntorusDataTypes +{ + // Encounters + DATA_GAROTHI_WORLDBREAKER = 0, + DATA_FELHOUNDS_OF_SAGERAS = 1, + DATA_ANTORAN_HIGH_COMMAND = 2, + DATA_PORTAL_KEEPER_HASABEL = 3, + DATA_EONAR_THE_LIFE_BINDER = 4, + DATA_IMONAR_THE_SOULHUNTER = 5, + DATA_KINGAROTH = 6, + DATA_VARIMATHRAS = 7, + DATA_THE_COVEN_OF_SHIVARRA = 8, + DATA_AGGRAMAR = 9, + DATA_ARGUS_THE_UNMAKER = 10, + + // Encounter related data + DATA_DECIMATOR, + DATA_ANNIHILATOR +}; + +enum AntorusCreatureIds +{ + // Bosses + BOSS_GAROTHI_WORLDBREAKER = 122450, + + // Encounter related creatures + /*Garothi Worldbreaker*/ + NPC_DECIMATOR = 122773, + NPC_ANNIHILATOR = 122778, + NPC_ANNIHILATION = 122818, + NPC_GAROTHI_WORLDBREAKER = 124167 +}; + +enum AntorusGameObjectIds +{ + GO_COLLISION = 277365, + GO_ROCK = 278488 +}; + +template <class AI, class T> +inline AI* GetAntorusTheBurningThroneAI(T* obj) +{ + return GetInstanceAI<AI>(obj, ABTScriptName); +} + +#define RegisterAntorusTheBurningThroneCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetAntorusTheBurningThroneAI) + +#endif diff --git a/src/server/scripts/Argus/AntorusTheBurningThrone/boss_garothi_worldbreaker.cpp b/src/server/scripts/Argus/AntorusTheBurningThrone/boss_garothi_worldbreaker.cpp new file mode 100644 index 00000000000..bc96be615eb --- /dev/null +++ b/src/server/scripts/Argus/AntorusTheBurningThrone/boss_garothi_worldbreaker.cpp @@ -0,0 +1,912 @@ +/* + * 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 "AreaTriggerAI.h" +#include "CreatureAI.h" +#include "CreatureAIImpl.h" +#include "GridNotifiers.h" +#include "InstanceScript.h" +#include "Map.h" +#include "ObjectAccessor.h" +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "SpellAuras.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" +#include "antorus_the_burning_throne.h" + +enum Texts +{ + // Garothi Worldbreaker + SAY_AGGRO = 0, + SAY_DISENGAGE = 1, + SAY_ANNOUNCE_APOCALYPSE_DRIVE = 2, + SAY_APOCALYPSE_DRIVE = 3, + SAY_ANNOUNCE_ERADICATION = 4, + SAY_FINISH_APOCALYPSE_DRIVE = 5, + SAY_DECIMATION = 6, + SAY_ANNIHILATION = 7, + SAY_ANNOUNCE_FEL_BOMBARDMENT = 8, + SAY_SLAY = 9, + SAY_DEATH = 10, + + // Decimator + SAY_ANNOUNCE_DECIMATION = 0 +}; + +enum Spells +{ + // Garothi Worldbreaker + SPELL_MELEE = 248229, + SPELL_APOCALYPSE_DRIVE = 244152, + SPELL_APOCALYPSE_DRIVE_PERIODIC_DAMAGE = 253300, + SPELL_APOCALYPSE_DRIVE_FINAL_DAMAGE = 240277, + SPELL_ERADICATION = 244969, + SPELL_EMPOWERED = 245237, + SPELL_RESTORE_HEALTH = 246012, + SPELL_ANNIHILATOR_CANNON_EJECT = 245527, + SPELL_DECIMATOR_CANNON_EJECT = 245515, + SPELL_FEL_BOMBARDMENT_SELECTOR = 244150, + SPELL_FEL_BOMBARDMENT_WARNING = 246220, + SPELL_FEL_BOMBARDMENT_DUMMY = 245219, + SPELL_FEL_BOMBARDMENT_PERIODIC = 244536, + SPELL_CANNON_CHOOSER = 245124, + SPELL_SEARING_BARRAGE_ANNIHILATOR = 246368, + SPELL_SEARING_BARRAGE_DECIMATOR = 244395, + SPELL_SEARING_BARRAGE_DUMMY_ANNIHILATOR = 244398, + SPELL_SEARING_BARRAGE_DUMMY_DECIMATOR = 246369, + SPELL_SEARING_BARRAGE_SELECTOR = 246360, + SPELL_SEARING_BARRAGE_DAMAGE_ANNIHILATOR = 244400, + SPELL_SEARING_BARRAGE_DAMAGE_DECIMATOR = 246373, + SPELL_CARNAGE = 244106, + + // Decimator + SPELL_DECIMATION_SELECTOR = 244399, + SPELL_DECIMATION_WARNING = 244410, + SPELL_DECIMATION_CAST_VISUAL = 245338, + SPELL_DECIMATION_MISSILE = 244448, + + // Annihilator + SPELL_ANNIHILATION_SUMMON = 244790, + SPELL_ANNIHILATION_SELECTOR = 247572, + SPELL_ANNIHILATION_DUMMY = 244294, + SPELL_ANNIHILATION_DAMAGE_UNSPLITTED = 244762, + + // Annihilation + SPELL_ANNIHILATION_AREA_TRIGGER = 244795, + SPELL_ANNIHILATION_WARNING = 244799, + + // Garothi Worldbreaker (Surging Fel) + SPELL_SURGING_FEL_AREA_TRIGGER = 246655, + SPELL_SURGING_FEL_DAMAGE = 246663 + +}; + +enum Events +{ + // Garothi Worldbreaker + EVENT_REENGAGE_PLAYERS = 1, + EVENT_FEL_BOMBARDMENT, + EVENT_SEARING_BARRAGE, + EVENT_CANNON_CHOOSER, + EVENT_SURGING_FEL +}; + +enum Data +{ + DATA_LAST_FIRED_CANNON = 0 +}; + +enum AnimKits +{ + ANIM_KIT_ID_CANNON_DESTROYED = 13264 +}; + +enum TargetSize : uint8 +{ + MIN_TARGETS_SIZE = 2, + MAX_TARGETS_SIZE = 6 +}; + +enum Misc +{ + SUMMON_GROUP_ID_SURGING_FEL = 0, + ENCOUNTER_ID_GAROTHI_WORLDBREAKER = 2076 +}; + +namespace TargetHandler +{ + class VictimCheck + { + public: + VictimCheck(Unit* caster, bool keepTank) : _caster(caster), _keepTank(keepTank) { } + + bool operator()(WorldObject* object) + { + Unit* unit = object->ToUnit(); + if (!unit) + return true; + + if (_caster->GetVictim() && _caster->GetVictim() != unit) + return _keepTank; + + return false; + } + private: + Unit* _caster; + bool _keepTank; // true = remove all nontank targets | false = remove current tank + }; + + void PreferNonTankTargetsAndResizeTargets(std::list<WorldObject*>& targets, Unit* caster) + { + if (targets.empty()) + return; + + std::list<WorldObject*> targetsCopy = targets; + uint8 size = targetsCopy.size(); + // Selecting our prefered target size based on total targets (min 10 player: 2, max 30 player: 6) + uint8 preferedSize = std::min<uint8>(std::max<uint8>(std::ceil(size / 5), MIN_TARGETS_SIZE), MAX_TARGETS_SIZE); + + // Now we get rid of the tank as these abilities prefer non-tanks above tanks as long as there are alternatives + targetsCopy.remove_if(TargetHandler::VictimCheck(caster, false)); + + // We have less available nontank targets than we want, include tanks + if (targetsCopy.size() < preferedSize) + Trinity::Containers::RandomResize(targets, preferedSize); + else + { + // Our target list has enough alternative targets, resize + Trinity::Containers::RandomResize(targetsCopy, preferedSize); + targets = targetsCopy; + } + } +} + +static constexpr uint32 const MaxApocalypseDriveCount = 2; +Position const AnnihilationCenterReferencePos = { -3296.72f, 9767.78f, -60.0f }; + +struct boss_garothi_worldbreaker : public BossAI +{ + boss_garothi_worldbreaker(Creature* creature) : BossAI(creature, DATA_GAROTHI_WORLDBREAKER) + { + Initialize(); + me->SetReactState(REACT_PASSIVE); + } + + void Initialize() + { + SetCombatMovement(false); + + switch (GetDifficulty()) + { + case DIFFICULTY_MYTHIC_RAID: + case DIFFICULTY_HEROIC_RAID: + _apocalypseDriveHealthLimit[0] = 65; + _apocalypseDriveHealthLimit[1] = 35; + break; + case DIFFICULTY_NORMAL_RAID: + case DIFFICULTY_LFR_NEW: + _apocalypseDriveHealthLimit[0] = 60; + _apocalypseDriveHealthLimit[1] = 20; + break; + default: + break; + } + + // Todo: move this section out of the ctor and remove the .clear call when dynamic spawns have been merged. + _apocalypseDriveCount = 0; + _searingBarrageSpellId = 0; + _lastCanonEntry = NPC_DECIMATOR; + _castEradication = false; + _surgingFelDummyGuids.clear(); + } + + void Reset() override + { + _Reset(); + Initialize(); + me->SummonCreatureGroup(SUMMON_GROUP_ID_SURGING_FEL); + } + + void EnterCombat(Unit* /*who*/) override + { + me->SetReactState(REACT_AGGRESSIVE); + _EnterCombat(); + Talk(SAY_AGGRO); + DoCastSelf(SPELL_MELEE); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + events.ScheduleEvent(EVENT_FEL_BOMBARDMENT, 9s); + events.ScheduleEvent(EVENT_CANNON_CHOOSER, 8s); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + Talk(SAY_DISENGAGE); + _EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + events.Reset(); + CleanupEncounter(); + _DespawnAtEvade(); + } + + void KilledUnit(Unit* victim) override + { + if (victim->IsPlayer()) + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + CleanupEncounter(); + instance->SendBossKillCredit(ENCOUNTER_ID_GAROTHI_WORLDBREAKER); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + void OnSuccessfulSpellCast(SpellInfo const* spell) override + { + switch (spell->Id) + { + case SPELL_APOCALYPSE_DRIVE_FINAL_DAMAGE: + if (_apocalypseDriveCount < MaxApocalypseDriveCount) + events.Reset(); + events.ScheduleEvent(EVENT_REENGAGE_PLAYERS, 3s + 500ms); + HideCannons(); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + break; + default: + break; + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPctDamaged(_apocalypseDriveHealthLimit[_apocalypseDriveCount], damage)) + { + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + me->InterruptNonMeleeSpells(true); + me->SetFacingTo(me->GetHomePosition().GetOrientation()); + events.Reset(); + + if (GetDifficulty() == DIFFICULTY_MYTHIC_RAID || GetDifficulty() == DIFFICULTY_HEROIC_RAID) + events.ScheduleEvent(EVENT_SURGING_FEL, 8s); + + DoCastSelf(SPELL_APOCALYPSE_DRIVE); + DoCastSelf(SPELL_APOCALYPSE_DRIVE_FINAL_DAMAGE); + Talk(SAY_ANNOUNCE_APOCALYPSE_DRIVE); + Talk(SAY_APOCALYPSE_DRIVE); + me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + + if (Creature* decimator = instance->GetCreature(DATA_DECIMATOR)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, decimator, 2); + decimator->AddUnitFlag(UNIT_FLAG_IN_COMBAT); + decimator->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } + + if (Creature* annihilator = instance->GetCreature(DATA_ANNIHILATOR)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, annihilator, 2); + annihilator->AddUnitFlag(UNIT_FLAG_IN_COMBAT); + annihilator->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } + ++_apocalypseDriveCount; + } + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + switch (summon->GetEntry()) + { + case NPC_ANNIHILATION: + summon->CastSpell(summon, SPELL_ANNIHILATION_WARNING); + summon->CastSpell(summon, SPELL_ANNIHILATION_AREA_TRIGGER); + break; + case NPC_ANNIHILATOR: + case NPC_DECIMATOR: + summon->SetReactState(REACT_PASSIVE); + break; + case NPC_GAROTHI_WORLDBREAKER: + _surgingFelDummyGuids.insert(summon->GetGUID()); + break; + default: + break; + } + } + + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + switch (summon->GetEntry()) + { + case NPC_DECIMATOR: + case NPC_ANNIHILATOR: + me->InterruptNonMeleeSpells(true); + me->RemoveAurasDueToSpell(SPELL_APOCALYPSE_DRIVE); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + + if (summon->GetEntry() == NPC_ANNIHILATOR) + _searingBarrageSpellId = SPELL_SEARING_BARRAGE_ANNIHILATOR; + else + _searingBarrageSpellId = SPELL_SEARING_BARRAGE_DECIMATOR; + + if (_apocalypseDriveCount < MaxApocalypseDriveCount) + events.Reset(); + + events.ScheduleEvent(EVENT_SEARING_BARRAGE, 3s + 500ms); + events.ScheduleEvent(EVENT_REENGAGE_PLAYERS, 3s + 500ms); + _castEradication = true; + + if (summon->GetEntry() == NPC_DECIMATOR) + DoCastSelf(SPELL_DECIMATOR_CANNON_EJECT); + else + DoCastSelf(SPELL_ANNIHILATOR_CANNON_EJECT); + + me->PlayOneShotAnimKitId(ANIM_KIT_ID_CANNON_DESTROYED); + HideCannons(); + break; + default: + break; + } + } + + uint32 GetData(uint32 type) const override + { + if (type == DATA_LAST_FIRED_CANNON) + return _lastCanonEntry; + + return 0; + } + + void SetData(uint32 type, uint32 value) override + { + if (type == DATA_LAST_FIRED_CANNON) + _lastCanonEntry = value; + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) && !me->HasAura(SPELL_APOCALYPSE_DRIVE)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_REENGAGE_PLAYERS: + DoCastSelf(SPELL_EMPOWERED); + DoCastSelf(SPELL_RESTORE_HEALTH); + if (_castEradication) + { + DoCastSelf(SPELL_ERADICATION); + Talk(SAY_ANNOUNCE_ERADICATION); + Talk(SAY_FINISH_APOCALYPSE_DRIVE); + _castEradication = false; + } + me->SetReactState(REACT_AGGRESSIVE); + events.ScheduleEvent(EVENT_FEL_BOMBARDMENT, 20s); + events.ScheduleEvent(EVENT_CANNON_CHOOSER, 18s); + break; + case EVENT_FEL_BOMBARDMENT: + DoCastAOE(SPELL_FEL_BOMBARDMENT_SELECTOR); + events.Repeat(20s); + break; + case EVENT_SEARING_BARRAGE: + DoCastSelf(_searingBarrageSpellId); + break; + case EVENT_CANNON_CHOOSER: + DoCastSelf(SPELL_CANNON_CHOOSER); + events.Repeat(16s); + break; + case EVENT_SURGING_FEL: + { + GuidSet guids = _surgingFelDummyGuids; + guids.erase(_lastSurgingFelDummyGuid); + _lastSurgingFelDummyGuid = Trinity::Containers::SelectRandomContainerElement(guids); + if (Creature* dummy = ObjectAccessor::GetCreature(*me, _lastSurgingFelDummyGuid)) + dummy->CastSpell(dummy, SPELL_SURGING_FEL_AREA_TRIGGER); + + events.Repeat(8s); + break; + } + default: + break; + } + } + + if (me->GetVictim() && me->GetVictim()->IsWithinMeleeRange(me)) + DoMeleeAttackIfReady(); + else + DoSpellAttackIfReady(SPELL_CARNAGE); + } + private: + uint8 _apocalypseDriveHealthLimit[MaxApocalypseDriveCount]; + uint8 _apocalypseDriveCount; + uint32 _searingBarrageSpellId; + uint32 _lastCanonEntry; + bool _castEradication; + ObjectGuid _lastSurgingFelDummyGuid; + GuidSet _surgingFelDummyGuids; + + void CleanupEncounter() + { + if (Creature* decimator = instance->GetCreature(DATA_DECIMATOR)) + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, decimator); + + if (Creature* annihilator = instance->GetCreature(DATA_ANNIHILATOR)) + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, annihilator); + + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DECIMATION_WARNING); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FEL_BOMBARDMENT_WARNING); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FEL_BOMBARDMENT_PERIODIC); + summons.DespawnAll(); + } + + void HideCannons() + { + if (Creature* decimator = instance->GetCreature(DATA_DECIMATOR)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, decimator); + decimator->AddUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_UNK_31)); + } + + if (Creature* annihilator = instance->GetCreature(DATA_ANNIHILATOR)) + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, annihilator); + annihilator->AddUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_UNK_31)); + } + } +}; + +struct at_garothi_annihilation : AreaTriggerAI +{ + at_garothi_annihilation(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) + { + Initialize(); + } + + void Initialize() + { + _playerCount = 0; + } + + void OnUnitEnter(Unit* unit) override + { + if (!unit->IsPlayer()) + return; + + _playerCount++; + + if (Unit* annihilation = at->GetCaster()) + annihilation->RemoveAurasDueToSpell(SPELL_ANNIHILATION_WARNING); + } + + void OnUnitExit(Unit* unit) override + { + if (!unit->IsPlayer()) + return; + + _playerCount--; + + if (!_playerCount && !at->IsRemoved()) + if (Unit* annihilation = at->GetCaster()) + annihilation->CastSpell(annihilation, SPELL_ANNIHILATION_WARNING); + } + +private: + uint8 _playerCount; +}; + +class spell_garothi_apocalypse_drive : public AuraScript +{ + PrepareAuraScript(spell_garothi_apocalypse_drive); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_APOCALYPSE_DRIVE_PERIODIC_DAMAGE }); + } + + void HandlePeriodic(AuraEffect const* aurEff) + { + GetTarget()->CastSpell(GetTarget(), SPELL_APOCALYPSE_DRIVE_PERIODIC_DAMAGE, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_garothi_apocalypse_drive::HandlePeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +class spell_garothi_fel_bombardment_selector : public SpellScript +{ + PrepareSpellScript(spell_garothi_fel_bombardment_selector); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_FEL_BOMBARDMENT_WARNING, + SPELL_FEL_BOMBARDMENT_DUMMY + }); + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + if (targets.empty()) + return; + + if (Unit* caster = GetCaster()) + targets.remove_if(TargetHandler::VictimCheck(caster, true)); + } + + void HandleWarningEffect(SpellEffIndex /*effIndex*/) + { + Creature* caster = GetCaster() ? GetCaster()->ToCreature() : nullptr; + if (!caster || !caster->IsAIEnabled) + return; + + Unit* target = GetHitUnit(); + caster->AI()->Talk(SAY_ANNOUNCE_FEL_BOMBARDMENT, target); + caster->CastSpell(target, SPELL_FEL_BOMBARDMENT_WARNING, true); + caster->CastSpell(target, SPELL_FEL_BOMBARDMENT_DUMMY, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_garothi_fel_bombardment_selector::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_garothi_fel_bombardment_selector::HandleWarningEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +class spell_garothi_fel_bombardment_warning : public AuraScript +{ + PrepareAuraScript(spell_garothi_fel_bombardment_warning); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FEL_BOMBARDMENT_PERIODIC }); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + if (Unit* caster = GetCaster()) + caster->CastSpell(GetTarget(), SPELL_FEL_BOMBARDMENT_PERIODIC, true); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_garothi_fel_bombardment_warning::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +class spell_garothi_fel_bombardment_periodic : public AuraScript +{ + PrepareAuraScript(spell_garothi_fel_bombardment_periodic); + + bool Validate(SpellInfo const* spellInfo) override + { + return ValidateSpellInfo({ uint32(spellInfo->GetEffect(DIFFICULTY_NONE, EFFECT_0)->BasePoints) }); + } + + void HandlePeriodic(AuraEffect const* /*aurEff*/) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(GetTarget(), uint32(GetSpellInfo()->GetEffect(DIFFICULTY_NONE, EFFECT_0)->BasePoints), true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_garothi_fel_bombardment_periodic::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; + +class spell_garothi_searing_barrage_dummy : public SpellScript +{ + PrepareSpellScript(spell_garothi_searing_barrage_dummy); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SEARING_BARRAGE_SELECTOR }); + } + + void HandleHit(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastCustomSpell(SPELL_SEARING_BARRAGE_SELECTOR, SPELLVALUE_BASE_POINT0, GetSpellInfo()->Id, GetHitUnit(), true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_garothi_searing_barrage_dummy::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +class spell_garothi_searing_barrage_selector : public SpellScript +{ + PrepareSpellScript(spell_garothi_searing_barrage_selector); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_SEARING_BARRAGE_DAMAGE_ANNIHILATOR, + SPELL_SEARING_BARRAGE_DAMAGE_DECIMATOR, + SPELL_SEARING_BARRAGE_DUMMY_ANNIHILATOR, + SPELL_SEARING_BARRAGE_DUMMY_DECIMATOR + }); + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + TargetHandler::PreferNonTankTargetsAndResizeTargets(targets, GetCaster()); + } + + void HandleHit(SpellEffIndex /*effIndex*/) + { + uint32 spellId = GetEffectValue() == SPELL_SEARING_BARRAGE_DUMMY_ANNIHILATOR ? SPELL_SEARING_BARRAGE_DAMAGE_ANNIHILATOR : SPELL_SEARING_BARRAGE_DAMAGE_DECIMATOR; + if (Unit* caster = GetCaster()) + caster->CastSpell(GetHitUnit(), spellId, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_garothi_searing_barrage_selector::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_garothi_searing_barrage_selector::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +class spell_garothi_decimation_selector : public SpellScript +{ + PrepareSpellScript(spell_garothi_decimation_selector); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DECIMATION_WARNING }); + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + TargetHandler::PreferNonTankTargetsAndResizeTargets(targets, GetCaster()); + } + + void HandleHit(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + caster->CastSpell(GetHitUnit(), SPELL_DECIMATION_WARNING, true); + if (Creature* decimator = caster->ToCreature()) + if (decimator->IsAIEnabled) + decimator->AI()->Talk(SAY_ANNOUNCE_DECIMATION, GetHitUnit()); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_garothi_decimation_selector::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_garothi_decimation_selector::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +class spell_garothi_decimation_warning : public AuraScript +{ + PrepareAuraScript(spell_garothi_decimation_warning); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DECIMATION_MISSILE }); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + { + if (Unit* caster = GetCaster()) + { + caster->CastSpell(GetTarget(), SPELL_DECIMATION_MISSILE, true); + if (!caster->HasUnitState(UNIT_STATE_CASTING)) + caster->CastSpell(caster, SPELL_DECIMATION_CAST_VISUAL); + } + } + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_garothi_decimation_warning::AfterRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +class spell_garothi_carnage : public AuraScript +{ + PrepareAuraScript(spell_garothi_carnage); + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo`*/) + { + // Usually we could just handle this via spell_proc but since we want + // to silence the console message because it's not a spell trigger proc, we need a script here. + PreventDefaultAction(); + Remove(); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_garothi_carnage::HandleProc, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; + +class spell_garothi_annihilation_selector : public SpellScript +{ + PrepareSpellScript(spell_garothi_annihilation_selector); + + bool Validate(SpellInfo const* spellInfo) override + { + return ValidateSpellInfo({ uint32(spellInfo->GetEffect(DIFFICULTY_NONE, EFFECT_0)->BasePoints) }); + } + + void HandleHit(SpellEffIndex effIndex) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(GetHitUnit(), uint32(GetSpellInfo()->GetEffect(DIFFICULTY_NONE, effIndex)->BasePoints), true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_garothi_annihilation_selector::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +class spell_garothi_annihilation_triggered : public SpellScript +{ + PrepareSpellScript(spell_garothi_annihilation_triggered); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_ANNIHILATION_DAMAGE_UNSPLITTED }); + } + + void HandleHit(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + if (target->HasAura(SPELL_ANNIHILATION_WARNING)) + target->CastSpell(target, SPELL_ANNIHILATION_DAMAGE_UNSPLITTED, true); + + target->RemoveAllAuras(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_garothi_annihilation_triggered::HandleHit, EFFECT_1, SPELL_EFFECT_DUMMY); + } +}; + +class spell_garothi_eradication : public SpellScript +{ + PrepareSpellScript(spell_garothi_eradication); + + void ChangeDamage() + { + if (Unit* caster = GetCaster()) + { + uint32 damageReduction = CalculatePct(GetHitDamage(), GetHitUnit()->GetDistance(caster)); + SetHitDamage(GetHitDamage() - damageReduction); + } + } + + void Register() override + { + OnHit += SpellHitFn(spell_garothi_eradication::ChangeDamage); + } +}; + +class spell_garothi_surging_fel : public AuraScript +{ + PrepareAuraScript(spell_garothi_surging_fel); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SURGING_FEL_DAMAGE }); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + GetTarget()->CastSpell(GetTarget(), SPELL_SURGING_FEL_DAMAGE, true); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_garothi_surging_fel::AfterRemove, EFFECT_0, SPELL_AURA_AREA_TRIGGER, AURA_EFFECT_HANDLE_REAL); + } +}; + +class spell_garothi_cannon_chooser : public SpellScript +{ + PrepareSpellScript(spell_garothi_cannon_chooser); + + void HandleDummyEffect(SpellEffIndex /*effIndex*/) + { + Creature* caster = GetHitCreature(); + if (!caster || !caster->IsAIEnabled) + return; + + InstanceScript* instance = caster->GetInstanceScript(); + if (!instance) + return; + + Creature* decimator = instance->GetCreature(DATA_DECIMATOR); + Creature* annihilator = instance->GetCreature(DATA_ANNIHILATOR); + uint32 lastCannonEntry = caster->AI()->GetData(DATA_LAST_FIRED_CANNON); + + if ((lastCannonEntry == NPC_ANNIHILATOR && decimator) || (decimator && !annihilator)) + { + decimator->CastSpell(decimator, SPELL_DECIMATION_SELECTOR, true); + caster->AI()->Talk(SAY_DECIMATION, decimator); + lastCannonEntry = NPC_DECIMATOR; + } + else if ((lastCannonEntry == NPC_DECIMATOR && annihilator) || (annihilator && !decimator)) + { + uint8 count = caster->GetMap()->GetDifficultyID() == DIFFICULTY_MYTHIC_RAID ? MAX_TARGETS_SIZE : + std::max<uint8>(MIN_TARGETS_SIZE, std::ceil(caster->GetMap()->GetPlayersCountExceptGMs() / 5)); + + for (uint8 i = 0; i < count; i++) + { + float x = AnnihilationCenterReferencePos.GetPositionX() + cos(frand(0.0f, float(M_PI * 2))) * frand(15.0f, 30.0f); + float y = AnnihilationCenterReferencePos.GetPositionY() + sin(frand(0.0f, float(M_PI * 2))) * frand(15.0f, 30.0f); + float z = caster->GetMap()->GetHeight(caster->GetPhaseShift(), x, y, AnnihilationCenterReferencePos.GetPositionZ()); + annihilator->CastSpell(x, y, z, SPELL_ANNIHILATION_SUMMON, true); + } + + annihilator->CastSpell(annihilator, SPELL_ANNIHILATION_DUMMY); + annihilator->CastSpell(annihilator, SPELL_ANNIHILATION_SELECTOR); + caster->AI()->Talk(SAY_ANNIHILATION); + lastCannonEntry = NPC_ANNIHILATOR; + } + + caster->AI()->SetData(DATA_LAST_FIRED_CANNON, lastCannonEntry); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_garothi_cannon_chooser::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +void AddSC_boss_garothi_worldbreaker() +{ + RegisterAntorusTheBurningThroneCreatureAI(boss_garothi_worldbreaker); + RegisterAreaTriggerAI(at_garothi_annihilation); + RegisterAuraScript(spell_garothi_apocalypse_drive); + RegisterSpellScript(spell_garothi_fel_bombardment_selector); + RegisterAuraScript(spell_garothi_fel_bombardment_warning); + RegisterAuraScript(spell_garothi_fel_bombardment_periodic); + RegisterSpellScript(spell_garothi_searing_barrage_dummy); + RegisterSpellScript(spell_garothi_searing_barrage_selector); + RegisterSpellScript(spell_garothi_decimation_selector); + RegisterAuraScript(spell_garothi_decimation_warning); + RegisterAuraScript(spell_garothi_carnage); + RegisterSpellScript(spell_garothi_annihilation_selector); + RegisterSpellScript(spell_garothi_annihilation_triggered); + RegisterSpellScript(spell_garothi_eradication); + RegisterAuraScript(spell_garothi_surging_fel); + RegisterSpellScript(spell_garothi_cannon_chooser); +} diff --git a/src/server/scripts/Argus/AntorusTheBurningThrone/instance_antorus_the_burning_throne.cpp b/src/server/scripts/Argus/AntorusTheBurningThrone/instance_antorus_the_burning_throne.cpp new file mode 100644 index 00000000000..5f13e2b0877 --- /dev/null +++ b/src/server/scripts/Argus/AntorusTheBurningThrone/instance_antorus_the_burning_throne.cpp @@ -0,0 +1,81 @@ +/* + * 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 "antorus_the_burning_throne.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "GameObject.h" +#include "InstanceScript.h" +#include "Map.h" + +ObjectData const creatureData[] = +{ + { BOSS_GAROTHI_WORLDBREAKER, DATA_GAROTHI_WORLDBREAKER }, + { NPC_DECIMATOR, DATA_DECIMATOR }, + { NPC_ANNIHILATOR, DATA_ANNIHILATOR }, + { 0, 0 } // END +}; + +DoorData const doorData[] = +{ + { GO_COLLISION, DATA_GAROTHI_WORLDBREAKER, DOOR_TYPE_PASSAGE }, + { GO_ROCK, DATA_GAROTHI_WORLDBREAKER, DOOR_TYPE_PASSAGE }, + { 0, 0, DOOR_TYPE_ROOM } // END +}; + +class instance_antorus_the_burning_throne: public InstanceMapScript +{ + public: + instance_antorus_the_burning_throne() : InstanceMapScript(ABTScriptName, 757) { } + + struct instance_antorus_the_burning_throne_InstanceMapScript: public InstanceScript + { + instance_antorus_the_burning_throne_InstanceMapScript(InstanceMap* map) : InstanceScript(map) + { + SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadObjectData(creatureData, nullptr); + LoadDoorData(doorData); + } + + void OnCreatureCreate(Creature* creature) override + { + InstanceScript::OnCreatureCreate(creature); + + switch (creature->GetEntry()) + { + case NPC_ANNIHILATION: + if (Creature* garothi = GetCreature(DATA_GAROTHI_WORLDBREAKER)) + garothi->AI()->JustSummoned(creature); + break; + default: + break; + } + } + }; + + InstanceScript* GetInstanceScript(InstanceMap* map) const + { + return new instance_antorus_the_burning_throne_InstanceMapScript(map); + } +}; + +void AddSC_instance_antorus_the_burning_throne() +{ + new instance_antorus_the_burning_throne(); +} diff --git a/src/server/scripts/Argus/argus_script_loader.cpp b/src/server/scripts/Argus/argus_script_loader.cpp new file mode 100644 index 00000000000..54690dd55d0 --- /dev/null +++ b/src/server/scripts/Argus/argus_script_loader.cpp @@ -0,0 +1,28 @@ +/* + * 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/>. + */ + +// This is where scripts' loading functions should be declared: +void AddSC_boss_garothi_worldbreaker(); +void AddSC_instance_antorus_the_burning_throne(); + +// The name of this function should match: +// void Add${NameOfDirectory}Scripts() +void AddArgusScripts() +{ + AddSC_boss_garothi_worldbreaker(); // Antorus the Burning Throne + AddSC_instance_antorus_the_burning_throne(); +} |