diff --git a/.github/agents/pr-reviewer.md b/.github/agents/pr-reviewer.md index 0c7ee6571..830c3533f 100644 --- a/.github/agents/pr-reviewer.md +++ b/.github/agents/pr-reviewer.md @@ -109,7 +109,7 @@ Based on CLAUDE.md, always verify: - Be constructive and educational - Reference specific sections of CLAUDE.md, C++ Code Standards, or SQL Standards when applicable -- Suggest specific fixes with code examples when possible +- Suggest specific fixes with code examples if necessary - Highlight both issues and good practices - For C++ issues, cite specific rule from C++ Code Standards wiki - For SQL issues, cite specific rule from SQL Standards wiki diff --git a/.gitignore b/.gitignore index a25db2997..40ae7c56b 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,11 @@ CMakeLists.txt.user *.BASE.* *.LOCAL.* +# +# AI tools +# +.claude + # # IDE & other software # diff --git a/apps/ci/ci-install-modules.sh b/apps/ci/ci-install-modules.sh index 7ff647075..cc27b349d 100755 --- a/apps/ci/ci-install-modules.sh +++ b/apps/ci/ci-install-modules.sh @@ -76,6 +76,7 @@ git clone --depth=1 --branch=master https://github.com/azerothcore/mod-pocket-po git clone --depth=1 --branch=master https://github.com/azerothcore/mod-premium modules/mod-premium git clone --depth=1 --branch=main https://github.com/azerothcore/mod-progression-system.git modules/mod-progression-system git clone --depth=1 --branch=master https://github.com/azerothcore/mod-promotion-azerothcore modules/mod-promotion-azerothcore +git clone --depth=1 --branch=master https://github.com/azerothcore/mod-ptr-template modules/mod-ptr-template git clone --depth=1 --branch=master https://github.com/azerothcore/mod-pvp-quests modules/mod-pvp-quests git clone --depth=1 --branch=master https://github.com/azerothcore/mod-pvp-titles modules/mod-pvp-titles git clone --depth=1 --branch=master https://github.com/azerothcore/mod-pvp-zones modules/mod-pvp-zones diff --git a/data/sql/updates/db_world/2026_03_21_00.sql b/data/sql/updates/db_world/2026_03_21_00.sql new file mode 100644 index 000000000..6a24761e2 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_21_00.sql @@ -0,0 +1,4 @@ +-- DB update 2026_03_20_01 -> 2026_03_21_00 +-- Spirit Burn (54647) - Add 10s internal cooldown +DELETE FROM `spell_proc` WHERE `SpellId` = 54647; +INSERT INTO `spell_proc` (`SpellId`, `Cooldown`) VALUES (54647, 8000); diff --git a/data/sql/updates/db_world/2026_03_21_01.sql b/data/sql/updates/db_world/2026_03_21_01.sql new file mode 100644 index 000000000..a7ee80b76 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_21_01.sql @@ -0,0 +1,5 @@ +-- DB update 2026_03_21_00 -> 2026_03_21_01 +-- Add language string for pinfo online time +DELETE FROM `acore_string` WHERE `entry` = 35410; +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(35410, '¦ Online for: {}', '¦ 온라인 시간: {}', '¦ Online depuis : {}', '¦ Online seit: {}', '¦ 在线时长:{}', '¦ 線上時間:{}', '¦ Conectado durante: {}', '¦ Conectado durante: {}', '¦ Онлайн: {}'); diff --git a/data/sql/updates/db_world/2026_03_21_02.sql b/data/sql/updates/db_world/2026_03_21_02.sql new file mode 100644 index 000000000..ec20ef557 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_21_02.sql @@ -0,0 +1,5 @@ +-- DB update 2026_03_21_01 -> 2026_03_21_02 +-- +-- swap trainers of Engineering (33611) and Leatherworking (33612) bookshelf creature entries +UPDATE `creature_default_trainer` SET `TrainerId` = 91 WHERE (`CreatureId` = 33611); +UPDATE `creature_default_trainer` SET `TrainerId` = 62 WHERE (`CreatureId` = 33612); diff --git a/data/sql/updates/db_world/2026_03_21_03.sql b/data/sql/updates/db_world/2026_03_21_03.sql new file mode 100644 index 000000000..15b58f5cc --- /dev/null +++ b/data/sql/updates/db_world/2026_03_21_03.sql @@ -0,0 +1,9 @@ +-- DB update 2026_03_21_02 -> 2026_03_21_03 +-- +DELETE FROM `command` WHERE `name`='learn all my quest'; +DELETE FROM `command` WHERE `name`='learn all my trainer'; +DELETE FROM `command` WHERE `name`='learn all my spells'; +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('learn all my quest', 2, 'Syntax: .learn all my quest Learn all spells rewarded from quest for your class.'), +('learn all my trainer', 2, 'Syntax: .learn all my trainer Learn all spells taught by trainers for your class.'); +UPDATE `command` SET `help` = 'Syntax: .learn all my class Learn all spells (trainer, talent, and quest rewards) for your class.' WHERE `name`='learn all my class'; diff --git a/data/sql/updates/db_world/2026_03_21_04.sql b/data/sql/updates/db_world/2026_03_21_04.sql new file mode 100644 index 000000000..39717e0a7 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_21_04.sql @@ -0,0 +1,3 @@ +-- DB update 2026_03_21_03 -> 2026_03_21_04 +DELETE FROM `spell_script_names` WHERE `spell_id` = 55002; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (55002, 'spell_item_flexweave_underlay'); diff --git a/data/sql/updates/db_world/2026_03_22_00.sql b/data/sql/updates/db_world/2026_03_22_00.sql new file mode 100644 index 000000000..c53290bce --- /dev/null +++ b/data/sql/updates/db_world/2026_03_22_00.sql @@ -0,0 +1,10 @@ +-- DB update 2026_03_21_04 -> 2026_03_22_00 +-- +UPDATE `creature_template` SET `ScriptName` = 'npc_oathbound_warder' WHERE `entry` = 30270; + +DELETE FROM `creature_template_spell` WHERE `CreatureID` = 30270; +INSERT INTO `creature_template_spell` (`CreatureID`, `Index`, `Spell`, `VerifiedBuild`) VALUES +(30270, 0, 56491, 0), +(30270, 1, 56425, 0), +(30270, 2, 56451, 0), +(30270, 3, 56506, 0); diff --git a/data/sql/updates/db_world/2026_03_22_01.sql b/data/sql/updates/db_world/2026_03_22_01.sql new file mode 100644 index 000000000..76d2c005d --- /dev/null +++ b/data/sql/updates/db_world/2026_03_22_01.sql @@ -0,0 +1,13 @@ +-- DB update 2026_03_22_00 -> 2026_03_22_01 +-- +-- Fix Quest 9927 (Ruthless Cunning): banner spell 32307 conditions only checked for Kil'sorrow Deathsworn (17148) +-- Each ElseGroup should check a different Kil'sorrow creature so banners work on all applicable mobs +-- No double-banner check needed: SpellEffects.cpp already removes corpse after banner placement + +-- Remove old conditions for spell 32307 and insert corrected ones: CONDITION_NEAR_CREATURE (29) per mob +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 17 AND `SourceEntry` = 32307; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17, 0, 32307, 0, 0, 29, 0, 17148, 5, 1, 0, 12, 0, '', 'Spell Place Kil''sorrow Banner requires nearby Kil''sorrow Deathsworn'), +(17, 0, 32307, 0, 1, 29, 0, 17147, 5, 1, 0, 12, 0, '', 'Spell Place Kil''sorrow Banner requires nearby Kil''sorrow Cultist'), +(17, 0, 32307, 0, 2, 29, 0, 17146, 5, 1, 0, 12, 0, '', 'Spell Place Kil''sorrow Banner requires nearby Kil''sorrow Spellbinder'), +(17, 0, 32307, 0, 3, 29, 0, 18391, 5, 1, 0, 12, 0, '', 'Spell Place Kil''sorrow Banner requires nearby Giselda the Crone'); diff --git a/data/sql/updates/db_world/2026_03_22_02.sql b/data/sql/updates/db_world/2026_03_22_02.sql new file mode 100644 index 000000000..624279128 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_22_02.sql @@ -0,0 +1,22 @@ +-- DB update 2026_03_22_01 -> 2026_03_22_02 +-- +-- Brandon Eiredeck (31023) should cower and say "Please, I'm just a delivery man!" +-- Agitated Stratholme Citizens (31126) near Brandon should chastise him about the grain +-- +DELETE FROM `creature_text` WHERE `CreatureID` = 31023; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(31023, 0, 0, 'Please, I''m just a delivery man!', 12, 0, 100, 431, 0, 0, 32044, 0, 'Brandon Eiredeck - Ambient'); + +-- Add group 3 for the Brandon Eiredeck crowd (different from Barthilas crowd) +DELETE FROM `creature_text` WHERE `CreatureID` = 31126 AND `GroupID` = 3; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(31126, 3, 0, 'I think it''s your grain that''s making us sick!', 12, 0, 100, 5, 0, 0, 32032, 0, 'Agitated Stratholme Citizen - Brandon Crowd'), +(31126, 3, 1, 'We were fine until we ate from this tainted grain of yours!', 12, 0, 100, 5, 0, 0, 32035, 0, 'Agitated Stratholme Citizen - Brandon Crowd'); + +-- Enable SmartAI for Brandon Eiredeck +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 31023; + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 31023 AND `source_type` = 0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(31023, 0, 0, 0, 1, 0, 100, 0, 5000, 15000, 15000, 25000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Brandon Eiredeck - OOC - Say Line 0'), +(31023, 0, 1, 0, 1, 0, 100, 0, 0, 0, 3000, 3000, 0, 0, 17, 431, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Brandon Eiredeck - OOC - Set Emote State Cower'); diff --git a/data/sql/updates/db_world/2026_03_22_03.sql b/data/sql/updates/db_world/2026_03_22_03.sql new file mode 100644 index 000000000..7c3a2d276 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_22_03.sql @@ -0,0 +1,24 @@ +-- DB update 2026_03_22_02 -> 2026_03_22_03 +-- +CREATE TABLE `creature_immunities` ( + `ID` int NOT NULL, + `SchoolMask` tinyint NOT NULL DEFAULT '0', + `DispelTypeMask` smallint NOT NULL DEFAULT '0', + `MechanicsMask` bigint NOT NULL DEFAULT '0', + `Effects` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `Auras` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `ImmuneAoE` tinyint(1) NOT NULL DEFAULT '0', + `ImmuneChain` tinyint(1) NOT NULL DEFAULT '0', + `Comment` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`ID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +ALTER TABLE `creature_template` ADD `CreatureImmunitiesId` int NOT NULL DEFAULT '0' AFTER `RegenHealth`; + +UPDATE `creature_template` SET `CreatureImmunitiesId`=COALESCE((SELECT ci.`ID` FROM `creature_immunities` ci WHERE ci.`SchoolMask`=`spell_school_immune_mask` AND ci.`MechanicsMask`=`mechanic_immune_mask`*2),0); + +ALTER TABLE `creature_template` + DROP `spell_school_immune_mask`, + DROP `mechanic_immune_mask`; + +ALTER TABLE `creature_template` DROP COLUMN `scale`; diff --git a/data/sql/updates/db_world/2026_03_22_04.sql b/data/sql/updates/db_world/2026_03_22_04.sql new file mode 100644 index 000000000..c07b7420d --- /dev/null +++ b/data/sql/updates/db_world/2026_03_22_04.sql @@ -0,0 +1,38 @@ +-- DB update 2026_03_22_03 -> 2026_03_22_04 +-- + +DELETE FROM `creature_immunities` WHERE `ID` IN (95,96,315,477,679,878,1537,1557,1614,1615,1630,1632,1664,1676,1682,1693,1694,1695,1733); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(95, 0, 0, 32, '', '', 0, 0, 'Immune to Fear'), +(96, 0, 0, 1234599598, '', '5,7,12,26,33', 0, 0, 'Immune to CC (Free Friend, Uncontrollable Frenzy, Warlord\'s Presence)'), +(315, 127, 0, 0, '', '', 0, 0, 'Immune to damage'), +(477, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'), +(679, 0, 0, 1234599078, '', '5,7,12,26,33', 0, 0, 'Immune to CC'), +(878, 0, 0, 4096, '', '12', 0, 0, 'Immune to Stun'), +(1537, 0, 0, 32, '', '7', 0, 0, 'Immune to Fear'), +(1557, 0, 0, 1234599078, '98,124,144,145', '', 0, 0, 'Immune CC+Stun+Move'), +(1614, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'), +(1615, 0, 0, 1301707942, '68', '5,7,12,26,27,33,60', 0, 0, 'Immune to CC+Interrupt'), +(1630, 0, 0, 69954075814, '114', '5,7,11,12,26,33', 0, 0, 'Immune to CC+Taunt'), +(1632, 0, 0, 584472182, '', '', 0, 0, 'Immune to hard CC'), +(1664, 0, 0, 2176, '', '', 0, 0, 'Immune to slow'), +(1676, 0, 0, 570425344, '', '', 0, 0, 'Immune to immunities'), +(1682, 0, 0, 4096, '', '', 0, 0, 'Immune to Stun'), +(1693, 0, 0, 584472182, '68', '', 0, 0, 'Immune to hard CC+Interrupt'), +(1694, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'), +(1695, 0, 0, 4096, '', '12', 0, 0, 'Immune to Stun'), +(1733, 0, 0, 1234599078, '98,124,144,145', '', 0, 0, 'Immune CC+Stun+Move'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (1762,1771,1794,1799,1808,1835,1887,1893,1944,1964,1971); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(1762, 0, 4, 0, '', '', 0, 0, 'Immune to Curses'), +(1771, 0, 0, 0, '', '', 1, 0, 'Immune to AoE'), +(1794, 0, 0, 67108864, '68', '', 0, 0, 'Immune to Interrupts'), +(1799, 0, 0, 67109376, '68', '27,60', 0, 0, 'Immune to Interrupts+Silence'), +(1808, 0, 0, 0, '98,124,144,145', '', 0, 0, 'Immune to knockback'), +(1835, 0, 0, 1075183616, '', '', 0, 0, 'Immune to polymorph+banish+shackle+sap'), +(1887, 0, 0, 0, '98,124,144,145', '', 0, 0, 'Immune to knockback'), +(1893, 0, 0, 1091728546, '', '', 0, 0, 'Immune to Charm+Fear+Freeze+Horror+Knockout+Polymorph+Root+Sap+Shackle+Sleep+Stun'), +(1944, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'), +(1964, 0, 0, 67109376, '68', '27,60', 0, 0, 'Immune to Interrupts+Silence'), +(1971, 0, 0, 4096, '', '', 0, 0, 'Immune to Stun'); diff --git a/data/sql/updates/db_world/2026_03_22_05.sql b/data/sql/updates/db_world/2026_03_22_05.sql new file mode 100644 index 000000000..2d77e73f9 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_22_05.sql @@ -0,0 +1,9 @@ +-- DB update 2026_03_22_04 -> 2026_03_22_05 +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 11) AND (`SourceEntry` = 21838) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 29) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23377) AND (`ConditionValue2` = 90) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 11, 21838, 0, 0, 29, 1, 23377, 90, 0, 0, 0, 0, '', 'Terokk will only Despawn on Evade if Skyguard Ace is nearby.'); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 12) AND (`SourceEntry` = 21838) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 29) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 23377) AND (`ConditionValue2` = 90) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 12, 21838, 0, 0, 29, 1, 23377, 90, 0, 0, 0, 0, '', 'Terokk will only Despawn on Evade if Skyguard Ace is nearby.'); diff --git a/data/sql/updates/db_world/2026_03_23_00.sql b/data/sql/updates/db_world/2026_03_23_00.sql new file mode 100644 index 000000000..729fd3d10 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_23_00.sql @@ -0,0 +1,13 @@ +-- DB update 2026_03_22_05 -> 2026_03_23_00 +-- Add creature_text for Stephanie Sindree (31019) +DELETE FROM `creature_text` WHERE `CreatureID` = 31019; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(31019, 0, 0, 'It wasn''t my food!', 12, 0, 100, 6, 0, 0, 31838, 0, 'Stephanie Sindree - Stephanie Crowd'), +(31019, 1, 0, 'Please, it''s not my fault! If... if I give you a refund, maybe you can speak to a healer? I don''t know what you expect me to do!', 12, 0, 100, 20, 0, 0, 31840, 0, 'Stephanie Sindree - Stephanie Crowd'); + +-- Add creature_text for Agitated Stratholme Citizen (31126) - Stephanie Crowd +DELETE FROM `creature_text` WHERE `CreatureID` = 31126 AND `GroupID` IN (4, 5, 6); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(31126, 4, 0, 'My wife and children are sick in bed because of the poison you sold me!', 12, 0, 100, 5, 0, 0, 31837, 0, 'Agitated Stratholme Citizen - Stephanie Crowd'), +(31126, 5, 0, 'Don''t try to weasel out of this!', 12, 0, 100, 25, 0, 0, 31839, 0, 'Agitated Stratholme Citizen - Stephanie Crowd'), +(31126, 6, 0, 'That... no, keep your filthy money! It won''t help my family!', 12, 0, 100, 5, 0, 0, 31842, 0, 'Agitated Stratholme Citizen - Stephanie Crowd'); diff --git a/data/sql/updates/db_world/2026_03_23_01.sql b/data/sql/updates/db_world/2026_03_23_01.sql new file mode 100644 index 000000000..eb0d4e96c --- /dev/null +++ b/data/sql/updates/db_world/2026_03_23_01.sql @@ -0,0 +1,16 @@ +-- DB update 2026_03_23_00 -> 2026_03_23_01 +-- Add velocity and smoothTransition columns to waypoint_data +ALTER TABLE `waypoint_data` + ADD COLUMN `velocity` FLOAT NOT NULL DEFAULT 0 AFTER `orientation`, + ADD COLUMN `smoothTransition` TINYINT NOT NULL DEFAULT 0 AFTER `delay`; + +-- Create waypoint_data_addon table for custom spline points +CREATE TABLE IF NOT EXISTS `waypoint_data_addon` ( + `PathID` INT UNSIGNED NOT NULL, + `PointID` INT UNSIGNED NOT NULL, + `SplinePointIndex` INT UNSIGNED NOT NULL, + `PositionX` FLOAT NOT NULL DEFAULT 0, + `PositionY` FLOAT NOT NULL DEFAULT 0, + `PositionZ` FLOAT NOT NULL DEFAULT 0, + PRIMARY KEY (`PathID`, `PointID`, `SplinePointIndex`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/data/sql/updates/db_world/2026_03_23_02.sql b/data/sql/updates/db_world/2026_03_23_02.sql new file mode 100644 index 000000000..d72285871 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_23_02.sql @@ -0,0 +1,23 @@ +-- DB update 2026_03_23_01 -> 2026_03_23_02 +-- Convert npc_slim (entry 19679) from C++ script to database gossip + conditions + +-- Remove ScriptName from creature_template +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` = 19679; + +-- Add gossip_menu entry for non-friendly text (9895) +DELETE FROM `gossip_menu` WHERE `MenuID` = 8021 AND `TextID` = 9895; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES (8021, 9895); + +-- Add conditions for gossip_menu text display +-- Show text 9896 (vendor greeting) only if player is Friendly+ with Consortium (faction 933) +-- Friendly(16) + Honored(32) + Revered(64) + Exalted(128) = 240 +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 8021 AND `SourceEntry` IN (9895, 9896); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 8021, 9896, 0, 0, 5, 0, 933, 240, 0, 0, 0, 0, '', 'Gossip Menu 8021 - Show text 9896 if player is Friendly+ with Consortium'), +(14, 8021, 9895, 0, 0, 5, 0, 933, 240, 0, 1, 0, 0, '', 'Gossip Menu 8021 - Show text 9895 if player is not Friendly+ with Consortium'); + +-- Add condition for gossip_menu_option vendor option +-- Show vendor option only if player is Friendly+ with Consortium +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 8021 AND `SourceEntry` = 0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8021, 0, 0, 0, 5, 0, 933, 240, 0, 0, 0, 0, '', 'Gossip Option 8021/0 - Show vendor option if player is Friendly+ with Consortium'); diff --git a/data/sql/updates/db_world/2026_03_24_00.sql b/data/sql/updates/db_world/2026_03_24_00.sql new file mode 100644 index 000000000..6f0246d8c --- /dev/null +++ b/data/sql/updates/db_world/2026_03_24_00.sql @@ -0,0 +1,23 @@ +-- DB update 2026_03_23_02 -> 2026_03_24_00 +-- Move npc_maredis_firestar (Mathredis Firestar, entry 9836) from C++ script to DB gossip + +-- Remove C++ ScriptName +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=9836; + +-- Move gossip options from sub-menus (2299-2303) to the NPC's main menu (2298) with conditions +DELETE FROM `gossip_menu_option` WHERE `MenuID` IN (2298,2299,2300,2301,2302,2303); +INSERT INTO `gossip_menu_option` (`MenuID`,`OptionID`,`OptionIcon`,`OptionText`,`OptionBroadcastTextID`,`OptionType`,`OptionNpcFlag`,`ActionMenuID`,`ActionPoiID`,`BoxCoded`,`BoxMoney`,`BoxText`,`BoxBroadcastTextID`,`VerifiedBuild`) VALUES +(2298,0,0,'I present to you the Libram of Rumination.',5291,1,1,2299,0,0,0,'',0,0), +(2298,1,0,'I present to you the Libram of Constitution.',5416,1,1,2300,0,0,0,'',0,0), +(2298,2,0,'I present to you the Libram of Tenacity.',5417,1,1,2301,0,0,0,'',0,0), +(2298,3,0,'I present to you the Libram of Resilience.',5418,1,1,2302,0,0,0,'',0,0), +(2298,4,0,'I present to you the Libram of Voracity.',5419,1,1,2303,0,0,0,'',0,0); + +-- Add conditions: show each option only if player has the corresponding Libram +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=2298; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(15,2298,0,0,0,2,0,11732,1,0,0,0,0,'','Show gossip option if player has Libram of Rumination'), +(15,2298,1,0,0,2,0,11733,1,0,0,0,0,'','Show gossip option if player has Libram of Constitution'), +(15,2298,2,0,0,2,0,11734,1,0,0,0,0,'','Show gossip option if player has Libram of Tenacity'), +(15,2298,3,0,0,2,0,11736,1,0,0,0,0,'','Show gossip option if player has Libram of Resilience'), +(15,2298,4,0,0,2,0,11737,1,0,0,0,0,'','Show gossip option if player has Libram of Voracity'); diff --git a/data/sql/updates/db_world/2026_03_24_01.sql b/data/sql/updates/db_world/2026_03_24_01.sql new file mode 100644 index 000000000..5cc234b7e --- /dev/null +++ b/data/sql/updates/db_world/2026_03_24_01.sql @@ -0,0 +1,57 @@ +-- DB update 2026_03_24_00 -> 2026_03_24_01 +-- Defias Tower Patroller smart ai +SET @ENTRY := 7052; +UPDATE `creature_template` SET `AIName` = 'SmartAI', `ScriptName` = '' WHERE `entry` = @ENTRY; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = @ENTRY); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(@ENTRY, 0, 0, 0, 0, 0, 100, 0, 3100, 4700, 8600, 10100, 0, 0, 11, 5679, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - In Combat - Cast \'Torch Burn\''), +(@ENTRY, 0, 1, 0, 8, 0, 100, 0, 53, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (53)\' - Kill Self'), +(@ENTRY, 0, 2, 0, 8, 0, 100, 0, 2589, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (2589)\' - Kill Self'), +(@ENTRY, 0, 3, 0, 8, 0, 100, 0, 2590, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (2590)\' - Kill Self'), +(@ENTRY, 0, 4, 0, 8, 0, 100, 0, 2591, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (2591)\' - Kill Self'), +(@ENTRY, 0, 5, 0, 8, 0, 100, 0, 8721, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (8721)\' - Kill Self'), +(@ENTRY, 0, 6, 0, 8, 0, 100, 0, 11279, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (11279)\' - Kill Self'), +(@ENTRY, 0, 7, 0, 8, 0, 100, 0, 11280, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (11280)\' - Kill Self'), +(@ENTRY, 0, 8, 0, 8, 0, 100, 0, 11281, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (11281)\' - Kill Self'), +(@ENTRY, 0, 9, 0, 8, 0, 100, 0, 25300, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (25300)\' - Kill Self'), +(@ENTRY, 0, 10, 0, 8, 0, 100, 0, 26863, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (26863)\' - Kill Self'), +(@ENTRY, 0, 11, 0, 8, 0, 100, 0, 48656, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (48656)\' - Kill Self'), +(@ENTRY, 0, 12, 0, 8, 0, 100, 0, 48657, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Backstab (48657)\' - Kill Self'), +(@ENTRY, 0, 13, 0, 8, 0, 100, 0, 8676, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (8676)\' - Kill Self'), +(@ENTRY, 0, 14, 0, 8, 0, 100, 0, 8724, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (8724)\' - Kill Self'), +(@ENTRY, 0, 15, 0, 8, 0, 100, 0, 8725, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (8725)\' - Kill Self'), +(@ENTRY, 0, 16, 0, 8, 0, 100, 0, 11267, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (11267)\' - Kill Self'), +(@ENTRY, 0, 17, 0, 8, 0, 100, 0, 11268, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (11268)\' - Kill Self'), +(@ENTRY, 0, 18, 0, 8, 0, 100, 0, 11269, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (11269)\' - Kill Self'), +(@ENTRY, 0, 19, 0, 8, 0, 100, 0, 27441, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (27441)\' - Kill Self'), +(@ENTRY, 0, 20, 0, 8, 0, 100, 0, 48689, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Patroller - On Spellhit \'Ambush (48689)\' - Kill Self'); + +-- Defias Tower Sentry smart ai +SET @ENTRY := 7056; +UPDATE `creature_template` SET `AIName` = 'SmartAI', `ScriptName` = '' WHERE `entry` = @ENTRY; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = @ENTRY); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(@ENTRY, 0, 0, 0, 1, 0, 100, 1, 1000, 1500, 0, 0, 0, 0, 11, 22766, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - Out of Combat - Cast \'Sneak\' (No Repeat)'), +(@ENTRY, 0, 1, 0, 8, 0, 100, 0, 53, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (53)\' - Kill Self'), +(@ENTRY, 0, 2, 0, 8, 0, 100, 0, 2589, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (2589)\' - Kill Self'), +(@ENTRY, 0, 3, 0, 8, 0, 100, 0, 2590, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (2590)\' - Kill Self'), +(@ENTRY, 0, 4, 0, 8, 0, 100, 0, 2591, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (2591)\' - Kill Self'), +(@ENTRY, 0, 5, 0, 8, 0, 100, 0, 8721, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (8721)\' - Kill Self'), +(@ENTRY, 0, 6, 0, 8, 0, 100, 0, 11279, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (11279)\' - Kill Self'), +(@ENTRY, 0, 7, 0, 8, 0, 100, 0, 11280, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (11280)\' - Kill Self'), +(@ENTRY, 0, 8, 0, 8, 0, 100, 0, 11281, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (11281)\' - Kill Self'), +(@ENTRY, 0, 9, 0, 8, 0, 100, 0, 25300, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (25300)\' - Kill Self'), +(@ENTRY, 0, 10, 0, 8, 0, 100, 0, 26863, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (26863)\' - Kill Self'), +(@ENTRY, 0, 11, 0, 8, 0, 100, 0, 48656, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (48656)\' - Kill Self'), +(@ENTRY, 0, 12, 0, 8, 0, 100, 0, 48657, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Backstab (48657)\' - Kill Self'), +(@ENTRY, 0, 13, 0, 8, 0, 100, 0, 8676, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (8676)\' - Kill Self'), +(@ENTRY, 0, 14, 0, 8, 0, 100, 0, 8724, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (8724)\' - Kill Self'), +(@ENTRY, 0, 15, 0, 8, 0, 100, 0, 8725, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (8725)\' - Kill Self'), +(@ENTRY, 0, 16, 0, 8, 0, 100, 0, 11267, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (11267)\' - Kill Self'), +(@ENTRY, 0, 17, 0, 8, 0, 100, 0, 11268, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (11268)\' - Kill Self'), +(@ENTRY, 0, 18, 0, 8, 0, 100, 0, 11269, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (11269)\' - Kill Self'), +(@ENTRY, 0, 19, 0, 8, 0, 100, 0, 27441, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (27441)\' - Kill Self'), +(@ENTRY, 0, 20, 0, 8, 0, 100, 0, 48689, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Defias Tower Sentry - On Spellhit \'Ambush (48689)\' - Kill Self'); + +-- Remove Conditions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 22 AND (`SourceEntry` IN (7052, 7056)) AND `SourceId` = 0; diff --git a/data/sql/updates/db_world/2026_03_24_02.sql b/data/sql/updates/db_world/2026_03_24_02.sql new file mode 100644 index 000000000..ff8d469f4 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_24_02.sql @@ -0,0 +1,199 @@ +-- DB update 2026_03_24_01 -> 2026_03_24_02 + +-- Ensure Grauf has RegenHealth disabled so harpoon damage persists +UPDATE `creature_template` SET `RegenHealth` = 0 WHERE `entry` IN (26893, 30775); + +-- Remove go_harpoon_canon script, use built-in GOOBER spell (48641) instead +UPDATE `gameobject_template` SET `ScriptName` = '' WHERE `entry` IN (192175, 192176, 192177); + +-- Register spell scripts +DELETE FROM `spell_script_names` WHERE `spell_id` IN (48642, 50255, 47574, 47594, 47579, 60020, 49308, 59275); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(48642, 'spell_skadi_launch_harpoon'), +(50255, 'spell_skadi_poisoned_spear'), +(47574, 'spell_freezing_cloud_area_left'), +(47594, 'spell_freezing_cloud_area_right'), +(47579, 'spell_freezing_cloud_damage'), +(60020, 'spell_freezing_cloud_damage'), +(59275, 'spell_summon_gauntlet_mobs_periodic'), +(49308, 'spell_skadi_reset_check'); + +-- Breath trigger SmartAI: on SpellHit 47563/47593 cast area spells 47574/47594 +UPDATE `creature_template` SET `AIName` = 'SmartAI', `ScriptName` = '' WHERE `entry` = 28351; +DELETE FROM `smart_scripts` WHERE `entryorguid` = 28351 AND `source_type` = 0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28351, 0, 0, 0, 8, 0, 100, 0, 47563, 0, 0, 0, 11, 47574, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Breath Trigger - On SpellHit (Freezing Cloud Left) - Cast Freezing Cloud Left Area'), +(28351, 0, 1, 0, 8, 0, 100, 0, 47593, 0, 0, 0, 11, 47594, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Breath Trigger - On SpellHit (Freezing Cloud Right) - Cast Freezing Cloud Right Area'); + +-- Conditions for freezing cloud spells to target correct entries +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` IN (47593, 47594, 47563, 48642, 47547, 49308); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 47593, 0, 0, 31, 0, 3, 28351, 0, 0, 0, 0, '', 'Freezing Cloud Right - Target Breath Trigger'), +(13, 1, 47594, 0, 0, 31, 0, 3, 28351, 0, 0, 0, 0, '', 'Freezing Cloud Right Area - Target Breath Trigger'), +(13, 1, 47563, 0, 0, 31, 0, 3, 28351, 0, 0, 0, 0, '', 'Freezing Cloud Left - Target Breath Trigger'), +(13, 1, 48642, 0, 0, 31, 0, 3, 26893, 0, 0, 0, 0, '', 'Launch Harpoon - Target Grauf'), +(13, 1, 48642, 0, 1, 31, 0, 3, 22515, 0, 0, 0, 0, '', 'Launch Harpoon - Target World Trigger'), +(13, 1, 47547, 0, 0, 31, 0, 3, 28351, 0, 0, 0, 0, '', 'Gauntlet Effect - Target Breath Trigger'), +(13, 1, 49308, 0, 0, 31, 0, 3, 28351, 0, 0, 0, 0, '', 'Gauntlet Reset Check - Target Breath Trigger'), +(13, 2, 49308, 0, 0, 31, 0, 3, 26693, 0, 0, 0, 0, '', 'Gauntlet Reset Check - Target Skadi'); + +-- Update condition for harpoon launcher spell (48641) to target trigger NPC (TC) +UPDATE `conditions` SET `ConditionValue2` = 19871 WHERE `SourceTypeOrReferenceId` = 13 AND `SourceGroup` = 1 AND `SourceEntry` = 48641; + +-- Spawn World Trigger (Not Immune NPC) near harpoon launchers (from TC TDB335) +DELETE FROM `creature` WHERE `guid` BETWEEN 5300675 AND 5300678 AND `id1` = 19871; +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(5300675, 19871, 0, 0, 575, 0, 0, 3, 1, 0, 389.929, -439.88, 83.0081, 2.79273, 300, 5, 0, 0, 0, 1, 0, 0, 0, '', NULL, 0, NULL), +(5300676, 19871, 0, 0, 575, 0, 0, 3, 1, 0, 490.516, -508.443, 107.042, 5.65487, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300677, 19871, 0, 0, 575, 0, 0, 3, 1, 0, 487.632, -517.329, 106.799, 5.55015, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300678, 19871, 0, 0, 575, 0, 0, 3, 1, 0, 480.202, -523.332, 107.042, 5.46288, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL); + +-- Skadi teleport destination (phase 2 transition) +DELETE FROM `spell_target_position` WHERE `ID` = 61790; +INSERT INTO `spell_target_position` (`ID`, `EffectIndex`, `MapID`, `PositionX`, `PositionY`, `PositionZ`, `Orientation`, `VerifiedBuild`) VALUES +(61790, 0, 575, 476.799, -511.167, 104.723, 3.14159, 0); + +-- Gauntlet area aura +DELETE FROM `spell_area` WHERE `spell` = 47546; +INSERT INTO `spell_area` (`spell`, `area`, `quest_start`, `quest_end`, `aura_spell`, `racemask`, `gender`, `autocast`, `quest_start_status`, `quest_end_status`) VALUES +(47546, 1196, 0, 0, 0, 0, 0, 1, 64, 11); + +-- Disable LoS for Gauntlet Reset Check +DELETE FROM `disables` WHERE `sourceType` = 0 AND `entry` = 49308; +INSERT INTO `disables` (`sourceType`, `entry`, `flags`, `params_0`, `params_1`, `comment`) VALUES +(0, 49308, 64, 0, 0, 'Disable LoS for spell Utgarde Pinnacle Reset Check'); + +-- Launch Harpoon ignore armor +DELETE FROM `spell_custom_attr` WHERE `spell_id` = 48642; +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES +(48642, 32768); + +-- Missing spell difficulty entries +DELETE FROM `spelldifficulty_dbc` WHERE `ID` IN (50228, 50258, 49084, 49089, 49091); +INSERT INTO `spelldifficulty_dbc` (`ID`, `DifficultySpellID_1`, `DifficultySpellID_2`, `DifficultySpellID_3`, `DifficultySpellID_4`) VALUES +(49084, 49084, 59246, 0, 0), +(49089, 49089, 59247, 0, 0), +(49091, 49091, 59249, 0, 0), +(50228, 50228, 59322, 0, 0), +(50258, 50258, 59334, 0, 0); + +-- Spawn Freezing Cloud Breath Triggers along the Skadi gauntlet corridor (ported from TrinityCore TDB335) +DELETE FROM `creature` WHERE `guid` BETWEEN 5300600 AND 5300674 AND `id1` = 28351; +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(5300600, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 483.221, -507.151, 104.806, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300601, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 474.404, -507.532, 104.807, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300602, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 473.898, -515.257, 104.806, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300603, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 477.645, -492.175, 104.82, 6.23082, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300604, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 477.021, -520.94, 104.806, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300605, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 425.011, -510.068, 104.832, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300606, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 430.051, -510.1, 104.944, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300607, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 434.669, -510.101, 104.983, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300608, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 440.11, -510.148, 104.97, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300609, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 445.131, -509.703, 104.831, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300610, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 450.289, -509.936, 104.705, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300611, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 455, -509.52, 104.757, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300612, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 460, -509.52, 104.757, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300613, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 465.341, -508.352, 104.438, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300614, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 470.032, -507.361, 104.807, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300615, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 489.557, -514.06, 105.487, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300616, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 482.133, -519.963, 104.806, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300617, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 325, -506.384, 104.419, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300618, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 330, -506.406, 104.434, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300619, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 335.192, -506.95, 105.056, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300620, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 339.973, -507.026, 104.669, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300621, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 344.931, -507.345, 104.592, 6.23082, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300622, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 349.864, -507.751, 104.655, 6.23082, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300623, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 354.289, -507.828, 104.759, 6.23082, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300624, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 359.405, -508.154, 104.871, 6.23082, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300625, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 364.746, -508.335, 104.896, 6.23082, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300626, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 370.505, -508.554, 104.898, 6.23082, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300627, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 374.827, -515.69, 104.877, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300628, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 379.7, -515.287, 104.841, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300629, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 384.809, -514.81, 104.812, 0.017453, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300630, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 390.011, -513.91, 104.811, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300631, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 394.974, -514.631, 104.808, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300632, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 399.738, -514.939, 104.809, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300633, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 404.576, -515.878, 104.827, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300634, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 409.497, -516.404, 105.045, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300635, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 415.076, -516.494, 105.342, 0.017453, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300636, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 420.109, -517.266, 105.089, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300637, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 488.181, -506.897, 104.806, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300638, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 477.551, -499.34, 104.81, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300639, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 485.686, -500.599, 104.806, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300640, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 471.163, -499.268, 104.81, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300641, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 478.264, -514.318, 104.806, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300642, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 483.638, -513.85, 104.806, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300643, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 471.796, -521.457, 104.806, 0.174533, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300644, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 424.786, -517.297, 105.033, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300645, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 429.779, -517.261, 105.039, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300646, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 434.894, -517.11, 105.029, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300647, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 439.402, -517.239, 105.008, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300648, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 444.91, -517.682, 104.947, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300649, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 450.614, -517.208, 104.903, 0.05236, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300650, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 455.604, -517.101, 104.832, 0.05236, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300651, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 460.245, -516.405, 104.679, 0.05236, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300652, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 465.499, -515.925, 104.59, 0.069813, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300653, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 470, -515.214, 105.112, 0.069813, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300654, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 375, -508.566, 104.878, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300655, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 380, -508.366, 104.876, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300656, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 385, -508.366, 104.876, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300657, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 390, -508.366, 104.876, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300658, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 395, -508.366, 104.876, 6.26573, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300659, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 400, -508.366, 104.876, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300660, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 405.287, -508.99, 105.087, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300661, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 409.909, -508.887, 105.106, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300662, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 415.074, -509.304, 105.271, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300663, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 420.093, -509.834, 104.897, 6.24828, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300664, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 478.792, -507.378, 104.806, 0.034907, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300665, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 324.531, -514.289, 104.761, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300666, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 329.63, -514.616, 104.712, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300667, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 334.909, -514.734, 104.623, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300668, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 339.554, -514.984, 104.61, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300669, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 344.693, -515.33, 104.573, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300670, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 349.831, -515.439, 104.556, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300671, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 354.77, -515.736, 104.57, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300672, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 359.338, -515.586, 104.638, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300673, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 364.401, -515.771, 104.757, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL), +(5300674, 28351, 0, 0, 575, 0, 0, 3, 1, 0, 369.849, -515.944, 104.886, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, '', NULL, 0, NULL); + +-- Grauf waypoint paths (ported from TrinityCore TDB335) +SET @PATH := 2689300; +DELETE FROM `waypoint_data` WHERE `id` IN (@PATH, @PATH+1, @PATH+2); +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +-- Initial path (takeoff to breach) +(@PATH, 0, 310.2165, -510.5302, 120.5487, 0, 0, 2, 0, 100, 0), +(@PATH, 1, 300.8685, -520.3249, 133.3664, 0, 0, 2, 0, 100, 0), +(@PATH, 2, 298.3447, -529.4818, 137.2831, 0, 0, 2, 0, 100, 0), +(@PATH, 3, 309.0959, -540.3195, 134.9774, 0, 0, 2, 0, 100, 0), +(@PATH, 4, 323.6415, -547.9715, 130.3106, 0, 0, 2, 0, 100, 0), +(@PATH, 5, 357.078, -549.1597, 116.3105, 0, 0, 2, 0, 100, 0), +(@PATH, 6, 401.2573, -550.2768, 114.9216, 0, 0, 2, 0, 100, 0), +(@PATH, 7, 464.5062, -555.9442, 114.4494, 0, 0, 2, 0, 100, 0), +(@PATH, 8, 496.192, -556.9628, 114.8661, 0, 0, 2, 0, 100, 0), +(@PATH, 9, 523.2011, -548.9916, 114.8661, 0, 0, 2, 0, 100, 0), +-- Right path (breach to right end and back) +(@PATH+1, 0, 453.1242, -517.17, 120.0273, 0, 0, 2, 0, 100, 0), +(@PATH+1, 1, 388.3312, -514.3768, 121.1193, 0, 0, 2, 0, 100, 0), +(@PATH+1, 2, 340.2752, -512.0926, 122.3138, 0, 0, 2, 0, 100, 0), +(@PATH+1, 3, 313.0756, -509.138, 125.175, 0, 0, 2, 0, 100, 0), +(@PATH+1, 4, 296.6964, -522.6705, 133.7026, 0, 0, 2, 0, 100, 0), +(@PATH+1, 5, 301.2957, -549.4583, 137.4249, 0, 0, 2, 0, 100, 0), +(@PATH+1, 6, 335.0746, -552.1211, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+1, 7, 397.6237, -553.9203, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+1, 8, 459.6321, -558.0078, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+1, 9, 505.5458, -568.7815, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+1, 10, 518.099, -560.8499, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+1, 11, 520.4827, -541.5633, 119.8416, 0, 0, 2, 0, 100, 0), +-- Left path (breach to left end and back) +(@PATH+2, 0, 451.9824, -509.2508, 120.0273, 0, 0, 2, 0, 100, 0), +(@PATH+2, 1, 399.2607, -510.0719, 121.1193, 0, 0, 2, 0, 100, 0), +(@PATH+2, 2, 350.6099, -508.3901, 122.3138, 0, 0, 2, 0, 100, 0), +(@PATH+2, 3, 325.8217, -506.7769, 125.175, 0, 0, 2, 0, 100, 0), +(@PATH+2, 4, 301.2941, -516.6706, 133.7026, 0, 0, 2, 0, 100, 0), +(@PATH+2, 5, 301.2957, -549.4583, 137.4249, 0, 0, 2, 0, 100, 0), +(@PATH+2, 6, 335.0746, -552.1211, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+2, 7, 397.6237, -553.9203, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+2, 8, 459.6321, -558.0078, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+2, 9, 505.5458, -568.7815, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+2, 10, 518.099, -560.8499, 119.8416, 0, 0, 2, 0, 100, 0), +(@PATH+2, 11, 520.4827, -541.5633, 119.8416, 0, 0, 2, 0, 100, 0); diff --git a/data/sql/updates/db_world/2026_03_24_03.sql b/data/sql/updates/db_world/2026_03_24_03.sql new file mode 100644 index 000000000..d49004ab0 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_24_03.sql @@ -0,0 +1,7 @@ +-- DB update 2026_03_24_02 -> 2026_03_24_03 +-- https://eu.forums.blizzard.com/en/wow/t/unnerf-unholy-dk-pet-names-plz/113162 +-- Removes extra 'Stone' from First names for DK Ghouls +DELETE FROM `pet_name_generation` WHERE `id`=261 AND `word` = 'Stone' AND `entry` = 26125 AND `half` = 0; + +-- Updates last name 'rawler' to 'crawler' for DK Ghouls +UPDATE `pet_name_generation` SET `word` = 'crawler' WHERE `id` = 308 AND `word` = 'rawler' AND `entry` = 26125 AND `half` = 1; diff --git a/data/sql/updates/db_world/2026_03_24_04.sql b/data/sql/updates/db_world/2026_03_24_04.sql new file mode 100644 index 000000000..3316cfc4a --- /dev/null +++ b/data/sql/updates/db_world/2026_03_24_04.sql @@ -0,0 +1,19 @@ +-- DB update 2026_03_24_03 -> 2026_03_24_04 +-- +DELETE FROM `command` WHERE `name` = 'debug loot'; +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('debug loot', 2, 'Syntax: .debug loot [count]\nSimulates loot generation for the given loot type and ID, outputting the results to chat without creating items.\nOptional count (1-100) repeats the simulation and shows aggregated drop rates.\nValid types: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player'); + +DELETE FROM `acore_string` WHERE `entry` IN (30099, 30100, 30101, 30102, 30103, 30104, 30105, 30106, 30107, 30108, 30109); +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(30099, 'Simulating loot for {} "{}" (ID: {})', '루팅 시뮬레이션: {} "{}" (ID: {})', 'Simulation du butin pour {} "{}" (ID: {})', 'Beutesimulation für {} "{}" (ID: {})', '模拟 {} "{}" 的战利品 (ID: {})', '模擬 {} "{}" 的戰利品 (ID: {})', 'Simulando botín para {} "{}" (ID: {})', 'Simulando botín para {} "{}" (ID: {})', 'Симуляция добычи для {} "{}" (ID: {})'), +(30100, ' [{}] x{} - {} ({}, RandProp: {}, RandSuffix: {})', ' [{}] x{} - {} ({}, 랜덤속성: {}, 랜덤접미사: {})', ' [{}] x{} - {} ({}, PropAléa: {}, SuffAléa: {})', ' [{}] x{} - {} ({}, ZufEig: {}, ZufSuf: {})', ' [{}] x{} - {} ({}, 随机属性: {}, 随机后缀: {})', ' [{}] x{} - {} ({}, 隨機屬性: {}, 隨機後綴: {})', ' [{}] x{} - {} ({}, PropAleat: {}, SufAleat: {})', ' [{}] x{} - {} ({}, PropAleat: {}, SufAleat: {})', ' [{}] x{} - {} ({}, СлСвойство: {}, СлСуффикс: {})'), +(30101, ' [{}] x{} - {} ({}) [Quest]', ' [{}] x{} - {} ({}) [퀘스트]', ' [{}] x{} - {} ({}) [Quête]', ' [{}] x{} - {} ({}) [Quest]', ' [{}] x{} - {} ({}) [任务]', ' [{}] x{} - {} ({}) [任務]', ' [{}] x{} - {} ({}) [Misión]', ' [{}] x{} - {} ({}) [Misión]', ' [{}] x{} - {} ({}) [Задание]'), +(30102, 'Gold: {} copper ({}g {}s {}c)', '골드: {} 코퍼 ({}금 {}은 {}동)', 'Or: {} cuivre ({}po {}pa {}pc)', 'Gold: {} Kupfer ({}G {}S {}K)', '金币: {} 铜 ({}金 {}银 {}铜)', '金幣: {} 銅 ({}金 {}銀 {}銅)', 'Oro: {} cobre ({}o {}p {}c)', 'Oro: {} cobre ({}o {}p {}c)', 'Золото: {} медь ({}зол {}сер {}мед)'), +(30103, 'No loot generated.', '생성된 전리품이 없습니다.', 'Aucun butin généré.', 'Keine Beute generiert.', '未生成战利品。', '未生成戰利品。', 'No se generó botín.', 'No se generó botín.', 'Добыча не сгенерирована.'), +(30104, 'Invalid loot type "{}". Valid: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', '잘못된 전리품 유형 "{}". 유효: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', 'Type de butin invalide "{}". Valides: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', 'Ungültiger Beutetyp "{}". Gültig: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', '无效的战利品类型 "{}"。有效: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', '無效的戰利品類型 "{}"。有效: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', 'Tipo de botín inválido "{}". Válidos: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', 'Tipo de botín inválido "{}". Válidos: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player', 'Неверный тип добычи "{}". Допустимые: creature, gameobject, fishing, item, pickpocketing, skinning, disenchant, prospecting, milling, spell, reference, mail, player'), +(30105, 'No loot template found for {} ID {}.', '{}의 전리품 템플릿을 찾을 수 없습니다 (ID: {}).', 'Aucun modèle de butin trouvé pour {} ID {}.', 'Keine Beutevorlage gefunden für {} ID {}.', '未找到 {} ID {} 的战利品模板。', '未找到 {} ID {} 的戰利品模板。', 'No se encontró plantilla de botín para {} ID {}.', 'No se encontró plantilla de botín para {} ID {}.', 'Шаблон добычи не найден для {} ID {}.'), +(30106, 'Simulating loot for {} "{}" (ID: {}) - {} iterations', '루팅 시뮬레이션: {} "{}" (ID: {}) - {} 반복', 'Simulation du butin pour {} "{}" (ID: {}) - {} itérations', 'Beutesimulation für {} "{}" (ID: {}) - {} Durchläufe', '模拟 {} "{}" 的战利品 (ID: {}) - {} 次迭代', '模擬 {} "{}" 的戰利品 (ID: {}) - {} 次迭代', 'Simulando botín para {} "{}" (ID: {}) - {} iteraciones', 'Simulando botín para {} "{}" (ID: {}) - {} iteraciones', 'Симуляция добычи для {} "{}" (ID: {}) - {} итераций'), +(30107, ' [{}] x{} - {} ({}) - dropped {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - 드롭 {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - obtenu {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - gefallen {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - 掉落 {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - 掉落 {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - obtenido {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - obtenido {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) - выпало {}/{} ({}.{}%)'), +(30108, ' [{}] x{} - {} ({}) [Quest] - dropped {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [퀘스트] - 드롭 {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [Quête] - obtenu {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [Quest] - gefallen {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [任务] - 掉落 {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [任務] - 掉落 {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [Misión] - obtenido {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [Misión] - obtenido {}/{} ({}.{}%)', ' [{}] x{} - {} ({}) [Задание] - выпало {}/{} ({}.{}%)'), +(30109, 'Gold avg: {} copper ({}g {}s {}c) over {} iterations', '골드 평균: {} 코퍼 ({}금 {}은 {}동) {} 반복', 'Or moy: {} cuivre ({}po {}pa {}pc) sur {} itérations', 'Gold Durchschn.: {} Kupfer ({}G {}S {}K) über {} Durchläufe', '金币均值: {} 铜 ({}金 {}银 {}铜) 共 {} 次迭代', '金幣均值: {} 銅 ({}金 {}銀 {}銅) 共 {} 次迭代', 'Oro prom: {} cobre ({}o {}p {}c) en {} iteraciones', 'Oro prom: {} cobre ({}o {}p {}c) en {} iteraciones', 'Золото сред.: {} медь ({}зол {}сер {}мед) за {} итераций'); diff --git a/data/sql/updates/db_world/2026_03_24_05.sql b/data/sql/updates/db_world/2026_03_24_05.sql new file mode 100644 index 000000000..4809cba70 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_24_05.sql @@ -0,0 +1,48 @@ +-- DB update 2026_03_24_04 -> 2026_03_24_05 + +-- Set High Priest Talet-Kha SAI. +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 26073; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 26073); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(26073, 0, 0, 1, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Set Event Phase 0'), +(26073, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(26073, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 10, 85118, 25422, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Respawn Closest Creature \'Mystical Webbing\''), +(26073, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 10, 85098, 25422, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Respawn Closest Creature \'Mystical Webbing\''), +(26073, 0, 4, 5, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 23, 0, 0, 0, 0, 0, 10, 85175, 23033, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Do Action ID 23'), +(26073, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 23, 0, 0, 0, 0, 0, 10, 85176, 23033, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Do Action ID 23'), +(26073, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 23, 0, 0, 0, 0, 0, 10, 85098, 25422, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Do Action ID 23'), +(26073, 0, 7, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 23, 0, 0, 0, 0, 0, 10, 85118, 25422, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Reset - Do Action ID 23'), +(26073, 0, 8, 0, 72, 0, 100, 1, 25, 0, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Action 25 Done - Increment Phase (No Repeat)'), +(26073, 0, 9, 0, 72, 0, 100, 1, 26, 0, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Action 26 Done - Increment Phase (No Repeat)'), +(26073, 0, 10, 0, 66, 0, 100, 1, 2, 0, 0, 0, 0, 0, 80, 2607300, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - On Event Phase 2 Set - Run Script (No Repeat)'), +(26073, 0, 11, 0, 2, 0, 100, 0, 1, 45, 1000, 3000, 0, 0, 11, 11640, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - Between 1-45% Health - Cast \'Renew\''), +(26073, 0, 12, 0, 0, 0, 100, 0, 4000, 6000, 5000, 8000, 0, 0, 11, 15587, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - In Combat - Cast \'Mind Blast\''); + +-- Set Action List +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2607300); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(2607300, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - Actionlist - Remove FlagStandstate Sit Down'), +(2607300, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - Actionlist - Set Fly Off'), +(2607300, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 3788.44, 3418.25, 85.0562, 0, 'High Priest Talet-Kha - Actionlist - Move To Position'), +(2607300, 9, 3, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1.1672, 'High Priest Talet-Kha - Actionlist - Set Orientation 1.1672'), +(2607300, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - Actionlist - Say Line 0'), +(2607300, 9, 5, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 11, 45492, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - Actionlist - Cast \'Shadow Nova\''), +(2607300, 9, 6, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 19, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - Actionlist - Remove Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(2607300, 9, 7, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 21, 40, 0, 0, 0, 0, 0, 0, 0, 'High Priest Talet-Kha - Actionlist - Start Attacking'); + +-- Set Invisible Stalker & Mystical Webbing Guid SAIs. +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` IN (-85175, -85176, -85098, -85118)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(-85176, 0, 0, 0, 72, 0, 100, 0, 23, 0, 0, 0, 0, 0, 11, 45497, 2, 0, 0, 0, 0, 11, 26073, 40, 0, 0, 0, 0, 0, 0, 'Invisible Stalker (Floating) - On Action 23 Done - Cast \'Web Beam\''), +(-85176, 0, 1, 0, 72, 0, 100, 0, 24, 0, 0, 0, 0, 0, 92, 0, 45497, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Invisible Stalker (Floating) - On Action 24 Done - Interrupt Spell \'Web Beam\''), +(-85175, 0, 0, 0, 72, 0, 100, 0, 23, 0, 0, 0, 0, 0, 11, 45497, 2, 0, 0, 0, 0, 11, 26073, 40, 0, 0, 0, 0, 0, 0, 'Invisible Stalker (Floating) - On Action 23 Done - Cast \'Web Beam\''), +(-85175, 0, 1, 0, 72, 0, 100, 0, 24, 0, 0, 0, 0, 0, 92, 0, 45497, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Invisible Stalker (Floating) - On Action 24 Done - Interrupt Spell \'Web Beam\''), +(-85098, 0, 0, 1, 72, 0, 100, 0, 23, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Action 23 Done - Set Reactstate Passive'), +(-85098, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 45497, 2, 0, 0, 0, 0, 11, 26073, 40, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Action 23 Done - Cast \'Web Beam\''), +(-85098, 0, 2, 3, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 24, 0, 0, 0, 0, 0, 10, 85176, 23033, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Just Died - Do Action ID 24'), +(-85098, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 26, 0, 0, 0, 0, 0, 10, 85240, 26073, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Just Died - Do Action ID 26'), +(-85118, 0, 0, 1, 72, 0, 100, 0, 23, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Action 23 Done - Set Reactstate Passive'), +(-85118, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 45497, 2, 0, 0, 0, 0, 11, 26073, 40, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Action 23 Done - Cast \'Web Beam\''), +(-85118, 0, 2, 3, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 24, 0, 0, 0, 0, 0, 10, 85175, 23033, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Just Died - Do Action ID 24'), +(-85118, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 25, 0, 0, 0, 0, 0, 10, 85240, 26073, 0, 0, 0, 0, 0, 0, 'Mystical Webbing - On Just Died - Do Action ID 25'); diff --git a/data/sql/updates/db_world/2026_03_25_00.sql b/data/sql/updates/db_world/2026_03_25_00.sql new file mode 100644 index 000000000..893b7c5cf --- /dev/null +++ b/data/sql/updates/db_world/2026_03_25_00.sql @@ -0,0 +1,25 @@ +-- DB update 2026_03_24_05 -> 2026_03_25_00 +-- Add acore_string entries (30110-30125) and command table entry for .debug factionchange + +DELETE FROM `acore_string` WHERE `entry` BETWEEN 30110 AND 30125; +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(30110, '=== Faction/Race Change Debug for {} (GUID: {}) ===', '=== {} 진영/종족 변경 디버그 (GUID: {}) ===', '=== Débogage changement de faction/race pour {} (GUID : {}) ===', '=== Fraktions-/Rassenwechsel-Debug für {} (GUID: {}) ===', '=== 阵营/种族变更调试:{} (GUID: {}) ===', '=== 陣營/種族變更調試:{} (GUID: {}) ===', '=== Depuración de cambio de facción/raza para {} (GUID: {}) ===', '=== Depuración de cambio de facción/raza para {} (GUID: {}) ===', '=== Отладка смены фракции/расы для {} (GUID: {}) ==='), +(30111, ' Faction change flag: SET [OK]', ' 진영 변경 플래그: 설정됨 [OK]', ' Flag changement de faction : ACTIVÉ [OK]', ' Fraktionswechsel-Flag: GESETZT [OK]', ' 阵营变更标记:已设置 [OK]', ' 陣營變更標記:已設置 [OK]', ' Flag de cambio de facción: ACTIVO [OK]', ' Flag de cambio de facción: ACTIVO [OK]', ' Флаг смены фракции: УСТАНОВЛЕН [OK]'), +(30112, ' Race change flag: SET [OK]', ' 종족 변경 플래그: 설정됨 [OK]', ' Flag changement de race : ACTIVÉ [OK]', ' Rassenwechsel-Flag: GESETZT [OK]', ' 种族变更标记:已设置 [OK]', ' 種族變更標記:已設置 [OK]', ' Flag de cambio de raza: ACTIVO [OK]', ' Flag de cambio de raza: ACTIVO [OK]', ' Флаг смены расы: УСТАНОВЛЕН [OK]'), +(30113, ' Change flag: NEITHER faction nor race change flag is set [FAIL]', ' 변경 플래그: 진영 및 종족 변경 플래그가 설정되지 않음 [실패]', ' Flag de changement : NI faction NI race n''est activé [ÉCHEC]', ' Wechsel-Flag: WEDER Fraktions- noch Rassenwechsel-Flag gesetzt [FEHLER]', ' 变更标记:阵营和种族变更标记均未设置 [失败]', ' 變更標記:陣營和種族變更標記均未設置 [失敗]', ' Flag de cambio: NI facción NI raza están activos [FALLO]', ' Flag de cambio: NI facción NI raza están activos [FALLO]', ' Флаг смены: НЕ установлен ни флаг фракции, ни расы [ОШИБКА]'), +(30114, ' Guild: member of \"{}\" - must leave before faction change [FAIL]', ' 길드: \"{}\"의 멤버 - 진영 변경 전 탈퇴 필요 [실패]', ' Guilde : membre de \"{}\" - doit quitter avant le changement de faction [ÉCHEC]', ' Gilde: Mitglied von \"{}\" - muss vor dem Fraktionswechsel austreten [FEHLER]', ' 公会:是\"{}\"的成员 - 必须在阵营变更前退出 [失败]', ' 公會:是\"{}\"的成員 - 必須在陣營變更前退出 [失敗]', ' Hermandad: miembro de \"{}\" - debe abandonar antes de cambiar de facción [FALLO]', ' Hermandad: miembro de \"{}\" - debe abandonar antes de cambiar de facción [FALLO]', ' Гильдия: состоит в \"{}\" - необходимо покинуть перед сменой фракции [ОШИБКА]'), +(30115, ' Guild: not in a guild or cross-faction guilds enabled [OK]', ' 길드: 길드 미가입 또는 교차 진영 길드 허용됨 [OK]', ' Guilde : pas dans une guilde ou guildes cross-faction activées [OK]', ' Gilde: nicht in einer Gilde oder fraktionsübergreifende Gilden aktiviert [OK]', ' 公会:未加入公会或已启用跨阵营公会 [OK]', ' 公會:未加入公會或已啟用跨陣營公會 [OK]', ' Hermandad: sin hermandad o hermandades cross-facción habilitadas [OK]', ' Hermandad: sin hermandad o hermandades cross-facción habilitadas [OK]', ' Гильдия: не в гильдии или межфракционные гильдии включены [OK]'), +(30116, ' Arena captain: is captain of an arena team - must transfer or disband [FAIL]', ' 아레나 대장: 아레나 팀의 대장 - 양도 또는 해체 필요 [실패]', ' Capitaine d''arène : est capitaine d''une équipe d''arène - doit transférer ou dissoudre [ÉCHEC]', ' Arena-Kapitän: ist Kapitän eines Arena-Teams - muss übergeben oder auflösen [FEHLER]', ' 竞技场队长:是竞技场队伍的队长 - 必须转让或解散 [失败]', ' 競技場隊長:是競技場隊伍的隊長 - 必須轉讓或解散 [失敗]', ' Capitán de arena: es capitán de un equipo de arena - debe transferir o disolver [FALLO]', ' Capitán de arena: es capitán de un equipo de arena - debe transferir o disolver [FALLO]', ' Капитан арены: является капитаном команды арены - необходимо передать или распустить [ОШИБКА]'), +(30117, ' Arena captain: not a captain [OK]', ' 아레나 대장: 대장 아님 [OK]', ' Capitaine d''arène : n''est pas capitaine [OK]', ' Arena-Kapitän: kein Kapitän [OK]', ' 竞技场队长:不是队长 [OK]', ' 競技場隊長:不是隊長 [OK]', ' Capitán de arena: no es capitán [OK]', ' Capitán de arena: no es capitán [OK]', ' Капитан арены: не является капитаном [OK]'), +(30118, ' Mail: {} message(s) in mailbox - must clear mail [FAIL]', ' 우편: 우편함에 {}개의 메시지 - 우편 정리 필요 [실패]', ' Courrier : {} message(s) dans la boîte - doit vider le courrier [ÉCHEC]', ' Post: {} Nachricht(en) im Postfach - Post muss gelöscht werden [FEHLER]', ' 邮件:邮箱中有{}封邮件 - 必须清空邮箱 [失败]', ' 郵件:信箱中有{}封郵件 - 必須清空信箱 [失敗]', ' Correo: {} mensaje(s) en el buzón - debe vaciar el correo [FALLO]', ' Correo: {} mensaje(s) en el buzón - debe vaciar el correo [FALLO]', ' Почта: {} сообщений в почтовом ящике - необходимо очистить почту [ОШИБКА]'), +(30119, ' Mail: mailbox is empty [OK]', ' 우편: 우편함 비어 있음 [OK]', ' Courrier : boîte aux lettres vide [OK]', ' Post: Postfach ist leer [OK]', ' 邮件:邮箱为空 [OK]', ' 郵件:信箱為空 [OK]', ' Correo: buzón vacío [OK]', ' Correo: buzón vacío [OK]', ' Почта: почтовый ящик пуст [OK]'), +(30120, ' Auctions: has active auctions or bids - must clear auctions [FAIL]', ' 경매: 활성 경매 또는 입찰 있음 - 경매 정리 필요 [실패]', ' Enchères : a des enchères ou offres actives - doit supprimer les enchères [ÉCHEC]', ' Auktionen: hat aktive Auktionen oder Gebote - Auktionen müssen gelöscht werden [FEHLER]', ' 拍卖:有活跃的拍卖或出价 - 必须清除拍卖 [失败]', ' 拍賣:有活躍的拍賣或出價 - 必須清除拍賣 [失敗]', ' Subastas: tiene subastas o pujas activas - debe eliminar las subastas [FALLO]', ' Subastas: tiene subastas o pujas activas - debe eliminar las subastas [FALLO]', ' Аукционы: есть активные аукционы или ставки - необходимо очистить аукционы [ОШИБКА]'), +(30121, ' Auctions: no active auctions or bids [OK]', ' 경매: 활성 경매 또는 입찰 없음 [OK]', ' Enchères : aucune enchère ni offre active [OK]', ' Auktionen: keine aktiven Auktionen oder Gebote [OK]', ' 拍卖:没有活跃的拍卖或出价 [OK]', ' 拍賣:沒有活躍的拍賣或出價 [OK]', ' Subastas: sin subastas ni pujas activas [OK]', ' Subastas: sin subastas ni pujas activas [OK]', ' Аукционы: нет активных аукционов или ставок [OK]'), +(30122, ' Gold: {} copper, exceeds limit of {} copper [FAIL]', ' 골드: {} 코퍼, 제한 {} 코퍼 초과 [실패]', ' Or : {} cuivres, dépasse la limite de {} cuivres [ÉCHEC]', ' Gold: {} Kupfer, überschreitet Limit von {} Kupfer [FEHLER]', ' 金币:{}铜币,超过限额{}铜币 [失败]', ' 金幣:{}銅幣,超過限額{}銅幣 [失敗]', ' Oro: {} cobres, excede el límite de {} cobres [FALLO]', ' Oro: {} cobres, excede el límite de {} cobres [FALLO]', ' Золото: {} медных, превышает лимит в {} медных [ОШИБКА]'), +(30123, ' Gold: {} copper (limit: {} copper) [OK]', ' 골드: {} 코퍼 (제한: {} 코퍼) [OK]', ' Or : {} cuivres (limite : {} cuivres) [OK]', ' Gold: {} Kupfer (Limit: {} Kupfer) [OK]', ' 金币:{}铜币(限额:{}铜币)[OK]', ' 金幣:{}銅幣(限額:{}銅幣)[OK]', ' Oro: {} cobres (límite: {} cobres) [OK]', ' Oro: {} cobres (límite: {} cobres) [OK]', ' Золото: {} медных (лимит: {} медных) [OK]'), +(30124, ' Gold: {} copper (no limit configured) [OK]', ' 골드: {} 코퍼 (제한 없음) [OK]', ' Or : {} cuivres (aucune limite configurée) [OK]', ' Gold: {} Kupfer (kein Limit konfiguriert) [OK]', ' 金币:{}铜币(未配置限额)[OK]', ' 金幣:{}銅幣(未配置限額)[OK]', ' Oro: {} cobres (sin límite configurado) [OK]', ' Oro: {} cobres (sin límite configurado) [OK]', ' Золото: {} медных (лимит не настроен) [OK]'), +(30125, ' Guild/Arena/Mail/Auction checks: N/A (faction change not requested, only applies to faction change)', ' 길드/아레나/우편/경매 검사: 해당 없음 (진영 변경이 요청되지 않음, 진영 변경에만 적용)', ' Vérifications guilde/arène/courrier/enchères : N/A (changement de faction non demandé, applicable uniquement au changement de faction)', ' Gilden-/Arena-/Post-/Auktionsprüfungen: Nicht zutreffend (kein Fraktionswechsel angefordert, gilt nur für Fraktionswechsel)', ' 公会/竞技场/邮件/拍卖检查:不适用(未请求阵营变更,仅适用于阵营变更)', ' 公會/競技場/郵件/拍賣檢查:不適用(未請求陣營變更,僅適用於陣營變更)', ' Comprobaciones de hermandad/arena/correo/subastas: N/A (no se solicitó cambio de facción, solo aplica a cambio de facción)', ' Comprobaciones de hermandad/arena/correo/subastas: N/A (no se solicitó cambio de facción, solo aplica a cambio de facción)', ' Проверки гильдии/арены/почты/аукционов: Н/Д (смена фракции не запрошена, применимо только к смене фракции)'); + +DELETE FROM `command` WHERE `name` = 'debug factionchange'; +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('debug factionchange', 3, 'Syntax: .debug factionchange [$playerName]\nChecks all faction/race change requirements for the target player and reports pass/fail for each condition (AT_LOGIN flags, guild, arena captain, mail, auctions, gold limit).'); diff --git a/data/sql/updates/db_world/2026_03_25_01.sql b/data/sql/updates/db_world/2026_03_25_01.sql new file mode 100644 index 000000000..f5314b3b0 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_25_01.sql @@ -0,0 +1,22 @@ +-- DB update 2026_03_25_00 -> 2026_03_25_01 +-- Add autobroadcast commands +DELETE FROM `command` WHERE `name` IN ('autobroadcast', 'autobroadcast list', 'autobroadcast add', 'autobroadcast locale', 'autobroadcast remove'); +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('autobroadcast', 2, 'Syntax: .autobroadcast $subcommand\nType .autobroadcast to see a list of subcommands or .help autobroadcast $subcommand to see info on subcommands.'), +('autobroadcast list', 2, 'Syntax: .autobroadcast list\nList all autobroadcast entries.'), +('autobroadcast add', 3, 'Syntax: .autobroadcast add $weight $text\nAdd a new autobroadcast entry with the given weight and text.'), +('autobroadcast locale', 3, 'Syntax: .autobroadcast locale $id $locale $text\nAdd or replace a localized text for the autobroadcast entry with the given ID.'), +('autobroadcast remove', 3, 'Syntax: .autobroadcast remove $id\nRemove the autobroadcast entry with the given ID and its locale entries.'); + +-- Add acore_string entries for autobroadcast commands +DELETE FROM `acore_string` WHERE `entry` IN (5126, 5127, 5128, 5129, 5130, 5131, 5132, 5133, 5134); +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(5126, 'Autobroadcast entries:', '자동 방송 항목:', 'Entrées d''annonce automatique :', 'Autobroadcast-Einträge:', '自动广播条目:', '自動廣播條目:', 'Entradas de transmisión automática:', 'Entradas de transmisión automática:', 'Записи автоматической рассылки:'), +(5127, ' ID: {} | Weight: {} | Text: {}', ' ID: {} | 가중치: {} | 텍스트: {}', ' ID : {} | Poids : {} | Texte : {}', ' ID: {} | Gewicht: {} | Text: {}', ' ID:{} | 权重:{} | 文本:{}', ' ID:{} | 權重:{} | 文本:{}', ' ID: {} | Peso: {} | Texto: {}', ' ID: {} | Peso: {} | Texto: {}', ' ID: {} | Вес: {} | Текст: {}'), +(5128, 'No autobroadcast entries found.', '자동 방송 항목을 찾을 수 없습니다.', 'Aucune entrée d''annonce automatique trouvée.', 'Keine Autobroadcast-Einträge gefunden.', '未找到自动广播条目。', '未找到自動廣播條目。', 'No se encontraron entradas de transmisión automática.', 'No se encontraron entradas de transmisión automática.', 'Записи автоматической рассылки не найдены.'), +(5129, 'Autobroadcast entry added with ID {}.', 'ID {}로 자동 방송 항목이 추가되었습니다.', 'Entrée d''annonce automatique ajoutée avec l''ID {}.', 'Autobroadcast-Eintrag mit ID {} hinzugefügt.', '已添加ID为{}的自动广播条目。', '已新增ID為{}的自動廣播條目。', 'Entrada de transmisión automática añadida con ID {}.', 'Entrada de transmisión automática añadida con ID {}.', 'Добавлена запись автоматической рассылки с ID {}.'), +(5130, 'Autobroadcast entry #{} removed.', '자동 방송 항목 #{}이(가) 제거되었습니다.', 'Entrée d''annonce automatique #{} supprimée.', 'Autobroadcast-Eintrag #{} entfernt.', '自动广播条目 #{} 已移除。', '自動廣播條目 #{} 已移除。', 'Entrada de transmisión automática #{} eliminada.', 'Entrada de transmisión automática #{} eliminada.', 'Запись автоматической рассылки #{} удалена.'), +(5131, 'Autobroadcast entry #{} not found.', '자동 방송 항목 #{}을(를) 찾을 수 없습니다.', 'Entrée d''annonce automatique #{} introuvable.', 'Autobroadcast-Eintrag #{} nicht gefunden.', '未找到自动广播条目 #{}。', '未找到自動廣播條目 #{}。', 'Entrada de transmisión automática #{} no encontrada.', 'Entrada de transmisión automática #{} no encontrada.', 'Запись автоматической рассылки #{} не найдена.'), +(5132, 'Autobroadcast locale ''{}'' added for entry #{}.', '자동 방송 항목 #{}에 로캘 ''{}''이(가) 추가되었습니다.', 'Localisation ''{}'' ajoutée pour l''entrée d''annonce automatique #{}.', 'Autobroadcast-Lokalisierung ''{}'' für Eintrag #{} hinzugefügt.', '已为自动广播条目 #{} 添加语言区域 ''{}''。', '已為自動廣播條目 #{} 新增語言區域 ''{}''。', 'Localización ''{}'' añadida para la entrada de transmisión automática #{}.', 'Localización ''{}'' añadida para la entrada de transmisión automática #{}.', 'Локализация ''{}'' добавлена для записи автоматической рассылки #{}.'), +(5133, ' Locale: {} | Text: {}', ' 로캘: {} | 텍스트: {}', ' Langue : {} | Texte : {}', ' Sprache: {} | Text: {}', ' 语言区域:{} | 文本:{}', ' 語言區域:{} | 文本:{}', ' Localización: {} | Texto: {}', ' Localización: {} | Texto: {}', ' Локализация: {} | Текст: {}'), +(5134, 'Invalid locale. Valid locales: enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.', '잘못된 로캘입니다. 유효한 로캘: enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.', 'Langue invalide. Langues valides : enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.', 'Ungültige Sprache. Gültige Sprachen: enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.', '无效的语言区域。有效的语言区域:enUS、koKR、frFR、deDE、zhCN、zhTW、esES、esMX、ruRU。', '無效的語言區域。有效的語言區域:enUS、koKR、frFR、deDE、zhCN、zhTW、esES、esMX、ruRU。', 'Localización inválida. Localizaciones válidas: enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.', 'Localización inválida. Localizaciones válidas: enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.', 'Недопустимая локализация. Допустимые локализации: enUS, koKR, frFR, deDE, zhCN, zhTW, esES, esMX, ruRU.'); diff --git a/data/sql/updates/db_world/2026_03_25_02.sql b/data/sql/updates/db_world/2026_03_25_02.sql new file mode 100644 index 000000000..4c96ce780 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_25_02.sql @@ -0,0 +1,820 @@ +-- DB update 2026_03_25_01 -> 2026_03_25_02 +-- +-- Fix: Repopulate CreatureImmunitiesId for all creatures that had mechanic/school immune masks +-- The original migration (2026_03_22_03) ran the UPDATE before creature_immunities rows existed, +-- resulting in all creatures getting CreatureImmunitiesId=0 +-- + +DELETE FROM `creature_immunities` WHERE `ID` IN (1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(1972, 0, 0, 2, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2'), +(1973, 0, 0, 4, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4'), +(1974, 0, 0, 16, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16'), +(1975, 0, 0, 18, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 18'), +(1976, 0, 0, 34, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 34'), +(1977, 0, 0, 40, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 40'), +(1978, 0, 0, 42, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 42'), +(1979, 0, 0, 64, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 64'), +(1980, 0, 0, 512, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 512'), +(1981, 0, 0, 514, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 514'), +(1982, 0, 0, 1024, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1024'), +(1983, 0, 0, 1056, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1056'), +(1984, 0, 0, 1058, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1058'), +(1985, 0, 0, 2048, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2048'), +(1986, 0, 0, 2050, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2050'), +(1987, 0, 0, 2052, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2052'), +(1988, 0, 0, 2080, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2080'), +(1989, 0, 0, 2082, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2082'), +(1990, 0, 0, 2178, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2178'), +(1991, 0, 0, 2192, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2192'), +(1992, 0, 0, 2208, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2208'), +(1993, 0, 0, 2210, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2210'), +(1994, 0, 0, 2562, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2562'), +(1995, 0, 0, 3074, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 3074'), +(1996, 0, 0, 4098, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4098'), +(1997, 0, 0, 4114, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4114'), +(1998, 0, 0, 4128, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4128'), +(1999, 0, 0, 4608, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4608'), +(2000, 0, 0, 4624, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4624'), +(2001, 0, 0, 6178, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 6178'), +(2002, 0, 0, 6810, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 6810'), +(2003, 0, 0, 8192, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 8192'), +(2004, 0, 0, 8224, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 8224'), +(2005, 0, 0, 12584, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 12584'), +(2006, 0, 0, 14374, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 14374'), +(2007, 0, 0, 14982, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 14982'), +(2008, 0, 0, 16384, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16384'), +(2009, 0, 0, 20516, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 20516'), +(2010, 0, 0, 21114, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 21114'), +(2011, 0, 0, 32768, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 32768'), +(2012, 0, 0, 65278, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 65278'), +(2013, 0, 0, 131072, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 131072'), +(2014, 0, 0, 131074, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 131074'), +(2015, 0, 0, 131106, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 131106'), +(2016, 0, 0, 132128, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 132128'), +(2017, 0, 0, 133122, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 133122'), +(2018, 0, 0, 133248, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 133248'), +(2019, 0, 0, 133250, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 133250'), +(2020, 0, 0, 139264, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 139264'), +(2021, 0, 0, 139298, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 139298'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (2022,2023,2024,2025,2026,2027,2028,2029,2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041,2042,2043,2044,2045,2046,2047,2048,2049,2050,2051,2052,2053,2054,2055,2056,2057,2058,2059,2060,2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(2022, 0, 0, 141440, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 141440'), +(2023, 0, 0, 143360, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 143360'), +(2024, 0, 0, 143396, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 143396'), +(2025, 0, 0, 262144, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 262144'), +(2026, 0, 0, 262146, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 262146'), +(2027, 0, 0, 262172, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 262172'), +(2028, 0, 0, 263222, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 263222'), +(2029, 0, 0, 265792, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 265792'), +(2030, 0, 0, 274592, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 274592'), +(2031, 0, 0, 278534, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 278534'), +(2032, 0, 0, 404998, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 404998'), +(2033, 0, 0, 407714, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 407714'), +(2034, 0, 0, 16777216, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16777216'), +(2035, 0, 0, 16777248, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16777248'), +(2036, 0, 0, 16777250, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16777250'), +(2037, 0, 0, 16777264, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16777264'), +(2038, 0, 0, 16777312, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16777312'), +(2039, 0, 0, 16777376, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16777376'), +(2040, 0, 0, 16779424, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16779424'), +(2041, 0, 0, 16779428, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16779428'), +(2042, 0, 0, 16779488, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16779488'), +(2043, 0, 0, 16781344, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16781344'), +(2044, 0, 0, 16781984, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16781984'), +(2045, 0, 0, 16783392, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16783392'), +(2046, 0, 0, 16788644, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16788644'), +(2047, 0, 0, 16791714, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16791714'), +(2048, 0, 0, 16794664, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16794664'), +(2049, 0, 0, 16794668, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16794668'), +(2050, 0, 0, 16794676, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16794676'), +(2051, 0, 0, 16827436, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16827436'), +(2052, 0, 0, 16932526, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 16932526'), +(2053, 0, 0, 17039394, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17039394'), +(2054, 0, 0, 17039520, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17039520'), +(2055, 0, 0, 17041570, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17041570'), +(2056, 0, 0, 17045536, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17045536'), +(2057, 0, 0, 17171490, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17171490'), +(2058, 0, 0, 17171494, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17171494'), +(2059, 0, 0, 17204258, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17204258'), +(2060, 0, 0, 17825824, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17825824'), +(2061, 0, 0, 17825826, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17825826'), +(2062, 0, 0, 17826336, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 17826336'), +(2063, 0, 0, 25198706, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 25198706'), +(2064, 0, 0, 25574434, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 25574434'), +(2065, 0, 0, 26216098, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 26216098'), +(2066, 0, 0, 67272418, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 67272418'), +(2067, 0, 0, 75497986, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 75497986'), +(2068, 0, 0, 75502082, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 75502082'), +(2069, 0, 0, 75765794, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 75765794'), +(2070, 0, 0, 84032160, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 84032160'), +(2071, 0, 0, 84180644, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 84180644'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (2072,2073,2074,2075,2076,2077,2078,2079,2080,2081,2082,2083,2084,2085,2086,2087,2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099,2100,2101,2102,2103,2104,2105,2106,2107,2108,2109,2110,2111,2112,2113,2114,2115,2116,2117,2118,2119,2120,2121); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(2072, 0, 0, 84180654, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 84180654'), +(2073, 0, 0, 92569262, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 92569262'), +(2074, 0, 0, 93617828, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 93617828'), +(2075, 0, 0, 134217728, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 134217728'), +(2076, 0, 0, 134219776, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 134219776'), +(2077, 0, 0, 134221834, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 134221834'), +(2078, 0, 0, 134368350, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 134368350'), +(2079, 0, 0, 134487696, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 134487696'), +(2080, 0, 0, 134487698, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 134487698'), +(2081, 0, 0, 134610954, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 134610954'), +(2082, 0, 0, 142884558, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 142884558'), +(2083, 0, 0, 151259878, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 151259878'), +(2084, 0, 0, 151262366, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 151262366'), +(2085, 0, 0, 151262398, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 151262398'), +(2086, 0, 0, 151273214, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 151273214'), +(2087, 0, 0, 151314166, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 151314166'), +(2088, 0, 0, 151401654, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 151401654'), +(2089, 0, 0, 152469182, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 152469182'), +(2090, 0, 0, 159516926, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 159516926'), +(2091, 0, 0, 160853686, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 160853686'), +(2092, 0, 0, 160857782, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 160857782'), +(2093, 0, 0, 160857790, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 160857790'), +(2094, 0, 0, 160857854, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 160857854'), +(2095, 0, 0, 161361662, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 161361662'), +(2096, 0, 0, 201719818, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 201719818'), +(2097, 0, 0, 226915902, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 226915902'), +(2098, 0, 0, 227962558, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 227962558'), +(2099, 0, 0, 688815860, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 688815860'), +(2100, 0, 0, 1002405630, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1002405630'), +(2101, 0, 0, 1073741824, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1073741824'), +(2102, 0, 0, 1073741826, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1073741826'), +(2103, 0, 0, 1073741892, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1073741892'), +(2104, 0, 0, 1073758240, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1073758240'), +(2105, 0, 0, 1073873920, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1073873920'), +(2106, 0, 0, 1073877158, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1073877158'), +(2107, 0, 0, 1073886882, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1073886882'), +(2108, 0, 0, 1074009602, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1074009602'), +(2109, 0, 0, 1074167486, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1074167486'), +(2110, 0, 0, 1075202048, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1075202048'), +(2111, 0, 0, 1082677838, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1082677838'), +(2112, 0, 0, 1082686030, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1082686030'), +(2113, 0, 0, 1082686046, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1082686046'), +(2114, 0, 0, 1090534560, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090534560'), +(2115, 0, 0, 1090679030, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090679030'), +(2116, 0, 0, 1090681014, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090681014'), +(2117, 0, 0, 1090681270, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090681270'), +(2118, 0, 0, 1090936558, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090936558'), +(2119, 0, 0, 1090936574, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090936574'), +(2120, 0, 0, 1090940076, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090940076'), +(2121, 0, 0, 1090940670, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090940670'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (2122,2123,2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153,2154,2155,2156,2157,2158,2159,2160,2161,2162,2163,2164,2165,2166,2167,2168,2169,2170,2171); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(2122, 0, 0, 1090944742, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090944742'), +(2123, 0, 0, 1090944766, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1090944766'), +(2124, 0, 0, 1099040290, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1099040290'), +(2125, 0, 0, 1099043874, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1099043874'), +(2126, 0, 0, 1099060258, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1099060258'), +(2127, 0, 0, 1100219550, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100219550'), +(2128, 0, 0, 1100248830, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100248830'), +(2129, 0, 0, 1100350654, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100350654'), +(2130, 0, 0, 1100359230, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100359230'), +(2131, 0, 0, 1100366910, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100366910'), +(2132, 0, 0, 1100375718, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100375718'), +(2133, 0, 0, 1100379710, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100379710'), +(2134, 0, 0, 1100379774, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100379774'), +(2135, 0, 0, 1100904190, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100904190'), +(2136, 0, 0, 1100906238, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1100906238'), +(2137, 0, 0, 1102477054, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1102477054'), +(2138, 0, 0, 1102872318, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1102872318'), +(2139, 0, 0, 1141118466, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1141118466'), +(2140, 0, 0, 1158053548, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1158053548'), +(2141, 0, 0, 1158053550, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1158053550'), +(2142, 0, 0, 1158053630, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1158053630'), +(2143, 0, 0, 1167484670, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1167484670'), +(2144, 0, 0, 1167488694, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1167488694'), +(2145, 0, 0, 1207983274, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1207983274'), +(2146, 0, 0, 1208099366, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1208099366'), +(2147, 0, 0, 1208110334, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1208110334'), +(2148, 0, 0, 1208117942, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1208117942'), +(2149, 0, 0, 1208118454, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1208118454'), +(2150, 0, 0, 1208123134, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1208123134'), +(2151, 0, 0, 1208245410, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1208245410'), +(2152, 0, 0, 1209171606, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1209171606'), +(2153, 0, 0, 1217817766, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1217817766'), +(2154, 0, 0, 1224758964, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1224758964'), +(2155, 0, 0, 1224758966, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1224758966'), +(2156, 0, 0, 1224764576, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1224764576'), +(2157, 0, 0, 1224891646, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1224891646'), +(2158, 0, 0, 1224892158, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1224892158'), +(2159, 0, 0, 1224899750, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1224899750'), +(2160, 0, 0, 1224899830, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1224899830'), +(2161, 0, 0, 1225030910, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225030910'), +(2162, 0, 0, 1225031358, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225031358'), +(2163, 0, 0, 1225064188, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225064188'), +(2164, 0, 0, 1225162494, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225162494'), +(2165, 0, 0, 1225293566, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225293566'), +(2166, 0, 0, 1225424638, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225424638'), +(2167, 0, 0, 1225588478, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225588478'), +(2168, 0, 0, 1225686782, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1225686782'), +(2169, 0, 0, 1226206902, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1226206902'), +(2170, 0, 0, 1226209946, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1226209946'), +(2171, 0, 0, 1226209974, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1226209974'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (2172,2173,2174,2175,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2221); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(2172, 0, 0, 1226210998, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1226210998'), +(2173, 0, 0, 1226211006, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1226211006'), +(2174, 0, 0, 1226211070, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1226211070'), +(2175, 0, 0, 1230420214, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1230420214'), +(2176, 0, 0, 1233288886, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1233288886'), +(2177, 0, 0, 1233419518, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1233419518'), +(2178, 0, 0, 1233551102, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1233551102'), +(2179, 0, 0, 1233944318, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1233944318'), +(2180, 0, 0, 1234075390, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234075390'), +(2181, 0, 0, 1234568766, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234568766'), +(2182, 0, 0, 1234583806, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234583806'), +(2183, 0, 0, 1234595510, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234595510'), +(2184, 0, 0, 1234595518, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234595518'), +(2185, 0, 0, 1234595582, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234595582'), +(2186, 0, 0, 1234597430, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234597430'), +(2187, 0, 0, 1234599094, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599094'), +(2188, 0, 0, 1234599158, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599158'), +(2189, 0, 0, 1234599606, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599606'), +(2190, 0, 0, 1234599610, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599610'), +(2191, 0, 0, 1234599614, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599614'), +(2192, 0, 0, 1234599654, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599654'), +(2193, 0, 0, 1234599670, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599670'), +(2194, 0, 0, 1234599678, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234599678'), +(2195, 0, 0, 1234632374, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234632374'), +(2196, 0, 0, 1234992894, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1234992894'), +(2197, 0, 0, 1235119870, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1235119870'), +(2198, 0, 0, 1235123964, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1235123964'), +(2199, 0, 0, 1235123966, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1235123966'), +(2200, 0, 0, 1237221118, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1237221118'), +(2201, 0, 0, 1275494326, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1275494326'), +(2202, 0, 0, 1291996862, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1291996862'), +(2203, 0, 0, 1292000954, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292000954'), +(2204, 0, 0, 1292000958, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292000958'), +(2205, 0, 0, 1292009150, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292009150'), +(2206, 0, 0, 1292033724, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292033724'), +(2207, 0, 0, 1292033726, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292033726'), +(2208, 0, 0, 1292270270, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292270270'), +(2209, 0, 0, 1292271166, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292271166'), +(2210, 0, 0, 1292271294, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292271294'), +(2211, 0, 0, 1292271358, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1292271358'), +(2212, 0, 0, 1293188798, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293188798'), +(2213, 0, 0, 1293311670, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293311670'), +(2214, 0, 0, 1293315754, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293315754'), +(2215, 0, 0, 1293315774, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293315774'), +(2216, 0, 0, 1293317684, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293317684'), +(2217, 0, 0, 1293318838, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293318838'), +(2218, 0, 0, 1293319862, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293319862'), +(2219, 0, 0, 1293319870, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293319870'), +(2220, 0, 0, 1293319926, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293319926'), +(2221, 0, 0, 1293876926, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1293876926'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (2222,2223,2224,2225,2226,2227,2228,2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2239,2240,2241,2242,2243,2244,2245,2246,2247,2248,2249,2250,2251,2252,2253,2254,2255,2256,2257,2258,2259,2260,2261,2262,2263,2264,2265,2266,2267,2268,2269,2270,2271); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(2222, 0, 0, 1300692542, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1300692542'), +(2223, 0, 0, 1300692670, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1300692670'), +(2224, 0, 0, 1301053166, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301053166'), +(2225, 0, 0, 1301184254, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301184254'), +(2226, 0, 0, 1301446398, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301446398'), +(2227, 0, 0, 1301677626, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301677626'), +(2228, 0, 0, 1301677630, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301677630'), +(2229, 0, 0, 1301702270, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301702270'), +(2230, 0, 0, 1301703934, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301703934'), +(2231, 0, 0, 1301704382, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301704382'), +(2232, 0, 0, 1301704446, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301704446'), +(2233, 0, 0, 1301706494, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301706494'), +(2234, 0, 0, 1301707958, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301707958'), +(2235, 0, 0, 1301708022, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708022'), +(2236, 0, 0, 1301708462, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708462'), +(2237, 0, 0, 1301708470, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708470'), +(2238, 0, 0, 1301708478, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708478'), +(2239, 0, 0, 1301708526, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708526'), +(2240, 0, 0, 1301708534, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708534'), +(2241, 0, 0, 1301708538, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708538'), +(2242, 0, 0, 1301708540, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708540'), +(2243, 0, 0, 1301708542, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301708542'), +(2244, 0, 0, 1301741246, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301741246'), +(2245, 0, 0, 1301741310, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1301741310'), +(2246, 0, 0, 1302232766, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1302232766'), +(2247, 0, 0, 1302232830, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1302232830'), +(2248, 0, 0, 1303871230, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1303871230'), +(2249, 0, 0, 1304395518, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1304395518'), +(2250, 0, 0, 1304428286, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1304428286'), +(2251, 0, 0, 1305378558, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1305378558'), +(2252, 0, 0, 1335787254, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1335787254'), +(2253, 0, 0, 1539276542, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1539276542'), +(2254, 0, 0, 1770663678, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1770663678'), +(2255, 0, 0, 1770946302, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1770946302'), +(2256, 0, 0, 1804534846, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 1804534846'), +(2257, 0, 0, 2013265920, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2013265920'), +(2258, 0, 0, 2013528064, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2013528064'), +(2259, 0, 0, 2013528100, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2013528100'), +(2260, 0, 0, 2013660192, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2013660192'), +(2261, 0, 0, 2022966272, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2022966272'), +(2262, 0, 0, 2030453792, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2030453792'), +(2263, 0, 0, 2031386302, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031386302'), +(2264, 0, 0, 2031502368, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031502368'), +(2265, 0, 0, 2031502372, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031502372'), +(2266, 0, 0, 2031512736, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031512736'), +(2267, 0, 0, 2031516704, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031516704'), +(2268, 0, 0, 2031516708, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031516708'), +(2269, 0, 0, 2031516836, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031516836'), +(2270, 0, 0, 2031517372, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2031517372'), +(2271, 0, 0, 2038842400, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2038842400'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (2272,2273,2274,2275,2276,2277,2278,2279,2280,2281,2282,2283,2284,2285,2286,2287,2288,2289,2290,2291,2292,2293,2294,2295,2296,2297,2298,2299,2300,2301,2302,2303,2304,2305,2306,2307,2308,2309,2310,2311,2312,2313,2314,2315,2316,2317,2318,2319,2320,2321); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(2272, 0, 0, 2038842402, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2038842402'), +(2273, 0, 0, 2039890978, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039890978'), +(2274, 0, 0, 2039890982, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039890982'), +(2275, 0, 0, 2039901750, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039901750'), +(2276, 0, 0, 2039905318, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039905318'), +(2277, 0, 0, 2039905444, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039905444'), +(2278, 0, 0, 2039905446, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039905446'), +(2279, 0, 0, 2039905454, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039905454'), +(2280, 0, 0, 2039905956, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039905956'), +(2281, 0, 0, 2039905974, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039905974'), +(2282, 0, 0, 2039906038, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039906038'), +(2283, 0, 0, 2039906046, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2039906046'), +(2284, 0, 0, 2041872062, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2041872062'), +(2285, 0, 0, 2042588340, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2042588340'), +(2286, 0, 0, 2042588342, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2042588342'), +(2287, 0, 0, 2042592436, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2042592436'), +(2288, 0, 0, 2042592438, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2042592438'), +(2289, 0, 0, 2044099622, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2044099622'), +(2290, 0, 0, 2076147382, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2076147382'), +(2291, 0, 0, 2107014838, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2107014838'), +(2292, 0, 0, 2143256254, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2143256254'), +(2293, 0, 0, 2143289022, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2143289022'), +(2294, 0, 0, 2147483646, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 2147483646'), +(2295, 0, 0, 3116367612, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 3116367612'), +(2296, 0, 0, 3386277110, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 3386277110'), +(2297, 0, 0, 4294934270, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4294934270'), +(2298, 0, 0, 4294967038, '', '', 0, 0, 'SchoolMask 0, MechanicsMask 4294967038'), +(2299, 1, 0, 16777248, '', '', 0, 0, 'SchoolMask 1, MechanicsMask 16777248'), +(2300, 1, 0, 1301708478, '', '', 0, 0, 'SchoolMask 1, MechanicsMask 1301708478'), +(2301, 4, 0, 0, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 0'), +(2302, 4, 0, 32, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 32'), +(2303, 4, 0, 17416, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 17416'), +(2304, 4, 0, 17420, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 17420'), +(2305, 4, 0, 17432, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 17432'), +(2306, 4, 0, 16784544, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 16784544'), +(2307, 4, 0, 1226210998, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1226210998'), +(2308, 4, 0, 1226211006, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1226211006'), +(2309, 4, 0, 1234599606, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1234599606'), +(2310, 4, 0, 1292000954, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1292000954'), +(2311, 4, 0, 1292271294, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1292271294'), +(2312, 4, 0, 1293188798, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1293188798'), +(2313, 4, 0, 1301704446, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1301704446'), +(2314, 4, 0, 1301708478, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1301708478'), +(2315, 4, 0, 1301708542, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 1301708542'), +(2316, 4, 0, 2039905974, '', '', 0, 0, 'SchoolMask 4, MechanicsMask 2039905974'), +(2317, 8, 0, 0, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 0'), +(2318, 8, 0, 16, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 16'), +(2319, 8, 0, 1090674362, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1090674362'), +(2320, 8, 0, 1217817766, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1217817766'), +(2321, 8, 0, 1234599606, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1234599606'); + +DELETE FROM `creature_immunities` WHERE `ID` IN (2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2333,2334,2335,2336,2337,2338,2339,2340,2341,2342,2343,2344,2345,2346,2347,2348,2349,2350,2351,2352,2353,2354,2355); +INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES +(2322, 8, 0, 1291998778, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1291998778'), +(2323, 8, 0, 1292000954, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1292000954'), +(2324, 8, 0, 1292000958, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1292000958'), +(2325, 8, 0, 1292271358, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1292271358'), +(2326, 8, 0, 1293188798, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1293188798'), +(2327, 8, 0, 1293319870, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1293319870'), +(2328, 8, 0, 1300397758, '', '', 0, 0, 'SchoolMask 8, MechanicsMask 1300397758'), +(2329, 16, 0, 0, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 0'), +(2330, 16, 0, 16, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 16'), +(2331, 16, 0, 20516, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 20516'), +(2332, 16, 0, 16777248, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 16777248'), +(2333, 16, 0, 1090674362, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1090674362'), +(2334, 16, 0, 1217817766, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1217817766'), +(2335, 16, 0, 1226210998, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1226210998'), +(2336, 16, 0, 1234599606, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1234599606'), +(2337, 16, 0, 1292000954, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1292000954'), +(2338, 16, 0, 1293188798, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1293188798'), +(2339, 16, 0, 1301708538, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1301708538'), +(2340, 16, 0, 1301708542, '', '', 0, 0, 'SchoolMask 16, MechanicsMask 1301708542'), +(2341, 32, 0, 0, '', '', 0, 0, 'SchoolMask 32, MechanicsMask 0'), +(2342, 64, 0, 0, '', '', 0, 0, 'SchoolMask 64, MechanicsMask 0'), +(2343, 64, 0, 32, '', '', 0, 0, 'SchoolMask 64, MechanicsMask 32'), +(2344, 64, 0, 6688, '', '', 0, 0, 'SchoolMask 64, MechanicsMask 6688'), +(2345, 64, 0, 10368, '', '', 0, 0, 'SchoolMask 64, MechanicsMask 10368'), +(2346, 64, 0, 1234599094, '', '', 0, 0, 'SchoolMask 64, MechanicsMask 1234599094'), +(2347, 64, 0, 1235123966, '', '', 0, 0, 'SchoolMask 64, MechanicsMask 1235123966'), +(2348, 96, 0, 0, '', '', 0, 0, 'SchoolMask 96, MechanicsMask 0'), +(2349, 116, 0, 0, '', '', 0, 0, 'SchoolMask 116, MechanicsMask 0'), +(2350, 124, 0, 17171490, '', '', 0, 0, 'SchoolMask 124, MechanicsMask 17171490'), +(2351, 124, 0, 1234599614, '', '', 0, 0, 'SchoolMask 124, MechanicsMask 1234599614'), +(2352, 126, 0, 0, '', '', 0, 0, 'SchoolMask 126, MechanicsMask 0'), +(2353, 126, 0, 1292271286, '', '', 0, 0, 'SchoolMask 126, MechanicsMask 1292271286'), +(2354, 127, 0, 16777248, '', '', 0, 0, 'SchoolMask 127, MechanicsMask 16777248'), +(2355, 127, 0, 1301741246, '', '', 0, 0, 'SchoolMask 127, MechanicsMask 1301741246'); + +-- Update creature_template.CreatureImmunitiesId +UPDATE `creature_template` SET `CreatureImmunitiesId`=1537 WHERE `entry` IN (17543,18404); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1664 WHERE `entry` IN (4468); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1794 WHERE `entry` IN (3253,36551,37564); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1964 WHERE `entry` IN (20047); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1971 WHERE `entry` IN (20038,25367,25368,25371,25507); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1972 WHERE `entry` IN (352,523,752,931,1387,1571,1572,1573,2299,2409,2432,2835,2851,2858,2859,2861,2941,2995,3305,3310,3615,3725,3728,4312,4314,4317,4321,6026,6726,7823,7824,8018,8020,8609,8610,10378,11139,11830,11899,11900,11901,12596,12616,12617,12740,13177,15178,16218,16461,16593,16699,16704,17083,17229,17398,17420,17429,17461,17491,17622,17695,18048,18049,18050,18052,18053,18054,18057,18058,18603,18609,18610,18612,18615,18619,18631,18632,18635,18636,18830,19885,19887,19889,19903,20033,20034,20255,20256,20257,20258,20259,20260,20261,20578,20579,20580,20581,20582,20586,20587,20590,20594,20621,20624,20626,20639,20640,20641,20642,20644,20645,20661,20693,20695,20699,20701,20923,21230,21522,21524,21527,21528,21532,21545,21548,21563,21564,21565,21570,21571,21573,21575,21576,21585,21587,21613,21857,21865,21873,22876,23168,24994,25509,25591,25593,25595,25599,25851,26799,26800,26801,26802,26803,26805,27947,27949,29503,29626,30460,30478,30485,30495,30496,30497,30498,30508,30509,32922,32923,32924,32925,33235,33236,33259,33287,33355,33527,33579,33620,33622,33624,33626,33627,33629,33662,33669,33672,33696,33701,33737,33741,33754,33755,33757,33758,33774,33775,33816,33818,33819); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1972 WHERE `entry` IN (33820,33822,33823,33824,33827,33828,33829,33830,33831,33832,33956,33957,34054,34113,34144,34145,34198,34199,34236,34237,34255,34256,34257,34441,34442,34443,34444,34445,34447,34448,34449,34450,34451,34453,34454,34455,34456,34458,34459,34460,34461,34463,34465,34466,34467,34468,34469,34470,34471,34472,34473,34474,34475,35465,35610,35662,35663,35664,35665,35666,35667,35668,35669,35670,35671,35672,35673,35674,35675,35676,35680,35681,35682,35683,35684,35685,35686,35687,35688,35689,35690,35691,35692,35693,35694,35695,35696,35697,35699,35700,35701,35702,35703,35704,35705,35706,35707,35708,35709,35710,35711,35712,35713,35714,35715,35716,35718,35719,35720,35721,35722,35723,35724,35725,35726,35728,35729,35730,35731,35732,35733,35734,35735,35736,35737,35738,35739,35740,35741,35742,35743,35744,35745,35746,35747,35748,35749,35774,35775,35776,36070,36301,36302,36303,36473,36474,36475); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1973 WHERE `entry` IN (12737); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1974 WHERE `entry` IN (13996,14462,25752,25753,25758,25792); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1975 WHERE `entry` IN (24553,24554,24555,24556,24557,24558,24559,24561,24656,25541,25549,25550,25553,25555,25556,25574,25578,25579); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1976 WHERE `entry` IN (8717,21549); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1977 WHERE `entry` IN (30839); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1978 WHERE `entry` IN (20869,21586); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1979 WHERE `entry` IN (2673,2674,4952,12426,16844,16968,17578,18215,19139,21157,21849,22038,22466,22482,24792,30527,31143,31144,31146,32541,32542,32543,32545,32546,32547,32666,32667,33229,33243,33272,37127,38126); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1980 WHERE `entry` IN (20897,23398); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1981 WHERE `entry` IN (21597,22879,22882,22945,23018,23049,23147,23172,23223,23235,23236,23237,24815,25566); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1982 WHERE `entry` IN (5277,5280,5283,8319,16098,17669,20574); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1983 WHERE `entry` IN (22299,22300); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1984 WHERE `entry` IN (3678); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1985 WHERE `entry` IN (23584); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1986 WHERE `entry` IN (19005,22874,22885); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1987 WHERE `entry` IN (18707); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1988 WHERE `entry` IN (18533,20772); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1989 WHERE `entry` IN (21166); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1990 WHERE `entry` IN (18044,22939); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1991 WHERE `entry` IN (8908); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1992 WHERE `entry` IN (18258); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1993 WHERE `entry` IN (18046); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1994 WHERE `entry` IN (22957,22962); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1995 WHERE `entry` IN (18420,21574); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1996 WHERE `entry` IN (20052); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1997 WHERE `entry` IN (21225); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1998 WHERE `entry` IN (24549); +UPDATE `creature_template` SET `CreatureImmunitiesId`=1999 WHERE `entry` IN (34702,34703,35569,35570,35571,35572,35617,36082,36083,36084,36085,36086,36087,36088,36089,36090,36091); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2000 WHERE `entry` IN (20048); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2001 WHERE `entry` IN (20883,21615); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2002 WHERE `entry` IN (28163,28168); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2003 WHERE `entry` IN (12800,12801,12802); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2004 WHERE `entry` IN (24047); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2005 WHERE `entry` IN (18423); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2006 WHERE `entry` IN (12458); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2007 WHERE `entry` IN (22875); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2008 WHERE `entry` IN (7397); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2009 WHERE `entry` IN (13601); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2010 WHERE `entry` IN (30829,30830,30831); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2011 WHERE `entry` IN (2667,2671,7076,7077,7309,8376,8816,9657,15104,15108,15429,15631,15698,15706,15904,15910,15964,15973,16511); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2012 WHERE `entry` IN (26688); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2013 WHERE `entry` IN (16700,17732,19632,20174,24179,29273); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2014 WHERE `entry` IN (14261,14262,14263,14264,14265,20588,20589); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2015 WHERE `entry` IN (17816,17817,19884,19892); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2016 WHERE `entry` IN (19513,19598,19865,21560,21561,21562); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2017 WHERE `entry` IN (17465,20583,22955); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2018 WHERE `entry` IN (32361,32377,32409,32417,32422,32429,32447,32475,32481,32485); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2019 WHERE `entry` IN (22965); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2020 WHERE `entry` IN (24180); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2021 WHERE `entry` IN (24065); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2022 WHERE `entry` IN (21515); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2023 WHERE `entry` IN (24530); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2024 WHERE `entry` IN (18322,20696); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2025 WHERE `entry` IN (15146,25363); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2026 WHERE `entry` IN (17818,17864,18848,20656,20881,20900,21619,21621,25592,25948); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2027 WHERE `entry` IN (7728); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2028 WHERE `entry` IN (25798,25799); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2029 WHERE `entry` IN (26928,26929,26930,30511,30512,30513); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2030 WHERE `entry` IN (18182); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2031 WHERE `entry` IN (32500); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2032 WHERE `entry` IN (17731,20173); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2033 WHERE `entry` IN (19191); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2034 WHERE `entry` IN (19261); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (3,36,114,115,200,201,204,210,302,314,315,392,412,480,503,511,522,570,572,573,604,624,625,626,725,846,882,948,1159,1270,1488,1489,1491,1501,1502,1525,1526,1527,1528,1529,1530,1532,1533,1654,1656,1663,1666,1674,1675,1696,1716,1717,1720,1753,1772,1773,1790,1791,1792,1793,1794,1795,1796,1798,1805,1847,1849,1850,1852,1866,1868,1917,1918,1919,1921,1939,1940,1941,1942,1943,1944,1948,1971,1974,2045,2056,2217,2218,2219,2220,2221,2222,2227,2278,2288,2325,2433,2475,2479,2520,2531,2535,2536,2537,2623,2676,2678,2748,2763,2922,3094,3301,3538,3558,3565,3617,3654,3669,3670,3671,3673,3674,3799,3802,3803,3869,3870,3876,3914,3940,3941,3942,3983,4073,4074,4251,4252,4260,4308,4473,4474,4475,4476,4550,4566,4606,4610,4612,4854,4872,4945,4946,4958,5097,5202,5263,5267,5270,5271,5400,5621,5624,5626,5627,5674,5685,5686,5687,5712,5713,5714,5715,5716,5717,5723,5775,5854,5912,5954,6036,6092,6106,6107,6108,6225,6226,6227,6230,6231,6232,6233,6234,6346,6386,6426,6427,6486,6493,6669,6906); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (6907,6908,6910,7023,7039,7050,7067,7068,7069,7070,7071,7072,7075,7166,7206,7209,7270,7273,7274,7276,7286,7291,7310,7327,7328,7329,7332,7333,7334,7340,7347,7348,7349,7351,7352,7353,7354,7355,7356,7357,7358,7374,7375,7503,7504,7527,7624,7784,7791,7806,7807,7849,7895,7897,8035,8447,8500,8501,8530,8531,8532,8534,8535,8536,8537,8543,8544,8545,8552,8555,8556,8557,8558,8559,8567,8580,8585,8615,8663,8856,9437,9438,9439,9441,9442,9443,9476,9623,10080,10081,10082,10117,10381,10382,10383,10395,10397,10402,10403,10405,10406,10407,10408,10409,10411,10412,10413,10414,10416,10417,10463,10464,10479,10480,10481,10483,10484,10492,10493,10494,10495,10497,10498,10499,10500,10580,10666,10684,10698,10699,10778,10801,10810,10818,10819,10820,10821,10825,10836,10926,10927,10936,10937,10939,10943,10944,10945,10946,10949,10950,10951,10953,10954,11027,11030,11075,11082,11151,11153,11154,11155,11179,11195,11199,11216,11217,11263,11277,11278,11279,11280,11281,11282,11283,11286,11287,11290,11291,11292,11316,11412,11414,11415,11446,11468,11469,11470,11471,11472,11473,11474,11475,11477,11517,11518,11519,11520,11551,11580,11598); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (11628,11684,11875,11936,12238,12239,12240,12241,12242,12243,12248,12250,12261,12262,12263,12363,12364,12365,12367,12368,12379,12380,12385,12473,12865,13146,13378,13416,13619,13738,13739,13740,13741,13742,14224,14308,14337,14358,14364,14484,14485,14494,14511,14512,14513,14514,14518,14519,14520,14521,14551,14552,14553,14554,14562,14563,14605,14683,14685,14686,14687,14688,14691,14692,14694,14695,14696,14697,14698,14699,14700,14701,14702,14703,14704,14705,14706,14707,14708,14709,14710,14711,14712,14713,14714,14825,14826,14986,15079,15081,15117,15121,15163,15195,15349,15364,15368,15374,15376,15377,15547,15548,15551,15654,15655,15656,15657,15658,15665,15720,15958,16025,16026,16039,16040,16059,16066,16067,16093,16099,16103,16104,16119,16120,16125,16138,16141,16145,16146,16148,16153,16154,16159,16163,16169,16184,16211,16245,16246,16247,16248,16249,16250,16298,16300,16301,16302,16303,16305,16306,16307,16308,16309,16311,16312,16313,16314,16316,16319,16320,16321,16322,16323,16324,16325,16326,16327,16328,16329,16342,16357,16382,16383,16388,16389,16390,16393,16394,16402,16403,16406,16407,16408,16409,16410,16411,16412,16415,16424,16425,16426,16432,16437,16439,16451,16459,16460,16468,16470,16471); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (16481,16482,16525,16526,16805,16806,16811,16812,16813,16814,16815,16861,16904,16905,16906,16976,16977,16978,17007,17063,17064,17067,17086,17261,17415,17466,17503,17588,17589,17592,17612,17660,17672,17674,17714,17878,17895,17897,17898,17902,17903,17905,17906,18043,18287,18319,18320,18327,18355,18367,18441,18460,18478,18498,18499,18500,18501,18506,18521,18524,18556,18557,18558,18559,18588,18643,18689,18700,18797,18872,18873,19416,19417,19460,19463,19464,19488,19489,19543,19544,19545,19546,19579,19580,19682,19698,19719,19736,19749,19751,19825,19826,19827,19846,19863,19864,19872,19873,19874,19875,19876,19878,19879,19881,19937,20117,20137,20298,20303,20305,20310,20311,20312,20313,20315,20316,20317,20319,20320,20321,20322,20323,20409,20410,20480,20483,20495,20496,20512,20662,20691,20697,20698,20934,21049,21058,21065,21093,21163,21184,21198,21199,21200,21201,21202,21324,21354,21384,21385,21386,21430,21446,21449,21450,21452,21628,21636,21638,21651,21763,21778,21779,21784,21787,21788,21795,21797,21801,21815,21866,21867,21869,21870,21941,22025,22041,22045,22062,22138,22226,22235,22369,22441,22452,22454,22701,22706,23066,23067,23068,23103,23109,23111,23126,23161,23193,23204,23370,23371,23389); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (23408,23503,23509,23545,23554,23555,23561,23562,23563,23575,23643,23644,23645,23786,23861,23898,23904,23935,23970,23992,23993,24013,24019,24027,24029,24041,24073,24074,24084,24118,24153,24168,24177,24207,24246,24247,24248,24258,24262,24266,24267,24272,24327,24344,24440,24446,24447,24485,24546,24562,24563,24564,24566,24621,24622,24623,24624,24625,24626,24627,24693,24695,24712,24789,24790,24796,24808,24814,24871,24872,24874,24875,24876,24877,24880,25027,25028,25041,25224,25227,25228,25268,25294,25296,25330,25331,25332,25333,25350,25351,25352,25359,25365,25366,25375,25377,25382,25383,25386,25387,25393,25396,25445,25451,25452,25453,25462,25463,25465,25469,25542,25546,25559,25582,25600,25611,25619,25622,25625,25629,25650,25652,25655,25660,25668,25678,25682,25684,25981,26073,26076,26103,26115,26125,26126,26165,26195,26202,26203,26224,26225,26250,26252,26286,26287,26292,26294,26317,26318,26320,26336,26343,26344,26402,26404,26413,26455,26457,26458,26461,26475,26490,26491,26492,26501,26509,26515,26517,26518,26526,26536,26555,26570,26573,26605,26606,26607,26608,26621,26623,26624,26635,26636,26637,26638,26658,26669,26670,26676,26690,26691,26692,26694,26696,26702,26703,26769,26770,26771,26811); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (26812,26824,26830,26840,26841,26858,26871,26891,26946,26948,26966,26967,27018,27059,27105,27122,27152,27153,27165,27219,27220,27224,27225,27226,27229,27240,27241,27268,27270,27272,27282,27283,27284,27285,27286,27287,27288,27290,27335,27360,27362,27363,27370,27383,27400,27401,27410,27465,27505,27510,27513,27531,27533,27534,27551,27552,27556,27561,27604,27605,27611,27614,27616,27618,27620,27623,27624,27630,27631,27680,27683,27685,27686,27691,27693,27694,27696,27712,27729,27733,27734,27736,27737,27766,27767,27768,27769,27770,27771,27772,27773,27774,27775,27776,27777,27778,27779,27780,27781,27782,27797,27799,27800,27807,27808,27809,27821,27822,27823,27824,27825,27829,27835,27836,27848,27854,27871,27874,27941,27957,28005,28006,28007,28014,28018,28022,28023,28026,28101,28103,28104,28108,28144,28158,28159,28162,28170,28194,28199,28200,28201,28207,28208,28211,28212,28218,28220,28232,28242,28243,28246,28249,28255,28257,28258,28268,28270,28278,28349,28350,28356,28357,28364,28365,28369,28377,28383,28395,28405,28406,28412,28414,28419,28420,28421,28422,28423,28424,28425,28426,28427,28428,28429,28430,28431,28432,28433,28434,28435,28436,28437,28438,28444,28445,28446,28447,28448,28449,28450,28471); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (28472,28474,28475,28476,28487,28488,28498,28499,28500,28503,28506,28510,28512,28519,28528,28534,28564,28565,28570,28589,28590,28599,28603,28615,28628,28641,28642,28647,28651,28653,28654,28657,28658,28659,28666,28669,28670,28711,28732,28733,28734,28735,28736,28745,28747,28748,28749,28750,28752,28754,28756,28759,28760,28768,28769,28782,28788,28793,28802,28803,28805,28843,28864,28866,28867,28868,28869,28870,28871,28872,28875,28878,28879,28889,28890,28897,28901,28903,28905,28906,28907,28908,28909,28910,28911,28913,28914,28915,28919,28924,28925,28930,28931,28933,28934,28937,28943,28948,28998,29030,29031,29047,29051,29053,29062,29063,29064,29096,29097,29098,29101,29106,29107,29108,29109,29110,29111,29112,29113,29115,29117,29118,29119,29123,29128,29129,29133,29136,29153,29172,29173,29183,29185,29186,29187,29188,29189,29190,29191,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29204,29205,29206,29207,29208,29209,29210,29212,29213,29214,29216,29217,29219,29221,29227,29230,29231,29232,29238,29239,29245,29246,29256,29263,29280,29286,29335,29340,29347,29349,29353,29354,29355,29356,29359,29362,29363,29371,29388,29394,29400,29414,29449,29450,29451,29453,29468,29480,29488,29501,29517); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (29519,29520,29522,29565,29566,29567,29570,29574,29576,29587,29609,29629,29632,29633,29634,29635,29646,29648,29654,29656,29697,29699,29704,29719,29720,29722,29738,29769,29770,29821,29823,29824,29825,29828,29831,29833,29835,29837,29840,29842,29851,29852,29856,29858,29859,29860,29863,29872,29882,29887,29890,29891,29892,29893,29894,29895,29897,29898,29899,29900,29901,29934,29935,29943,29974,29985,29986,29987,29988,29989,29990,30015,30016,30018,30027,30028,30029,30030,30031,30032,30033,30034,30047,30048,30049,30071,30074,30075,30083,30085,30087,30097,30135,30144,30150,30202,30203,30204,30205,30216,30230,30232,30235,30250,30264,30276,30277,30278,30283,30284,30285,30286,30287,30288,30303,30306,30307,30309,30310,30311,30333,30335,30338,30378,30389,30403,30404,30406,30409,30424,30432,30443,30471,30482,30486,30501,30523,30541,30542,30543,30544,30545,30546,30575,30593,30594,30597,30609,30629,30670,30687,30689,30696,30697,30698,30701,30728,30746,30778,30791,30806,30816,30817,30818,30819,30821,30822,30823,30836,30843,30851,30863,30864,30865,30894,30895,30920,30921,30922,30944,30945,30946,30947,30949,30951,30952,30953,30954,30955,30956,30957,30958,30960,30984,30985,30986,30987,30988,30989,30992); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (30993,31012,31014,31015,31029,31037,31039,31040,31042,31043,31048,31078,31082,31083,31084,31087,31088,31089,31090,31094,31095,31096,31097,31098,31099,31100,31104,31107,31110,31123,31135,31137,31139,31140,31141,31142,31147,31150,31152,31154,31155,31157,31159,31160,31161,31163,31178,31179,31183,31184,31187,31188,31191,31192,31193,31194,31195,31196,31198,31199,31200,31205,31207,31208,31220,31221,31222,31223,31224,31225,31226,31227,31231,31237,31251,31255,31263,31266,31268,31271,31274,31278,31283,31301,31318,31320,31321,31322,31323,31324,31325,31326,31327,31341,31342,31346,31347,31351,31352,31354,31355,31357,31361,31363,31396,31398,31411,31413,31438,31442,31443,31447,31449,31450,31451,31457,31460,31466,31468,31530,31531,31532,31554,31555,31556,31565,31583,31585,31586,31587,31588,31589,31590,31591,31593,31594,31595,31596,31597,31598,31599,31600,31601,31602,31603,31604,31605,31606,31607,31608,31609,31613,31614,31635,31647,31671,31681,31692,31702,31718,31721,31754,31775,31779,31780,31783,31787,31812,31813,31815,31843,31844,31847,31853,31900,32031,32037,32149,32160,32161,32163,32164,32175,32176,32178,32179,32181,32182,32183,32184,32188,32255,32257,32267,32272,32278,32280,32283,32284,32288); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (32292,32299,32300,32309,32323,32326,32350,32390,32391,32404,32408,32410,32423,32446,32467,32479,32480,32482,32483,32490,32492,32497,32499,32502,32503,32505,32507,32508,32511,32556,32593,32609,32767,32769,32770,32771,32772,32786,32787,33016,33017,33429,33438,33441,33499,33513,33519,33550,33663,33664,33667,33668,33687,33704,33798,34127,34154,34238,34801,35169,35255,35444,35461,35474,35493,35547,35557,35559,35560,35652,35653,35654,35763,36065,36066,36128,36173,36478,36503,36504,36522,36564,36595,36666,36677,36724,36725,36726,36792,36794,36796,36807,36824,36829,36830,36840,36841,36842,36844,36875,36877,36879,36880,36881,36886,36893,36896,36907,36916,36940,36941,37007,37011,37012,37022,37023,37038,37069,37120,37128,37228,37229,37232,37351,37357,37491,37493,37494,37495,37501,37502,37530,37532,37535,37538,37539,37541,37542,37544,37545,37546,37549,37550,37551,37563,37565,37569,37571,37586,37595,37612,37622,37635,37636,37637,37638,37639,37640,37642,37644,37655,37656,37657,37658,37662,37663,37664,37665,37666,37678,37711,37728,37729,37730,37731,37755,37845,37846,37857,37881,37893,37906,37945,37976,38004,38031,38057,38058,38059,38061,38062,38063,38073,38074,38077,38098,38099,38100,38101,38102); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2035 WHERE `entry` IN (38104,38105,38108,38134,38137,38151,38172,38173,38175,38176,38177,38184,38185,38186,38193,38197,38198,38216,38222,38248,38249,38260,38308,38309,38349,38350,38351,38352,38362,38367,38386,38388,38391,38392,38410,38419,38429,38430,38445,38446,38479,38480,38481,38487,38524,38525,38544,38545,38563,38564,38567,38568,38572,38610,38763,38857,38883,39048,39231,39232,39233,39234,39296,39639,40274); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2036 WHERE `entry` IN (25597,37695,39309,39310,39311); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2037 WHERE `entry` IN (8836); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2038 WHERE `entry` IN (7915,16111); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2039 WHERE `entry` IN (14682); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2040 WHERE `entry` IN (18259,18648); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2041 WHERE `entry` IN (20682); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2042 WHERE `entry` IN (20783,20784,20785,20786,20788,20789,20790); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2043 WHERE `entry` IN (14684,14690); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2044 WHERE `entry` IN (14693); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2045 WHERE `entry` IN (17725,17871,20188,20190); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2046 WHERE `entry` IN (22848); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2047 WHERE `entry` IN (11447,11497,11498); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2048 WHERE `entry` IN (1157,1158,1160,1534,1655,1800,1801,1802,1804,1946,1983,2044,2176,2177,2178,2638,3667,3801,4472,6116,6117,6133,7073,7074,7370,7523,7524,7864,8538,8539,8540,8541,8542,10387,10388,10389,10940,10947,10948,11064,11078,11141,11285,11288,11289,11296,11560,11620,11621,11686,11687,11873,12178,12179,12199,12377,12378,14564,16423); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2049 WHERE `entry` IN (1531,2184,3863,3872,3873,3875,3877,6118,10358,10384,10385,10938,10996,16143,16379); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2050 WHERE `entry` IN (18856); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2051 WHERE `entry` IN (6021,9299,10988); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2052 WHERE `entry` IN (15554); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2053 WHERE `entry` IN (22338); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2054 WHERE `entry` IN (23269); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2055 WHERE `entry` IN (19354); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2056 WHERE `entry` IN (17724,20185,20465,21943); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2057 WHERE `entry` IN (48,202,203,531,623,785,787,1110,1520,1522,1523,1657,1658,1783,1784,1785,1787,1788,1789,1865,1869,1870,1871,1890,1916,1973,2454,6388,6412,7343,7344,7346,7786,8477,8523,8524,8525,8526,8527,8528,8529,8884,10390,10391,10482,10816,10952,11076,11077,11156,11197,11200,11258,11476,11547,12208,12341,12342,12343,12344,14486,14489,14558,16299,16422,16438); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2058 WHERE `entry` IN (771,2283,2946,7341,7342,7345,10393,10394,10478,10486,10487,10488,10489,10491,10826,16380); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2059 WHERE `entry` IN (11561); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2060 WHERE `entry` IN (22953); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2061 WHERE `entry` IN (5711); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2062 WHERE `entry` IN (23401); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2063 WHERE `entry` IN (28443); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2064 WHERE `entry` IN (19307,20265); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2065 WHERE `entry` IN (17958); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2066 WHERE `entry` IN (12557); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2067 WHERE `entry` IN (22853); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2068 WHERE `entry` IN (22869,23339); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2069 WHERE `entry` IN (20875,21604); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2070 WHERE `entry` IN (21102); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2071 WHERE `entry` IN (21181,21251); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2072 WHERE `entry` IN (16504); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2073 WHERE `entry` IN (20866,20898,21598,21614,22954); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2074 WHERE `entry` IN (16414,16473); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2075 WHERE `entry` IN (35644,36558); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2076 WHERE `entry` IN (33060,33062,33067,33109,33167,34045); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2077 WHERE `entry` IN (19797); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2078 WHERE `entry` IN (17454); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2079 WHERE `entry` IN (17940,19891,21127); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2080 WHERE `entry` IN (21843); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2081 WHERE `entry` IN (21270,21274); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2082 WHERE `entry` IN (20873,21605); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2083 WHERE `entry` IN (20175); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2084 WHERE `entry` IN (17726,17735,20191,20193); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2085 WHERE `entry` IN (17727,20192); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2086 WHERE `entry` IN (20908,20909,21616,21617); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2087 WHERE `entry` IN (22849); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2088 WHERE `entry` IN (14349); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2089 WHERE `entry` IN (644); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2090 WHERE `entry` IN (15334,15725,15726,15728,15802); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2091 WHERE `entry` IN (18497,20299); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2092 WHERE `entry` IN (21694,21914,24068,25372,31655); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2093 WHERE `entry` IN (18796,20652); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2094 WHERE `entry` IN (17907); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2095 WHERE `entry` IN (20043,20044); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2096 WHERE `entry` IN (21268,21269,21271,21272,21273); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2097 WHERE `entry` IN (14987); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2098 WHERE `entry` IN (15630,15984); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2099 WHERE `entry` IN (25334,26523,27881,27894,28094,28312,28319,28366,28781,32627,32629,32795,32796,34775,34776,34777,34778,34793,34802,34922,34924,34929,34935,34944,35069,35273,35403,35405,35410,35413,35415,35417,35419,35421,35427,35429,35431,35433,35436,35819,36355,36356,36357,36358); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2100 WHERE `entry` IN (20992); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2101 WHERE `entry` IN (16507); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2102 WHERE `entry` IN (20593); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2103 WHERE `entry` IN (16857); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2104 WHERE `entry` IN (18554); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2105 WHERE `entry` IN (23586); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2106 WHERE `entry` IN (19886); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2107 WHERE `entry` IN (34657,34701,34705); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2108 WHERE `entry` IN (22845,23374); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2109 WHERE `entry` IN (12339); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2110 WHERE `entry` IN (10814); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2111 WHERE `entry` IN (23402); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2112 WHERE `entry` IN (23403); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2113 WHERE `entry` IN (23400); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2114 WHERE `entry` IN (6584); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2115 WHERE `entry` IN (17827,20165); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2116 WHERE `entry` IN (22930); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2117 WHERE `entry` IN (17671,20584,20993); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2118 WHERE `entry` IN (23196); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2119 WHERE `entry` IN (22880,22956,22964,23222,23239,23337,23399); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2120 WHERE `entry` IN (22199); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2121 WHERE `entry` IN (18680,22877,22884); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2122 WHERE `entry` IN (22960); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2123 WHERE `entry` IN (22844,22847,22873,22878,22963,23028,23030); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2124 WHERE `entry` IN (34605,34650,35658,35659); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2125 WHERE `entry` IN (36561,36571,40681,40682,40683,40684); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2126 WHERE `entry` IN (40417,40418,40419,40420,40421,40422,40423,40424); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2127 WHERE `entry` IN (37890,37949,38393,38394,38625,38626,38628,38629); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2128 WHERE `entry` IN (18692); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2129 WHERE `entry` IN (38009,38010,38135,38136,38395,38396,38397,38398,38630,38631,38632,38633,38634,38635,39000,39001); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2130 WHERE `entry` IN (38508,38596,38597,38598); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2131 WHERE `entry` IN (38472,38485); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2132 WHERE `entry` IN (34607,34648,35655,35656); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2133 WHERE `entry` IN (37863,38171,38727,38737); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2134 WHERE `entry` IN (36791,37868,37886,37934,38166,38167,38169,38170,38721,38722,38723,38724,38725,38733,38734,38735); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2135 WHERE `entry` IN (20032); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2136 WHERE `entry` IN (20040); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2137 WHERE `entry` IN (20035); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2138 WHERE `entry` IN (20050); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2139 WHERE `entry` IN (22846); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2140 WHERE `entry` IN (20216,20600,22910); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2141 WHERE `entry` IN (16545,22911); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2142 WHERE `entry` IN (23330); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2143 WHERE `entry` IN (36609,37799,39120,39121,39122,39284,39285,39286); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2144 WHERE `entry` IN (33346,33886); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2145 WHERE `entry` IN (22075); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2146 WHERE `entry` IN (20692); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2147 WHERE `entry` IN (18836); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2148 WHERE `entry` IN (11347); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2149 WHERE `entry` IN (11348); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2150 WHERE `entry` IN (17256); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2151 WHERE `entry` IN (22074); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2152 WHERE `entry` IN (14101); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2153 WHERE `entry` IN (639,642,643,645,646,647,1763,3586,3886,3887,3927,4274,4275,4278,4279,4420,4421,4422,4424,4428,4829,4830,4831,4832,4842,6168,6228,6229,6235,6243,6488,6489,6490,7361,7800,10439,11356,12203,12225,12236,12258,12902,13021,13596); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2154 WHERE `entry` IN (24683,24684,24686,24687,24688,24689,24690,24762,25565,25567,25568,25570,25572,25575,25576,25577); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2155 WHERE `entry` IN (24685,25569); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2156 WHERE `entry` IN (12803); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2157 WHERE `entry` IN (19389,21350); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2158 WHERE `entry` IN (18829); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2159 WHERE `entry` IN (8923,9217,9218,9219,9718,10263,10809,11467); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2160 WHERE `entry` IN (30385,31474); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2161 WHERE `entry` IN (17723,20164); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2162 WHERE `entry` IN (11663); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2163 WHERE `entry` IN (21346,21612); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2164 WHERE `entry` IN (23061,23261,23281,23282); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2165 WHERE `entry` IN (18731,20636); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2166 WHERE `entry` IN (22076); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2167 WHERE `entry` IN (17990,20189); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2168 WHERE `entry` IN (17796,20630); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2169 WHERE `entry` IN (8929,9025,9502,10076,14321,14323,14326); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2170 WHERE `entry` IN (12422); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2171 WHERE `entry` IN (10376,10596); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2172 WHERE `entry` IN (8983,9018,9019,9024,9027,9028,9029,9030,9031,9032,9033,9034,9035,9036,9037,9038,9039,9040,9041,9042,9056,9156,9196,9236,9237,9319,9537,9543,9568,9736,9938,10220,10268,10339,10363,10429,10430,10503,10509,10584,10812,11486,11487,11488,11489,11490,11492,11496,11501,12459,12461,12463,12464,14322,14324,14325,14327,14354,14502,14506,15229,15230,15233,15235,15236,15240,15246,15249,15250,15252,15262,15264,15277,15311,15312,16042,16080,16097); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2173 WHERE `entry` IN (11662,12460,12467); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2174 WHERE `entry` IN (17862,20521); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2175 WHERE `entry` IN (24238); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2176 WHERE `entry` IN (1840); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2177 WHERE `entry` IN (20901,20902,21610,21611); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2178 WHERE `entry` IN (21218,21221,21298,21299,21301,21339,21508); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2179 WHERE `entry` IN (20885,20886,21466,21467,21590,21599,21600,21624); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2180 WHERE `entry` IN (17826,17882,18105,20168,20183,20184); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2181 WHERE `entry` IN (33836,34218); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2182 WHERE `entry` IN (30245,30249,31750,31751); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2183 WHERE `entry` IN (33202,33398); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2184 WHERE `entry` IN (15516,32857,33694); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2185 WHERE `entry` IN (36272,36296,36565); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2186 WHERE `entry` IN (32916,33400); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2187 WHERE `entry` IN (12899); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2188 WHERE `entry` IN (16472); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2189 WHERE `entry` IN (929,1284,1748,2425,3974,3975,3976,3977,4542,4543,6487,7228,9095,10156,10162,10432,10433,10435,10436,10437,10438,10502,10504,10505,10506,10507,10508,10558,10808,10811,10813,10901,10942,10997,11032,11142,11261,11380,11439,11622,11946,11947,11948,11949,11978,11982,12129,12756,12804,13256,13419,14352,14507,14509,14510,14515,14517,14831,14834,14861,14988,15082,15083,15084,15085,15114,15192,15204,15205,15299,15302,15348,15362,15369,15370,15378,15379,15380,15381,15382,15410,15411,15412,15413,15427,15481,15491,15509,15510,15511,15514,15543,15544,15571,15608,15625,15628,15633,15667,15688,15776,15963,16077,16101,16102,16118,16128,16440,16524,16775,16776,16777,16778,16816,17008,17009,17010,17011,17012,17026,17076,17233,17354,17380,17381,17500,17521,17533,17534,17651,17772,17797,17798,17852,17874,17941,17942,17948,17949,17991,18040,18041,18060,18063,18075,18076,18141,18254,18338,18343,18344,18371,18373,18472,18601,18621,18624,18667,18728,18780,18932,19214,19556,19597,19604,19647,19893,19894,19895,20266,20268,20306,20318,20481,20629,20633,20637,20690,21003,21538,21768,21823,21845,21932,22409,22522,22605,22606,22627,22629,22641,22644,22996,23035,23054,23230,23426,23467,23499,23508); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2189 WHERE `entry` IN (23578,23759,23812,23863,23877,23878,23879,23880,23899,23912,23913,23914,23944,24143,24144,24239,24505,31055,31818,31819,31820,31821,31822,32918,32919,33399,33401,37243,37244,37283,37444); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2190 WHERE `entry` IN (15552); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2191 WHERE `entry` IN (11382,12017,15263,23576,23577); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2192 WHERE `entry` IN (37226); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2193 WHERE `entry` IN (2784,3057,3516,4949,4968,7937,7999,10181,10540,20888,21224,21227,22825,22826,22827,22828,22920); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2194 WHERE `entry` IN (16028,16800,16802,17468,19622,20906,21606,21964,22951,23419,23953,23954,23980,24200,24201,26529,26530,26532,26533,27389,27390,28684,29249,29268,29278,29305,29311,29315,29324,29373,29417,29446,29447,29448,29611,29615,29701,29718,29940,29955,29991,30057,30061,30452,30530,30549,30600,30601,30602,30603,30748,31125,31211,31212,31215,31217,31464,31507,31534,31612,31656,31657,31673,31674,31679,31680,31722,32273,32295,32313,32353,32368,32865,33147,33271,33350,33388,33449,33453,33515,33524,33846,33850,33851,33852,33985,33986,33988,33989,33993,33994,34015,34016,34057,34115,34152,34175,34496,34497,34780,35013,35143,35216,35268,35269,35347,35348,35349,35350,35351,35352,35359,35360,36064,36067,36476,36477,36494,36502,36612,36658,36723,36855,36938,37613,37627,37629,37677,37720,37813,37957,37958,37959,38106,38112,38113,38296,38297,38402,38433,38462,38482,38483,38582,38583,38599,38603); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2195 WHERE `entry` IN (15193); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2196 WHERE `entry` IN (20905,20910,20911,21588,21589,21618,21838); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2197 WHERE `entry` IN (24560,25560); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2198 WHERE `entry` IN (20896,21702); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2199 WHERE `entry` IN (11980,16807,16808,16809,17711,17734,18481,20039,20045,20046,20049,20187,20568,20596,20597,20864,20867,20868,20879,20880,21174,21303,21304,21591,21592,21593,21594,21595,21596,21608,21623,22330,22346,23397,24723,24744,25562,25573); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2200 WHERE `entry` IN (20036,20037,24664,24857); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2201 WHERE `entry` IN (25772); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2202 WHERE `entry` IN (4814); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2203 WHERE `entry` IN (329,2359,7031,7032,8324,14331); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2204 WHERE `entry` IN (16193); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2205 WHERE `entry` IN (11665,11672,12076,12099,12100,12101); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2206 WHERE `entry` IN (14401); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2207 WHERE `entry` IN (16336,16338); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2208 WHERE `entry` IN (15141); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2209 WHERE `entry` IN (11669); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2210 WHERE `entry` IN (11658,11659,11661,11671,11673,11988,12057,12098,12118,12259,12264,14081,14465,14466,14751,14752); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2211 WHERE `entry` IN (24722,25552); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2212 WHERE `entry` IN (14464); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2213 WHERE `entry` IN (10264); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2214 WHERE `entry` IN (15748,15751,15754,15758,15807,15810); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2215 WHERE `entry` IN (11664,15747,15753,15757,15812); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2216 WHERE `entry` IN (10516); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2217 WHERE `entry` IN (12465); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2218 WHERE `entry` IN (9499,12468,15247); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2219 WHERE `entry` IN (1842,5709,5710,5719,5720,5721,5722,7267,7271,7272,7275,7604,7795,7796,7797,8127,8443,11878,12018,15743,15744,15750,15806,15813,15814,15815,15816,15817,15818); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2220 WHERE `entry` IN (10184); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2221 WHERE `entry` IN (15286,15288,15290); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2222 WHERE `entry` IN (14662,14663,14664,14666); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2223 WHERE `entry` IN (14503); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2224 WHERE `entry` IN (22855); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2225 WHERE `entry` IN (17770,20169); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2226 WHERE `entry` IN (15689); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2227 WHERE `entry` IN (30084); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2228 WHERE `entry` IN (32187); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2229 WHERE `entry` IN (22056); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2230 WHERE `entry` IN (25588); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2231 WHERE `entry` IN (37698,39299,39300,39301); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2232 WHERE `entry` IN (22952,34815,35262); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2233 WHERE `entry` IN (21362); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2234 WHERE `entry` IN (33203,33376); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2235 WHERE `entry` IN (34147,34148); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2236 WHERE `entry` IN (17767,17808,17842,17888); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2237 WHERE `entry` IN (1853,12397,12435,15517,15589,15690,15727,17225,17535,17968,18341,18473,18708,18732,20267,20653,20657,20706,23574); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2238 WHERE `entry` IN (10340,10538,14020,15206,15207,15208,15220,15467,15740,15741,15742,18168,33052,33116); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2239 WHERE `entry` IN (25741); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2240 WHERE `entry` IN (15550,16152,18832,18834,18835,23682,23709,29306,29310,31368,31465,34796,35438); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2241 WHERE `entry` IN (27386,30178,30756,31448); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2242 WHERE `entry` IN (32877,33155); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2243 WHERE `entry` IN (10404,15687,15691,16151,16457,17096,17257,17281,17306,17308,17455,17536,17537,17848,17879,17880,17881,17975,17976,17977,17978,17980,18051,18055,18069,18096,18398,18399,18400,18401,18402,18432,18433,18434,18436,18805,18831,18945,19044,19218,19219,19220,19221,19516,19710,19781,19782,19783,20060,20062,20063,20064,20243,20531,20535,20737,20738,20745,21212,21213,21214,21215,21216,21217,21364,21369,21525,21526,21533,21536,21537,21551,21558,21559,21581,21582,21697,21698,21712,21875,21920,21958,21965,21966,22055,22119,22120,22140,22167,22841,22856,22871,22887,22898,22917,22947,22948,22949,22950,22997,23089,23191,23197,23375,23418,23420,23421,23469,23965,24850,24882,24891,24892,25038,25165,25166,25315,25502,25708,25840,26630,26631,26632,26668,26683,26684,26685,26686,26687,26693,26723,26731,26763,26794,26796,26798,26861,26893,26918,27447,27483,27654,27655,27656,27975,27977,27978,28132,28171,28546,28586,28587,28729,28730,28731,28859,28860,28921,28922,28923,29120,29266,29281,29304,29307,29308,29309,29312,29313,29314,29316,29573,29932,30114,30176,30397,30398,30449,30451,30510,30522,30529,30532,30540,30770,30772,30774,30775,30788,30790,30803,30807,30809,30810,31134,31311,31349,31350,31360,31362); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2243 WHERE `entry` IN (31365,31367,31370,31381,31384,31386,31441,31456,31469,31473,31506,31508,31509,31510,31511,31512,31520,31533,31535,31536,31537,31538,31558,31559,31560,31561,31592,31610,31611,31615,31616,31617,31672,31734,32845,32846,32867,32871,32872,32873,32874,32875,32876,32878,32882,32883,32885,32886,32904,32906,32907,32908,32913,32914,32915,32927,32930,32933,32934,32955,33070,33089,33113,33118,33121,33148,33149,33150,33151,33152,33153,33154,33156,33157,33158,33159,33161,33162,33163,33186,33190,33191,33228,33288,33293,33329,33344,33360,33385,33391,33392,33393,33432,33433,33567,33651,33670,33692,33693,33699,33700,33715,33716,33717,33718,33719,33720,33722,33723,33724,33756,33772,33773,33885,33888,33890,33909,33910,33911,33943,33954,33955,33959,33966,33967,33983,33984,33990,33995,34003,34004,34014,34035,34069,34071,34097,34106,34108,34109,34133,34134,34139,34141,34164,34165,34166,34171,34185,34197,34215,34221,34222,34226,34564,34566,34660,34797,34799,34800,34813,34825,34826,34928,34942,35028,35029,35030,35031,35032,35033,35034,35036,35037,35038,35039,35040,35041,35042,35043,35044,35045,35046,35047,35048,35049,35050,35051,35052,35119,35144,35263,35264,35265,35266,35267,35270,35271,35272,35278,35279); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2243 WHERE `entry` IN (35280,35311,35439,35440,35441,35442,35443,35447,35448,35449,35451,35490,35511,35512,35513,35514,35515,35516,35517,35518,35519,35520,35521,35522,35523,35524,35525,35527,35528,35529,35530,35531,35532,35533,35534,35535,35536,35537,35538,35539,35540,35541,35542,35543,35544,35615,35616,36135,36497,36498,36538,36597,36626,36627,36633,36678,36701,36789,36838,36839,36853,36897,36899,36939,36948,36950,36954,36957,36960,36961,36968,36969,36970,36971,36978,36982,37025,37098,37116,37117,37126,37182,37184,37215,37217,37227,37230,37488,37504,37505,37506,37531,37533,37534,37540,37562,37697,37833,37955,37970,37972,37973,38064,38103,38110,38123,38128,38129,38138,38139,38156,38157,38174,38219,38220,38256,38257,38258,38261,38262,38265,38266,38267,38369,38390,38399,38400,38401,38403,38404,38405,38406,38407,38408,38418,38431,38434,38435,38436,38444,38454,38490,38494,38549,38550,38585,38586,38602,38604,38637,38638,38639,38640,38675,38676,38677,38678,38679,38680,38681,38682,38683,38684,38685,38686,38687,38688,38689,38690,38691,38692,38693,38694,38699,38700,38701,38702,38758,38759,38760,38761,38769,38770,38771,38772,38775,38776,38777,38784,38785,39166,39167,39168,39190,39287,39288,39289,39302,39303,39304); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2243 WHERE `entry` IN (39305,39306,39307,39746,39747,39751,39805,39814,39815,39823,39863,39864,39899,39920,39922,39944,39945,40142,40143,40144,40145); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2244 WHERE `entry` IN (14862); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2245 WHERE `entry` IN (32926,32938,33352,33353,36980,38320,38321,38322); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2246 WHERE `entry` IN (15261); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2247 WHERE `entry` IN (20041,23394); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2248 WHERE `entry` IN (18503,20309); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2249 WHERE `entry` IN (36619,38233,38459,38460,38711,38712,38970,38971,38972,38973,38974,38975); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2250 WHERE `entry` IN (38028); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2251 WHERE `entry` IN (20870,20912,21601,21626); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2252 WHERE `entry` IN (17839,20744,21104,21140,21148,22170,22171,22172); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2253 WHERE `entry` IN (22128,22129); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2254 WHERE `entry` IN (23597); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2255 WHERE `entry` IN (24374); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2256 WHERE `entry` IN (2675,4277,8937); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2257 WHERE `entry` IN (16127); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2258 WHERE `entry` IN (16124); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2259 WHERE `entry` IN (16149); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2260 WHERE `entry` IN (16156,16157,16158); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2261 WHERE `entry` IN (11657,11677,12096,12097,13078,13079,13086,13088,13137,13138,13139,13143,13144,13145,13147,13152,13153,13154,13176,13179,13180,13181,13216,13218,13236,13257,13296,13297,13298,13299,13300,13318,13319,13320,13356,13357,13437,13438,13439,13441,13442,13447,13577,13597,13598,13616,13617,13797,13798); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2262 WHERE `entry` IN (16244,16360,16427,16983); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2263 WHERE `entry` IN (16150); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2264 WHERE `entry` IN (16022,16981); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2265 WHERE `entry` IN (16027,16164,16449); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2266 WHERE `entry` IN (16167); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2267 WHERE `entry` IN (16020); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2268 WHERE `entry` IN (16024); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2269 WHERE `entry` IN (16375); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2270 WHERE `entry` IN (16290); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2271 WHERE `entry` IN (16447); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2272 WHERE `entry` IN (16982,16984); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2273 WHERE `entry` IN (16036,16056,16057,16236,16297,16698,17055); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2274 WHERE `entry` IN (16037); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2275 WHERE `entry` IN (15318,15319,15320,15323,15324,15325,15336); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2276 WHERE `entry` IN (16034); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2277 WHERE `entry` IN (16428,16429); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2278 WHERE `entry` IN (16021,16165,16368,16448,16452); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2279 WHERE `entry` IN (16194,16215,16216); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2280 WHERE `entry` IN (16017,16018,16029); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2281 WHERE `entry` IN (11352,12159,14750,14762,14763,14764,14765,14766,14767,14768,14769,14770,14771,14772,14773,14774,14775,14776,14777,14943,14944,14945,14946,14947,14948,15327,15335,15338,15339,15340,15341,15343,15355,15385,15386,15388,15389,15390,15391,15392,15555,16062); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2282 WHERE `entry` IN (15928,15929,15930,15931,15932,15936,15952,15953,15954,15956,15989,15990,16011,16060,16061,16063,16064,16065); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2283 WHERE `entry` IN (16441); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2284 WHERE `entry` IN (16126); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2285 WHERE `entry` IN (15974,15975,16505,29274); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2286 WHERE `entry` IN (15980,15981,16506); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2287 WHERE `entry` IN (16573); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2288 WHERE `entry` IN (15976,15978,15979,16142,16453); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2289 WHERE `entry` IN (16243); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2290 WHERE `entry` IN (16387); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2291 WHERE `entry` IN (10440); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2292 WHERE `entry` IN (12119,15620,16168,16446); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2293 WHERE `entry` IN (13116,13117,14302,15047,15224,15922); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2294 WHERE `entry` IN (22250); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2295 WHERE `entry` IN (24175); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2296 WHERE `entry` IN (12900); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2297 WHERE `entry` IN (32357,32358,32471,32495,32501); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2298 WHERE `entry` IN (32386,32398,32400,32438); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2299 WHERE `entry` IN (11284); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2300 WHERE `entry` IN (15276); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2301 WHERE `entry` IN (2091,2108,2726,4339,4676,6073,7135,7136,7137,8608,8616,14668,15209,15438,17267,19203,19973,20703,21706,22323,25001,25417,28584,30416,30847,30983,31453); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2302 WHERE `entry` IN (12143); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2303 WHERE `entry` IN (2760,3417,4036,4037,4038,5893,5896,6521,7266,7738,9178,9878,9879); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2304 WHERE `entry` IN (2745,8281,8909,8910,8911,9026,9376); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2305 WHERE `entry` IN (14460); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2306 WHERE `entry` IN (2447); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2307 WHERE `entry` IN (9016,9017,9816); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2308 WHERE `entry` IN (11981,11983,14601); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2309 WHERE `entry` IN (7846,8680,15203); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2310 WHERE `entry` IN (575,5850,5852,6520,17003); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2311 WHERE `entry` IN (11502,11667,11668,12056,12265); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2312 WHERE `entry` IN (14461); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2313 WHERE `entry` IN (19551); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2314 WHERE `entry` IN (11583,13020); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2315 WHERE `entry` IN (19514); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2316 WHERE `entry` IN (11666); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2317 WHERE `entry` IN (832,2258,2592,2736,2752,2762,4034,4035,4499,4526,5898,5902,6239,8667,9377,9397,11576,11577,11578,11744,11745,11777,11778,11779,11783,11784,14399,14400,14478,15352,16043,17085,17154,17156,17157,17158,17159,17160,18062,18181,21707,22036,23029,26407,28411,28547,28585,28825,30418,30965,30970,30979,31452); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2318 WHERE `entry` IN (14455); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2319 WHERE `entry` IN (5855); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2320 WHERE `entry` IN (12237); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2321 WHERE `entry` IN (14435); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2322 WHERE `entry` IN (92); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2323 WHERE `entry` IN (2735,2791); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2324 WHERE `entry` IN (11321); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2325 WHERE `entry` IN (30258,31463); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2326 WHERE `entry` IN (14454); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2327 WHERE `entry` IN (12201); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2328 WHERE `entry` IN (14887,14888,14889,14890); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2329 WHERE `entry` IN (2761,2776,2794,3851,3861,3950,4978,5462,5894,5895,5897,6047,6220,6748,7132,7428,7429,8519,8520,8521,8522,8837,9453,10198,10642,11256,11862,12759,13278,13279,13322,13456,13696,13736,14269,14350,15211,16570,17153,17155,17167,17207,17358,19204,20704,21428,21728,22035,22309,24228,24601,25226,25514,25715,25755,25756,25757,26116,26178,26204,26214,26215,26216,26316,26340,26341,26342,30419,30846,31454,37994); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2330 WHERE `entry` IN (14458); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2331 WHERE `entry` IN (13282); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2332 WHERE `entry` IN (12876); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2333 WHERE `entry` IN (3917); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2334 WHERE `entry` IN (7079); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2335 WHERE `entry` IN (13280); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2336 WHERE `entry` IN (15305); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2337 WHERE `entry` IN (510,691,5461,10756,10757,10955); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2338 WHERE `entry` IN (14457); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2339 WHERE `entry` IN (22009); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2340 WHERE `entry` IN (25740,25865,26338,26339); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2341 WHERE `entry` IN (703,1043,1364,19206,20705); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2342 WHERE `entry` IN (6492,6550,11480,11483,11484,14397,15527,16530,16854,18864,18865,18866,18867,19205,20516,20702,22310,26370,30848); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2343 WHERE `entry` IN (10202); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2344 WHERE `entry` IN (10662); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2345 WHERE `entry` IN (10663); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2346 WHERE `entry` IN (10664); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2347 WHERE `entry` IN (6109); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2348 WHERE `entry` IN (21032); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2349 WHERE `entry` IN (7734,7735); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2350 WHERE `entry` IN (10485); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2351 WHERE `entry` IN (15275); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2352 WHERE `entry` IN (16491); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2353 WHERE `entry` IN (12457); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2354 WHERE `entry` IN (28912); +UPDATE `creature_template` SET `CreatureImmunitiesId`=2355 WHERE `entry` IN (8317); diff --git a/data/sql/updates/db_world/2026_03_25_03.sql b/data/sql/updates/db_world/2026_03_25_03.sql new file mode 100644 index 000000000..4113897c0 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_25_03.sql @@ -0,0 +1,880 @@ +-- DB update 2026_03_25_02 -> 2026_03_25_03 +-- +-- ============================================================ +-- creature_immunities seed +-- ============================================================ +DELETE FROM `creature_immunities` WHERE `ID` < 0; +DELETE FROM `creature_immunities` WHERE `ID` >= 1972; +INSERT INTO `creature_immunities` + (`ID`,`SchoolMask`,`DispelTypeMask`,`MechanicsMask`, + `Effects`,`Auras`,`ImmuneAoE`,`ImmuneChain`,`Comment`) +VALUES +(-1,0,0,0,'114','11',0,0,'flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-2,0,0,0,'','',1,0,'flags=AVOID_AOE'), +(-3,0,0,0,'98,144','',0,0,'flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-4,4,0,0,'','',0,0,'school=0x4(FIRE)'), +(-5,8,0,0,'','',0,0,'school=0x8(NATURE)'), +(-6,16,0,0,'','',0,0,'school=0x10(FROST)'), +(-7,32,0,0,'','',0,0,'school=0x20(SHADOW)'), +(-8,64,0,0,'','',0,0,'school=0x40(ARCANE)'), +(-9,96,0,0,'','',0,0,'school=0x60(SHADOW|ARCANE)'), +(-10,116,0,0,'','',0,0,'school=0x74(FIRE|FROST|SHADOW|ARCANE)'), +(-11,126,0,0,'','',0,0,'school=0x7E(HOLY|FIRE|NATURE|FROST|SHADOW|ARCANE)'), +(-12,0,0,2,'','',0,0,'mech=0x1(CHARM)'), +(-13,0,0,2,'114','11',0,0,'mech=0x1(CHARM), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-14,0,0,4,'','',0,0,'mech=0x2(DISORIENTED)'), +(-15,0,0,16,'','',0,0,'mech=0x8(DISTRACT)'), +(-16,8,0,16,'','',0,0,'school=0x8(NATURE), mech=0x8(DISTRACT)'), +(-17,16,0,16,'','',0,0,'school=0x10(FROST), mech=0x8(DISTRACT)'), +(-18,0,0,18,'','',0,0,'mech=0x9(CHARM|DISTRACT)'), +(-19,0,0,18,'114','11',0,0,'mech=0x9(CHARM|DISTRACT), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-20,0,0,32,'','',0,0,'mech=0x10(FEAR)'), +(-21,4,0,32,'','',0,0,'school=0x4(FIRE), mech=0x10(FEAR)'), +(-22,64,0,32,'','',0,0,'school=0x40(ARCANE), mech=0x10(FEAR)'), +(-23,0,0,34,'','',0,0,'mech=0x11(CHARM|FEAR)'), +(-24,0,0,40,'','',0,0,'mech=0x14(DISARM|FEAR)'), +(-25,0,0,42,'114','11',0,0,'mech=0x15(CHARM|DISARM|FEAR), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-26,0,0,64,'','',0,0,'mech=0x20(GRIP)'), +(-27,0,0,64,'98,144','',0,0,'mech=0x20(GRIP), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-28,0,0,512,'','',0,0,'mech=0x100(SILENCE)'), +(-29,0,0,514,'','',0,0,'mech=0x101(CHARM|SILENCE)'), +(-30,0,0,514,'114','11',0,0,'mech=0x101(CHARM|SILENCE), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-31,0,0,1024,'','',0,0,'mech=0x200(SLEEP)'), +(-32,0,0,1056,'','',0,0,'mech=0x210(FEAR|SLEEP)'), +(-33,0,0,1058,'','',0,0,'mech=0x211(CHARM|FEAR|SLEEP)'), +(-34,0,0,2048,'','',0,0,'mech=0x400(SNARE)'), +(-35,0,0,2050,'','',0,0,'mech=0x401(CHARM|SNARE)'), +(-36,0,0,2052,'','',0,0,'mech=0x402(DISORIENTED|SNARE)'), +(-37,0,0,2080,'','',0,0,'mech=0x410(FEAR|SNARE)'), +(-38,0,0,2082,'','',0,0,'mech=0x411(CHARM|FEAR|SNARE)'), +(-39,0,0,2176,'','',0,0,'mech=0x440(ROOT|SNARE)'), +(-40,0,0,2178,'','',0,0,'mech=0x441(CHARM|ROOT|SNARE)'), +(-41,0,0,2192,'','',0,0,'mech=0x448(DISTRACT|ROOT|SNARE)'), +(-42,0,0,2208,'','',0,0,'mech=0x450(FEAR|ROOT|SNARE)'), +(-43,0,0,2210,'','',0,0,'mech=0x451(CHARM|FEAR|ROOT|SNARE)'), +(-44,0,0,2562,'','',0,0,'mech=0x501(CHARM|SILENCE|SNARE)'), +(-45,0,0,3074,'','',0,0,'mech=0x601(CHARM|SLEEP|SNARE)'), +(-46,0,0,4096,'','',0,0,'mech=0x800(STUN)'), +(-47,0,0,4098,'','',0,0,'mech=0x801(CHARM|STUN)'), +(-48,0,0,4114,'','',0,0,'mech=0x809(CHARM|DISTRACT|STUN)'), +(-49,0,0,4128,'114','11',0,0,'mech=0x810(FEAR|STUN), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-50,0,0,4608,'98,144','',0,0,'mech=0x900(SILENCE|STUN), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-51,0,0,4624,'','',0,0,'mech=0x908(DISTRACT|SILENCE|STUN)'), +(-52,0,0,6178,'114','11',0,0,'mech=0xC11(CHARM|FEAR|SNARE|STUN), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-53,64,0,6688,'','',0,0,'school=0x40(ARCANE), mech=0xD10(FEAR|SILENCE|SNARE|STUN)'), +(-54,0,0,6810,'','',0,0,'mech=0xD4D(CHARM|DISARM|DISTRACT|ROOT|SILENCE|SNARE|STUN)'), +(-55,0,0,8192,'','',0,0,'mech=0x1000(FREEZE)'), +(-56,0,0,8224,'','',0,0,'mech=0x1010(FEAR|FREEZE)'), +(-57,64,0,10368,'','',0,0,'school=0x40(ARCANE), mech=0x1440(ROOT|SNARE|FREEZE)'), +(-58,0,0,12584,'','',0,0,'mech=0x1894(DISARM|FEAR|SLOW_ATTACK|STUN|FREEZE)'), +(-59,0,0,14374,'','',0,0,'mech=0x1C13(CHARM|DISORIENTED|FEAR|SNARE|STUN|FREEZE)'), +(-60,0,0,14982,'','',0,0,'mech=0x1D43(CHARM|DISORIENTED|ROOT|SILENCE|SNARE|STUN|FREEZE)'), +(-61,0,0,16384,'','',0,0,'mech=0x2000(KNOCKOUT)'), +(-62,4,0,17416,'','',0,0,'school=0x4(FIRE), mech=0x2204(DISARM|SLEEP|KNOCKOUT)'), +(-63,4,0,17420,'','',0,0,'school=0x4(FIRE), mech=0x2206(DISORIENTED|DISARM|SLEEP|KNOCKOUT)'), +(-64,4,0,17432,'','',0,0,'school=0x4(FIRE), mech=0x220C(DISARM|DISTRACT|SLEEP|KNOCKOUT)'), +(-65,0,0,20516,'','',0,0,'mech=0x2812(DISORIENTED|FEAR|STUN|KNOCKOUT)'), +(-66,16,0,20516,'','',0,0,'school=0x10(FROST), mech=0x2812(DISORIENTED|FEAR|STUN|KNOCKOUT)'), +(-67,0,0,21114,'','',0,0,'mech=0x293D(CHARM|DISARM|DISTRACT|FEAR|GRIP|SILENCE|STUN|KNOCKOUT)'), +(-68,0,0,32768,'','',0,0,'mech=0x4000(BLEED)'), +(-69,0,0,65278,'','',0,0,'mech=0x7F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED)'), +(-70,0,0,131072,'','',0,0,'mech=0x10000(POLYMORPH)'), +(-71,0,0,131074,'','',0,0,'mech=0x10001(CHARM|POLYMORPH)'), +(-72,0,0,131106,'','',0,0,'mech=0x10011(CHARM|FEAR|POLYMORPH)'), +(-73,0,0,132128,'','',0,0,'mech=0x10210(FEAR|SLEEP|POLYMORPH)'), +(-74,0,0,133122,'','',0,0,'mech=0x10401(CHARM|SNARE|POLYMORPH)'), +(-75,0,0,133248,'','',0,0,'mech=0x10440(ROOT|SNARE|POLYMORPH)'), +(-76,0,0,133250,'','',0,0,'mech=0x10441(CHARM|ROOT|SNARE|POLYMORPH)'), +(-77,0,0,139264,'','',0,0,'mech=0x11000(FREEZE|POLYMORPH)'), +(-78,0,0,139298,'','',0,0,'mech=0x11011(CHARM|FEAR|FREEZE|POLYMORPH)'), +(-79,0,0,141440,'','',0,0,'mech=0x11440(ROOT|SNARE|FREEZE|POLYMORPH)'), +(-80,0,0,143360,'','',0,0,'mech=0x11800(STUN|FREEZE|POLYMORPH)'), +(-81,0,0,143396,'','',0,0,'mech=0x11812(DISORIENTED|FEAR|STUN|FREEZE|POLYMORPH)'), +(-82,0,0,262144,'','',0,0,'mech=0x20000(BANISH)'), +(-83,0,0,262146,'','',0,0,'mech=0x20001(CHARM|BANISH)'), +(-84,0,0,262146,'114','11',0,0,'mech=0x20001(CHARM|BANISH), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-85,0,0,262172,'','',0,0,'mech=0x2000E(DISORIENTED|DISARM|DISTRACT|BANISH)'), +(-86,0,0,263222,'','',0,0,'mech=0x2021B(CHARM|DISORIENTED|DISTRACT|FEAR|SLEEP|BANISH)'), +(-87,0,0,265792,'98,144','',0,0,'mech=0x20720(GRIP|SILENCE|SLEEP|SNARE|BANISH), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-88,0,0,274592,'','',0,0,'mech=0x21850(FEAR|ROOT|STUN|FREEZE|BANISH)'), +(-89,0,0,278534,'','',0,0,'mech=0x22003(CHARM|DISORIENTED|KNOCKOUT|BANISH)'), +(-90,0,0,404998,'','',0,0,'mech=0x31703(CHARM|DISORIENTED|SILENCE|SLEEP|SNARE|FREEZE|POLYMORPH|BANISH)'), +(-91,0,0,407714,'','',0,0,'mech=0x31C51(CHARM|FEAR|ROOT|SNARE|STUN|FREEZE|POLYMORPH|BANISH)'), +(-92,0,0,16777216,'','',0,0,'mech=0x800000(HORROR)'), +(-93,0,0,16777248,'','',0,0,'mech=0x800010(FEAR|HORROR)'), +(-94,0,0,16777248,'114','11',0,0,'mech=0x800010(FEAR|HORROR), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-95,0,0,16777248,'','',1,0,'mech=0x800010(FEAR|HORROR), flags=AVOID_AOE'), +(-96,0,0,16777248,'98,144','',0,0,'mech=0x800010(FEAR|HORROR), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-97,1,0,16777248,'','',0,0,'school=0x1(NORMAL), mech=0x800010(FEAR|HORROR)'), +(-98,16,0,16777248,'','',0,0,'school=0x10(FROST), mech=0x800010(FEAR|HORROR)'), +(-99,127,0,16777248,'','',0,0,'school=0x7F(NORMAL|HOLY|FIRE|NATURE|FROST|SHADOW|ARCANE), mech=0x800010(FEAR|HORROR)'), +(-100,0,0,16777250,'','',0,0,'mech=0x800011(CHARM|FEAR|HORROR)'), +(-101,0,0,16777264,'','',0,0,'mech=0x800018(DISTRACT|FEAR|HORROR)'), +(-102,0,0,16777312,'','',0,0,'mech=0x800030(FEAR|GRIP|HORROR)'), +(-103,0,0,16777376,'','',0,0,'mech=0x800050(FEAR|ROOT|HORROR)'), +(-104,0,0,16779424,'','',0,0,'mech=0x800450(FEAR|ROOT|SNARE|HORROR)'), +(-105,0,0,16779428,'','',0,0,'mech=0x800452(DISORIENTED|FEAR|ROOT|SNARE|HORROR)'), +(-106,0,0,16779488,'','',0,0,'mech=0x800470(FEAR|GRIP|ROOT|SNARE|HORROR)'), +(-107,0,0,16781344,'','',0,0,'mech=0x800810(FEAR|STUN|HORROR)'), +(-108,0,0,16781984,'','',0,0,'mech=0x800950(FEAR|ROOT|SILENCE|STUN|HORROR)'), +(-109,0,0,16783392,'','',0,0,'mech=0x800C10(FEAR|SNARE|STUN|HORROR)'), +(-110,4,0,16784544,'','',0,0,'school=0x4(FIRE), mech=0x800E50(FEAR|ROOT|SLEEP|SNARE|STUN|HORROR)'), +(-111,0,0,16788644,'','',0,0,'mech=0x801652(DISORIENTED|FEAR|ROOT|SLEEP|SNARE|FREEZE|HORROR)'), +(-112,0,0,16791714,'','',0,0,'mech=0x801C51(CHARM|FEAR|ROOT|SNARE|STUN|FREEZE|HORROR)'), +(-113,0,0,16794664,'','',0,0,'mech=0x802214(DISARM|FEAR|SLEEP|KNOCKOUT|HORROR)'), +(-114,0,0,16794668,'','',0,0,'mech=0x802216(DISORIENTED|DISARM|FEAR|SLEEP|KNOCKOUT|HORROR)'), +(-115,0,0,16794676,'','',0,0,'mech=0x80221A(DISORIENTED|DISTRACT|FEAR|SLEEP|KNOCKOUT|HORROR)'), +(-116,0,0,16827436,'','',0,0,'mech=0x806216(DISORIENTED|DISARM|FEAR|SLEEP|KNOCKOUT|BLEED|HORROR)'), +(-117,0,0,16932526,'','',0,0,'mech=0x812F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR)'), +(-118,0,0,17039394,'','',0,0,'mech=0x820011(CHARM|FEAR|BANISH|HORROR)'), +(-119,0,0,17039520,'','',0,0,'mech=0x820050(FEAR|ROOT|BANISH|HORROR)'), +(-120,0,0,17041570,'','',0,0,'mech=0x820451(CHARM|FEAR|ROOT|SNARE|BANISH|HORROR)'), +(-121,0,0,17045536,'','',0,0,'mech=0x820C10(FEAR|SNARE|STUN|BANISH|HORROR)'), +(-122,0,0,17171490,'','',0,0,'mech=0x830211(CHARM|FEAR|SLEEP|POLYMORPH|BANISH|HORROR)'), +(-123,124,0,17171490,'','',0,0,'school=0x7C(FIRE|NATURE|FROST|SHADOW|ARCANE), mech=0x830211(CHARM|FEAR|SLEEP|POLYMORPH|BANISH|HORROR)'), +(-124,0,0,17171494,'','',0,0,'mech=0x830213(CHARM|DISORIENTED|FEAR|SLEEP|POLYMORPH|BANISH|HORROR)'), +(-125,0,0,17204258,'','',0,0,'mech=0x834211(CHARM|FEAR|SLEEP|BLEED|POLYMORPH|BANISH|HORROR)'), +(-126,0,0,17825824,'','',0,0,'mech=0x880010(FEAR|SHACKLE|HORROR)'), +(-127,0,0,17825826,'','',0,0,'mech=0x880011(CHARM|FEAR|SHACKLE|HORROR)'), +(-128,0,0,17826336,'','',0,0,'mech=0x880110(FEAR|SILENCE|SHACKLE|HORROR)'), +(-129,0,0,25198706,'','',0,0,'mech=0xC04039(CHARM|DISTRACT|FEAR|GRIP|BLEED|TURN|HORROR)'), +(-130,0,0,25574434,'','',0,0,'mech=0xC31E11(CHARM|FEAR|SLEEP|SNARE|STUN|FREEZE|POLYMORPH|BANISH|TURN|HORROR)'), +(-131,0,0,26216098,'','',0,0,'mech=0xC80351(CHARM|FEAR|ROOT|SILENCE|SLEEP|SHACKLE|TURN|HORROR)'), +(-132,0,0,67108864,'','',0,0,'mech=0x2000000(INTERRUPT)'), +(-133,0,0,67109376,'','',0,0,'mech=0x2000100(SILENCE|INTERRUPT)'), +(-134,0,0,67272418,'','',0,0,'mech=0x2013F71(CHARM|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|INTERRUPT)'), +(-135,0,0,75497986,'','',0,0,'mech=0x2400101(CHARM|SILENCE|TURN|INTERRUPT)'), +(-136,0,0,75502082,'','',0,0,'mech=0x2400901(CHARM|SILENCE|STUN|TURN|INTERRUPT)'), +(-137,0,0,75765794,'','',0,0,'mech=0x2420C11(CHARM|FEAR|SNARE|STUN|BANISH|TURN|INTERRUPT)'), +(-138,0,0,84032160,'98,144','',0,0,'mech=0x2811D50(FEAR|ROOT|SILENCE|SNARE|STUN|FREEZE|POLYMORPH|HORROR|INTERRUPT), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-139,0,0,84180644,'','',0,0,'mech=0x2823F52(DISORIENTED|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|HORROR|INTERRUPT)'), +(-140,0,0,84180654,'','',0,0,'mech=0x2823F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|HORROR|INTERRUPT)'), +(-141,0,0,92569262,'','',0,0,'mech=0x2C23F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|TURN|HORROR|INTERRUPT)'), +(-142,0,0,92569262,'114','11',0,0,'mech=0x2C23F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|TURN|HORROR|INTERRUPT), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-143,0,0,92569262,'98,144','',0,0,'mech=0x2C23F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|TURN|HORROR|INTERRUPT), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-144,0,0,93617828,'114','11',0,0,'mech=0x2CA3F52(DISORIENTED|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|TURN|HORROR|INTERRUPT), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-145,0,0,134217728,'','',0,0,'mech=0x4000000(DAZE)'), +(-146,0,0,134219776,'','',0,0,'mech=0x4000400(SNARE|DAZE)'), +(-147,0,0,134221834,'','',0,0,'mech=0x4000805(CHARM|DISARM|STUN|DAZE)'), +(-148,0,0,134368350,'','',0,0,'mech=0x401262F(CHARM|DISORIENTED|DISARM|DISTRACT|GRIP|SLEEP|SNARE|KNOCKOUT|POLYMORPH|DAZE)'), +(-149,0,0,134487696,'','',0,0,'mech=0x4020F48(DISTRACT|ROOT|SILENCE|SLEEP|SNARE|STUN|BANISH|DAZE)'), +(-150,0,0,134487698,'','',0,0,'mech=0x4020F49(CHARM|DISTRACT|ROOT|SILENCE|SLEEP|SNARE|STUN|BANISH|DAZE)'), +(-151,0,0,134610954,'98,144','',0,0,'mech=0x4030005(CHARM|DISARM|POLYMORPH|BANISH|DAZE), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-152,0,0,142884558,'','',0,0,'mech=0x4421F67(CHARM|DISORIENTED|DISARM|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|BANISH|TURN|DAZE)'), +(-153,0,0,151259878,'','',0,0,'mech=0x4820573(CHARM|DISORIENTED|FEAR|GRIP|ROOT|SILENCE|SNARE|BANISH|HORROR|DAZE)'), +(-154,0,0,151262366,'','',0,0,'mech=0x4820A4F(CHARM|DISORIENTED|DISARM|DISTRACT|ROOT|SLEEP|STUN|BANISH|HORROR|DAZE)'), +(-155,0,0,151262398,'','',0,0,'mech=0x4820A5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SLEEP|STUN|BANISH|HORROR|DAZE)'), +(-156,0,0,151273214,'','',0,0,'mech=0x4821F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|BANISH|HORROR|DAZE)'), +(-157,0,0,151314166,'','',0,0,'mech=0x4826F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|BLEED|BANISH|HORROR|DAZE)'), +(-158,0,0,151401654,'','',0,0,'mech=0x4831A5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|STUN|FREEZE|POLYMORPH|BANISH|HORROR|DAZE)'), +(-159,0,0,152469182,'','',0,0,'mech=0x48B3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE)'), +(-160,0,0,159516926,'','',0,0,'mech=0x4C1047F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SNARE|POLYMORPH|TURN|HORROR|DAZE)'), +(-161,0,0,160853686,'','',0,0,'mech=0x4CB375B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE)'), +(-162,0,0,160857782,'','',0,0,'mech=0x4CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE)'), +(-163,0,0,160857790,'','',0,0,'mech=0x4CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE)'), +(-164,0,0,160857854,'','',0,0,'mech=0x4CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE)'), +(-165,0,0,161361662,'','',0,0,'mech=0x4CF177F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|DAZE)'), +(-166,0,0,201719818,'98,144','',0,0,'mech=0x6030005(CHARM|DISARM|POLYMORPH|BANISH|INTERRUPT|DAZE), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-167,0,0,226915902,'','',0,0,'mech=0x6C33B1F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|TURN|HORROR|INTERRUPT|DAZE)'), +(-168,0,0,227962558,'','',0,0,'mech=0x6CB375F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE)'), +(-169,0,0,688815860,'','',0,0,'mech=0x14873F7A(DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|HORROR|DAZE|IMMUNE_SHIELD)'), +(-170,0,0,1002405630,'','',0,0,'mech=0x1DDFBF7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|INVULNERABILITY|DAZE|DISCOVERY|IMMUNE_SHIELD)'), +(-171,0,0,1073741824,'','',0,0,'mech=0x20000000(SAPPED)'), +(-172,0,0,1073741826,'','',0,0,'mech=0x20000001(CHARM|SAPPED)'), +(-173,0,0,1073741892,'98,144','',0,0,'mech=0x20000022(DISORIENTED|GRIP|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-174,0,0,1073758240,'','',0,0,'mech=0x20002010(FEAR|KNOCKOUT|SAPPED)'), +(-175,0,0,1073873920,'','',0,0,'mech=0x20010200(SLEEP|POLYMORPH|SAPPED)'), +(-176,0,0,1073877158,'','',0,0,'mech=0x20010853(CHARM|DISORIENTED|FEAR|ROOT|STUN|POLYMORPH|SAPPED)'), +(-177,0,0,1073886882,'98,144','',0,0,'mech=0x20011B51(CHARM|FEAR|ROOT|SILENCE|SLEEP|STUN|FREEZE|POLYMORPH|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-178,0,0,1074009602,'','',0,0,'mech=0x20020B01(CHARM|SILENCE|SLEEP|STUN|BANISH|SAPPED)'), +(-179,0,0,1074167486,'','',0,0,'mech=0x20033F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SAPPED)'), +(-180,0,0,1075202048,'','',0,0,'mech=0x200B2400(SNARE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|SAPPED)'), +(-181,0,0,1082677838,'','',0,0,'mech=0x20442D27(CHARM|DISORIENTED|DISARM|GRIP|SILENCE|SNARE|STUN|KNOCKOUT|SHIELD|TURN|SAPPED)'), +(-182,0,0,1082686030,'','',0,0,'mech=0x20443D27(CHARM|DISORIENTED|DISARM|GRIP|SILENCE|SNARE|STUN|FREEZE|KNOCKOUT|SHIELD|TURN|SAPPED)'), +(-183,0,0,1082686046,'','',0,0,'mech=0x20443D2F(CHARM|DISORIENTED|DISARM|DISTRACT|GRIP|SILENCE|SNARE|STUN|FREEZE|KNOCKOUT|SHIELD|TURN|SAPPED)'), +(-184,0,0,1090534560,'','',0,0,'mech=0x20801E50(FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|HORROR|SAPPED)'), +(-185,8,0,1090674362,'','',0,0,'school=0x8(NATURE), mech=0x20812F5D(CHARM|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|SAPPED)'), +(-186,16,0,1090674362,'','',0,0,'school=0x10(FROST), mech=0x20812F5D(CHARM|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|SAPPED)'), +(-187,0,0,1090679030,'','',0,0,'mech=0x2081387B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|STUN|FREEZE|KNOCKOUT|POLYMORPH|HORROR|SAPPED)'), +(-188,0,0,1090681014,'','',0,0,'mech=0x20813C5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|HORROR|SAPPED)'), +(-189,0,0,1090681270,'','',0,0,'mech=0x20813CDB(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLOW_ATTACK|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|HORROR|SAPPED)'), +(-190,0,0,1090936558,'','',0,0,'mech=0x20832F77(CHARM|DISORIENTED|DISARM|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|BANISH|HORROR|SAPPED)'), +(-191,0,0,1090936574,'','',0,0,'mech=0x20832F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|BANISH|HORROR|SAPPED)'), +(-192,0,0,1090940076,'','',0,0,'mech=0x20833656(DISORIENTED|DISARM|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|SAPPED)'), +(-193,0,0,1090940670,'','',0,0,'mech=0x2083377F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|SAPPED)'), +(-194,0,0,1090944742,'114','11',0,0,'mech=0x20833F73(CHARM|DISORIENTED|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-195,0,0,1090944766,'','',0,0,'mech=0x20833F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|SAPPED)'), +(-196,0,0,1090944766,'114','11',0,0,'mech=0x20833F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-197,0,0,1099040290,'','',0,0,'mech=0x20C10311(CHARM|FEAR|SILENCE|SLEEP|POLYMORPH|TURN|HORROR|SAPPED)'), +(-198,0,0,1099043874,'','',0,0,'mech=0x20C10A11(CHARM|FEAR|SLEEP|STUN|POLYMORPH|TURN|HORROR|SAPPED)'), +(-199,0,0,1099043874,'114','11',0,0,'mech=0x20C10A11(CHARM|FEAR|SLEEP|STUN|POLYMORPH|TURN|HORROR|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-200,0,0,1099043874,'98,144','',0,0,'mech=0x20C10A11(CHARM|FEAR|SLEEP|STUN|POLYMORPH|TURN|HORROR|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-201,0,0,1099060258,'','',0,0,'mech=0x20C12A11(CHARM|FEAR|SLEEP|STUN|KNOCKOUT|POLYMORPH|TURN|HORROR|SAPPED)'), +(-202,0,0,1100219550,'','',0,0,'mech=0x20CA024F(CHARM|DISORIENTED|DISARM|DISTRACT|ROOT|SLEEP|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-203,0,0,1100248830,'','',0,0,'mech=0x20CA3B7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-204,0,0,1100350654,'','',0,0,'mech=0x20CB025F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SLEEP|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-205,0,0,1100359230,'','',0,0,'mech=0x20CB131F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|FREEZE|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-206,0,0,1100366910,'','',0,0,'mech=0x20CB221F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-207,0,0,1100375718,'','',0,0,'mech=0x20CB3353(CHARM|DISORIENTED|FEAR|ROOT|SILENCE|SLEEP|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-208,0,0,1100379710,'','',0,0,'mech=0x20CB3B1F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-209,0,0,1100379774,'','',0,0,'mech=0x20CB3B3F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|SAPPED)'), +(-210,0,0,1100904190,'','',0,0,'mech=0x20CF3B7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|SAPPED)'), +(-211,0,0,1100906238,'98,144','',0,0,'mech=0x20CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-212,0,0,1102477054,'','',0,0,'mech=0x20DB3B7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|MOUNT|TURN|HORROR|SAPPED)'), +(-213,0,0,1102872318,'','',0,0,'mech=0x20DE3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|SAPPED)'), +(-214,0,0,1141118466,'','',0,0,'mech=0x22020B01(CHARM|SILENCE|SLEEP|STUN|BANISH|INTERRUPT|SAPPED)'), +(-215,0,0,1158053548,'','',0,0,'mech=0x22833F56(DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|SAPPED)'), +(-216,0,0,1158053550,'','',0,0,'mech=0x22833F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|SAPPED)'), +(-217,0,0,1158053630,'114','11',0,0,'mech=0x22833F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-218,0,0,1167484670,'','',0,0,'mech=0x22CB337F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|SAPPED)'), +(-219,0,0,1167484670,'98,144','',0,0,'mech=0x22CB337F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-220,0,0,1167488694,'','',0,0,'mech=0x22CB3B5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|SAPPED)'), +(-221,0,0,1207983274,'','',0,0,'mech=0x24002E55(CHARM|DISARM|FEAR|ROOT|SLEEP|SNARE|STUN|KNOCKOUT|DAZE|SAPPED)'), +(-222,0,0,1208099366,'','',0,0,'mech=0x24011113(CHARM|DISORIENTED|FEAR|SILENCE|FREEZE|POLYMORPH|DAZE|SAPPED)'), +(-223,0,0,1208110334,'98,144','',0,0,'mech=0x2401267F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|KNOCKOUT|POLYMORPH|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-224,0,0,1208117942,'','',0,0,'mech=0x2401355B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SNARE|FREEZE|KNOCKOUT|POLYMORPH|DAZE|SAPPED)'), +(-225,0,0,1208118454,'','',0,0,'mech=0x2401365B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|DAZE|SAPPED)'), +(-226,0,0,1208123134,'','',0,0,'mech=0x24013F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|DAZE|SAPPED)'), +(-227,0,0,1208245410,'','',0,0,'mech=0x24022E51(CHARM|FEAR|ROOT|SLEEP|SNARE|STUN|KNOCKOUT|BANISH|DAZE|SAPPED)'), +(-228,0,0,1209171606,'','',0,0,'mech=0x24093F4B(CHARM|DISORIENTED|DISTRACT|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|SHACKLE|DAZE|SAPPED)'), +(-229,0,0,1217817766,'','',0,0,'mech=0x244B3653(CHARM|DISORIENTED|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|DAZE|SAPPED)'), +(-230,0,0,1217817766,'114','11',0,0,'mech=0x244B3653(CHARM|DISORIENTED|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-231,8,0,1217817766,'','',0,0,'school=0x8(NATURE), mech=0x244B3653(CHARM|DISORIENTED|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|DAZE|SAPPED)'), +(-232,16,0,1217817766,'','',0,0,'school=0x10(FROST), mech=0x244B3653(CHARM|DISORIENTED|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|DAZE|SAPPED)'), +(-233,0,0,1224758964,'','',0,0,'mech=0x24802B5A(DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|STUN|KNOCKOUT|HORROR|DAZE|SAPPED)'), +(-234,0,0,1224758966,'','',0,0,'mech=0x24802B5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|STUN|KNOCKOUT|HORROR|DAZE|SAPPED)'), +(-235,0,0,1224764576,'','',0,0,'mech=0x24803650(FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|HORROR|DAZE|SAPPED)'), +(-236,0,0,1224891646,'98,144','',0,0,'mech=0x24812E7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-237,0,0,1224892158,'','',0,0,'mech=0x24812F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|DAZE|SAPPED)'), +(-238,0,0,1224899750,'','',0,0,'mech=0x24813E53(CHARM|DISORIENTED|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|HORROR|DAZE|SAPPED)'), +(-239,0,0,1224899830,'','',0,0,'mech=0x24813E7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|HORROR|DAZE|SAPPED)'), +(-240,0,0,1225030910,'','',0,0,'mech=0x24823E7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|HORROR|DAZE|SAPPED)'), +(-241,0,0,1225031358,'','',0,0,'mech=0x24823F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|HORROR|DAZE|SAPPED)'), +(-242,0,0,1225064188,'','',0,0,'mech=0x24827F7E(DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|BANISH|HORROR|DAZE|SAPPED)'), +(-243,0,0,1225162494,'','',0,0,'mech=0x24833F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|DAZE|SAPPED)'), +(-244,0,0,1225293566,'','',0,0,'mech=0x24843F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|SHIELD|HORROR|DAZE|SAPPED)'), +(-245,0,0,1225424638,'','',0,0,'mech=0x24853F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|SHIELD|HORROR|DAZE|SAPPED)'), +(-246,0,0,1225588478,'','',0,0,'mech=0x24867F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|BANISH|SHIELD|HORROR|DAZE|SAPPED)'), +(-247,0,0,1225686782,'','',0,0,'mech=0x24873F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|HORROR|DAZE|SAPPED)'), +(-248,0,0,1226206902,'','',0,0,'mech=0x248B375B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-249,0,0,1226209946,'','',0,0,'mech=0x248B3D4D(CHARM|DISARM|DISTRACT|ROOT|SILENCE|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-250,0,0,1226209974,'','',0,0,'mech=0x248B3D5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-251,0,0,1226210998,'','',0,0,'mech=0x248B3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-252,4,0,1226210998,'','',0,0,'school=0x4(FIRE), mech=0x248B3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-253,16,0,1226210998,'','',0,0,'school=0x10(FROST), mech=0x248B3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-254,0,0,1226211006,'','',0,0,'mech=0x248B3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-255,4,0,1226211006,'','',0,0,'school=0x4(FIRE), mech=0x248B3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED)'), +(-256,0,0,1226211070,'98,144','',0,0,'mech=0x248B3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-257,0,0,1230420214,'','',0,0,'mech=0x24AB5C7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SNARE|STUN|FREEZE|BLEED|POLYMORPH|BANISH|SHACKLE|INFECTED|HORROR|DAZE|SAPPED)'), +(-258,0,0,1233288886,'','',0,0,'mech=0x24C13F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|TURN|HORROR|DAZE|SAPPED)'), +(-259,0,0,1233419518,'','',0,0,'mech=0x24C23E7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|TURN|HORROR|DAZE|SAPPED)'), +(-260,0,0,1233551102,'','',0,0,'mech=0x24C33F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|TURN|HORROR|DAZE|SAPPED)'), +(-261,0,0,1233944318,'','',0,0,'mech=0x24C63F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHIELD|TURN|HORROR|DAZE|SAPPED)'), +(-262,0,0,1234075390,'','',0,0,'mech=0x24C73F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|TURN|HORROR|DAZE|SAPPED)'), +(-263,0,0,1234568766,'','',0,0,'mech=0x24CB031F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-264,0,0,1234583806,'98,144','',0,0,'mech=0x24CB207F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-265,0,0,1234595510,'','',0,0,'mech=0x24CB375B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-266,0,0,1234595518,'','',0,0,'mech=0x24CB375F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-267,0,0,1234595518,'98,144','',0,0,'mech=0x24CB375F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-268,0,0,1234595582,'','',0,0,'mech=0x24CB377F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-269,0,0,1234597430,'','',0,0,'mech=0x24CB3B1B(CHARM|DISORIENTED|DISTRACT|FEAR|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-270,0,0,1234599094,'','',0,0,'mech=0x24CB3E5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-271,64,0,1234599094,'','',0,0,'school=0x40(ARCANE), mech=0x24CB3E5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-272,0,0,1234599158,'','',0,0,'mech=0x24CB3E7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-273,0,0,1234599606,'','',0,0,'mech=0x24CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-274,0,0,1234599606,'114','11',0,0,'mech=0x24CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-275,0,0,1234599606,'98,144','',0,0,'mech=0x24CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-276,4,0,1234599606,'','',0,0,'school=0x4(FIRE), mech=0x24CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-277,8,0,1234599606,'','',0,0,'school=0x8(NATURE), mech=0x24CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-278,16,0,1234599606,'','',0,0,'school=0x10(FROST), mech=0x24CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-279,0,0,1234599610,'','',0,0,'mech=0x24CB3F5D(CHARM|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-280,0,0,1234599614,'','',0,0,'mech=0x24CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-281,0,0,1234599614,'114','11',0,0,'mech=0x24CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-282,124,0,1234599614,'114','11',0,0,'school=0x7C(FIRE|NATURE|FROST|SHADOW|ARCANE), mech=0x24CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-283,0,0,1234599654,'','',0,0,'mech=0x24CB3F73(CHARM|DISORIENTED|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-284,0,0,1234599670,'','',0,0,'mech=0x24CB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-285,0,0,1234599678,'','',0,0,'mech=0x24CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-286,0,0,1234599678,'98,144','',0,0,'mech=0x24CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-287,0,0,1234599678,'98,114,144','11',0,0,'mech=0x24CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),144(KNOCK_BACK_DEST), auras=11(BIND)'), +(-288,0,0,1234632374,'','',0,0,'mech=0x24CB7F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-289,0,0,1234992894,'','',0,0,'mech=0x24CE3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHIELD|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-290,0,0,1235119870,'114','11',0,0,'mech=0x24CF377F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-291,0,0,1235123964,'','',0,0,'mech=0x24CF3F7E(DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-292,0,0,1235123966,'','',0,0,'mech=0x24CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-293,0,0,1235123966,'114','11',0,0,'mech=0x24CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-294,64,0,1235123966,'','',0,0,'school=0x40(ARCANE), mech=0x24CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|DAZE|SAPPED)'), +(-295,0,0,1237221118,'','',0,0,'mech=0x24DF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|DAZE|SAPPED)'), +(-296,0,0,1275494326,'','',0,0,'mech=0x26033FDB(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLOW_ATTACK|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|INTERRUPT|DAZE|SAPPED)'), +(-297,0,0,1291996862,'','',0,0,'mech=0x2681275F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-298,8,0,1291998778,'','',0,0,'school=0x8(NATURE), mech=0x26812B1D(CHARM|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|STUN|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-299,0,0,1292000954,'','',0,0,'mech=0x26812F5D(CHARM|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-300,4,0,1292000954,'','',0,0,'school=0x4(FIRE), mech=0x26812F5D(CHARM|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-301,8,0,1292000954,'','',0,0,'school=0x8(NATURE), mech=0x26812F5D(CHARM|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-302,16,0,1292000954,'','',0,0,'school=0x10(FROST), mech=0x26812F5D(CHARM|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-303,0,0,1292000958,'','',0,0,'mech=0x26812F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-304,8,0,1292000958,'','',0,0,'school=0x8(NATURE), mech=0x26812F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-305,0,0,1292009150,'','',0,0,'mech=0x26813F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-306,0,0,1292033724,'','',0,0,'mech=0x26816F5E(DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|BLEED|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-307,0,0,1292033726,'','',0,0,'mech=0x26816F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|BLEED|POLYMORPH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-308,0,0,1292270270,'','',0,0,'mech=0x26833D5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-309,0,0,1292271166,'','',0,0,'mech=0x26833F1F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-310,126,0,1292271286,'','',0,0,'school=0x7E(HOLY|FIRE|NATURE|FROST|SHADOW|ARCANE), mech=0x26833F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-311,0,0,1292271294,'','',0,0,'mech=0x26833F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-312,4,0,1292271294,'','',0,0,'school=0x4(FIRE), mech=0x26833F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-313,0,0,1292271358,'','',0,0,'mech=0x26833F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-314,8,0,1292271358,'','',0,0,'school=0x8(NATURE), mech=0x26833F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-315,0,0,1293188798,'','',0,0,'mech=0x268A3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-316,4,0,1293188798,'','',0,0,'school=0x4(FIRE), mech=0x268A3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-317,8,0,1293188798,'','',0,0,'school=0x8(NATURE), mech=0x268A3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-318,16,0,1293188798,'','',0,0,'school=0x10(FROST), mech=0x268A3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-319,0,0,1293311670,'','',0,0,'mech=0x268B2F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-320,0,0,1293315754,'','',0,0,'mech=0x268B3755(CHARM|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-321,0,0,1293315774,'','',0,0,'mech=0x268B375F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-322,0,0,1293317684,'','',0,0,'mech=0x268B3B1A(DISORIENTED|DISTRACT|FEAR|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-323,0,0,1293318838,'','',0,0,'mech=0x268B3D5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-324,0,0,1293319862,'','',0,0,'mech=0x268B3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-325,0,0,1293319870,'','',0,0,'mech=0x268B3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-326,8,0,1293319870,'','',0,0,'school=0x8(NATURE), mech=0x268B3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-327,0,0,1293319926,'98,144','',0,0,'mech=0x268B3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-328,0,0,1293876926,'','',0,0,'mech=0x268F7F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|SHIELD|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-329,8,0,1300397758,'','',0,0,'school=0x8(NATURE), mech=0x26C13F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-330,0,0,1300692542,'114','11',1,0,'mech=0x26C37F1F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT|AVOID_AOE, effects=114(ATTACK_ME), auras=11(BIND)'), +(-331,0,0,1300692670,'','',0,0,'mech=0x26C37F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-332,0,0,1301053166,'114','11',0,0,'mech=0x26C63F77(CHARM|DISORIENTED|DISARM|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHIELD|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-333,0,0,1301184254,'','',0,0,'mech=0x26C73F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-334,0,0,1301446398,'114','11',0,0,'mech=0x26C93F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-335,0,0,1301677626,'98,144','',0,0,'mech=0x26CB031D(CHARM|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-336,0,0,1301677630,'98,144','',0,0,'mech=0x26CB031F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-337,0,0,1301702270,'','',0,0,'mech=0x26CB333F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|SILENCE|SLEEP|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-338,0,0,1301703934,'','',0,0,'mech=0x26CB367F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-339,0,0,1301704382,'98,144','',0,0,'mech=0x26CB375F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-340,0,0,1301704446,'98,144','',0,0,'mech=0x26CB377F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-341,4,0,1301704446,'','',0,0,'school=0x4(FIRE), mech=0x26CB377F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-342,0,0,1301706494,'98,114,144','11',0,0,'mech=0x26CB3B7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),144(KNOCK_BACK_DEST), auras=11(BIND)'), +(-343,0,0,1301707958,'','',0,0,'mech=0x26CB3E5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-344,0,0,1301708022,'','',0,0,'mech=0x26CB3E7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-345,0,0,1301708462,'','',0,0,'mech=0x26CB3F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-346,0,0,1301708470,'','',0,0,'mech=0x26CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-347,0,0,1301708470,'114','11',0,0,'mech=0x26CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-348,0,0,1301708478,'','',0,0,'mech=0x26CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-349,0,0,1301708478,'114','11',0,0,'mech=0x26CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-350,0,0,1301708478,'98,144','',0,0,'mech=0x26CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-351,1,0,1301708478,'114','11',0,0,'school=0x1(NORMAL), mech=0x26CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-352,4,0,1301708478,'114','11',0,0,'school=0x4(FIRE), mech=0x26CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-353,0,0,1301708526,'98,144','',0,0,'mech=0x26CB3F77(CHARM|DISORIENTED|DISARM|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-354,0,0,1301708534,'','',0,0,'mech=0x26CB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-355,0,0,1301708534,'98,144','',0,0,'mech=0x26CB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-356,0,0,1301708534,'98,114,144','11',0,0,'mech=0x26CB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),144(KNOCK_BACK_DEST), auras=11(BIND)'), +(-357,0,0,1301708538,'98,144','',0,0,'mech=0x26CB3F7D(CHARM|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-358,16,0,1301708538,'','',0,0,'school=0x10(FROST), mech=0x26CB3F7D(CHARM|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-359,0,0,1301708540,'98,144','',0,0,'mech=0x26CB3F7E(DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-360,0,0,1301708542,'','',0,0,'mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-361,0,0,1301708542,'98,144','',0,0,'mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-362,0,0,1301708542,'98,114,144','11',0,0,'mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),144(KNOCK_BACK_DEST), auras=11(BIND)'), +(-363,0,0,1301708542,'98,144','',1,0,'mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=AVOID_AOE|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-364,4,0,1301708542,'98,144','',0,0,'school=0x4(FIRE), mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-365,16,0,1301708542,'','',0,0,'school=0x10(FROST), mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-366,16,0,1301708542,'98,144','',0,0,'school=0x10(FROST), mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-367,0,0,1301741246,'','',0,0,'mech=0x26CB7F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-368,127,0,1301741246,'','',0,0,'school=0x7F(NORMAL|HOLY|FIRE|NATURE|FROST|SHADOW|ARCANE), mech=0x26CB7F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-369,0,0,1301741310,'98,144','',0,0,'mech=0x26CB7F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-370,0,0,1302232766,'','',1,0,'mech=0x26CF3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=AVOID_AOE'), +(-371,0,0,1302232830,'','',0,0,'mech=0x26CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-372,0,0,1302232830,'98,144','',0,0,'mech=0x26CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-373,0,0,1303871230,'','',0,0,'mech=0x26DBBF7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHACKLE|MOUNT|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-374,0,0,1304395518,'','',0,0,'mech=0x26DFBF7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-375,0,0,1304428286,'','',0,0,'mech=0x26DFFF7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-376,0,0,1305378558,'','',0,0,'mech=0x26E73F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|INFECTED|TURN|HORROR|INTERRUPT|DAZE|SAPPED)'), +(-377,0,0,1335787254,'','',0,0,'mech=0x27CF3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|INVULNERABILITY|INTERRUPT|DAZE|SAPPED)'), +(-378,0,0,1539276542,'','',0,0,'mech=0x2DDFBF7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|INVULNERABILITY|DAZE|DISCOVERY|SAPPED)'), +(-379,0,0,1770663678,'','',0,0,'mech=0x34C5177F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|POLYMORPH|SHIELD|TURN|HORROR|DAZE|IMMUNE_SHIELD|SAPPED)'), +(-380,0,0,1770946302,'114','11',0,0,'mech=0x34C73F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|TURN|HORROR|DAZE|IMMUNE_SHIELD|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-381,0,0,1804534846,'','',0,0,'mech=0x35C7821F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SLEEP|BANDAGE|POLYMORPH|BANISH|SHIELD|TURN|HORROR|INVULNERABILITY|DAZE|IMMUNE_SHIELD|SAPPED)'), +(-382,0,0,2013265920,'','',0,0,'mech=0x3C000000(DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-383,0,0,2013528064,'','',0,0,'mech=0x3C020000(BANISH|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-384,0,0,2013528100,'','',0,0,'mech=0x3C020012(DISORIENTED|FEAR|BANISH|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-385,0,0,2013660192,'','',0,0,'mech=0x3C030210(FEAR|SLEEP|POLYMORPH|BANISH|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-386,0,0,2022966272,'','',0,0,'mech=0x3C4A0200(SLEEP|BANISH|SHACKLE|TURN|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-387,0,0,2030453792,'','',0,0,'mech=0x3C832210(FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-388,0,0,2031386302,'','',0,0,'mech=0x3C8A3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-389,0,0,2031502368,'','',0,0,'mech=0x3C8B2210(FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-390,0,0,2031502372,'','',0,0,'mech=0x3C8B2212(DISORIENTED|FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-391,0,0,2031512736,'','',0,0,'mech=0x3C8B3650(FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-392,0,0,2031516704,'','',0,0,'mech=0x3C8B3E10(FEAR|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-393,0,0,2031516708,'','',0,0,'mech=0x3C8B3E12(DISORIENTED|FEAR|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-394,0,0,2031516836,'','',0,0,'mech=0x3C8B3E52(DISORIENTED|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-395,0,0,2031517372,'','',0,0,'mech=0x3C8B3F5E(DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-396,0,0,2038842400,'','',0,0,'mech=0x3CC32210(FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-397,0,0,2038842402,'','',0,0,'mech=0x3CC32211(CHARM|FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-398,0,0,2039890978,'','',0,0,'mech=0x3CCB2211(CHARM|FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-399,0,0,2039890982,'','',0,0,'mech=0x3CCB2213(CHARM|DISORIENTED|FEAR|SLEEP|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-400,0,0,2039901750,'','',0,0,'mech=0x3CCB371B(CHARM|DISORIENTED|DISTRACT|FEAR|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-401,0,0,2039905318,'','',0,0,'mech=0x3CCB3E13(CHARM|DISORIENTED|FEAR|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-402,0,0,2039905444,'','',0,0,'mech=0x3CCB3E52(DISORIENTED|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-403,0,0,2039905446,'','',0,0,'mech=0x3CCB3E53(CHARM|DISORIENTED|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-404,0,0,2039905454,'','',0,0,'mech=0x3CCB3E57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-405,0,0,2039905956,'','',0,0,'mech=0x3CCB3F52(DISORIENTED|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-406,0,0,2039905974,'','',0,0,'mech=0x3CCB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-407,0,0,2039905974,'98,144','',0,0,'mech=0x3CCB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-408,0,0,2039905974,'114','11',0,0,'mech=0x3CCB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED), flags=NO_TAUNT, effects=114(ATTACK_ME), auras=11(BIND)'), +(-409,4,0,2039905974,'','',0,0,'school=0x4(FIRE), mech=0x3CCB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-410,0,0,2039906038,'98,144','',0,0,'mech=0x3CCB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),144(KNOCK_BACK_DEST)'), +(-411,0,0,2039906046,'','',0,0,'mech=0x3CCB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-412,0,0,2041872062,'','',0,0,'mech=0x3CDA3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|SHACKLE|MOUNT|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-413,0,0,2042588340,'','',0,0,'mech=0x3CDFB65A(DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-414,0,0,2042588342,'','',0,0,'mech=0x3CDFB65B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-415,0,0,2042592436,'','',0,0,'mech=0x3CDFBE5A(DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-416,0,0,2042592438,'','',0,0,'mech=0x3CDFBE5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-417,0,0,2044099622,'','',0,0,'mech=0x3CEB3E13(CHARM|DISORIENTED|FEAR|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|INFECTED|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-418,0,0,2076147382,'','',0,0,'mech=0x3DDFBF5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|INVULNERABILITY|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-419,0,0,2107014838,'','',0,0,'mech=0x3ECB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-420,0,0,2143256254,'','',0,0,'mech=0x3FDFBF5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|INVULNERABILITY|INTERRUPT|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-421,0,0,2143289022,'','',0,0,'mech=0x3FDFFF5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|INVULNERABILITY|INTERRUPT|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-422,0,0,2147483646,'','',0,0,'mech=0x3FFFFFFF(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLOW_ATTACK|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|INFECTED|TURN|HORROR|INVULNERABILITY|INTERRUPT|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED)'), +(-423,0,0,3116367612,'','',0,0,'mech=0x5CDFFF7E(DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|ENRAGED)'), +(-424,0,0,3386277110,'','',0,0,'mech=0x64EB3E7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|INFECTED|TURN|HORROR|DAZE|SAPPED|ENRAGED)'), +(-425,0,0,4294934270,'','',0,0,'mech=0x7FFFBF7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|INFECTED|TURN|HORROR|INVULNERABILITY|INTERRUPT|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED|ENRAGED)'), +(-426,0,0,4294967038,'','',0,0,'mech=0x7FFFFF7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|BANDAGE|POLYMORPH|BANISH|SHIELD|SHACKLE|MOUNT|INFECTED|TURN|HORROR|INVULNERABILITY|INTERRUPT|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED|ENRAGED)'); + +-- ============================================================ +-- Clear existing CreatureImmunitiesId references +-- ============================================================ +UPDATE `creature_template` SET `CreatureImmunitiesId` = 0; + +-- ============================================================ +-- Update creature_template to reference the new IDs by entry +-- ============================================================ +UPDATE `creature_template` SET `CreatureImmunitiesId` = -1 WHERE `entry` IN (14667,17680,22379,23498); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -2 WHERE `entry` IN (18703,20700,24745,25328); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -3 WHERE `entry` IN (18696,20132,21380,23285,37132,37133,37134,37950,38125,38130,38131,38132,38133,38153,38752); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -4 WHERE `entry` IN (2091,2108,2726,4339,4676,6073,7135,7136,7137,8608,8616,14668,15209,15438,17267,19203,19973,20703,21706,22323,25001,25417,28584,30416,30847,30983,31453); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -5 WHERE `entry` IN (832,2258,2592,2736,2752,2762,4034,4035,4499,4526,5898,5902,6239,8667,9377,9397,11576,11577,11578,11744,11745,11777,11778,11779,11783,11784,14399,14400,14478,15352,16043,17085,17154,17156,17157,17158,17159,17160,18062,18181,21707,22036,23029,26407,28411,28547,28585,28825,30418,30965,30970,30979,31452); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -6 WHERE `entry` IN (2761,2776,2794,3851,3861,3950,4978,5462,5894,5895,5897,6047,6220,6748,7132,7428,7429,8519,8520,8521,8522,8837,9453,10198,10642,11256,11862,12759,13278,13279,13322,13456,13696,13736,14269,14350,15211,16570,17153,17155,17167,17207,17358,19204,20704,21428,21728,22035,22309,24228,24601,25226,25514,25715,25755,25756,25757,26116,26178,26204,26214,26215,26216,26316,26340,26341,26342,30419,30846,31454,37994); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -7 WHERE `entry` IN (703,1043,1364,19206,20705); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -8 WHERE `entry` IN (6492,6550,11480,11483,11484,14397,15527,16530,16854,18864,18865,18866,18867,19205,20516,20702,22310,26370,30848); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -9 WHERE `entry` IN (21032); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -10 WHERE `entry` IN (7734,7735); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -11 WHERE `entry` IN (16491); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -12 WHERE `entry` IN (352,523,752,931,1387,1571,1572,1573,2299,2409,2432,2835,2851,2858,2859,2861,2941,2995,3305,3310,3615,3725,3728,4312,4314,4317,4321,6026,6726,7823,7824,8018,8020,8609,8610,10378,11139,11830,11899,11900,11901,12596,12616,12617,12740,13177,15178,16218,16461,16593,16699,16704,17083,17229,17398,17420,17429,17461,17491,17622,17695,18048,18049,18050,18052,18053,18054,18057,18058,18603,18609,18610,18612,18615,18619,18631,18632,18635,18636,18830,19885,19887,19889,19903,20033,20034,20255,20256,20257,20258,20259,20260,20261,20578,20579,20580,20581,20582,20586,20587,20590,20594,20621,20624,20626,20639,20640,20641,20642,20644,20645,20661,20693,20695,20699,20701,20923,21230,21522,21524,21527,21528,21532,21545,21548,21563,21564,21565,21570,21571,21573,21575,21576,21585,21587,21613,21857,21865,21873,22876,23168,24994,25509,25591,25593,25595,25599,25851,26799,26800,26801,26802,26803,26805,27947,27949,29503,29626,30460,30478,30485,30495,30496,30497,30498,30508,30509,32922,32923,32924,32925,33235,33236,33259,33287,33355,33527,33579,33620,33622,33624,33626,33627,33629,33662,33669,33672,33696,33701,33737,33741,33754,33755,33757,33758,33774,33775,33816,33818,33819,33820,33822,33823,33824,33827,33828,33829,33830,33831,33832,33956,33957,34054,34113,34144,34145,34198,34199,34236,34237,34255,34256,34257,34441,34442,34444,34445,34447,34448,34449,34450,34451,34453,34454,34455,34456,34458,34459,34460,34461,34463,34465,34466,34467,34468,34469,34470,34471,34472,34473,34474,34475,35465,35610,35662,35665,35668,35671,35674,35680,35683,35686,35689,35692,35695,35699,35702,35705,35708,35711,35714,35718,35721,35724,35728,35731,35734,35737,35740,35743,35746,35774,36070,36301,36473); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -13 WHERE `entry` IN (34443,35663,35664,35666,35667,35669,35670,35672,35673,35675,35676,35681,35682,35684,35685,35687,35688,35690,35691,35693,35694,35696,35697,35700,35701,35703,35704,35706,35707,35709,35710,35712,35713,35715,35716,35719,35720,35722,35723,35725,35726,35729,35730,35732,35733,35735,35736,35738,35739,35741,35742,35744,35745,35747,35748,35749,35775,35776,36302,36303,36474,36475); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -14 WHERE `entry` IN (12737); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -15 WHERE `entry` IN (13996,14462,25752,25753,25758,25792); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -16 WHERE `entry` IN (14455); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -17 WHERE `entry` IN (14458); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -18 WHERE `entry` IN (24656,25553); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -19 WHERE `entry` IN (24553,24554,24555,24556,24557,24558,24559,24561,25541,25549,25550,25555,25556,25574,25578,25579); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -20 WHERE `entry` IN (17543,18404); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -21 WHERE `entry` IN (12143); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -22 WHERE `entry` IN (10202); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -23 WHERE `entry` IN (8717,21549); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -24 WHERE `entry` IN (30839); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -25 WHERE `entry` IN (20869,21586); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -26 WHERE `entry` IN (2673,2674,4952,12426,17578,18215,19139,21157,24792,30527,31143,31144,31146,32541,32542,32543,32545,32546,32547,32666,32667,33229,33243,33272); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -27 WHERE `entry` IN (16844,16968,21849,22038,22466,22482,37127,38126); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -28 WHERE `entry` IN (20897,23398); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -29 WHERE `entry` IN (21597,22879,22882,22945,23018,23049,23147,23223,23235,23236,23237,24815,25566); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -30 WHERE `entry` IN (23172); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -31 WHERE `entry` IN (5277,5280,5283,8319,16098,17669,20574); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -32 WHERE `entry` IN (22299,22300); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -33 WHERE `entry` IN (3678); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -34 WHERE `entry` IN (23584); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -35 WHERE `entry` IN (19005,22874,22885); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -36 WHERE `entry` IN (18707); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -37 WHERE `entry` IN (18533,20772); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -38 WHERE `entry` IN (21166); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -39 WHERE `entry` IN (4468); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -40 WHERE `entry` IN (18044,22939); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -41 WHERE `entry` IN (8908); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -42 WHERE `entry` IN (18258); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -43 WHERE `entry` IN (18046); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -44 WHERE `entry` IN (22957,22962); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -45 WHERE `entry` IN (18420,21574); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -46 WHERE `entry` IN (20038,25367,25368,25371,25507); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -47 WHERE `entry` IN (20052); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -48 WHERE `entry` IN (21225); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -49 WHERE `entry` IN (24549); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -50 WHERE `entry` IN (34702,34703,35569,35570,35571,35572,35617,36082,36083,36084,36085,36086,36087,36088,36089,36090,36091); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -51 WHERE `entry` IN (20048); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -52 WHERE `entry` IN (20883,21615); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -53 WHERE `entry` IN (10662); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -54 WHERE `entry` IN (28163,28168); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -55 WHERE `entry` IN (12800,12801,12802); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -56 WHERE `entry` IN (24047); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -57 WHERE `entry` IN (10663); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -58 WHERE `entry` IN (18423); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -59 WHERE `entry` IN (12458); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -60 WHERE `entry` IN (22875); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -61 WHERE `entry` IN (7397); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -62 WHERE `entry` IN (2760,3417,4036,4037,4038,5893,5896,6521,7266,7738,9178,9878,9879); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -63 WHERE `entry` IN (2745,8281,8909,8910,8911,9026,9376); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -64 WHERE `entry` IN (14460); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -65 WHERE `entry` IN (13601); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -66 WHERE `entry` IN (13282); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -67 WHERE `entry` IN (30829,30830,30831); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -68 WHERE `entry` IN (2667,2671,7076,7077,7309,8376,8816,9657,15104,15108,15429,15631,15698,15706,15904,15910,15964,15973,16511); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -69 WHERE `entry` IN (26688); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -70 WHERE `entry` IN (16700,17732,19632,20174,24179,29273); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -71 WHERE `entry` IN (14261,14262,14263,14264,14265,20588,20589); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -72 WHERE `entry` IN (17816,17817,19884,19892); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -73 WHERE `entry` IN (19513,19598,19865,21560,21561,21562); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -74 WHERE `entry` IN (17465,20583,22955); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -75 WHERE `entry` IN (32361,32377,32409,32417,32422,32429,32447,32475,32481,32485); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -76 WHERE `entry` IN (22965); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -77 WHERE `entry` IN (24180); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -78 WHERE `entry` IN (24065); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -79 WHERE `entry` IN (21515); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -80 WHERE `entry` IN (24530); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -81 WHERE `entry` IN (18322,20696); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -82 WHERE `entry` IN (15146,25363); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -83 WHERE `entry` IN (17864,18848,20656,20881,20900,21619,21621,25592,25948); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -84 WHERE `entry` IN (17818); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -85 WHERE `entry` IN (7728); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -86 WHERE `entry` IN (25798,25799); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -87 WHERE `entry` IN (26928,26929,26930,30511,30512,30513); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -88 WHERE `entry` IN (18182); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -89 WHERE `entry` IN (32500); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -90 WHERE `entry` IN (17731,20173); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -91 WHERE `entry` IN (19191); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -92 WHERE `entry` IN (19261); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -93 WHERE `entry` IN (3,36,114,115,200,201,204,210,302,314,315,392,412,480,503,511,522,570,572,573,604,624,625,626,725,846,882,948,1159,1270,1488,1489,1491,1501,1502,1525,1526,1527,1528,1529,1530,1532,1533,1654,1656,1663,1666,1674,1675,1696,1716,1717,1720,1753,1772,1773,1790,1791,1792,1793,1794,1795,1796,1798,1805,1847,1849,1850,1852,1866,1868,1917,1918,1919,1921,1939,1940,1941,1942,1943,1944,1948,1971,1974,2045,2056,2217,2218,2219,2220,2221,2222,2227,2278,2288,2325,2433,2475,2479,2520,2531,2535,2536,2537,2623,2676,2678,2748,2763,2922,3094,3301,3538,3558,3565,3617,3654,3669,3670,3671,3673,3674,3799,3802,3803,3869,3870,3876,3914,3940,3941,3942,3983,4073,4074,4251,4252,4260,4308,4473,4474,4475,4476,4550,4566,4606,4610,4612,4854,4872,4945,4946,4958,5097,5202,5263,5267,5270,5271,5400,5621,5624,5626,5627,5674,5685,5686,5687,5712,5713,5714,5715,5716,5717,5723,5775,5854,5912,5954,6036,6092,6106,6107,6108,6225,6226,6227,6230,6231,6232,6233,6234,6346,6386,6426,6427,6486,6493,6669,6906,6907,6908,6910,7023,7039,7050,7067,7068,7069,7070,7071,7072,7075,7166,7206,7209,7270,7273,7274,7276,7286,7291,7310,7327,7328,7329,7332,7333,7334,7340,7347,7348,7349,7351,7352,7353,7354,7355,7356,7357,7358,7374,7375,7503,7504,7527,7624,7784,7791,7806,7807,7849,7895,7897,8035,8447,8500,8501,8530,8531,8532,8534,8535,8536,8537,8543,8544,8545,8552,8555,8556,8557,8558,8559,8567,8580,8585,8615,8663,8856,9437,9438,9439,9441,9442,9443,9476,9623,10080,10081,10082,10117,10381,10382,10383,10395,10397,10402,10403,10405,10406,10407,10408,10409,10411,10412,10413,10414,10416,10417,10463,10464,10479,10480,10481,10483,10484,10492,10493,10494,10495,10497,10498,10499,10500,10580,10666,10684,10698,10699,10778,10801,10810,10818,10819,10820,10821,10825,10836,10926,10927,10936,10937,10939,10943,10944,10945,10946,10949,10950,10951,10953,10954,11027,11030,11075,11082,11151,11153,11154,11155,11179,11195,11199,11216,11217,11263,11277,11278,11279,11280,11281,11282,11283,11286,11287,11290,11291,11292,11316,11412,11414,11415,11468,11469,11470,11471,11472,11473,11474,11475,11477,11517,11518,11519,11520,11551,11580,11598,11628,11684,11875,11936,12238,12239,12240,12241,12242,12243,12248,12250,12261,12262,12263,12363,12364,12365,12367,12368,12379,12380,12385,12473,12865,13146,13378,13416,13619,13738,13739,13740,13741,13742,14224,14308,14337,14358,14364,14484,14485,14494,14511,14512,14513,14514,14518,14519,14520,14521,14551,14552,14553,14554,14562,14563,14605,14683,14685,14686,14687,14688,14691,14692,14694,14695,14696,14697,14698,14699,14700,14701,14702,14703,14704,14705,14706,14707,14708,14709,14710,14711,14712,14713,14714,14825,14826,15079,15081,15117,15121,15195,15349,15364,15368,15374,15376,15377,15547,15548,15551,15654,15655,15656,15657,15658,15665,15720,15958,16025,16026,16039,16040,16059,16066,16067,16093,16099,16103,16104,16119,16120,16125,16138,16141,16145,16146,16148,16153,16154,16159,16163,16169,16184,16211,16245,16246,16247,16248,16249,16250,16298,16300,16301,16302,16303,16305,16306,16307,16308,16309,16311,16312,16313,16314,16316,16319,16320,16321,16322,16323,16324,16325,16326,16327,16328,16329,16342,16357,16382,16383,16388,16389,16390,16393,16394,16402,16403,16406,16407,16408,16409,16410,16411,16412,16415,16424,16425,16426,16432,16437,16439,16451,16459,16460,16468,16470,16471,16481,16482,16525,16526,16805,16806,16811,16812,16813,16814,16815,16861,16904,16905,16906,16976,16977,16978,17007,17063,17064,17067,17086,17261,17415,17466,17503,17588,17589,17592,17612,17660,17672,17674,17714,17878,17895,17897,17898,17902,17903,17905,17906,18043,18287,18319,18320,18327,18355,18367,18441,18460,18478,18498,18499,18500,18501,18506,18521,18524,18556,18557,18558,18559,18588,18643,18689,18700,18797,18872,18873,19416,19417,19460,19463,19464,19488,19489,19543,19544,19545,19546,19579,19580,19682,19698,19719,19736,19749,19751,19825,19826,19827,19846,19863,19864,19872,19873,19874,19875,19876,19878,19879,19881,19937,20117,20137,20298,20303,20305,20310,20311,20312,20313,20315,20316,20317,20319,20320,20321,20322,20323,20409,20410,20480,20483,20495,20496,20512,20662,20691,20697,20698,20934,21049,21058,21065,21093,21163,21184,21198,21199,21200,21201,21202,21324,21354,21384,21385,21386,21430,21446,21449,21450,21452,21628,21636,21638,21651,21763,21778,21779,21784,21787,21788,21795,21797,21801,21815,21866,21867,21869,21870,21941,22025,22041,22045,22062,22138,22226,22235,22369,22441,22452,22454,22701,22706,23066,23067,23068,23103,23109,23111,23126,23161,23193,23204,23370,23371,23389,23408,23503,23509,23545,23554,23555,23561,23562,23563,23575,23643,23644,23645,23786,23861,23898,23904,23935,23970,23992,23993,24013,24019,24027,24029,24041,24073,24074,24084,24118,24153,24168,24177,24207,24246,24247,24248,24258,24262,24266,24267,24272,24327,24344,24440,24446,24447,24485,24546,24562,24563,24564,24566,24621,24622,24623,24624,24625,24626,24627,24693,24695,24712,24789,24790,24796,24808,24814,24871,24872,24874,24875,24876,24877,24880,25027,25028,25041,25224,25227,25228,25268,25294,25296,25330,25331,25332,25333,25350,25351,25352,25359,25365,25366,25375,25377,25382,25383,25386,25387,25393,25396,25445,25451,25452,25453,25462,25463,25465,25469,25542,25546,25559,25582,25600,25611,25619,25622,25625,25629,25650,25652,25655,25660,25668,25678,25682,25684,25981,26073,26076,26103,26115,26125,26126,26165,26195,26202,26203,26224,26225,26250,26252,26286,26287,26292,26294,26317,26318,26320,26336,26343,26344,26402,26404,26413,26455,26457,26458,26461,26475,26490,26491,26492,26501,26509,26515,26517,26518,26526,26536,26555,26570,26573,26605,26606,26607,26608,26621,26623,26624,26635,26636,26637,26638,26658,26669,26670,26676,26690,26691,26692,26694,26696,26702,26703,26769,26770,26771,26811,26812,26824,26830); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -93 WHERE `entry` IN (26840,26841,26858,26871,26891,26946,26948,26966,26967,27018,27059,27105,27122,27152,27153,27165,27219,27220,27224,27225,27226,27229,27240,27241,27268,27270,27272,27282,27283,27284,27285,27286,27287,27288,27290,27335,27360,27362,27363,27370,27383,27400,27401,27410,27465,27505,27510,27513,27531,27533,27534,27551,27552,27556,27561,27604,27605,27611,27614,27616,27618,27620,27623,27624,27630,27631,27680,27683,27685,27686,27691,27693,27694,27696,27712,27729,27733,27734,27736,27737,27766,27767,27768,27769,27770,27771,27772,27773,27774,27775,27776,27777,27778,27779,27780,27781,27782,27797,27799,27800,27807,27808,27809,27821,27822,27823,27824,27825,27829,27835,27836,27848,27854,27871,27874,27941,27957,28005,28006,28007,28014,28018,28022,28023,28026,28101,28103,28104,28108,28144,28158,28159,28162,28170,28194,28199,28200,28201,28207,28208,28211,28212,28218,28220,28232,28242,28243,28246,28249,28255,28257,28258,28268,28270,28278,28349,28350,28356,28357,28364,28365,28369,28377,28383,28395,28405,28406,28412,28414,28419,28420,28421,28422,28423,28424,28425,28426,28427,28428,28429,28430,28431,28432,28433,28434,28435,28436,28437,28438,28444,28445,28446,28447,28448,28449,28450,28471,28472,28474,28475,28476,28487,28488,28498,28499,28500,28503,28506,28510,28512,28519,28528,28534,28564,28565,28570,28589,28590,28599,28603,28615,28628,28641,28642,28647,28651,28653,28654,28657,28658,28659,28666,28669,28670,28711,28732,28733,28734,28735,28736,28745,28747,28748,28749,28750,28752,28754,28756,28759,28760,28768,28769,28782,28788,28793,28802,28803,28805,28843,28864,28866,28867,28868,28869,28870,28871,28872,28875,28878,28879,28889,28890,28897,28901,28903,28905,28906,28907,28908,28909,28910,28911,28913,28914,28915,28919,28924,28925,28930,28931,28933,28934,28937,28943,28948,28998,29030,29031,29047,29051,29053,29062,29063,29064,29096,29097,29098,29101,29106,29107,29108,29109,29110,29111,29112,29113,29115,29117,29118,29119,29123,29128,29129,29133,29136,29153,29172,29173,29183,29185,29186,29187,29188,29189,29190,29191,29193,29194,29195,29196,29197,29198,29199,29200,29201,29202,29204,29205,29206,29207,29208,29209,29210,29212,29213,29214,29216,29217,29219,29221,29227,29230,29231,29232,29238,29239,29245,29246,29256,29263,29280,29286,29335,29340,29347,29349,29353,29354,29355,29356,29359,29362,29363,29371,29388,29394,29400,29414,29449,29450,29451,29453,29468,29480,29488,29501,29517,29519,29520,29522,29565,29566,29567,29570,29574,29576,29587,29609,29629,29632,29633,29634,29635,29646,29648,29654,29656,29697,29699,29704,29719,29720,29722,29738,29769,29770,29821,29823,29824,29825,29828,29831,29833,29835,29837,29840,29842,29851,29852,29856,29858,29859,29860,29863,29872,29882,29887,29890,29891,29892,29893,29894,29895,29897,29898,29899,29900,29901,29934,29935,29943,29974,29985,29986,29987,29988,29989,29990,30015,30016,30018,30027,30028,30029,30030,30031,30032,30033,30034,30047,30048,30049,30071,30074,30075,30083,30085,30087,30097,30135,30144,30150,30202,30203,30204,30205,30216,30230,30232,30235,30250,30264,30276,30277,30278,30283,30284,30285,30286,30287,30288,30303,30306,30307,30309,30310,30311,30333,30335,30338,30378,30389,30403,30404,30406,30409,30424,30432,30443,30471,30482,30486,30501,30523,30541,30542,30543,30544,30545,30546,30575,30593,30594,30597,30609,30629,30670,30687,30689,30696,30697,30698,30701,30728,30746,30778,30791,30806,30816,30817,30818,30819,30821,30822,30823,30836,30843,30851,30863,30864,30865,30894,30895,30920,30921,30922,30944,30945,30946,30947,30949,30951,30952,30953,30954,30955,30956,30957,30958,30960,30984,30985,30986,30987,30988,30989,30992,30993,31012,31014,31015,31029,31037,31039,31040,31042,31043,31048,31078,31082,31083,31084,31087,31088,31089,31090,31094,31095,31096,31097,31098,31099,31100,31104,31107,31110,31123,31135,31137,31139,31140,31141,31142,31147,31150,31152,31154,31155,31157,31159,31160,31161,31163,31178,31179,31183,31184,31187,31188,31191,31192,31193,31194,31195,31196,31198,31199,31200,31205,31207,31208,31220,31221,31222,31223,31224,31225,31226,31227,31231,31237,31251,31255,31263,31266,31268,31271,31274,31278,31283,31301,31318,31320,31321,31322,31323,31324,31325,31326,31327,31341,31342,31346,31347,31351,31352,31354,31355,31357,31361,31363,31396,31398,31411,31413,31438,31442,31443,31447,31449,31450,31451,31457,31460,31466,31468,31530,31531,31532,31554,31555,31556,31565,31583,31585,31586,31587,31588,31589,31590,31591,31593,31594,31595,31596,31597,31598,31599,31600,31601,31602,31603,31604,31605,31606,31607,31608,31609,31613,31614,31635,31647,31671,31681,31692,31702,31718,31721,31754,31775,31779,31780,31783,31787,31812,31813,31815,31843,31844,31847,31853,31900,32031,32037,32149,32160,32161,32163,32164,32175,32176,32178,32179,32181,32182,32183,32184,32188,32255,32257,32267,32272,32278,32280,32283,32284,32288,32292,32299,32300,32309,32323,32326,32350,32390,32391,32404,32408,32410,32423,32446,32467,32479,32480,32482,32483,32490,32492,32497,32499,32502,32503,32505,32507,32508,32511,32556,32593,32609,32767,32769,32770,32771,32772,32786,32787,33016,33017,33429,33438,33441,33499,33513,33519,33550,33663,33664,33667,33668,33687,33704,33798,34127,34154,34238,34801,35169,35255,35444,35461,35474,35493,35547,35557,35559,35560,35652,35653,35654,35763,36065,36066,36128,36173,36478,36503,36504,36522,36564,36595,36666,36677,36724,36725,36726,36792,36794,36796,36807,36829,36830,36840,36841,36842,36844,36875,36877,36879,36880,36881,36886,36893,36896,36907,36916,36940,36941,37011,37012,37022,37023,37038,37069,37120,37128,37228,37229,37232,37351,37357,37491,37493,37494,37495,37501,37502,37530,37532,37535,37538,37539,37541,37542,37544,37545,37546,37549,37550,37551,37563,37565,37569,37571,37586,37595,37612,37622,37635,37636,37637,37638,37639,37640,37642,37644,37655,37656,37657,37658,37662,37663,37664,37665,37666,37678,37711,37728,37729,37730,37731,37755,37845,37846,37857,37881,37893,37906,37945,37976,38004,38057,38058,38059,38061,38062,38063,38073,38074,38077,38098,38099,38100,38101,38102,38104,38105,38108,38134,38137,38151); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -93 WHERE `entry` IN (38172,38173,38175,38176,38177,38184,38185,38186,38193,38197,38198,38216,38222,38249,38260,38308,38309,38349,38350,38351,38352,38362,38367,38386,38388,38391,38392,38410,38419,38429,38430,38445,38446,38479,38480,38481,38487,38524,38525,38544,38545,38563,38564,38567,38568,38572,38610,38763,38857,38883,39048,39231,39232,39233,39234,39639,40274); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -94 WHERE `entry` IN (36824,37007,38031,39296); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -95 WHERE `entry` IN (14986,15163); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -96 WHERE `entry` IN (11446,38248); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -97 WHERE `entry` IN (11284); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -98 WHERE `entry` IN (12876); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -99 WHERE `entry` IN (28912); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -100 WHERE `entry` IN (25597,37695,39309,39310,39311); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -101 WHERE `entry` IN (8836); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -102 WHERE `entry` IN (7915,16111); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -103 WHERE `entry` IN (14682); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -104 WHERE `entry` IN (18259,18648); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -105 WHERE `entry` IN (20682); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -106 WHERE `entry` IN (20783,20784,20785,20786,20788,20789,20790); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -107 WHERE `entry` IN (14684,14690); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -108 WHERE `entry` IN (14693); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -109 WHERE `entry` IN (17725,17871,20188,20190); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -110 WHERE `entry` IN (2447); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -111 WHERE `entry` IN (22848); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -112 WHERE `entry` IN (11447,11497,11498); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -113 WHERE `entry` IN (1157,1158,1160,1534,1655,1800,1801,1802,1804,1946,1983,2044,2176,2177,2178,2638,3667,3801,4472,6116,6117,6133,7073,7074,7370,7523,7524,7864,8538,8539,8540,8541,8542,10387,10388,10389,10940,10947,10948,11064,11078,11141,11285,11288,11289,11296,11560,11620,11621,11686,11687,11873,12178,12179,12199,12377,12378,14564,16423); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -114 WHERE `entry` IN (1531,2184,3863,3872,3873,3875,3877,6118,10358,10384,10385,10938,10996,16143,16379); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -115 WHERE `entry` IN (18856); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -116 WHERE `entry` IN (6021,9299,10988); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -117 WHERE `entry` IN (15554); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -118 WHERE `entry` IN (22338); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -119 WHERE `entry` IN (23269); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -120 WHERE `entry` IN (19354); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -121 WHERE `entry` IN (17724,20185,20465,21943); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -122 WHERE `entry` IN (48,202,203,531,623,785,787,1110,1520,1522,1523,1657,1658,1783,1784,1785,1787,1788,1789,1865,1869,1870,1871,1890,1916,1973,2454,6388,6412,7343,7344,7346,7786,8477,8523,8524,8525,8526,8527,8528,8529,8884,10390,10391,10482,10816,10952,11076,11077,11156,11197,11200,11258,11476,11547,12208,12341,12342,12343,12344,14486,14489,14558,16299,16422,16438); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -123 WHERE `entry` IN (10485); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -124 WHERE `entry` IN (771,2283,2946,7341,7342,7345,10393,10394,10478,10486,10487,10488,10489,10491,10826,16380); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -125 WHERE `entry` IN (11561); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -126 WHERE `entry` IN (22953); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -127 WHERE `entry` IN (5711); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -128 WHERE `entry` IN (23401); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -129 WHERE `entry` IN (28443); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -130 WHERE `entry` IN (19307,20265); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -131 WHERE `entry` IN (17958); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -132 WHERE `entry` IN (3253,36551,37564); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -133 WHERE `entry` IN (20047); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -134 WHERE `entry` IN (12557); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -135 WHERE `entry` IN (22853); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -136 WHERE `entry` IN (22869,23339); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -137 WHERE `entry` IN (20875,21604); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -138 WHERE `entry` IN (21102); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -139 WHERE `entry` IN (21181,21251); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -140 WHERE `entry` IN (16504); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -141 WHERE `entry` IN (20866,21614); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -142 WHERE `entry` IN (22954); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -143 WHERE `entry` IN (20898,21598); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -144 WHERE `entry` IN (16414,16473); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -145 WHERE `entry` IN (35644,36558); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -146 WHERE `entry` IN (33060,33062,33067,33109,33167,34045); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -147 WHERE `entry` IN (19797); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -148 WHERE `entry` IN (17454); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -149 WHERE `entry` IN (17940,19891,21127); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -150 WHERE `entry` IN (21843); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -151 WHERE `entry` IN (21270,21274); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -152 WHERE `entry` IN (20873,21605); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -153 WHERE `entry` IN (20175); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -154 WHERE `entry` IN (17726,17735,20191,20193); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -155 WHERE `entry` IN (17727,20192); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -156 WHERE `entry` IN (20908,20909,21616,21617); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -157 WHERE `entry` IN (22849); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -158 WHERE `entry` IN (14349); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -159 WHERE `entry` IN (644); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -160 WHERE `entry` IN (15334,15725,15726,15728,15802); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -161 WHERE `entry` IN (18497,20299); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -162 WHERE `entry` IN (21694,21914,24068,25372,31655); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -163 WHERE `entry` IN (18796,20652); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -164 WHERE `entry` IN (17907); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -165 WHERE `entry` IN (20043,20044); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -166 WHERE `entry` IN (21268,21269,21271,21272,21273); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -167 WHERE `entry` IN (14987); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -168 WHERE `entry` IN (15630,15984); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -169 WHERE `entry` IN (25334,26523,27881,27894,28094,28312,28319,28366,28781,32627,32629,32795,32796,34775,34776,34777,34778,34793,34802,34922,34924,34929,34935,34944,35069,35273,35403,35405,35410,35413,35415,35417,35419,35421,35427,35429,35431,35433,35436,35819,36355,36356,36357,36358); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -170 WHERE `entry` IN (20992); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -171 WHERE `entry` IN (16507); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -172 WHERE `entry` IN (20593); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -173 WHERE `entry` IN (16857); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -174 WHERE `entry` IN (18554); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -175 WHERE `entry` IN (23586); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -176 WHERE `entry` IN (19886); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -177 WHERE `entry` IN (34657,34701,34705); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -178 WHERE `entry` IN (22845,23374); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -179 WHERE `entry` IN (12339); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -180 WHERE `entry` IN (10814); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -181 WHERE `entry` IN (23402); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -182 WHERE `entry` IN (23403); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -183 WHERE `entry` IN (23400); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -184 WHERE `entry` IN (6584); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -185 WHERE `entry` IN (5855); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -186 WHERE `entry` IN (3917); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -187 WHERE `entry` IN (17827,20165); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -188 WHERE `entry` IN (22930); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -189 WHERE `entry` IN (17671,20584,20993); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -190 WHERE `entry` IN (23196); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -191 WHERE `entry` IN (22880,22956,22964,23222,23239,23337,23399); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -192 WHERE `entry` IN (22199); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -193 WHERE `entry` IN (18680,22877,22884); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -194 WHERE `entry` IN (22960); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -195 WHERE `entry` IN (22844,22847,22873,22963); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -196 WHERE `entry` IN (22878,23028,23030); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -197 WHERE `entry` IN (34605,34650,35658,35659); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -198 WHERE `entry` IN (40683,40684); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -199 WHERE `entry` IN (40681,40682); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -200 WHERE `entry` IN (36561,36571); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -201 WHERE `entry` IN (40417,40418,40419,40420,40421,40422,40423,40424); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -202 WHERE `entry` IN (37890,37949,38393,38394,38625,38626,38628,38629); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -203 WHERE `entry` IN (18692); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -204 WHERE `entry` IN (38009,38010,38135,38136,38395,38396,38397,38398,38630,38631,38632,38633,38634,38635,39000,39001); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -205 WHERE `entry` IN (38508,38596,38597,38598); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -206 WHERE `entry` IN (38472,38485); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -207 WHERE `entry` IN (34607,34648,35655,35656); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -208 WHERE `entry` IN (37863,38171,38727,38737); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -209 WHERE `entry` IN (36791,37868,37886,37934,38166,38167,38169,38170,38721,38722,38723,38724,38725,38733,38734,38735); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -210 WHERE `entry` IN (20032); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -211 WHERE `entry` IN (20040); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -212 WHERE `entry` IN (20035); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -213 WHERE `entry` IN (20050); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -214 WHERE `entry` IN (22846); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -215 WHERE `entry` IN (20216,20600,22910); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -216 WHERE `entry` IN (16545,22911); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -217 WHERE `entry` IN (23330); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -218 WHERE `entry` IN (37799,39284,39285,39286); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -219 WHERE `entry` IN (36609,39120,39121,39122); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -220 WHERE `entry` IN (33346,33886); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -221 WHERE `entry` IN (22075); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -222 WHERE `entry` IN (20692); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -223 WHERE `entry` IN (18836); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -224 WHERE `entry` IN (11347); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -225 WHERE `entry` IN (11348); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -226 WHERE `entry` IN (17256); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -227 WHERE `entry` IN (22074); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -228 WHERE `entry` IN (14101); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -229 WHERE `entry` IN (639,642,643,645,646,647,1763,3586,3886,3887,3927,4274,4275,4278,4279,4420,4421,4422,4424,4428,4829,4830,4831,4832,4842,6168,6228,6229,6235,6243,6488,6489,6490,7361,10439,11356,12203,12225,12236,12258,12902,13021,13596); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -230 WHERE `entry` IN (7800); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -231 WHERE `entry` IN (12237); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -232 WHERE `entry` IN (7079); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -233 WHERE `entry` IN (24683,24684,24686,24687,24688,24689,24690,24762,25565,25567,25568,25570,25572,25575,25576,25577); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -234 WHERE `entry` IN (24685,25569); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -235 WHERE `entry` IN (12803); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -236 WHERE `entry` IN (19389,21350); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -237 WHERE `entry` IN (18829); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -238 WHERE `entry` IN (8923,9217,9218,9219,9718,10263,10809,11467); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -239 WHERE `entry` IN (30385,31474); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -240 WHERE `entry` IN (17723,20164); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -241 WHERE `entry` IN (11663); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -242 WHERE `entry` IN (21346,21612); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -243 WHERE `entry` IN (23061,23261,23281,23282); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -244 WHERE `entry` IN (18731,20636); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -245 WHERE `entry` IN (22076); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -246 WHERE `entry` IN (17990,20189); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -247 WHERE `entry` IN (17796,20630); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -248 WHERE `entry` IN (8929,9025,9502,10076,14321,14323,14326); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -249 WHERE `entry` IN (12422); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -250 WHERE `entry` IN (10376,10596); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -251 WHERE `entry` IN (8983,9018,9019,9024,9027,9028,9029,9030,9031,9032,9033,9034,9035,9036,9037,9038,9039,9040,9041,9042,9056,9156,9196,9236,9237,9319,9537,9543,9568,9736,9938,10220,10268,10339,10363,10429,10430,10503,10509,10584,10812,11486,11487,11488,11489,11490,11492,11496,11501,12459,12461,12463,12464,14322,14324,14325,14327,14354,14502,14506,15229,15230,15233,15235,15236,15240,15246,15249,15250,15252,15262,15264,15277,15311,15312,16042,16080,16097); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -252 WHERE `entry` IN (9016,9017,9816); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -253 WHERE `entry` IN (13280); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -254 WHERE `entry` IN (11662,12460,12467); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -255 WHERE `entry` IN (11981,11983,14601); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -256 WHERE `entry` IN (17862,20521); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -257 WHERE `entry` IN (24238); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -258 WHERE `entry` IN (1840); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -259 WHERE `entry` IN (20901,20902,21610,21611); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -260 WHERE `entry` IN (21218,21221,21298,21299,21301,21339,21508); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -261 WHERE `entry` IN (20885,20886,21466,21467,21590,21599,21600,21624); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -262 WHERE `entry` IN (17826,17882,18105,20168,20183,20184); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -263 WHERE `entry` IN (33836,34218); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -264 WHERE `entry` IN (30245,30249,31750,31751); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -265 WHERE `entry` IN (33202,33398); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -266 WHERE `entry` IN (15516); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -267 WHERE `entry` IN (32857,33694); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -268 WHERE `entry` IN (36272,36296,36565); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -269 WHERE `entry` IN (32916,33400); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -270 WHERE `entry` IN (12899); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -271 WHERE `entry` IN (10664); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -272 WHERE `entry` IN (16472); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -273 WHERE `entry` IN (929,1284,1748,2425,3974,3975,3976,3977,4542,4543,6487,7228,9095,10156,10162,10432,10433,10435,10436,10437,10438,10502,10504,10505,10506,10507,10508,10558,10808,10811,10813,10901,10942,10997,11032,11142,11261,11380,11439,11622,11946,11947,11948,11949,11978,11982,12129,12756,12804,13256,13419,14352,14507,14509,14510,14515,14517,14831,14861,14988,15083,15084,15085,15114,15192,15204,15205,15299,15302,15348,15362,15378,15379,15380,15381,15382,15410,15411,15412,15413,15427,15481,15491,15510,15511,15514,15543,15544,15571,15608,15625,15628,15633,15688,15776,15963,16077,16101,16102,16118,16128,16440,16524,16775,16776,16777,16778,16816,17008,17009,17010,17011,17012,17026,17076,17233,17354,17380,17381,17500,17533,17534,17651,17772,17797,17798,17852,17874,17941,17942,17948,17949,17991,18040,18041,18060,18063,18075,18076,18141,18254,18338,18343,18344,18371,18373,18472,18601,18621,18624,18667,18728,18780,18932,19214,19556,19597,19604,19647,19893,19894,19895,20266,20268,20306,20318,20629,20633,20637,20690,21003,21768,21823,21845,21932,22409,22522,22605,22606,22627,22629,22641,22644,22996,23035,23054,23230,23426,23467,23499,23508,23759,23812,23877,23878,23879,23880,23899,23912,23913,23914,23944,24143,24144,24505,31055,31818,31819,31820,31821,31822,32918,32919,33399,33401,37243,37244,37283,37444); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -274 WHERE `entry` IN (14834,15082,15369,15370,15509,17521,23578,23863,24239); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -275 WHERE `entry` IN (15667,20481,21538); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -276 WHERE `entry` IN (7846,8680,15203); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -277 WHERE `entry` IN (14435); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -278 WHERE `entry` IN (15305); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -279 WHERE `entry` IN (15552); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -280 WHERE `entry` IN (11382,15263,23576,23577); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -281 WHERE `entry` IN (12017); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -282 WHERE `entry` IN (15275); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -283 WHERE `entry` IN (37226); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -284 WHERE `entry` IN (2784,3057,3516,4949,4968,7937,7999,10181,10540,20888,21224,21227,22825,22826,22827,22828,22920); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -285 WHERE `entry` IN (16800,16802,17468,20906,21606,29611,30057,32295,33350,34057,34115); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -286 WHERE `entry` IN (16028,21964,22951,23953,23954,23980,24200,24201,26529,26530,26532,26533,27389,27390,28684,29249,29268,29278,29305,29311,29315,29324,29373,29417,29446,29447,29448,29615,29701,29718,29940,29955,29991,30061,30452,30530,30549,30600,30601,30602,30603,30748,31125,31211,31212,31215,31217,31464,31507,31534,31612,31656,31657,31673,31674,31679,31680,31722,32273,32313,32353,32368,32865,33147,33388,33453,33515,33846,33850,33851,33852,33985,33986,33988,33989,33993,33994,34015,34016,34175,34496,34497,34780,35013,35143,35216,35268,35269,35347,35348,35349,35350,35351,35352,35359,35360,36064,36067,36476,36477,36494,36502,36612,36658,36723,36855,36938,37613,37627,37629,37677,37720,37813,37957,37958,37959,38106,38112,38113,38402,38433,38462,38482,38483,38582,38583,38599,38603); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -287 WHERE `entry` IN (19622,23419,33271,33449,33524,34152,38296,38297); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -288 WHERE `entry` IN (15193); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -289 WHERE `entry` IN (20905,20910,20911,21588,21589,21618,21838); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -290 WHERE `entry` IN (24560,25560); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -291 WHERE `entry` IN (20896,21702); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -292 WHERE `entry` IN (11980,16807,16808,17711,17734,18481,20039,20045,20046,20049,20187,20568,20597,20864,20867,20868,20879,20880,21174,21303,21304,21591,21592,21593,21594,21595,21596,21608,21623,22330,22346,23397,24723,24744,25562,25573); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -293 WHERE `entry` IN (16809,20596); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -294 WHERE `entry` IN (6109); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -295 WHERE `entry` IN (20036,20037,24664,24857); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -296 WHERE `entry` IN (25772); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -297 WHERE `entry` IN (4814); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -298 WHERE `entry` IN (92); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -299 WHERE `entry` IN (329,2359,7031,7032,8324,14331); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -300 WHERE `entry` IN (575,5850,5852,6520,17003); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -301 WHERE `entry` IN (2735,2791); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -302 WHERE `entry` IN (510,691,5461,10756,10757,10955); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -303 WHERE `entry` IN (16193); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -304 WHERE `entry` IN (11321); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -305 WHERE `entry` IN (11665,11672,12076,12099,12100,12101); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -306 WHERE `entry` IN (14401); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -307 WHERE `entry` IN (16336,16338); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -308 WHERE `entry` IN (15141); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -309 WHERE `entry` IN (11669); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -310 WHERE `entry` IN (12457); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -311 WHERE `entry` IN (11658,11659,11661,11671,11673,11988,12057,12098,12118,12259,12264,14081,14465,14466,14751,14752); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -312 WHERE `entry` IN (11502,11667,11668,12056,12265); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -313 WHERE `entry` IN (24722,25552); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -314 WHERE `entry` IN (30258,31463); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -315 WHERE `entry` IN (14464); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -316 WHERE `entry` IN (14461); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -317 WHERE `entry` IN (14454); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -318 WHERE `entry` IN (14457); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -319 WHERE `entry` IN (10264); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -320 WHERE `entry` IN (15748,15751,15754,15758,15807,15810); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -321 WHERE `entry` IN (11664,15747,15753,15757,15812); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -322 WHERE `entry` IN (10516); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -323 WHERE `entry` IN (12465); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -324 WHERE `entry` IN (9499,12468,15247); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -325 WHERE `entry` IN (1842,5709,5710,5719,5720,5721,5722,7267,7271,7272,7275,7604,7795,7796,7797,8127,8443,11878,12018,15743,15744,15750,15806,15813,15814,15815,15816,15817,15818); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -326 WHERE `entry` IN (12201); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -327 WHERE `entry` IN (10184); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -328 WHERE `entry` IN (15286,15288,15290); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -329 WHERE `entry` IN (14887,14888,14889,14890); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -330 WHERE `entry` IN (14662,14663,14664,14666); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -331 WHERE `entry` IN (14503); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -332 WHERE `entry` IN (22855); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -333 WHERE `entry` IN (17770,20169); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -334 WHERE `entry` IN (15689); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -335 WHERE `entry` IN (30084); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -336 WHERE `entry` IN (32187); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -337 WHERE `entry` IN (22056); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -338 WHERE `entry` IN (25588); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -339 WHERE `entry` IN (37698,39299,39300,39301); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -340 WHERE `entry` IN (22952,34815,35262); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -341 WHERE `entry` IN (19551); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -342 WHERE `entry` IN (21362); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -343 WHERE `entry` IN (33203,33376); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -344 WHERE `entry` IN (34147,34148); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -345 WHERE `entry` IN (17767,17808,17842,17888); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -346 WHERE `entry` IN (1853,12397,15589,15690,15727,17535,18341,18473,18708,18732,20267,20653,20657,20706); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -347 WHERE `entry` IN (12435,15517,17225,17968,23574); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -348 WHERE `entry` IN (15206,15207,15208,15220,15467,15740,15741,15742,18168,33052,33116); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -349 WHERE `entry` IN (14020); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -350 WHERE `entry` IN (10340,10538); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -351 WHERE `entry` IN (15276); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -352 WHERE `entry` IN (11583,13020); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -353 WHERE `entry` IN (25741); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -354 WHERE `entry` IN (23682,23709); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -355 WHERE `entry` IN (18832,18834,18835,29306,29310,31368,31465,34796,35438); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -356 WHERE `entry` IN (15550,16152); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -357 WHERE `entry` IN (27386,30178,30756,31448); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -358 WHERE `entry` IN (22009); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -359 WHERE `entry` IN (32877,33155); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -360 WHERE `entry` IN (17281,17455,18051,18055,18069,18398,18399,18400,18401,18402,21369,21875,21920,22055,22119,22120,22140,22856,22997,23421,23469,33699,33700,33715,33722,33723,33756,33772,33773,34069,34133,34134,34139,34141,34164,34165,34185,34197,34226,37227,37488); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -361 WHERE `entry` IN (10404,17306,17308,17536,17537,17848,17879,17881,17975,17976,17977,17978,17980,18096,18432,18433,18434,18436,18805,18831,18945,19218,19219,19220,19221,19710,20243,20531,20535,20737,20738,21212,21213,21214,21217,21525,21526,21533,21536,21537,21551,21558,21559,21581,21582,21697,21712,21958,21965,21966,22887,22898,23089,23191,23197,23375,23420,23965,24850,24882,24891,24892,25502,25708,26630,26631,26632,26668,26683,26684,26685,26686,26687,26693,26723,26731,26763,26794,26796,26798,26861,26893,26918,27447,27483,27654,27655,27656,27975,27977,27978,28132,28171,28546,28586,28587,28729,28730,28731,28859,28860,28921,28922,28923,29120,29266,29281,29304,29307,29308,29309,29312,29313,29314,29316,29573,29932,30114,30176,30397,30398,30449,30451,30510,30522,30529,30532,30540,30770,30772,30774,30775,30788,30790,30803,30807,30809,30810,31134,31311,31349,31350,31360,31362,31365,31367,31370,31381,31384,31386,31441,31456,31469,31473,31506,31508,31509,31510,31511,31512,31520,31533,31535,31536,31537,31538,31558,31559,31560,31561,31592,31610,31611,31615,31616,31617,31672,31734,32845,32846,32867,32871,32872,32873,32874,32875,32876,32878,32882,32883,32885,32886,32904,32906,32907,32908,32913,32914,32915,32927,32930,32933,32934,32955,33070,33089,33118,33121,33148,33149,33150,33151,33152,33153,33154,33156,33157,33158,33159,33161,33162,33163,33186,33190,33191,33228,33288,33293,33329,33344,33360,33385,33391,33392,33393,33432,33433,33567,33651,33670,33692,33693,33716,33717,33718,33719,33720,33724,33885,33888,33890,33909,33910,33911,33943,33954,33955,33959,33966,33967,33983,33984,33990,33995,34004,34014,34035,34071,34097,34106,34108,34109,34166,34171,34215,34221,34222,34564,34566,34660,34797,34799,34800,34813,34825,34826,34928,34942,35028,35029,35030,35031,35032,35033,35034,35036,35037,35038,35039,35040,35041,35042,35043,35044,35045,35046,35047,35048,35049,35050,35051,35052,35119,35144,35263,35264,35265,35266,35267,35270,35271,35272,35278,35279,35280,35311,35439,35440,35441,35442,35443,35447,35448,35449,35451,35490,35511,35512,35513,35514,35515,35516,35517,35518,35519,35520,35521,35522,35523,35524,35525,35527,35528,35529,35530,35531,35532,35533,35534,35535,35536,35537,35538,35539,35540,35541,35542,35543,35544,35615,35616,36135,36497,36498,36597,36626,36627,36678,36701,36789,36838,36839,36853,36899,36939,36948,36950,36954,36957,36960,36961,36968,36969,36970,36971,36978,36982,37025,37098,37116,37117,37126,37182,37184,37215,37217,37230,37504,37505,37506,37531,37533,37534,37540,37833,37955,37970,37972,37973,38064,38103,38110,38123,38128,38129,38139,38156,38157,38174,38219,38220,38256,38257,38258,38261,38262,38265,38266,38267,38390,38399,38400,38401,38403,38404,38405,38406,38407,38408,38418,38431,38434,38435,38436,38444,38454,38490,38494,38549,38550,38585,38586,38637,38638,38639,38640,38675,38676,38677,38678,38679,38680,38681,38682,38683,38684,38685,38686,38687,38688,38689,38690,38691,38692,38693,38694,38699,38700,38701,38702,38769,38770,38771,38772,38775,38776,38777,38784,38785,39166,39167,39168,39302,39303,39304,39746,39747,39751,39805,39814,39815,39823,39863,39864,39899,39920,39922,39944,39945,40142,40143,40144,40145); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -362 WHERE `entry` IN (15687,15691,16151,16457,17096,17257,17880,19044,19516,19781,19782,19783,20060,20062,20063,20064,20745,21215,21216,21364,21698,22167,22841,22871,22917,22947,22948,22949,22950,23418,25038,25165,25166,25315,25840,33113,34003,36538,36633,36897,37562,37697,38138,38602,38604,38758,38759,38760,38761,39190,39287,39288,39289,39305,39306,39307); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -363 WHERE `entry` IN (38369); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -364 WHERE `entry` IN (19514); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -365 WHERE `entry` IN (25740,26338,26339); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -366 WHERE `entry` IN (25865); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -367 WHERE `entry` IN (14862); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -368 WHERE `entry` IN (8317); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -369 WHERE `entry` IN (32926,32938,33352,33353,36980,38320,38321,38322); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -370 WHERE `entry` IN (15261); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -371 WHERE `entry` IN (23394); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -372 WHERE `entry` IN (20041); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -373 WHERE `entry` IN (18503,20309); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -374 WHERE `entry` IN (36619,38233,38459,38460,38711,38712,38970,38971,38972,38973,38974,38975); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -375 WHERE `entry` IN (38028); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -376 WHERE `entry` IN (20870,20912,21601,21626); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -377 WHERE `entry` IN (17839,20744,21104,21140,21148,22170,22171,22172); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -378 WHERE `entry` IN (22128,22129); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -379 WHERE `entry` IN (23597); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -380 WHERE `entry` IN (24374); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -381 WHERE `entry` IN (2675,4277,8937); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -382 WHERE `entry` IN (16127); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -383 WHERE `entry` IN (16124); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -384 WHERE `entry` IN (16149); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -385 WHERE `entry` IN (16156,16157,16158); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -386 WHERE `entry` IN (11657,11677,12096,12097,13078,13079,13086,13088,13137,13138,13139,13143,13144,13145,13147,13152,13153,13154,13176,13179,13180,13181,13216,13218,13236,13257,13296,13297,13298,13299,13300,13318,13319,13320,13356,13357,13437,13438,13439,13441,13442,13447,13577,13597,13598,13616,13617,13797,13798); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -387 WHERE `entry` IN (16244,16360,16427,16983); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -388 WHERE `entry` IN (16150); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -389 WHERE `entry` IN (16022,16981); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -390 WHERE `entry` IN (16027,16164,16449); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -391 WHERE `entry` IN (16167); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -392 WHERE `entry` IN (16020); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -393 WHERE `entry` IN (16024); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -394 WHERE `entry` IN (16375); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -395 WHERE `entry` IN (16290); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -396 WHERE `entry` IN (16447); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -397 WHERE `entry` IN (16982,16984); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -398 WHERE `entry` IN (16036,16056,16057,16236,16297,16698,17055); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -399 WHERE `entry` IN (16037); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -400 WHERE `entry` IN (15318,15319,15320,15323,15324,15325,15336); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -401 WHERE `entry` IN (16034); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -402 WHERE `entry` IN (16428,16429); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -403 WHERE `entry` IN (16021,16165,16368,16448,16452); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -404 WHERE `entry` IN (16194,16215,16216); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -405 WHERE `entry` IN (16017,16018,16029); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -406 WHERE `entry` IN (11352,12159,14750,14762,14763,14764,14765,14766,14767,14768,14769,14770,14771,14772,14773,14774,14775,14776,14777,14943,14944,14945,14946,14947,14948,15327,15335,15338,15340,15341,15343,15355,15385,15386,15388,15389,15390,15391,15392,16062); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -407 WHERE `entry` IN (15555); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -408 WHERE `entry` IN (15339); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -409 WHERE `entry` IN (11666); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -410 WHERE `entry` IN (15928,15929,15930,15931,15932,15936,15952,15953,15954,15956,15989,15990,16011,16060,16061,16063,16064,16065); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -411 WHERE `entry` IN (16441); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -412 WHERE `entry` IN (16126); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -413 WHERE `entry` IN (15974,15975,16505,29274); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -414 WHERE `entry` IN (15980,15981,16506); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -415 WHERE `entry` IN (16573); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -416 WHERE `entry` IN (15976,15978,15979,16142,16453); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -417 WHERE `entry` IN (16243); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -418 WHERE `entry` IN (16387); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -419 WHERE `entry` IN (10440); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -420 WHERE `entry` IN (12119,15620,16168,16446); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -421 WHERE `entry` IN (13116,13117,14302,15047,15224,15922); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -422 WHERE `entry` IN (22250); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -423 WHERE `entry` IN (24175); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -424 WHERE `entry` IN (12900); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -425 WHERE `entry` IN (32357,32358,32471,32495,32501); +UPDATE `creature_template` SET `CreatureImmunitiesId` = -426 WHERE `entry` IN (32386,32398,32400,32438); + +-- ============================================================ +-- Clear no-longer-needed flags_extra bits (AOE / KNOCKBACK / TAUNT) +-- ============================================================ +SET @MASK = 0x400000 | 0x40000000 | 0x100; +UPDATE `creature_template` SET `flags_extra` = `flags_extra` & ~@MASK WHERE (`flags_extra` & @MASK) != 0; diff --git a/data/sql/updates/db_world/2026_03_25_04.sql b/data/sql/updates/db_world/2026_03_25_04.sql new file mode 100644 index 000000000..c5d345e67 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_25_04.sql @@ -0,0 +1,336 @@ +-- DB update 2026_03_25_03 -> 2026_03_25_04 +-- Enable smooth waypoint transitions for flying creatures +-- Paths with actions or delays are excluded + +-- Buzzard (2830) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (71370, 72510, 72520, 72530, 76140); + +-- Antilus the Soarer (5347) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (518400); + +-- Soaring Razorbeak (8276) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (928800, 928810); + +-- Bloodseeker Bat (11368) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (491230, 491460, 491580, 521360, 522670, 915550); + +-- Bat Rider Guard (15242) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (548400, 548410, 548420, 548430); + +-- OLDWorld Trigger (DO NOT DELETE) (15384) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (876470); + +-- Eye of Thrallmar (16598) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (575850, 575860, 575870, 575880); + +-- Eye of Honor Hold (16887) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (584380); + +-- Skeletal Gryphon (17660) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (125540, 125550); + +-- Large AOI Underbat (18409) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1383120, 1383140, 1383160, 1383210, 1383270, 1383410, 1383480, 1383520); + +-- Night Elf Wisp (18502) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1396910, 1396920, 1396930, 1396940, 1396950, 1396960, 1396970, 1396990); + +-- Torgos (18707) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2469160); + +-- Flying Raging Soul (18726) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1324010, 1324030, 1324100, 1324150, 1324320); + +-- Wildhammer Gryphon Rider (19382) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (11580, 1259220, 1259270, 1259300, 1259310, 1259320, 1260520); + +-- Al'ar (19514) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1580410); + +-- Nuramoc (20932) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (249410, 249420, 249430); + +-- Bladewing Bloodletter (21033) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (738490, 738500, 738520, 738530, 738580, 738600); + +-- Kor'kron Wyvern Rider (21153) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (742190); + +-- Mature Netherwing Drake (21648) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (755410, 755420, 755430, 755440, 755450, 755460, 755470, 755480, 755490, 755500, 755510, 755520, 755530, 755540, 755550, 755560, 755570, 755580, 861030); + +-- Vhel'kur (21801) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (218010); + +-- Vilewing Chimaera (21879) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (844880, 845940, 845970, 846130, 846320); + +-- Avian Flyer (21931) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1387630, 1387660, 1387690); + +-- [PH] Cave Bat JZB (22040) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (771910, 771920, 771930, 771940, 771950, 771960); + +-- Dragonmaw Skybreaker (22274) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (782910, 782920, 782930, 782940, 782950, 782960, 782970, 782980, 782990, 783000); + +-- Corvax (22842) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (697120); + +-- Rook (22843) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (697110); + +-- Dragonmaw Sky Stalker (23030) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1481220, 1481230, 1481270, 1481280, 1481290, 1481380, 1481390); + +-- Invisible Stalker (Floating) (23033) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1312500, 1312520, 1338940, 1338990, 1339000); + +-- Monstrous Kaliri (23051) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (790170, 832370, 832380, 860990, 861150, 861160, 861170, 861310, 861330, 1325530, 1325540, 1325550, 1325560, 1325570, 1325580, 1325600, 1325620, 1325640, 1325650, 1325660, 1325670); + +-- Dragonmaw Transporter (23188) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1328140, 1328180); + +-- Lady Sinestra (23283) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (287960); + +-- Dragonflayer Raider (23557) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (971330, 971340, 971350, 971390, 971400); + +-- Plagued Proto-Dragon (23680) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1076260); + +-- Proto-Drake (23689) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1081500, 1081510, 1081520, 1081550, 1081560, 1081580); + +-- Spotted Hippogryph (23772) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (938020, 938030, 938040, 938050, 938060, 938070, 938080, 938090, 938100, 938110, 938120, 938130, 938140, 938150, 938160, 938170, 938180, 938190, 938200, 938210, 938220, 938230, 938240, 938250, 938260, 938270, 938280, 938290, 938300, 938310, 938320, 938330, 938340, 938350, 938360, 938370, 938380, 938390, 938400, 938410, 938420, 938430, 938440); + +-- Val'kyr Watcher (23935) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (482340, 482350, 482360, 482370, 482380, 482390, 482400, 482410, 482420, 482430, 482440); + +-- Windy Cloud (24222) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1410000, 1410010, 1410020, 1410030, 1410040, 1410060, 1410070, 1410080); + +-- Steelfeather (24514) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1142380); + +-- Talonshrike (24518) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1147130); + +-- Buoy (24707) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1078110); + +-- Fjord Hawk (24747) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (938510, 938530, 938560, 938590, 938600, 938640, 938660, 938680, 938730, 938750, 938790, 938810, 938880, 938900); + +-- Nexus Watcher (24770) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1241160, 1241170, 1241180, 1241190, 1241200, 1241210, 1241220, 1241230, 1241240, 1241250, 1241260); + +-- Fjord Hawk Matriarch (24787) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1158350, 1158370, 1158430, 1158440, 1158450); + +-- Proto-Drake Rider (24849) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1259340, 1259360, 1259370, 1259400); + +-- Dawnblade Hawkrider (25063) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (839990, 840020, 840030, 840040, 840050, 840060); + +-- Shattered Sun Bombardier (25144) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (853700, 853720, 853730, 853740); + +-- Bloodspore Moth (25464) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1327080); + +-- Spirit of the North (25711) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1210760, 1225710); + +-- Fizzcrank Bomber (25765) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1113600); + +-- D.E.H.T.A. Enforcer (25819) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1329840); + +-- Shaman Beam Bunny 000 (25964) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (125390); + +-- Shaman Beam Bunny 001 (25965) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (125400); + +-- Shaman Beam Bunny 002 (25966) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (125410); + +-- Nexus Drake Hatchling (26127) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1210410, 1210420, 1210430, 1210440, 1210450, 1210460, 1210470); + +-- Carrion Condor (26174) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1018170, 1018400, 1018550, 1018860, 1018870, 1019250, 1019290); + +-- Nexus Guardian (26276) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1113040, 1113590); + +-- Emberwyrm (26286) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1123860, 1123870); + +-- Dreadtalon (26838) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1161740); + +-- Reanimated Frost Wyrm (26841) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (404970, 404980, 404990, 405000, 405010, 405020, 405030, 405040, 405050); + +-- Sarathstra (26858) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1124010); + +-- Wyrmrest Temple Drake (26925) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1001700, 1002060, 1002070, 1002080, 1002090, 1002100, 1002130, 1002150, 1002160, 1002260, 1309990); + +-- Wyrmrest Guardian (26933) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1008230, 1008240, 1008250, 1008260, 1310350, 1310360, 1310370, 1310380); + +-- Forgotten Captain (27220) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1033880, 1033910, 1033940, 1033960, 1033980, 1034000, 1034030, 1034110); + +-- Risen Gryphon (27241) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1311770, 1311780); + +-- Emerald Skytalon (27244) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1057430, 1057510, 1057710, 1057730, 1057760, 1057900, 1057920, 1057930); + +-- Reconstructed Frost Wyrm (27285) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1099150, 1099160, 1099270); + +-- Cosmetic Drakkari Bat [PH] (27490) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1275080, 1275090, 1275100, 1275110, 1275120, 1275130); + +-- Azure Dragon (27608) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (991050, 991060, 991070, 991080, 991090, 991100, 991110, 991120, 991130, 991140, 991150, 991160, 991170, 991180); + +-- Azure Drake (27682) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (990780, 990790, 990830, 990840, 990850, 990860, 990870, 990880, 990890, 990920, 990930, 990940, 990950, 990960, 990970, 990980, 990990, 991030, 991040); + +-- Heb'Drakkar Striker (28465) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1005360, 1005400); + +-- Scarlet Gryphon (28614) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1293080, 1293090); + +-- Olrun the Battlecaller (29047) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1287390); + +-- Gundrak Bat Rider (29332) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1097750, 1097780, 1097820, 1097840, 1097860, 1097870, 1097930, 1097950, 1098560); + +-- Onslaught Gryphon Rider (29333) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1231100, 1231120, 1231290, 1231300, 1231320, 1231330, 1231420, 1231430, 1231460, 1231470, 1231560, 1231570); + +-- Vargul Plaguetalon (29453) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (976340); + +-- Frigid Proto-Drake (29460) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (979910, 979920, 979950, 980250, 980260, 980270, 980280); + +-- Nascent Val'kyr (29570) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1077080, 1077240, 1077780, 1077820, 1077870, 1077890, 1077910, 1077930, 1077950, 1077960, 1077970, 1077980, 1077990); + +-- Hyldsmeet Proto-Drake (29625) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1134270, 1134280, 1134290, 1134300, 1134460, 1134470, 1134480, 1134490, 1134500, 1134510, 1134520, 1134570, 1134580, 1134590, 1134610, 1134620, 1134630, 1134640, 1134650, 1134660, 1134670); + +-- Stormpeak Wyrm (29753) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1010000, 1011070, 1011080, 1011090, 1011100, 1011110, 1011120, 1011130, 1011140, 1011150, 1011170, 1011180, 1011190, 1011200, 1011210, 1011220); + +-- Sirana Iceshriek (29794) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2025870); + +-- Stormcrest Eagle (30013) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1017130, 1017140, 1017150, 1018010); + +-- Wild Wyrm (30275) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2029720, 2029730, 2029740, 2029750, 2029760, 2029770); + +-- Jotunheim Proto-Drake (30330) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1227590, 1227600, 1227610, 1227620, 1227630, 1227640, 1227650, 1227660, 1227670, 1227680, 1227690, 1227700, 1227710, 1227720, 1227730, 1227740, 1227750, 1227760, 1241110, 1241120); + +-- Wrathstrike Gargoyle (30482) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1348070, 1348080, 1348090, 1348100, 1348110, 1348120, 1348130, 1348140); + +-- Scourgebeak Fleshripper (30988) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1210070, 1210110, 1210140, 1210170, 1210190, 1210230, 1210250); + +-- Wrathstrike Gargoyle (31040) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1217480, 1217490, 1217500, 1217510, 1217520, 1217530, 1217540, 1217550); + +-- Frostbrood Skytalon (31137) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1234960, 1234970, 1234980, 1234990, 1235020, 1235030, 1235040, 1235070, 1235090); + +-- Kor'kron Battle Wyvern (31269) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (992270, 992280, 992300); + +-- Crystal Wyrm (31393) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1111560, 1111620); + +-- Frostbrood Spawn (31702) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1244190, 1244200, 1244210, 1244220, 1244230, 1244240, 1244250); + +-- Stabled Hunter Pet (31768) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2087870); + +-- Stabled Hunter Pet (31769) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2087880); + +-- Tempus Wyrm (32180) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1129570); + +-- Infinite Eradicator (32185) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1134360, 1134380); + +-- Skybreaker Recon Fighter (32189) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1240870, 1240880, 1240890, 1240900); + +-- Orgrim's Hammer Scout (32201) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1241600, 1241870, 1241880, 1241890, 1241900, 1241910, 1241920, 1241930, 1241940, 1241950, 1241960, 1242070, 1242080, 1242090, 1242120, 1242130, 1242140, 1242150, 1242160, 1242180, 1242190, 1242200, 1242210, 1242220); + +-- Northrend Daily Dungeon Image Bunny (32265) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (973340); + +-- Fumblub Gearwind (32358) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1519380); + +-- Ebon Blade Gargoyle (32472) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1244280, 1244290); + +-- Time-Lost Proto Drake (32491) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (392030, 392040, 392050, 392060); + +-- Frostbrood Matriarch (32492) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1181850, 1181860, 1181880, 1181890, 1181900); + +-- Vyragosa (32630) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (392030, 392040, 392050, 392060); + +-- Frostbrood Sentry (32767) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1251080, 1251090, 1251100, 1251550, 1251620); + +-- Mechanolift 304-A (33214) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (13681700, 13681801, 13681802, 13681803, 13681804, 13681805, 13681806, 13681807, 13681808, 13681809, 13681810, 13681811, 13681812, 13681813, 13681814, 13681815, 13681816, 13681817, 13681818, 13682000, 13682101, 13682102, 13682103, 13682104, 13682105, 13682106, 13682107, 13682108, 13682109, 13682110, 13682300, 13682700, 13683000, 13683100, 13685300, 13686200, 13686800, 13687000, 13688500); + +-- Guardian of Life (33528) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1375280, 1375290, 1375300, 1375310); + +-- Chillmaw (33687) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2008510); + +-- Tournament Hippogryph (33778) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (1231520); + +-- Stonespine Gargoyle (36896) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2018220, 2018240, 2018340, 2018380, 2018460); + +-- Val'kyr Herald (37098) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2012450, 2013220); + +-- Spire Frostwyrm (Ambient) (37528) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (2008870, 2009600, 2009750); + +-- Argent Hippogryph (37968) +UPDATE `waypoint_data` SET `smoothTransition` = 1 WHERE `id` IN (887940, 887960, 887980, 887990); diff --git a/data/sql/updates/db_world/2026_03_25_05.sql b/data/sql/updates/db_world/2026_03_25_05.sql new file mode 100644 index 000000000..da09a219e --- /dev/null +++ b/data/sql/updates/db_world/2026_03_25_05.sql @@ -0,0 +1,3 @@ +-- DB update 2026_03_25_04 -> 2026_03_25_05 +-- Also add spark visual aura from Power Spark to Power Spark (1) +UPDATE `creature_template_addon` SET `auras` = '55845' WHERE (`entry` = 32187); diff --git a/data/sql/updates/db_world/2026_03_25_06.sql b/data/sql/updates/db_world/2026_03_25_06.sql new file mode 100644 index 000000000..65228e388 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_25_06.sql @@ -0,0 +1,3 @@ +-- DB update 2026_03_25_05 -> 2026_03_25_06 +-- Remove smoothTransition for Guardian of Life (33528) +UPDATE `waypoint_data` SET `smoothTransition` = 0 WHERE `id` IN (1375280, 1375290, 1375300, 1375310); diff --git a/data/sql/updates/db_world/2026_03_26_00.sql b/data/sql/updates/db_world/2026_03_26_00.sql new file mode 100644 index 000000000..8bed49dd2 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_26_00.sql @@ -0,0 +1,20 @@ +-- DB update 2026_03_25_06 -> 2026_03_26_00 +-- Add mail commands +DELETE FROM `command` WHERE `name` IN ('mail', 'mail list', 'mail return'); +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('mail', 2, 'Syntax: .mail $subcommand\nType .mail to see a list of subcommands or .help mail $subcommand to see info on subcommands.'), +('mail list', 2, 'Syntax: .mail list [$player]\nDisplays all mail data (except subject and body) for the target player.'), +('mail return', 2, 'Syntax: .mail return $player $mailId\nReturns the specified mail to its original sender.'); + +-- Add acore_string entries for mail commands +DELETE FROM `acore_string` WHERE `entry` IN (5135, 5136, 5137, 5138, 5139, 5140, 5141, 5142, 5143); +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(5135, 'Mail list for player {}:', '플레이어 {}의 우편 목록:', 'Liste des courriers du joueur {} :', 'Postliste für Spieler {}:', '玩家 {} 的邮件列表:', '玩家 {} 的郵件列表:', 'Lista de correo del jugador {}:', 'Lista de correo del jugador {}:', 'Список писем игрока {}:'), +(5136, ' ID: {} | Type: {} | Stationery: {} | Template: {} | Sender: {} ({}) | Receiver: {} | Expires: {} | Delivered: {} | Money: {} | COD: {} | Flags: {} | Items: {}', ' ID: {} | 유형: {} | 편지지: {} | 템플릿: {} | 발신자: {} ({}) | 수신자: {} | 만료: {} | 배달: {} | 금액: {} | 대금상환: {} | 플래그: {} | 아이템: {}', ' ID : {} | Type : {} | Papeterie : {} | Modèle : {} | Expéditeur : {} ({}) | Destinataire : {} | Expiration : {} | Livré : {} | Argent : {} | Remboursement : {} | Drapeaux : {} | Objets : {}', ' ID: {} | Typ: {} | Briefpapier: {} | Vorlage: {} | Absender: {} ({}) | Empfänger: {} | Ablauf: {} | Zugestellt: {} | Gold: {} | Nachnahme: {} | Flags: {} | Gegenstände: {}', ' ID:{} | 类型:{} | 信纸:{} | 模板:{} | 发件人:{} ({}) | 收件人:{} | 过期:{} | 投递:{} | 金币:{} | 货到付款:{} | 标记:{} | 物品:{}', ' ID:{} | 類型:{} | 信紙:{} | 範本:{} | 寄件人:{} ({}) | 收件人:{} | 到期:{} | 投遞:{} | 金幣:{} | 貨到付款:{} | 標記:{} | 物品:{}', ' ID: {} | Tipo: {} | Papelería: {} | Plantilla: {} | Remitente: {} ({}) | Destinatario: {} | Expira: {} | Entregado: {} | Dinero: {} | Contrareembolso: {} | Marcas: {} | Objetos: {}', ' ID: {} | Tipo: {} | Papelería: {} | Plantilla: {} | Remitente: {} ({}) | Destinatario: {} | Expira: {} | Entregado: {} | Dinero: {} | Contrareembolso: {} | Marcas: {} | Objetos: {}', ' ID: {} | Тип: {} | Бланк: {} | Шаблон: {} | Отправитель: {} ({}) | Получатель: {} | Истекает: {} | Доставлено: {} | Деньги: {} | Наложенный платёж: {} | Флаги: {} | Предметы: {}'), +(5137, 'No mail found for player {}.', '플레이어 {}의 우편이 없습니다.', 'Aucun courrier trouvé pour le joueur {}.', 'Keine Post für Spieler {} gefunden.', '未找到玩家 {} 的邮件。', '未找到玩家 {} 的郵件。', 'No se encontró correo para el jugador {}.', 'No se encontró correo para el jugador {}.', 'Письма для игрока {} не найдены.'), +(5138, 'Mail #{} returned to sender for player {}.', '우편 #{}이(가) 플레이어 {}의 발신자에게 반송되었습니다.', 'Courrier #{} renvoyé à l''expéditeur pour le joueur {}.', 'Post #{} wurde an den Absender für Spieler {} zurückgesendet.', '邮件 #{} 已退回给玩家 {} 的发件人。', '郵件 #{} 已退回給玩家 {} 的寄件人。', 'Correo #{} devuelto al remitente del jugador {}.', 'Correo #{} devuelto al remitente del jugador {}.', 'Письмо #{} возвращено отправителю для игрока {}.'), +(5139, 'Mail #{} not found.', '우편 #{}을(를) 찾을 수 없습니다.', 'Courrier #{} introuvable.', 'Post #{} nicht gefunden.', '未找到邮件 #{}。', '未找到郵件 #{}。', 'Correo #{} no encontrado.', 'Correo #{} no encontrado.', 'Письмо #{} не найдено.'), +(5140, 'Only normal mail can be returned.', '일반 우편만 반송할 수 있습니다.', 'Seul le courrier normal peut être renvoyé.', 'Nur normale Post kann zurückgesendet werden.', '只能退回普通邮件。', '只能退回普通郵件。', 'Solo se puede devolver correo normal.', 'Solo se puede devolver correo normal.', 'Можно вернуть только обычные письма.'), +(5141, 'Mail has no valid sender to return to.', '반송할 유효한 발신자가 없습니다.', 'Le courrier n''a pas d''expéditeur valide.', 'Die Post hat keinen gültigen Absender.', '邮件没有有效的发件人可退回。', '郵件沒有有效的寄件人可退回。', 'El correo no tiene un remitente válido.', 'El correo no tiene un remitente válido.', 'У письма нет действительного отправителя для возврата.'), +(5142, 'Mail has already been returned.', '이미 반송된 우편입니다.', 'Ce courrier a déjà été renvoyé.', 'Diese Post wurde bereits zurückgesendet.', '邮件已被退回。', '郵件已被退回。', 'El correo ya ha sido devuelto.', 'El correo ya ha sido devuelto.', 'Письмо уже было возвращено.'), +(5143, 'A script hook prevented this mail from being returned.', '스크립트 후크가 이 우편의 반송을 차단했습니다.', 'Un hook de script a empêché le renvoi de ce courrier.', 'Ein Script-Hook hat die Rücksendung dieser Post verhindert.', '脚本钩子阻止了此邮件的退回。', '腳本鉤子阻止了此郵件的退回。', 'Un hook de script impidió la devolución de este correo.', 'Un hook de script impidió la devolución de este correo.', 'Хук скрипта предотвратил возврат этого письма.'); diff --git a/data/sql/updates/db_world/2026_03_26_01.sql b/data/sql/updates/db_world/2026_03_26_01.sql new file mode 100644 index 000000000..7b4a9cdaf --- /dev/null +++ b/data/sql/updates/db_world/2026_03_26_01.sql @@ -0,0 +1,4 @@ +-- DB update 2026_03_26_00 -> 2026_03_26_01 + +-- Set Extra Flag Ignore Pathfinding +UPDATE `creature_template` SET `flags_extra` = `flags_extra` |536871042 WHERE (`entry` = 30616); diff --git a/data/sql/updates/db_world/2026_03_27_00.sql b/data/sql/updates/db_world/2026_03_27_00.sql new file mode 100644 index 000000000..a559a7265 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_27_00.sql @@ -0,0 +1,4 @@ +-- DB update 2026_03_26_01 -> 2026_03_27_00 +-- Move Flexweave Underlay script from enchant application spell (55002) to parachute use-spell (55001) +DELETE FROM `spell_script_names` WHERE `spell_id` IN (55001, 55002) AND `ScriptName` = 'spell_item_flexweave_underlay'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (55001, 'spell_item_flexweave_underlay'); diff --git a/data/sql/updates/db_world/2026_03_27_01.sql b/data/sql/updates/db_world/2026_03_27_01.sql new file mode 100644 index 000000000..20623a290 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_27_01.sql @@ -0,0 +1,43 @@ +-- DB update 2026_03_27_00 -> 2026_03_27_01 +-- Doomrel (9039): Create "Continue..." menu and chain from initial gossip +DELETE FROM `gossip_menu` WHERE `MenuID` = 1950; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES (1950, 2605); + +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 1950; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(1950, 0, 0, 'Continue...', 5256, 1, 1, 0, 0, 0, 0, '', 0, 0); + +UPDATE `gossip_menu_option` SET `ActionMenuID` = 1950 WHERE `MenuID` = 1947 AND `OptionID` = 0; + +-- Gloomrel (9037): Create "Continue..." menus for smelt and chalice paths +DELETE FROM `gossip_menu` WHERE `MenuID` IN (1952, 1953); +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(1952, 2606), +(1953, 2604); + +DELETE FROM `gossip_menu_option` WHERE `MenuID` IN (1952, 1953); +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(1952, 0, 0, 'Continue...', 5256, 1, 1, 0, 0, 0, 0, '', 0, 0), +(1953, 0, 0, 'Continue...', 5256, 1, 1, 0, 0, 0, 0, '', 0, 0); + +UPDATE `gossip_menu_option` SET `ActionMenuID` = 1952 WHERE `MenuID` = 1945 AND `OptionID` = 0; +UPDATE `gossip_menu_option` SET `ActionMenuID` = 1953 WHERE `MenuID` = 1945 AND `OptionID` = 1; + +-- Gloomrel: Conditional gossip text when quest completed +DELETE FROM `gossip_menu` WHERE `MenuID` = 1945 AND `TextID` = 2605; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES (1945, 2605); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 1945 AND `SourceEntry` = 2605; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 1945, 2605, 0, 0, 8, 0, 4083, 0, 0, 0, 0, 0, '', 'Gloomrel - text requires quest 4083 rewarded'), +(14, 1945, 2605, 0, 0, 7, 0, 186, 230, 0, 0, 0, 0, '', 'Gloomrel - text requires Mining >= 230'), +(14, 1945, 2605, 0, 0, 16, 0, 14891, 0, 0, 1, 0, 0, '', 'Gloomrel - text requires NOT having Smelt Dark Iron'); + +-- Gloomrel: Conditions for gossip options +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 1945 AND `SourceEntry` IN (0, 1); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 1945, 0, 0, 0, 8, 0, 4083, 0, 0, 0, 0, 0, '', 'Gloomrel - option 0 requires quest 4083 rewarded'), +(15, 1945, 0, 0, 0, 7, 0, 186, 230, 0, 0, 0, 0, '', 'Gloomrel - option 0 requires Mining >= 230'), +(15, 1945, 0, 0, 0, 16, 0, 14891, 0, 0, 1, 0, 0, '', 'Gloomrel - option 0 requires NOT having Smelt Dark Iron'), +(15, 1945, 1, 0, 0, 8, 0, 4083, 0, 0, 1, 0, 0, '', 'Gloomrel - option 1 requires quest 4083 NOT rewarded'), +(15, 1945, 1, 0, 0, 7, 0, 186, 230, 0, 0, 0, 0, '', 'Gloomrel - option 1 requires Mining >= 230'); diff --git a/data/sql/updates/db_world/2026_03_27_02.sql b/data/sql/updates/db_world/2026_03_27_02.sql new file mode 100644 index 000000000..af129ab55 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_27_02.sql @@ -0,0 +1,8 @@ +-- DB update 2026_03_27_01 -> 2026_03_27_02 + +-- Change row 3 event from ON_WAYPOINT_REACHED to ON_WAYPOINT_ENDED. +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = -129210); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(-129210, 0, 2, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 232, 12921000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Havenshire Stallion - On Respawn - Start Path 12921000'), +(-129210, 0, 3, 4, 109, 0, 100, 0, 0, 12921000, 0, 0, 0, 0, 234, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Havenshire Stallion - On Path 12921000 Finished - Stop Movement'), +(-129210, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2860500, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Havenshire Stallion - On Path 12921000 Finished - Run Script'); diff --git a/data/sql/updates/db_world/2026_03_27_03.sql b/data/sql/updates/db_world/2026_03_27_03.sql new file mode 100644 index 000000000..79ede9d56 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_27_03.sql @@ -0,0 +1,9 @@ +-- DB update 2026_03_27_02 -> 2026_03_27_03 +-- +DELETE FROM `creature_equip_template` WHERE (`CreatureID` = 28912); +INSERT INTO `creature_equip_template` (`CreatureID`, `ID`, `ItemID1`, `ItemID2`, `ItemID3`, `VerifiedBuild`) VALUES +(28912, 1, 38633, 0, 0, 42010); + +UPDATE `creature` SET `spawntimesecs` = 60 WHERE (`id1` = 28912) AND (`guid` IN (130354)); + +UPDATE `creature_template` SET `CreatureImmunitiesId` = -93 WHERE (`entry` = 28912); diff --git a/data/sql/updates/db_world/2026_03_27_04.sql b/data/sql/updates/db_world/2026_03_27_04.sql new file mode 100644 index 000000000..a0ef70ced --- /dev/null +++ b/data/sql/updates/db_world/2026_03_27_04.sql @@ -0,0 +1,4 @@ +-- DB update 2026_03_27_03 -> 2026_03_27_04 +-- +-- School immunities are set by `creature_addon.auras` immunity auras +UPDATE `creature_template` SET `CreatureImmunitiesId` = -367 WHERE (`entry` = 8317); diff --git a/data/sql/updates/db_world/2026_03_28_00.sql b/data/sql/updates/db_world/2026_03_28_00.sql new file mode 100644 index 000000000..5a20217e1 --- /dev/null +++ b/data/sql/updates/db_world/2026_03_28_00.sql @@ -0,0 +1,173 @@ +-- DB update 2026_03_27_04 -> 2026_03_28_00 +-- Move Kalimdor gossip handlers from C++ to database +-- NPCs: Rivern Frostwind, Stone Watcher of Norgannon, Great Bear Spirit, +-- Braug Dimspirit, Steward of Time, Thrall Warchief + +-- ===================================================== +-- 1. npc_rivern_frostwind (10618) - zone_winterspring +-- Show vendor option only if Exalted with Wintersaber Trainers (faction 589) +-- ===================================================== + +-- Condition: gossip_menu_option MenuID=3130, OptionID=0 requires Exalted (rank 7, mask 128) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=3130 AND `SourceEntry`=0 AND `ConditionTypeOrReference`=5; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 3130, 0, 0, 0, 5, 0, 589, 128, 0, 0, 0, 0, '', 'Rivern Frostwind - Show vendor option only if Exalted with Wintersaber Trainers'); + +-- Remove ScriptName +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=10618; + +-- ===================================================== +-- 2. npc_stone_watcher_of_norgannon (7918) - zone_tanaris +-- 5-step gossip chain, quest 2954 credit at end +-- ===================================================== + +-- Add missing gossip_menu entries (MenuID -> TextID) +DELETE FROM `gossip_menu` WHERE `MenuID` IN (57000,57001,57002,57003,57004,57005); +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(57000, 1674), +(57001, 1675), +(57002, 1676), +(57003, 1677), +(57004, 1678), +(57005, 1679); + +-- Condition: show first option only if quest 2954 is taken (incomplete) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=57000 AND `SourceEntry`=0 AND `ConditionTypeOrReference`=9; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 57000, 0, 0, 0, 9, 0, 2954, 0, 0, 0, 0, 0, '', 'Stone Watcher of Norgannon - Show gossip option if quest 2954 is taken'); + +-- SAI: on final gossip select (menu 57005, option 0) -> close gossip + quest credit 2954 +DELETE FROM `smart_scripts` WHERE `entryorguid`=7918 AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(7918, 0, 0, 1, 62, 0, 100, 512, 57005, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Stone Watcher of Norgannon - On Gossip Select - Close Gossip'), +(7918, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 15, 2954, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Stone Watcher of Norgannon - Linked - Quest Credit 2954'); + +-- Update creature_template: set gossip_menu_id, add gossip npcflag, set SmartAI +UPDATE `creature_template` SET `gossip_menu_id`=57000, `npcflag`=`npcflag`|1, `ScriptName`='', `AIName`='SmartAI' WHERE `entry`=7918; + +-- ===================================================== +-- 3. npc_great_bear_spirit (11956) - zone_moonglade +-- 4-step gossip chain, quest 5929/5930 credit at end +-- ===================================================== + +-- Add quest-active text for menu 3882 +DELETE FROM `gossip_menu` WHERE `MenuID`=3882 AND `TextID`=4719; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(3882, 4719); + +-- Condition on gossip_menu: show TextID 4719 if quest 5929 OR 5930 is taken +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=14 AND `SourceGroup`=3882 AND `SourceEntry`=4719; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 3882, 4719, 0, 0, 9, 0, 5929, 0, 0, 0, 0, 0, '', 'Great Bear Spirit - Show text 4719 if quest 5929 is taken'), +(14, 3882, 4719, 0, 1, 9, 0, 5930, 0, 0, 0, 0, 0, '', 'Great Bear Spirit - Show text 4719 if quest 5930 is taken'); + +-- Condition on gossip_menu_option: show option if quest 5929 OR 5930 is taken +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=3882 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 3882, 0, 0, 0, 9, 0, 5929, 0, 0, 0, 0, 0, '', 'Great Bear Spirit - Show gossip option if quest 5929 is taken'), +(15, 3882, 0, 0, 1, 9, 0, 5930, 0, 0, 0, 0, 0, '', 'Great Bear Spirit - Show gossip option if quest 5930 is taken'); + +-- Clean up duplicate OptionID=1 on menu 3884 (identical to OptionID=0) +DELETE FROM `gossip_menu_option` WHERE `MenuID`=3884 AND `OptionID`=1; + +-- SAI: on gossip select (menu 3884, option 0) -> quest credit for both 5929 and 5930 +DELETE FROM `smart_scripts` WHERE `entryorguid`=11956 AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(11956, 0, 0, 1, 62, 0, 100, 512, 3884, 0, 0, 0, 0, 0, 15, 5929, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Great Bear Spirit - On Gossip Select - Quest Credit 5929'), +(11956, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 15, 5930, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Great Bear Spirit - On Gossip Select - Quest Credit 5930'); + +-- Update creature_template +UPDATE `creature_template` SET `ScriptName`='', `AIName`='SmartAI' WHERE `entry`=11956; + +-- ===================================================== +-- 4. npc_braug_dimspirit (4489) - zone_stonetalon_mountains +-- Quiz: 5 dragon options, Neltharion = correct, others = wrong spell +-- ===================================================== + +-- Add missing Nozdormu option (OptionID=4) +DELETE FROM `gossip_menu_option` WHERE `MenuID`=4763 AND `OptionID`=4; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`) VALUES +(4763, 4, 0, 'Nozdormu is my answer.', 0, 1, 1, 0, 0, 0, 0, ''); + +-- Condition on gossip_menu: show text 5820 (quiz text) if quest 6627 is taken +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=14 AND `SourceGroup`=4763 AND `SourceEntry`=5820; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 4763, 5820, 0, 0, 9, 0, 6627, 0, 0, 0, 0, 0, '', 'Braug Dimspirit - Show quiz text if quest 6627 is taken'); + +-- Conditions on all 5 options: show only if quest 6627 is taken +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4763 AND `SourceEntry` IN (0,1,2,3,4) AND `ConditionTypeOrReference`=9; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4763, 0, 0, 0, 9, 0, 6627, 0, 0, 0, 0, 0, '', 'Braug Dimspirit - Show Ysera option if quest 6627 is taken'), +(15, 4763, 1, 0, 0, 9, 0, 6627, 0, 0, 0, 0, 0, '', 'Braug Dimspirit - Show Neltharion option if quest 6627 is taken'), +(15, 4763, 2, 0, 0, 9, 0, 6627, 0, 0, 0, 0, 0, '', 'Braug Dimspirit - Show Alexstrasza option if quest 6627 is taken'), +(15, 4763, 3, 0, 0, 9, 0, 6627, 0, 0, 0, 0, 0, '', 'Braug Dimspirit - Show Malygos option if quest 6627 is taken'), +(15, 4763, 4, 0, 0, 9, 0, 6627, 0, 0, 0, 0, 0, '', 'Braug Dimspirit - Show Nozdormu option if quest 6627 is taken'); + +-- SAI: Neltharion (option 1) = correct answer -> quest credit +-- All others (options 0,2,3,4) = wrong answer -> cast spell 6766 +DELETE FROM `smart_scripts` WHERE `entryorguid`=4489 AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(4489, 0, 0, 0, 62, 0, 100, 512, 4763, 1, 0, 0, 0, 0, 15, 6627, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Braug Dimspirit - On Gossip Select Neltharion - Quest Credit 6627'), +(4489, 0, 1, 0, 62, 0, 100, 512, 4763, 0, 0, 0, 0, 0, 11, 6766, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Braug Dimspirit - On Gossip Select Ysera - Cast Wrong Answer'), +(4489, 0, 2, 0, 62, 0, 100, 512, 4763, 2, 0, 0, 0, 0, 11, 6766, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Braug Dimspirit - On Gossip Select Alexstrasza - Cast Wrong Answer'), +(4489, 0, 3, 0, 62, 0, 100, 512, 4763, 3, 0, 0, 0, 0, 11, 6766, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Braug Dimspirit - On Gossip Select Malygos - Cast Wrong Answer'), +(4489, 0, 4, 0, 62, 0, 100, 512, 4763, 4, 0, 0, 0, 0, 11, 6766, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Braug Dimspirit - On Gossip Select Nozdormu - Cast Wrong Answer'); + +-- Update creature_template +UPDATE `creature_template` SET `ScriptName`='', `AIName`='SmartAI' WHERE `entry`=4489; + +-- ===================================================== +-- 5. npc_steward_of_time (20142) - zone_tanaris +-- Gossip shows flight option if quest 10279 incomplete or rewarded +-- On select casts spell 34891. Keep OnQuestAccept in C++. +-- ===================================================== + +-- Condition on gossip_menu: show text 9978 if quest 10279 is taken OR rewarded +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=14 AND `SourceGroup`=8072 AND `SourceEntry`=9978; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 8072, 9978, 0, 0, 9, 0, 10279, 0, 0, 0, 0, 0, '', 'Steward of Time - Show text 9978 if quest 10279 is taken'), +(14, 8072, 9978, 0, 1, 8, 0, 10279, 0, 0, 0, 0, 0, '', 'Steward of Time - Show text 9978 if quest 10279 is rewarded'); + +-- Condition on gossip_menu_option: show option if quest 10279 is taken OR rewarded +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8072 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8072, 0, 0, 0, 9, 0, 10279, 0, 0, 0, 0, 0, '', 'Steward of Time - Show gossip option if quest 10279 is taken'), +(15, 8072, 0, 0, 1, 8, 0, 10279, 0, 0, 0, 0, 0, '', 'Steward of Time - Show gossip option if quest 10279 is rewarded'); + +-- SAI: on gossip select (menu 8072, option 0) -> invoker self-casts spell 34891 +-- Using SMART_ACTION_INVOKER_CAST (134) so the player casts on themselves, +-- matching original C++: player->CastSpell(player, 34891, true) +DELETE FROM `smart_scripts` WHERE `entryorguid`=20142 AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(20142, 0, 0, 0, 62, 0, 100, 512, 8072, 0, 0, 0, 0, 0, 134, 34891, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Steward of Time - On Gossip Select - Invoker Cast Flight Through Caverns'); + +-- Update creature_template: add SmartAI (keep ScriptName for OnQuestAccept) +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=20142; + +-- ===================================================== +-- 6. npc_thrall_warchief (4949) - zone_orgrimmar +-- 7-step gossip chain, quest 6566 credit at end +-- Keep ScriptName for OnQuestReward + AI +-- ===================================================== + +-- Add missing gossip_menu entries for chain steps +DELETE FROM `gossip_menu` WHERE `MenuID` IN (3665,3666,3667,3668,3669,3670); +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(3665, 5733), +(3666, 5734), +(3667, 5735), +(3668, 5736), +(3669, 5737), +(3670, 5738); + +-- Fix menu 3670: ActionMenuID should be 0 (close), not 3664 (loop) +UPDATE `gossip_menu_option` SET `ActionMenuID`=0 WHERE `MenuID`=3670 AND `OptionID`=0; + +-- Condition on gossip_menu_option: show first option only if quest 6566 is taken +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=3664 AND `SourceEntry`=0 AND `ConditionTypeOrReference`=9; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 3664, 0, 0, 0, 9, 0, 6566, 0, 0, 0, 0, 0, '', 'Thrall - Show gossip option if quest 6566 is taken'); + +-- Quest credit on final gossip step handled via sGossipSelect in C++ AI struct. +-- Cannot use SAI because Thrall's custom C++ AI (GetAI) takes precedence over SmartAI. +-- No AIName change needed. diff --git a/src/common/Collision/Management/IVMapMgr.h b/src/common/Collision/Management/IVMapMgr.h index 410743f76..4af323372 100644 --- a/src/common/Collision/Management/IVMapMgr.h +++ b/src/common/Collision/Management/IVMapMgr.h @@ -31,6 +31,8 @@ This is the minimum interface to the VMapMamager. namespace VMAP { + class StaticMapTree; + enum VMAP_LOAD_RESULT { VMAP_LOAD_RESULT_ERROR, @@ -88,20 +90,8 @@ namespace VMAP virtual ~IVMapMgr() = default; - virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; - virtual LoadResult existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; - virtual void unloadMap(unsigned int pMapId, int x, int y) = 0; - virtual void unloadMap(unsigned int pMapId) = 0; - - virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) = 0; - virtual float getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) = 0; - /** - test if we hit an object. return true if we hit one. rx, ry, rz will hold the hit position or the dest position, if no intersection was found - return a position, that is pReduceDist closer to the origin - */ - virtual bool GetObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float pModifyDist) = 0; /** send debug commands */ @@ -123,12 +113,6 @@ namespace VMAP [[nodiscard]] bool isMapLoadingEnabled() const { return (iEnableLineOfSightCalc || iEnableHeightCalc ); } [[nodiscard]] virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const = 0; - - /** - Query world model area info. - \param z gets adjusted to the ground height for which this are info is valid - */ - virtual bool GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional reqLiquidType, AreaAndLiquidData& data) const = 0; }; } diff --git a/src/common/Collision/Management/MMapFactory.cpp b/src/common/Collision/Management/MMapFactory.cpp deleted file mode 100644 index 4409bcc77..000000000 --- a/src/common/Collision/Management/MMapFactory.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of the AzerothCore 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 . - */ - -#include "MMapFactory.h" -#include -namespace MMAP -{ - // ######################## MMapFactory ######################## - // our global singleton copy - MMapMgr* g_MMapMgr = nullptr; - bool MMapFactory::forbiddenMaps[1000] = {0}; - - MMapMgr* MMapFactory::createOrGetMMapMgr() - { - if (!g_MMapMgr) - { - g_MMapMgr = new MMapMgr(); - } - - return g_MMapMgr; - } - - void MMapFactory::InitializeDisabledMaps() - { - memset(&forbiddenMaps, 0, sizeof(forbiddenMaps)); - int32 f[] = {616 /*EoE*/, 649 /*ToC25*/, 650 /*ToC5*/, -1}; - uint32 i = 0; - while (f[i] >= 0) - { - forbiddenMaps[f[i++]] = true; - } - } - - void MMapFactory::clear() - { - if (g_MMapMgr) - { - delete g_MMapMgr; - g_MMapMgr = nullptr; - } - } -} diff --git a/src/common/Collision/Management/MMapFactory.h b/src/common/Collision/Management/MMapFactory.h deleted file mode 100644 index 923954ab4..000000000 --- a/src/common/Collision/Management/MMapFactory.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the AzerothCore 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 . - */ - -#ifndef _MMAP_FACTORY_H -#define _MMAP_FACTORY_H - -#include "MMapMgr.h" - -namespace MMAP -{ - enum MMAP_LOAD_RESULT - { - MMAP_LOAD_RESULT_ERROR, - MMAP_LOAD_RESULT_OK, - MMAP_LOAD_RESULT_IGNORED, - }; - - // static class - // holds all mmap global data - // access point to MMapMgr singleton - class MMapFactory - { - public: - static MMapMgr* createOrGetMMapMgr(); - static void clear(); - static void InitializeDisabledMaps(); - static bool forbiddenMaps[1000]; - }; -} - -#endif diff --git a/src/common/Collision/Management/MMapMgr.cpp b/src/common/Collision/Management/MMapMgr.cpp index d3c450097..9944995e4 100644 --- a/src/common/Collision/Management/MMapMgr.cpp +++ b/src/common/Collision/Management/MMapMgr.cpp @@ -24,63 +24,8 @@ namespace MMAP { // ######################## MMapMgr ######################## - MMapMgr::~MMapMgr() + std::shared_ptr MMapMgr::LoadNavMesh(uint32 mapId) { - for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i) - { - delete i->second; - } - - // by now we should not have maps loaded - // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost! - } - - void MMapMgr::InitializeThreadUnsafe(const std::vector& mapIds) - { - // the caller must pass the list of all mapIds that will be used in the VMapMgr2 lifetime - for (const uint32& mapId : mapIds) - { - loadedMMaps.emplace(mapId, nullptr); - } - - thread_safe_environment = false; - } - - MMapDataSet::const_iterator MMapMgr::GetMMapData(uint32 mapId) const - { - // return the iterator if found or end() if not found/NULL - MMapDataSet::const_iterator itr = loadedMMaps.find(mapId); - if (itr != loadedMMaps.cend() && !itr->second) - { - itr = loadedMMaps.cend(); - } - - return itr; - } - - bool MMapMgr::loadMapData(uint32 mapId) - { - // we already have this map loaded? - MMapDataSet::iterator itr = loadedMMaps.find(mapId); - if (itr != loadedMMaps.end()) - { - if (itr->second) - { - return true; - } - } - else - { - if (thread_safe_environment) - { - itr = loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr)).first; - } - else - { - ABORT("Invalid mapId {} passed to MMapMgr after startup in thread unsafe environment", mapId); - } - } - // load and init dtNavMesh - read parameters from file std::string fileName = Acore::StringFormat(MAP_FILE_NAME_FORMAT, sConfigMgr->GetOption("DataDir", "."), mapId); @@ -88,7 +33,7 @@ namespace MMAP if (!file) { LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '{}'", fileName); - return false; + return nullptr; } dtNavMeshParams params; @@ -97,7 +42,7 @@ namespace MMAP if (count != 1) { LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '{}'", fileName); - return false; + return nullptr; } dtNavMesh* mesh = dtAllocNavMesh(); @@ -106,15 +51,13 @@ namespace MMAP { dtFreeNavMesh(mesh); LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:03} from file {}", mapId, fileName); - return false; + return nullptr; } LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:03}.mmap", mapId); - // store inside our map list - MMapData* mmap_data = new MMapData(mesh); - itr->second = mmap_data; - return true; + std::shared_ptr navMesh = std::shared_ptr(mesh, NavMeshDeleter()); + return navMesh; } uint32 MMapMgr::packTileID(int32 x, int32 y) @@ -122,26 +65,8 @@ namespace MMAP return uint32(x << 16 | y); } - bool MMapMgr::loadMap(uint32 mapId, int32 x, int32 y) + bool MMapMgr::LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y) { - // make sure the mmap is loaded and ready to load tiles - if (!loadMapData(mapId)) - { - return false; - } - - // get this mmap data - MMapData* mmap = loadedMMaps[mapId]; - ASSERT(mmap->navMesh); - - // check if we already have this tile loaded - uint32 packedGridPos = packTileID(x, y); - if (mmap->loadedTileRefs.find(packedGridPos) != mmap->loadedTileRefs.end()) - { - LOG_ERROR("maps", "MMAP:loadMap: Asked to load already loaded navmesh tile. {:03}{:02}{:02}.mmtile", mapId, x, y); - return false; - } - // load this tile :: mmaps/MMMXXYY.mmtile std::string fileName = Acore::StringFormat(TILE_FILE_NAME_FORMAT, sConfigMgr->GetOption("DataDir", "."), mapId, x, y); FILE* file = fopen(fileName.c_str(), "rb"); @@ -184,10 +109,8 @@ namespace MMAP dtTileRef tileRef = 0; // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed - if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))) + if (dtStatusSucceed(navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))) { - mmap->loadedTileRefs.insert(std::pair(packedGridPos, tileRef)); - ++loadedTiles; dtMeshHeader* header = (dtMeshHeader*)data; LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile {:03}[{:02},{:02}] into {:03}[{:02},{:02}]", mapId, x, y, mapId, header->x, header->y); return true; @@ -198,149 +121,19 @@ namespace MMAP return false; } - bool MMapMgr::unloadMap(uint32 mapId, int32 x, int32 y) + ManagedNavMeshQuery MMapMgr::CreateNavMeshQuery(dtNavMesh* navMesh) { - // check if we have this map loaded - MMapDataSet::const_iterator itr = GetMMapData(mapId); - if (itr == loadedMMaps.end()) - { - // file may not exist, therefore not loaded - LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:03}{:02}{:02}.mmtile", mapId, x, y); - return false; - } - - MMapData* mmap = itr->second; - - // check if we have this tile loaded - uint32 packedGridPos = packTileID(x, y); - if (mmap->loadedTileRefs.find(packedGridPos) == mmap->loadedTileRefs.end()) - { - // file may not exist, therefore not loaded - LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {:03}{:02}{:02}.mmtile", mapId, x, y); - return false; - } - - dtTileRef tileRef = mmap->loadedTileRefs[packedGridPos]; - - // unload, and mark as non loaded - if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, nullptr, nullptr))) - { - // this is technically a memory leak - // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used - // we cannot recover from this error - assert out - LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y); - ABORT(); - } - - mmap->loadedTileRefs.erase(packedGridPos); - --loadedTiles; - LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02},{:02}] from {:03}", mapId, x, y, mapId); - return true; - } - - bool MMapMgr::unloadMap(uint32 mapId) - { - MMapDataSet::iterator itr = loadedMMaps.find(mapId); - if (itr == loadedMMaps.end() || !itr->second) - { - // file may not exist, therefore not loaded - LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map {:03}", mapId); - return false; - } - - // unload all tiles from given map - MMapData* mmap = itr->second; - for (auto& i : mmap->loadedTileRefs) - { - uint32 x = (i.first >> 16); - uint32 y = (i.first & 0x0000FFFF); - - if (dtStatusFailed(mmap->navMesh->removeTile(i.second, nullptr, nullptr))) - { - LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y); - } - else - { - --loadedTiles; - LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02},{:02}] from {:03}", mapId, x, y, mapId); - } - } - - delete mmap; - itr->second = nullptr; - LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded {:03}.mmap", mapId); - - return true; - } - - bool MMapMgr::unloadMapInstance(uint32 mapId, uint32 instanceId) - { - // check if we have this map loaded - MMapDataSet::const_iterator itr = GetMMapData(mapId); - if (itr == loadedMMaps.end()) - { - // file may not exist, therefore not loaded - LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map {:03}", mapId); - return false; - } - - MMapData* mmap = itr->second; - if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end()) - { - LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId {:03} instanceId {}", mapId, instanceId); - return false; - } - - dtNavMeshQuery* query = mmap->navMeshQueries[instanceId]; - - dtFreeNavMeshQuery(query); - mmap->navMeshQueries.erase(instanceId); - LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId {:03} instanceId {}", mapId, instanceId); - - return true; - } - - dtNavMesh const* MMapMgr::GetNavMesh(uint32 mapId) - { - MMapDataSet::const_iterator itr = GetMMapData(mapId); - if (itr == loadedMMaps.end()) + // allocate mesh query + dtNavMeshQuery* query = dtAllocNavMeshQuery(); + ASSERT(query); + + if (dtStatusFailed(query->init(navMesh, 1024))) { + dtFreeNavMeshQuery(query); return nullptr; } - return itr->second->navMesh; - } - - dtNavMeshQuery const* MMapMgr::GetNavMeshQuery(uint32 mapId, uint32 instanceId) - { - MMapDataSet::const_iterator itr = GetMMapData(mapId); - if (itr == loadedMMaps.end()) - { - return nullptr; - } - - MMapData* mmap = itr->second; - if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end()) - { - // check again after acquiring mutex - if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end()) - { - // allocate mesh query - dtNavMeshQuery* query = dtAllocNavMeshQuery(); - ASSERT(query); - - if (dtStatusFailed(query->init(mmap->navMesh, 1024))) - { - dtFreeNavMeshQuery(query); - LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId); - return nullptr; - } - - LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId); - mmap->navMeshQueries.insert(std::pair(instanceId, query)); - } - } - - return mmap->navMeshQueries[instanceId]; + ManagedNavMeshQuery navMeshQuery = ManagedNavMeshQuery(query); + return navMeshQuery; } } diff --git a/src/common/Collision/Management/MMapMgr.h b/src/common/Collision/Management/MMapMgr.h index 823142cde..3b02d280a 100644 --- a/src/common/Collision/Management/MMapMgr.h +++ b/src/common/Collision/Management/MMapMgr.h @@ -22,8 +22,7 @@ #include "DetourAlloc.h" #include "DetourExtended.h" #include "DetourNavMesh.h" -#include -#include +#include // memory management inline void* dtCustomAlloc(std::size_t size, dtAllocHint /*hint*/) @@ -39,67 +38,40 @@ inline void dtCustomFree(void* ptr) // move map related classes namespace MMAP { + enum MMAP_LOAD_RESULT + { + MMAP_LOAD_RESULT_ERROR, + MMAP_LOAD_RESULT_OK, + MMAP_LOAD_RESULT_IGNORED, + }; + static char const* const MAP_FILE_NAME_FORMAT = "{}/mmaps/{:03}.mmap"; static char const* const TILE_FILE_NAME_FORMAT = "{}/mmaps/{:03}{:02}{:02}.mmtile"; - typedef std::unordered_map MMapTileSet; - typedef std::unordered_map NavMeshQuerySet; - - // dummy struct to hold map's mmap data - struct MMapData + struct NavMeshDeleter { - MMapData(dtNavMesh* mesh) : navMesh(mesh) { } - - ~MMapData() - { - for (auto& navMeshQuerie : navMeshQueries) - { - dtFreeNavMeshQuery(navMeshQuerie.second); - } - - if (navMesh) - { - dtFreeNavMesh(navMesh); - } - } - - // we have to use single dtNavMeshQuery for every instance, since those are not thread safe - NavMeshQuerySet navMeshQueries; // instanceId to query - dtNavMesh* navMesh; - MMapTileSet loadedTileRefs; // maps [map grid coords] to [dtTile] + void operator()(dtNavMesh* navMesh) noexcept { dtFreeNavMesh(navMesh); } }; - typedef std::unordered_map MMapDataSet; + struct NavMeshQueryDeleter + { + void operator()(dtNavMeshQuery* query) noexcept { dtFreeNavMeshQuery(query); } + }; + + using ManagedNavMeshQuery = std::unique_ptr; - // singleton class - // holds all all access to mmap loading unloading and meshes class MMapMgr { public: - MMapMgr() = default; - ~MMapMgr(); + MMapMgr() = default; + ~MMapMgr() = default; - void InitializeThreadUnsafe(const std::vector& mapIds); - bool loadMap(uint32 mapId, int32 x, int32 y); - bool unloadMap(uint32 mapId, int32 x, int32 y); - bool unloadMap(uint32 mapId); - bool unloadMapInstance(uint32 mapId, uint32 instanceId); - - // the returned [dtNavMeshQuery const*] is NOT threadsafe - dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId); - dtNavMesh const* GetNavMesh(uint32 mapId); - - [[nodiscard]] uint32 getLoadedTilesCount() const { return loadedTiles; } - [[nodiscard]] uint32 getLoadedMapsCount() const { return loadedMMaps.size(); } + static std::shared_ptr LoadNavMesh(uint32 mapId); + static bool LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y); + static ManagedNavMeshQuery CreateNavMeshQuery(dtNavMesh* navMesh); private: - bool loadMapData(uint32 mapId); - uint32 packTileID(int32 x, int32 y); - [[nodiscard]] MMapDataSet::const_iterator GetMMapData(uint32 mapId) const; - - MMapDataSet loadedMMaps; - uint32 loadedTiles{0}; - bool thread_safe_environment{true}; + static uint32 packTileID(int32 x, int32 y); }; } diff --git a/src/common/Collision/Management/VMapMgr2.cpp b/src/common/Collision/Management/VMapMgr2.cpp index 7639c9c6b..24e4745d7 100644 --- a/src/common/Collision/Management/VMapMgr2.cpp +++ b/src/common/Collision/Management/VMapMgr2.cpp @@ -17,11 +17,8 @@ #include "VMapMgr2.h" #include "Errors.h" -#include "Log.h" #include "MapDefines.h" #include "MapTree.h" -#include "ModelInstance.h" -#include "WorldModel.h" #include #include #include @@ -35,34 +32,13 @@ namespace VMAP { GetLiquidFlagsPtr = &GetLiquidFlagsDummy; IsVMAPDisabledForPtr = &IsVMAPDisabledForDummy; - thread_safe_environment = true; } VMapMgr2::~VMapMgr2() { - for (InstanceTreeMap::iterator i = iInstanceMapTrees.begin(); i != iInstanceMapTrees.end(); ++i) - { - delete i->second; - } - - for (ModelFileMap::iterator i = iLoadedModelFiles.begin(); i != iLoadedModelFiles.end(); ++i) - { - delete i->second.getModel(); - } } - void VMapMgr2::InitializeThreadUnsafe(const std::vector& mapIds) - { - // the caller must pass the list of all mapIds that will be used in the VMapMgr2 lifetime - for (const uint32& mapId : mapIds) - { - iInstanceMapTrees.emplace(mapId, nullptr); - } - - thread_safe_environment = false; - } - - Vector3 VMapMgr2::convertPositionToInternalRep(float x, float y, float z) const + Vector3 VMapMgr2::convertPositionToInternalRep(float x, float y, float z) { Vector3 pos; const float mid = 0.5f * MAX_NUMBER_OF_GRIDS * SIZE_OF_GRIDS; @@ -73,18 +49,6 @@ namespace VMAP return pos; } - InstanceTreeMap::const_iterator VMapMgr2::GetMapTree(uint32 mapId) const - { - // return the iterator if found or end() if not found/NULL - InstanceTreeMap::const_iterator itr = iInstanceMapTrees.find(mapId); - if (itr != iInstanceMapTrees.cend() && !itr->second) - { - itr = iInstanceMapTrees.cend(); - } - - return itr; - } - // move to MapTree too? std::string VMapMgr2::getMapFileName(unsigned int mapId) { @@ -95,245 +59,9 @@ namespace VMAP return fname.str(); } - int VMapMgr2::loadMap(const char* basePath, unsigned int mapId, int x, int y) - { - int result = VMAP_LOAD_RESULT_IGNORED; - if (isMapLoadingEnabled()) - { - if (_loadMap(mapId, basePath, x, y)) - { - result = VMAP_LOAD_RESULT_OK; - } - else - { - result = VMAP_LOAD_RESULT_ERROR; - } - } - - return result; - } - - // load one tile (internal use only) - bool VMapMgr2::_loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY) - { - InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId); - if (instanceTree == iInstanceMapTrees.end()) - { - if (thread_safe_environment) - { - instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(mapId, nullptr)).first; - } - else - ABORT("Invalid mapId {} tile [{}, {}] passed to VMapMgr2 after startup in thread unsafe environment", - mapId, tileX, tileY); - } - - if (!instanceTree->second) - { - std::string mapFileName = getMapFileName(mapId); - StaticMapTree* newTree = new StaticMapTree(mapId, basePath); - if (!newTree->InitMap(mapFileName, this)) - { - delete newTree; - return false; - } - instanceTree->second = newTree; - } - - return instanceTree->second->LoadMapTile(tileX, tileY, this); - } - - void VMapMgr2::unloadMap(unsigned int mapId) - { - InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId); - if (instanceTree != iInstanceMapTrees.end() && instanceTree->second) - { - instanceTree->second->UnloadMap(this); - if (instanceTree->second->numLoadedTiles() == 0) - { - delete instanceTree->second; - instanceTree->second = nullptr; - } - } - } - - void VMapMgr2::unloadMap(unsigned int mapId, int x, int y) - { - InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId); - if (instanceTree != iInstanceMapTrees.end() && instanceTree->second) - { - instanceTree->second->UnloadMapTile(x, y, this); - if (instanceTree->second->numLoadedTiles() == 0) - { - delete instanceTree->second; - instanceTree->second = nullptr; - } - } - } - - bool VMapMgr2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) - { -#if defined(ENABLE_VMAP_CHECKS) - if (!isLineOfSightCalcEnabled() || IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS)) - { - return true; - } -#endif - - InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId); - if (instanceTree != iInstanceMapTrees.end()) - { - Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1); - Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); - if (pos1 != pos2) - { - return instanceTree->second->isInLineOfSight(pos1, pos2, ignoreFlags); - } - } - - return true; - } - - /** - get the hit position and return true if we hit something - otherwise the result pos will be the dest pos - */ - bool VMapMgr2::GetObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) - { -#if defined(ENABLE_VMAP_CHECKS) - if (isLineOfSightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS)) -#endif - { - InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId); - if (instanceTree != iInstanceMapTrees.end()) - { - Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1); - Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); - Vector3 resultPos; - bool result = instanceTree->second->GetObjectHitPos(pos1, pos2, resultPos, modifyDist); - resultPos = convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z); - rx = resultPos.x; - ry = resultPos.y; - rz = resultPos.z; - return result; - } - } - - rx = x2; - ry = y2; - rz = z2; - - return false; - } - - /** - get height or INVALID_HEIGHT if no height available - */ - - float VMapMgr2::getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist) - { -#if defined(ENABLE_VMAP_CHECKS) - if (isHeightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_HEIGHT)) -#endif - { - InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId); - if (instanceTree != iInstanceMapTrees.end()) - { - Vector3 pos = convertPositionToInternalRep(x, y, z); - float height = instanceTree->second->getHeight(pos, maxSearchDist); - if (height >= G3D::finf()) - { - return height = VMAP_INVALID_HEIGHT_VALUE; // No height - } - - return height; - } - } - - return VMAP_INVALID_HEIGHT_VALUE; - } - - bool VMapMgr2::GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional reqLiquidType, AreaAndLiquidData& data) const - { - InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId); - if (instanceTree != iInstanceMapTrees.end()) - { - LocationInfo info; - Vector3 pos = convertPositionToInternalRep(x, y, z); - if (instanceTree->second->GetLocationInfo(pos, info)) - { - data.floorZ = info.ground_Z; - if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS)) - { - uint32 liquidType = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc - float liquidLevel; - if (!reqLiquidType || (GetLiquidFlagsPtr(liquidType) & *reqLiquidType)) - if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel)) - data.liquidInfo.emplace(liquidType, liquidLevel); - } - - if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG)) - data.areaInfo.emplace(info.hitModel->GetWmoID(), info.hitInstance->adtId, info.rootId, info.hitModel->GetMogpFlags(), info.hitInstance->ID); - return true; - } - } - - return false; - } - - WorldModel* VMapMgr2::acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags/* Only used when creating the model */) - { - //! Critical section, thread safe access to iLoadedModelFiles - std::lock_guard lock(LoadedModelFilesLock); - - ModelFileMap::iterator model = iLoadedModelFiles.find(filename); - if (model == iLoadedModelFiles.end()) - { - WorldModel* worldmodel = new WorldModel(); - if (!worldmodel->readFile(basepath + filename + ".vmo")) - { - LOG_ERROR("maps", "VMapMgr2: could not load '{}{}.vmo'", basepath, filename); - delete worldmodel; - return nullptr; - } - LOG_DEBUG("maps", "VMapMgr2: loading file '{}{}'", basepath, filename); - - worldmodel->Flags = flags; - - model = iLoadedModelFiles.insert(std::pair(filename, ManagedModel())).first; - model->second.setModel(worldmodel); - } - - return model->second.getModel(); - } - - void VMapMgr2::releaseModelInstance(const std::string& filename) - { - //! Critical section, thread safe access to iLoadedModelFiles - std::lock_guard lock(LoadedModelFilesLock); - - ModelFileMap::iterator model = iLoadedModelFiles.find(filename); - if (model == iLoadedModelFiles.end()) - { - LOG_ERROR("maps", "VMapMgr2: trying to unload non-loaded file '{}'", filename); - return; - } - if (model->second.decRefCount() == 0) - { - LOG_DEBUG("maps", "VMapMgr2: unloading file '{}'", filename); - delete model->second.getModel(); - iLoadedModelFiles.erase(model); - } - } - LoadResult VMapMgr2::existsMap(const char* basePath, unsigned int mapId, int x, int y) { return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y); } - void VMapMgr2::GetInstanceMapTree(InstanceTreeMap& instanceMapTree) - { - instanceMapTree = iInstanceMapTrees; - } - } // namespace VMAP diff --git a/src/common/Collision/Management/VMapMgr2.h b/src/common/Collision/Management/VMapMgr2.h index 5fc4106d2..3c221b170 100644 --- a/src/common/Collision/Management/VMapMgr2.h +++ b/src/common/Collision/Management/VMapMgr2.h @@ -19,9 +19,6 @@ #define _VMAPMANAGER2_H #include "IVMapMgr.h" -#include -#include -#include //=========================================================== @@ -46,24 +43,6 @@ namespace G3D namespace VMAP { - class StaticMapTree; - class WorldModel; - - class ManagedModel - { - public: - ManagedModel() { } - void setModel(WorldModel* model) { iModel = model; } - WorldModel* getModel() { return iModel; } - int decRefCount() { return --iRefCount; } - protected: - WorldModel* iModel{nullptr}; - int iRefCount{0}; - }; - - typedef std::unordered_map InstanceTreeMap; - typedef std::unordered_map ModelFileMap; - enum DisableTypes { VMAP_DISABLE_AREAFLAG = 0x1, @@ -75,58 +54,25 @@ namespace VMAP class VMapMgr2 : public IVMapMgr { protected: - // Tree to check collision - ModelFileMap iLoadedModelFiles; - InstanceTreeMap iInstanceMapTrees; - bool thread_safe_environment; - - // Mutex for iLoadedModelFiles - std::mutex LoadedModelFilesLock; - - bool _loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY); - /* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */ - static uint32 GetLiquidFlagsDummy(uint32) { return 0; } static bool IsVMAPDisabledForDummy(uint32 /*entry*/, uint8 /*flags*/) { return false; } - InstanceTreeMap::const_iterator GetMapTree(uint32 mapId) const; - public: // public for debug - [[nodiscard]] G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const; + static G3D::Vector3 convertPositionToInternalRep(float x, float y, float z); static std::string getMapFileName(unsigned int mapId); VMapMgr2(); ~VMapMgr2() override; - void InitializeThreadUnsafe(const std::vector& mapIds); - - int loadMap(const char* pBasePath, unsigned int mapId, int x, int y) override; - - void unloadMap(unsigned int mapId, int x, int y) override; - void unloadMap(unsigned int mapId) override; - - bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) override ; - /** - fill the hit pos and return true, if an object was hit - */ - bool GetObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) override; - float getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist) override; - bool processCommand(char* /*command*/) override { return false; } // for debug and extensions - bool GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional reqLiquidType, AreaAndLiquidData& data) const override; - - WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags); - void releaseModelInstance(const std::string& filename); - // what's the use of this? o.O [[nodiscard]] std::string getDirFileName(unsigned int mapId, int /*x*/, int /*y*/) const override { return getMapFileName(mapId); } LoadResult existsMap(const char* basePath, unsigned int mapId, int x, int y) override; - void GetInstanceMapTree(InstanceTreeMap& instanceMapTree); typedef uint32(*GetLiquidFlagsFn)(uint32 liquidType); GetLiquidFlagsFn GetLiquidFlagsPtr; diff --git a/src/common/Collision/Management/WorldModelStore.cpp b/src/common/Collision/Management/WorldModelStore.cpp new file mode 100644 index 000000000..af78a01d5 --- /dev/null +++ b/src/common/Collision/Management/WorldModelStore.cpp @@ -0,0 +1,43 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#include "Log.h" +#include "WorldModelStore.h" + +std::shared_ptr WorldModelStore::AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags/* Only used when creating the model */) +{ + //! Critical section, thread safe access + std::lock_guard lock(_lock); + + ModelFileMap::iterator model = _loadedModels.find(filename); + if (model == _loadedModels.end()) + { + std::shared_ptr worldmodel = std::make_shared(); + LOG_DEBUG("maps", "WorldModelStore: loading file '{}{}'", basepath, filename); + if (!worldmodel->readFile(basepath + filename + ".vmo")) + { + LOG_ERROR("maps", "WorldModelStore: could not load '{}{}.vmo'", basepath, filename); + return nullptr; + } + + worldmodel->Flags = flags; + + model = _loadedModels.insert(std::pair>(filename, worldmodel)).first; + } + + return model->second; +} diff --git a/src/common/Collision/Management/IMMAPMgr.h b/src/common/Collision/Management/WorldModelStore.h similarity index 55% rename from src/common/Collision/Management/IMMAPMgr.h rename to src/common/Collision/Management/WorldModelStore.h index 49452c4a7..a175e0d1f 100644 --- a/src/common/Collision/Management/IMMAPMgr.h +++ b/src/common/Collision/Management/WorldModelStore.h @@ -15,32 +15,32 @@ * with this program. If not, see . */ -#ifndef _IMMAPMANAGER_H -#define _IMMAPMANAGER_H +#ifndef _WORLDMODELSTORE_H +#define _WORLDMODELSTORE_H -// Interface for IMMapManger -namespace MMAP +#include "WorldModel.h" +#include +#include +#include + +class WorldModelStore { - enum MMAP_LOAD_RESULT +public: + static WorldModelStore* instance() { - MMAP_LOAD_RESULT_ERROR, - MMAP_LOAD_RESULT_OK, - MMAP_LOAD_RESULT_IGNORED, - }; + static WorldModelStore instance; + return &instance; + } - class IMMapMgr - { - private: - bool iEnablePathFinding; + std::shared_ptr AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags); - public: - IMMapMgr() : iEnablePathFinding(true) {} - virtual ~IMMapMgr(void) {} +private: + typedef std::unordered_map> ModelFileMap; + ModelFileMap _loadedModels; - //Enabled/Disabled Pathfinding - void setEnablePathFinding(bool value) { iEnablePathFinding = value; } - bool isEnablePathFinding() const { return (iEnablePathFinding); } - }; -} + std::mutex _lock; +}; + +#define sWorldModelStore WorldModelStore::instance() #endif diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp index bb9577d4e..877b17085 100644 --- a/src/common/Collision/Maps/MapTree.cpp +++ b/src/common/Collision/Maps/MapTree.cpp @@ -22,6 +22,7 @@ #include "ModelInstance.h" #include "VMapDefinitions.h" #include "VMapMgr2.h" +#include "WorldModelStore.h" #include #include #include @@ -259,7 +260,7 @@ namespace VMAP //========================================================= - bool StaticMapTree::InitMap(const std::string& fname, VMapMgr2* vm) + bool StaticMapTree::InitMap(const std::string& fname) { //VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname); bool success = false; @@ -291,13 +292,12 @@ namespace VMAP #endif if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn)) { - WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags); + std::shared_ptr model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags); //VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading {}", spawn.name); if (model) { // assume that global model always is the first and only tree value (could be improved...) iTreeValues[0] = ModelInstance(spawn, model); - iLoadedSpawns[0] = 1; } else { @@ -312,23 +312,14 @@ namespace VMAP //========================================================= - void StaticMapTree::UnloadMap(VMapMgr2* vm) + void StaticMapTree::UnloadMap() { - for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i) - { - iTreeValues[i->first].setUnloaded(); - for (uint32 refCount = 0; refCount < i->second; ++refCount) - { - vm->releaseModelInstance(iTreeValues[i->first].name); - } - } - iLoadedSpawns.clear(); iLoadedTiles.clear(); } //========================================================= - bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm) + bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY) { if (!iIsTiled) { @@ -367,10 +358,11 @@ namespace VMAP if (result) { // acquire model instance - WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags); + std::shared_ptr model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags); if (!model) { LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY); + // why do we continue to try to load if the model was unsuccessful here? } // update tree @@ -378,22 +370,22 @@ namespace VMAP if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1) { - if (!iLoadedSpawns.count(referencedVal)) + if (referencedVal >= iNTreeValues) { -#if defined(VMAP_DEBUG) - if (referencedVal > iNTreeValues) - { - LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{})", referencedVal, iNTreeValues); - continue; - } -#endif - iTreeValues[referencedVal] = ModelInstance(spawn, model); - iLoadedSpawns[referencedVal] = 1; + LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{})", referencedVal, iNTreeValues); + continue; } + + // This looks odd and is confusing, took some research to figure it out: + // the first WorldModel will create a "groupmodel" of all other same-models in the tile + // we don't actually care about anything else + if (!iTreeValues[referencedVal].getWorldModel()) + { + iTreeValues[referencedVal] = ModelInstance(spawn, model); + } +#if defined(VMAP_DEBUG) else { - ++iLoadedSpawns[referencedVal]; -#if defined(VMAP_DEBUG) if (iTreeValues[referencedVal].ID != spawn.ID) { LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node"); @@ -402,8 +394,8 @@ namespace VMAP { LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID); } -#endif } +#endif } else { @@ -427,7 +419,7 @@ namespace VMAP //========================================================= - void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm) + void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY) { uint32 tileID = packTileID(tileX, tileY); loadedTileMap::iterator tile = iLoadedTiles.find(tileID); @@ -436,57 +428,6 @@ namespace VMAP LOG_ERROR("maps", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY); return; } - if (tile->second) // file associated with tile - { - std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY); - FILE* tf = fopen(tilefile.c_str(), "rb"); - if (tf) - { - bool result = true; - char chunk[8]; - if (!readChunk(tf, chunk, VMAP_MAGIC, 8)) - { - result = false; - } - uint32 numSpawns; - if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1) - { - result = false; - } - for (uint32 i = 0; i < numSpawns && result; ++i) - { - // read model spawns - ModelSpawn spawn; - result = ModelSpawn::readFromFile(tf, spawn); - if (result) - { - // release model instance - vm->releaseModelInstance(spawn.name); - - // update tree - uint32 referencedNode; - - if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1) - { - result = false; - } - else - { - if (!iLoadedSpawns.count(referencedNode)) - { - LOG_ERROR("maps", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '{}' (ID:{})", spawn.name, spawn.ID); - } - else if (--iLoadedSpawns[referencedNode] == 0) - { - iTreeValues[referencedNode].setUnloaded(); - iLoadedSpawns.erase(referencedNode); - } - } - } - } - fclose(tf); - } - } iLoadedTiles.erase(tile); METRIC_EVENT("map_events", "UnloadMapTile", diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h index 09e426e3f..88f0b8302 100644 --- a/src/common/Collision/Maps/MapTree.h +++ b/src/common/Collision/Maps/MapTree.h @@ -60,8 +60,6 @@ namespace VMAP // some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed // empty tiles have no tile file, hence map with bool instead of just a set (consistency check) loadedTileMap iLoadedTiles; - // stores to invalidate tree values, unload map, and to be able to report errors - loadedSpawnMap iLoadedSpawns; std::string iBasePath; private: @@ -81,10 +79,10 @@ namespace VMAP [[nodiscard]] float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const; bool GetLocationInfo(const G3D::Vector3& pos, LocationInfo& info) const; - bool InitMap(const std::string& fname, VMapMgr2* vm); - void UnloadMap(VMapMgr2* vm); - bool LoadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm); - void UnloadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm); + bool InitMap(const std::string& fname); + void UnloadMap(); + bool LoadMapTile(uint32 tileX, uint32 tileY); + void UnloadMapTile(uint32 tileX, uint32 tileY); [[nodiscard]] bool isTiled() const { return iIsTiled; } [[nodiscard]] uint32 numLoadedTiles() const { return iLoadedTiles.size(); } void GetModelInstances(ModelInstance*& models, uint32& count); diff --git a/src/common/Collision/Models/GameObjectModel.cpp b/src/common/Collision/Models/GameObjectModel.cpp index eebc7e62d..6a28164a2 100644 --- a/src/common/Collision/Models/GameObjectModel.cpp +++ b/src/common/Collision/Models/GameObjectModel.cpp @@ -24,6 +24,7 @@ #include "VMapFactory.h" #include "VMapMgr2.h" #include "WorldModel.h" +#include "WorldModelStore.h" using G3D::Vector3; using G3D::Ray; @@ -101,14 +102,6 @@ void LoadGameObjectModelList(std::string const& dataPath) LOG_INFO("server.loading", " "); } -GameObjectModel::~GameObjectModel() -{ - if (iModel) - { - VMAP::VMapFactory::createOrGetVMapMgr()->releaseModelInstance(name); - } -} - bool GameObjectModel::initialize(std::unique_ptr modelOwner, std::string const& dataPath) { ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId()); @@ -125,7 +118,7 @@ bool GameObjectModel::initialize(std::unique_ptr model return false; } - iModel = VMAP::VMapFactory::createOrGetVMapMgr()->acquireModelInstance(dataPath + "vmaps/", it->second.name, + iModel = sWorldModelStore->AcquireModelInstance(dataPath + "vmaps/", it->second.name, it->second.isWmo ? VMAP::ModelFlags::MOD_WORLDSPAWN : VMAP::ModelFlags::MOD_M2); if (!iModel) diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h index 5938a5c1d..695a5ee43 100644 --- a/src/common/Collision/Models/GameObjectModel.h +++ b/src/common/Collision/Models/GameObjectModel.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace VMAP { @@ -58,7 +59,7 @@ public: [[nodiscard]] const G3D::AABox& GetBounds() const { return iBound; } - ~GameObjectModel(); + ~GameObjectModel() = default; [[nodiscard]] const G3D::Vector3& GetPosition() const { return iPos; } @@ -86,7 +87,7 @@ private: G3D::Vector3 iPos; float iInvScale{0}; float iScale{0}; - VMAP::WorldModel* iModel{nullptr}; + std::shared_ptr iModel; std::unique_ptr owner; bool isWmo{false}; }; diff --git a/src/common/Collision/Models/ModelInstance.cpp b/src/common/Collision/Models/ModelInstance.cpp index c5b6fbfdb..ecc97e85f 100644 --- a/src/common/Collision/Models/ModelInstance.cpp +++ b/src/common/Collision/Models/ModelInstance.cpp @@ -24,7 +24,7 @@ using G3D::Ray; namespace VMAP { - ModelInstance::ModelInstance(const ModelSpawn& spawn, WorldModel* model): ModelSpawn(spawn), iModel(model) + ModelInstance::ModelInstance(const ModelSpawn& spawn, std::shared_ptr model): ModelSpawn(spawn), iModel(model) { iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi() * iRot.y / 180.f, G3D::pi() * iRot.x / 180.f, G3D::pi() * iRot.z / 180.f).inverse(); iInvScale = 1.f / iScale; diff --git a/src/common/Collision/Models/ModelInstance.h b/src/common/Collision/Models/ModelInstance.h index 3476a514d..f8c0ecbb1 100644 --- a/src/common/Collision/Models/ModelInstance.h +++ b/src/common/Collision/Models/ModelInstance.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace VMAP { @@ -63,16 +64,15 @@ namespace VMAP { public: ModelInstance() { } - ModelInstance(const ModelSpawn& spawn, WorldModel* model); - void setUnloaded() { iModel = nullptr; } + ModelInstance(const ModelSpawn& spawn, std::shared_ptr model); bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const; bool GetLocationInfo(const G3D::Vector3& p, LocationInfo& info) const; bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo& info, float& liqHeight) const; - WorldModel* getWorldModel() { return iModel; } + WorldModel* getWorldModel() { return iModel.get(); } protected: G3D::Matrix3 iInvRot; float iInvScale{0.0f}; - WorldModel* iModel{nullptr}; + std::shared_ptr iModel; }; } // namespace VMAP diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 8a4eb638c..5a4675869 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3629,6 +3629,15 @@ Wintergrasp.NoBattleTimer = 150 Wintergrasp.CrashRestartTimer = 10 +# +# Wintergrasp.SkipBattleSessionCount +# Description: When the peace timer expires, if the number of active sessions +# exceeds this value, a new war will not start. +# Set to 0 to disable this check. +# Default: 3500 + +Wintergrasp.SkipBattleSessionCount = 3500 + # ################################################################################################### diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 19f21dc62..fc28f7d0f 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -119,6 +119,13 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED, "SELECT id, locale, text FROM autobroadcast_locale WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH); + PrepareStatement(LOGIN_INS_AUTOBROADCAST, "INSERT INTO autobroadcast (realmid, weight, text) VALUES (?, ?, ?)", CONNECTION_SYNCH); + PrepareStatement(LOGIN_DEL_AUTOBROADCAST, "DELETE FROM autobroadcast WHERE id = ? AND (realmid = ? OR realmid = -1)", CONNECTION_SYNCH); + PrepareStatement(LOGIN_INS_AUTOBROADCAST_LOCALE, "REPLACE INTO autobroadcast_locale (realmid, id, locale, text) VALUES (?, ?, ?, ?)", CONNECTION_SYNCH); + PrepareStatement(LOGIN_DEL_AUTOBROADCAST_LOCALE, "DELETE FROM autobroadcast_locale WHERE id = ? AND (realmid = ? OR realmid = -1)", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_AUTOBROADCAST_BY_ID, "SELECT 1 FROM autobroadcast WHERE id = ? AND (realmid = ? OR realmid = -1)", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_AUTOBROADCAST_LOCALE_BY_ID, "SELECT locale, text FROM autobroadcast_locale WHERE (realmid = ? OR realmid = -1) AND id = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_AUTOBROADCAST_MAX_ID, "SELECT MAX(id) FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_MOTD, "SELECT text FROM motd WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_MOTD_LOCALE, "SELECT locale, text FROM motd_localized WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH); PrepareStatement(LOGIN_REP_MOTD, "REPLACE INTO motd (realmid, text) VALUES (?, ?)", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 4f0129782..5d99e7ce8 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -101,6 +101,13 @@ enum LoginDatabaseStatements : uint32 LOGIN_DEL_ACCOUNT, LOGIN_SEL_AUTOBROADCAST, LOGIN_SEL_AUTOBROADCAST_LOCALIZED, + LOGIN_INS_AUTOBROADCAST, + LOGIN_DEL_AUTOBROADCAST, + LOGIN_INS_AUTOBROADCAST_LOCALE, + LOGIN_DEL_AUTOBROADCAST_LOCALE, + LOGIN_SEL_AUTOBROADCAST_BY_ID, + LOGIN_SEL_AUTOBROADCAST_LOCALE_BY_ID, + LOGIN_SEL_AUTOBROADCAST_MAX_ID, LOGIN_SEL_MOTD, LOGIN_SEL_MOTD_LOCALE, LOGIN_REP_MOTD, diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index ea97d9a8c..128c0c7c0 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -52,7 +52,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID, "UPDATE waypoint_data SET wpguid = ? WHERE id = ? and point = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_ID, "SELECT MAX(id) FROM waypoint_data", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_POINT, "SELECT MAX(point) FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, velocity, delay, smoothTransition, move_type, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, "SELECT point, position_x, position_y, position_z FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, "SELECT position_x, position_y, position_z FROM waypoint_data WHERE point = 1 AND id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID, "SELECT position_x, position_y, position_z, orientation FROM waypoint_data WHERE id = ? ORDER BY point DESC LIMIT 1", CONNECTION_SYNCH); @@ -77,7 +77,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, CreatureImmunitiesId, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id1 = ? OR id2 = ? OR id3 = ?", CONNECTION_SYNCH); diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp index b0d605926..79ac51fa8 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.cpp +++ b/src/server/game/AI/CoreAI/PassiveAI.cpp @@ -20,7 +20,12 @@ PassiveAI::PassiveAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } -NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } +NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) +{ + me->SetReactState(REACT_PASSIVE); + // TODO: Remove once WorldObject casting is ported (triggers won't create combat refs from spell casts) + me->SetIsCombatDisallowed(true); +} int32 NullCreatureAI::Permissible(Creature const* creature) { diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index fbe681109..665b915cb 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -198,8 +198,12 @@ void CreatureAI::OnOwnerCombatInteraction(Unit* target) if (!target || !me->IsAlive()) return; + // Prevent guardian from disengaging from current target + if (me->GetVictim() && me->GetVictim()->IsAlive()) + return; + if (!me->HasReactState(REACT_PASSIVE) && me->CanStartAttack(target, true)) - me->EngageWithTarget(target); + AttackStart(target); } // Distract creature, if player gets too close while stealthed/prowling @@ -211,6 +215,9 @@ void CreatureAI::TriggerAlert(Unit const* who) const // If this unit isn't an NPC, is already distracted, is in combat, is confused, stunned or fleeing, do nothing if (!me->IsCreature() || me->IsEngaged() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED)) return; + // If the creature is immune to players, it should not be alerted by them + if (me->IsImmuneToPC()) + return; // Only alert for hostiles! if (me->IsCivilian() || me->HasReactState(REACT_PASSIVE) || !me->IsHostileTo(who) || !me->_IsTargetAcceptable(who)) return; @@ -236,14 +243,19 @@ void CreatureAI::EnterEvadeMode(EvadeReason why) { if (Unit* owner = me->GetCharmerOrOwner()) { - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); + // Owned creatures (pets/guardians) follow their owner — clear evade state + // so they can re-enter combat immediately via CanBeginCombat + me->ClearUnitState(UNIT_STATE_EVADE); + if (!me->IsVehicle()) // vehicles should not follow their owner (passenger) + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); + } } else { // Required to prevent attacking creatures that are evading and cause them to reenter combat - // Does not apply to MoveFollow - me->AddUnitState(UNIT_STATE_EVADE); + // Does not apply to MoveFollow — UNIT_STATE_EVADE is already set from _EnterEvadeMode me->GetMotionMaster()->MoveTargetedHome(); } } @@ -378,8 +390,11 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) return false; } - // Set evade state early to prevent recursion when CombatStop triggers JustExitedCombat - // This will be cleared in MoveTargetedHome for pets/guardians following their owner + // Set evade state early to prevent recursion: CombatStop below purges combat + // refs, which triggers JustExitedCombat -> EnterEvadeMode -> _EnterEvadeMode. + // The IsInEvadeMode() check above will catch it. + // EnterEvadeMode will clear this for owned creatures (pets/guardians) that + // use MoveFollow instead of MoveTargetedHome. me->AddUnitState(UNIT_STATE_EVADE); // don't remove vehicle auras, passengers aren't supposed to drop off the vehicle diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 6509e4c0e..06d250a38 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -186,6 +186,13 @@ public: // Called at MovePath End virtual void PathEndReached(uint32 /*pathId*/) {} + /// == Waypoints system ============================= + + virtual void WaypointPathStarted(uint32 /*pathId*/) { } + virtual void WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointReached(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointPathEnded(uint32 /*nodeId*/, uint32 /*pathId*/) { } + void OnCharmed(bool apply) override; // Called at reaching home after evade diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index b19fdddce..ce9ea88e9 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -472,7 +472,7 @@ void ScriptedAI::DoResetThreat(Unit* unit) void ScriptedAI::DoResetThreatList() { - if (!me->CanHaveThreatList() || me->GetThreatMgr().IsThreatListEmpty()) + if (!me->CanHaveThreatList() || me->GetThreatMgr().IsThreatListEmpty(true)) { LOG_ERROR("entities.unit.ai", "DoResetThreatList called for creature that either cannot have threat list or has empty threat list (me entry = {})", me->GetEntry()); return; diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h index a1e2aeee4..83039da38 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -87,6 +87,7 @@ public: void GenerateWaypointArray(Movement::PointsArray* points); + using CreatureAI::WaypointReached; virtual void WaypointReached(uint32 pointId) = 0; virtual void WaypointStart(uint32 /*pointId*/) {} diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 284a6e661..80a65b113 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -109,27 +109,67 @@ void SmartAI::UpdateDespawn(const uint32 diff) mDespawnTime -= diff; } -WaypointData const* SmartAI::GetNextWayPoint() +void SmartAI::UpdateFollow(const uint32 diff) { - if (!mWayPoints || mWayPoints->empty()) + if (!mFollowGuid) + return; + + if (mFollowArrivedTimer < diff) + { + if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, mFollowArrivedAlive)) + StopFollow(true); + else + mFollowArrivedTimer = 1000; + } + else + mFollowArrivedTimer -= diff; + + if (mFollowGuid.IsPlayer()) + { + if (_followCheckTimer < diff) + { + bool shouldDespawn = false; + if (Player* player = ObjectAccessor::FindPlayer(mFollowGuid)) + { + float checkDist = me->GetInstanceScript() ? SMART_ESCORT_MAX_PLAYER_DIST * 2 : SMART_ESCORT_MAX_PLAYER_DIST; + if (!me->IsWithinDistInMap(player, checkDist)) + shouldDespawn = true; + } + else + shouldDespawn = true; + + if (shouldDespawn) + me->DespawnOrUnsummon(); + + _followCheckTimer = 1000; + } + else + _followCheckTimer -= diff; + } +} + +WaypointNode const* SmartAI::GetNextWayPoint() +{ + if (!mWayPoints || mWayPoints->Nodes.empty()) return nullptr; mCurrentWPID++; - auto itr = mWayPoints->find(mCurrentWPID); - if (itr != mWayPoints->end()) - { - mLastWP = &(*itr).second; - if (mLastWP->id != mCurrentWPID) - LOG_ERROR("scripts.ai.sai", "SmartAI::GetNextWayPoint: Got not expected waypoint id {}, expected {}", mLastWP->id, mCurrentWPID); + // mCurrentWPID is 1-based for SmartAI escort paths + if (mCurrentWPID > mWayPoints->Nodes.size()) + return nullptr; - return &(*itr).second; - } - return nullptr; + // Nodes are 0-indexed, mCurrentWPID is 1-based + WaypointNode const& node = mWayPoints->Nodes[mCurrentWPID - 1]; + mLastWP = &node; + if (mLastWP->Id != mCurrentWPID) + LOG_ERROR("scripts.ai.sai", "SmartAI::GetNextWayPoint: Got not expected waypoint id {}, expected {}", mLastWP->Id, mCurrentWPID); + + return mLastWP; } void SmartAI::GenerateWayPointArray(Movement::PointsArray* points) { - if (!mWayPoints || mWayPoints->empty()) + if (!mWayPoints || mWayPoints->Nodes.empty()) return; // Flying unit, just fill array @@ -138,16 +178,12 @@ void SmartAI::GenerateWayPointArray(Movement::PointsArray* points) // xinef: first point in vector is unit real position points->clear(); points->push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - uint32 wpCounter = mCurrentWPID; - auto itr = mWayPoints->find(wpCounter++); - do + // mCurrentWPID is 1-based + for (uint32 i = mCurrentWPID > 0 ? mCurrentWPID - 1 : 0; i < mWayPoints->Nodes.size(); ++i) { - WaypointData const& wp = (*itr).second; - points->push_back(G3D::Vector3(wp.x, wp.y, wp.z)); - - itr = mWayPoints->find(wpCounter++); + WaypointNode const& wp = mWayPoints->Nodes[i]; + points->push_back(G3D::Vector3(wp.X, wp.Y, wp.Z)); } - while (itr != mWayPoints->end()); } else { @@ -156,15 +192,15 @@ void SmartAI::GenerateWayPointArray(Movement::PointsArray* points) std::vector pVector; // xinef: first point in vector is unit real position pVector.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - uint32 wpCounter = mCurrentWPID; - uint32 length = (mWayPoints->size() - mCurrentWPID) * size; + uint32 startIdx = mCurrentWPID > 0 ? mCurrentWPID - 1 : 0; + uint32 length = (uint32)((mWayPoints->Nodes.size() - startIdx) * size); uint32 cnt = 0; - for (auto itr = mWayPoints->find(wpCounter); itr != mWayPoints->end() && cnt++ <= length; ++itr) + for (uint32 i = startIdx; i < mWayPoints->Nodes.size() && cnt++ <= length; ++i) { - WaypointData const& wp = (*itr).second; - pVector.push_back(G3D::Vector3(wp.x, wp.y, wp.z)); + WaypointNode const& wp = mWayPoints->Nodes[i]; + pVector.push_back(G3D::Vector3(wp.X, wp.Y, wp.Z)); } if (pVector.size() > 2) // more than source + dest @@ -204,10 +240,10 @@ void SmartAI::StartPath(ForcedMovement forcedMovement, uint32 path, bool repeat, return; } - if (!mWayPoints || mWayPoints->empty()) + if (!mWayPoints || mWayPoints->Nodes.empty()) return; - if (WaypointData const* wp = GetNextWayPoint()) + if (WaypointNode const* wp = GetNextWayPoint()) { AddEscortState(SMART_ESCORT_ESCORTING); mCanRepeatPath = repeat; @@ -223,7 +259,7 @@ void SmartAI::StartPath(ForcedMovement forcedMovement, uint32 path, bool repeat, GenerateWayPointArray(&pathPoints); me->GetMotionMaster()->MoveSplinePath(&pathPoints, mForcedMovement); - GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_START, nullptr, wp->id, GetScript()->GetPathId()); + GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_START, nullptr, wp->Id, GetScript()->GetPathId()); } } @@ -282,10 +318,11 @@ void SmartAI::PausePath(uint32 delay, bool forced) me->StopMoving(); me->GetMotionMaster()->MoveIdle();//force stop - auto waypoint = mWayPoints->find(mCurrentWPID); - if (waypoint->second.orientation.has_value()) + if (mCurrentWPID > 0 && mCurrentWPID <= mWayPoints->Nodes.size()) { - me->SetFacingTo(*waypoint->second.orientation); + WaypointNode const& waypoint = mWayPoints->Nodes[mCurrentWPID - 1]; + if (waypoint.Orientation.has_value()) + me->SetFacingTo(*waypoint.Orientation); } } GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_PAUSED, nullptr, mCurrentWPID, GetScript()->GetPathId()); @@ -536,23 +573,7 @@ void SmartAI::UpdateAI(uint32 diff) GetScript()->OnUpdate(diff); UpdatePath(diff); UpdateDespawn(diff); - - //TODO move to void - if (mFollowGuid) - { - if (mFollowArrivedTimer < diff) - { - if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, mFollowArrivedAlive)) - { - StopFollow(true); - return; - } - - mFollowArrivedTimer = 1000; - } - else - mFollowArrivedTimer -= diff; - } + UpdateFollow(diff); if (!IsAIControlled()) { @@ -632,7 +653,7 @@ void SmartAI::MovepointReached(uint32 id) if (mLastWP) { - me->SetPosition(mLastWP->x, mLastWP->y, mLastWP->z, me->GetOrientation()); + me->SetPosition(mLastWP->X, mLastWP->Y, mLastWP->Z, me->GetOrientation()); me->SetHomePosition(me->GetPosition()); } @@ -755,7 +776,7 @@ void SmartAI::MoveInLineOfSight(Unit* who) bool SmartAI::CanAIAttack(Unit const* /*who*/) const { - return !(me->GetReactState() == REACT_PASSIVE); + return true; } bool SmartAI::AssistPlayerInCombatAgainst(Unit* who) @@ -1255,6 +1276,32 @@ void SmartAI::PathEndReached(uint32 /*pathId*/) me->LoadPath(0); } +void SmartAI::WaypointPathStarted(uint32 /*pathId*/) +{ +} + +void SmartAI::WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) +{ +} + +void SmartAI::WaypointReached(uint32 nodeId, uint32 pathId) +{ + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, nodeId, pathId); + return; + } +} + +void SmartAI::WaypointPathEnded(uint32 nodeId, uint32 pathId) +{ + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, nodeId, pathId); + return; + } +} + void SmartAI::DistancingEnded() { SetCurrentRangeMode(true, _pendingDistancing); diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 5165058bb..5449e4970 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -58,7 +58,7 @@ public: void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false); void EndPath(bool fail = false); void ResumePath(); - WaypointData const* GetNextWayPoint(); + WaypointNode const* GetNextWayPoint(); void GenerateWayPointArray(Movement::PointsArray* points); bool HasEscortState(uint32 uiEscortState) { return (mEscortState & uiEscortState); } void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; } @@ -206,6 +206,11 @@ public: void PathEndReached(uint32 pathId) override; + void WaypointPathStarted(uint32 pathId) override; + void WaypointStarted(uint32 nodeId, uint32 pathId) override; + void WaypointReached(uint32 nodeId, uint32 pathId) override; + void WaypointPathEnded(uint32 nodeId, uint32 pathId) override; + bool CanRespawn() override { return mcanSpawn; }; void SetCanRespawn(bool canSpawn) { mcanSpawn = canSpawn; } @@ -222,6 +227,7 @@ private: bool mIsCharmed; uint32 mFollowCreditType; uint32 mFollowArrivedTimer; + uint32 _followCheckTimer; uint32 mFollowCredit; uint32 mFollowArrivedEntry; bool mFollowArrivedAlive; @@ -238,9 +244,9 @@ private: bool mWPReached; bool mOOCReached; uint32 mWPPauseTimer; - WaypointData const* mLastWP; + WaypointNode const* mLastWP; uint32 mEscortNPCFlags; - uint32 GetWPCount() { return mWayPoints ? mWayPoints->size() : 0; } + uint32 GetWPCount() { return mWayPoints ? mWayPoints->Nodes.size() : 0; } bool mCanRepeatPath; bool mEvadeDisabled; bool mCanAutoAttack; @@ -253,6 +259,7 @@ private: uint32 mDespawnTime; uint32 mDespawnState; void UpdateDespawn(const uint32 diff); + void UpdateFollow(const uint32 diff); uint32 mEscortInvokerCheckTimer; bool mJustReset; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index d126ff9d8..4024052b4 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2533,14 +2533,12 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u break; } - if (!path || path->empty()) + if (!path || path->Nodes.empty()) continue; - auto itrWp = path->find(1); - if (itrWp != path->end()) { - WaypointData const& wpData = itrWp->second; - float distToThisPath = creature->GetExactDistSq(wpData.x, wpData.y, wpData.z); + WaypointNode const& wpData = path->Nodes[0]; + float distToThisPath = creature->GetExactDistSq(wpData.X, wpData.Y, wpData.Z); if (distToThisPath < distanceToClosest) { @@ -2957,7 +2955,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (!IsPlayer(target)) continue; - target->ToPlayer()->SendCinematicStart(e.action.cinematic.entry); + target->ToPlayer()->GetCinematicMgr().StartCinematic(e.action.cinematic.entry); } break; } diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 3f0cbdef0..847856b35 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -48,11 +48,6 @@ void SmartWaypointMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - for (auto itr : waypoint_map) - { - delete itr.second; - } - waypoint_map.clear(); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); @@ -78,14 +73,15 @@ void SmartWaypointMgr::LoadFromDB() float x = fields[2].Get(); float y = fields[3].Get(); float z = fields[4].Get(); - Optional o; + std::optional o; if (!fields[5].IsNull()) o = fields[5].Get(); uint32 delay = fields[6].Get(); if (last_entry != entry) { - waypoint_map[entry] = new WaypointPath(); + waypoint_map[entry] = WaypointPath(); + waypoint_map[entry].Id = entry; last_id = 1; count++; } @@ -94,15 +90,15 @@ void SmartWaypointMgr::LoadFromDB() LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry {}, unexpected point id {}, expected {}.", entry, id, last_id); last_id++; - WaypointData data; - data.id = id; - data.x = x; - data.y = y; - data.z = z; - data.orientation = o; - data.delay = delay; - data.move_type = WAYPOINT_MOVE_TYPE_MAX; - (*waypoint_map[entry]).emplace(id, data); + WaypointNode node; + node.Id = id; + node.X = x; + node.Y = y; + node.Z = z; + node.Orientation = o; + node.Delay = delay; + node.MoveType = WAYPOINT_MOVE_TYPE_MAX; + waypoint_map[entry].Nodes.push_back(std::move(node)); last_entry = entry; total++; @@ -112,14 +108,6 @@ void SmartWaypointMgr::LoadFromDB() LOG_INFO("server.loading", " "); } -SmartWaypointMgr::~SmartWaypointMgr() -{ - for (auto itr : waypoint_map) - { - delete itr.second; - } -} - SmartAIMgr* SmartAIMgr::instance() { static SmartAIMgr instance; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index e87e01e62..8163866e2 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -2064,21 +2064,22 @@ class SmartWaypointMgr { SmartWaypointMgr() {} public: - ~SmartWaypointMgr(); + ~SmartWaypointMgr() = default; static SmartWaypointMgr* instance(); void LoadFromDB(); - WaypointPath* GetPath(uint32 id) + WaypointPath const* GetPath(uint32 id) const { - if (waypoint_map.find(id) != waypoint_map.end()) - return waypoint_map[id]; - else return 0; + auto itr = waypoint_map.find(id); + if (itr != waypoint_map.end()) + return &itr->second; + return nullptr; } private: - std::unordered_map waypoint_map; + std::unordered_map waypoint_map; }; // all events for a single entry diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index b9d9d43e8..a7494f1e6 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -31,6 +31,7 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Transport.h" +#include "World.h" #include "WorldPacket.h" #include "WorldSessionMgr.h" @@ -57,9 +58,6 @@ Battlefield::Battlefield() : NoWarBattleTime(0), RestartAfterCrash(0), TimeForAcceptInvite(20), - KickDontAcceptTimer(1000), - KickAfkPlayersTimer(1000), - LastResurrectTimer(RESURRECTION_INTERVAL), StartGroupingTimer(0), StartGrouping(false) { @@ -132,6 +130,7 @@ void Battlefield::HandlePlayerLeaveZone(Player* player, uint32 /*zone*/) cp->HandlePlayerLeave(player); InvitedPlayers[player->GetTeamId()].erase(player->GetGUID()); + PlayersInQueue[player->GetTeamId()].erase(player->GetGUID()); PlayersWillBeKick[player->GetTeamId()].erase(player->GetGUID()); Players[player->GetTeamId()].erase(player->GetGUID()); SendRemoveWorldStates(player); @@ -147,7 +146,11 @@ bool Battlefield::Update(uint32 diff) { if (Timer <= diff) { - if (!IsEnabled() || (!IsWarTime() && sWorldSessionMgr->GetActiveSessionCount() > 3500)) // if WG is disabled or there is more than 3500 connections, switch automatically + uint32 sessionLimit = sWorld->getIntConfig(CONFIG_WINTERGRASP_SKIP_BATTLE_SESSION_COUNT); + bool tooManySessions = sessionLimit && !IsWarTime() + && sWorldSessionMgr->GetActiveSessionCount() > sessionLimit; + + if (!IsEnabled() || tooManySessions) { Active = true; EndBattle(false); @@ -174,52 +177,16 @@ bool Battlefield::Update(uint32 diff) SendUpdateWorldStates(); } + _scheduler.Update(diff); + bool objectiveChanged = false; if (IsWarTime()) { - if (KickAfkPlayersTimer <= diff) - { - KickAfkPlayersTimer = 20000; - KickAfkPlayers(); - } - else - KickAfkPlayersTimer -= diff; - - // Kick players who chose not to accept invitation to the battle - if (KickDontAcceptTimer <= diff) - { - time_t now = GameTime::GetGameTime().count(); - for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team) - for (PlayerTimerMap::value_type const& pair : InvitedPlayers[team]) - if (pair.second <= now) - KickPlayerFromBattlefield(pair.first); - - InvitePlayersInZoneToWar(); - for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team) - for (PlayerTimerMap::value_type const& pair : PlayersWillBeKick[team]) - if (pair.second <= now) - KickPlayerFromBattlefield(pair.first); - - KickDontAcceptTimer = 5000; - } - else - KickDontAcceptTimer -= diff; - for (BfCapturePoint* cp : CapturePoints) if (cp->Update(diff)) objectiveChanged = true; } - if (LastResurrectTimer <= diff) - { - for (BfGraveyard* gy : GraveyardList) - if (gy) - gy->Resurrect(); - LastResurrectTimer = RESURRECTION_INTERVAL; - } - else - LastResurrectTimer -= diff; - return objectiveChanged; } @@ -324,7 +291,8 @@ void Battlefield::KickAfkPlayers() void Battlefield::KickPlayerFromBattlefield(ObjectGuid guid) { if (Player* player = ObjectAccessor::FindPlayer(guid)) - if (player->GetZoneId() == GetZoneId() && !player->IsGameMaster()) + if (player->GetZoneId() == GetZoneId() && !player->IsGameMaster() + && !PlayersInWar[player->GetTeamId()].count(guid)) player->TeleportTo(KickPosition); } @@ -342,6 +310,30 @@ void Battlefield::StartBattle() Timer = BattleTime; Active = true; + // Schedule war-only periodic timers + _scheduler.Schedule(1s, BATTLEFIELD_TIMER_GROUP_WAR, [this](TaskContext context) + { + KickAfkPlayers(); + context.Repeat(20s); + }); + + _scheduler.Schedule(1s, BATTLEFIELD_TIMER_GROUP_WAR, [this](TaskContext context) + { + time_t now = GameTime::GetGameTime().count(); + for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team) + for (PlayerTimerMap::value_type const& pair : InvitedPlayers[team]) + if (pair.second <= now) + KickPlayerFromBattlefield(pair.first); + + InvitePlayersInZoneToWar(); + for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team) + for (PlayerTimerMap::value_type const& pair : PlayersWillBeKick[team]) + if (pair.second <= now) + KickPlayerFromBattlefield(pair.first); + + context.Repeat(5s); + }); + InvitePlayersInZoneToWar(); InvitePlayersInQueueToWar(); @@ -359,6 +351,8 @@ void Battlefield::EndBattle(bool endByTimer) Active = false; + _scheduler.CancelGroup(BATTLEFIELD_TIMER_GROUP_WAR); + StartGrouping = false; if (!endByTimer) @@ -424,7 +418,7 @@ void Battlefield::PlayerAcceptInviteToWar(Player* player) { player->GetSession()->SendBfEntered(BattleId); PlayersInWar[player->GetTeamId()].insert(player->GetGUID()); - InvitedPlayers[player->GetTeamId(true)].erase(player->GetGUID()); + InvitedPlayers[player->GetTeamId()].erase(player->GetGUID()); if (player->isAFK()) player->ToggleAFK(); @@ -626,7 +620,9 @@ void Battlefield::RemovePlayerFromResurrectQueue(ObjectGuid playerGuid) void Battlefield::SendAreaSpiritHealerQueryOpcode(Player* player, ObjectGuid const& guid) { WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); - uint32 time = LastResurrectTimer; // resurrect every 30 seconds + Milliseconds remaining = _scheduler.GetNextGroupOccurrence(BATTLEFIELD_TIMER_GROUP_RESURRECT); + uint32 time = static_cast(std::clamp(remaining, + Milliseconds::zero(), Milliseconds(RESURRECTION_INTERVAL)).count()); data << guid << time; ASSERT(player); diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h index ad2a3394c..b3b45cb42 100644 --- a/src/server/game/Battlefield/Battlefield.h +++ b/src/server/game/Battlefield/Battlefield.h @@ -22,6 +22,7 @@ #include "GameObject.h" #include "ObjectAccessor.h" #include "SharedDefines.h" +#include "TaskScheduler.h" #include "ZoneScript.h" enum BattlefieldTypes @@ -55,6 +56,13 @@ enum BattlefieldSounds constexpr auto BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL = 1000; +enum BattlefieldTimerGroups +{ + BATTLEFIELD_TIMER_GROUP_RESURRECT = 1, + BATTLEFIELD_TIMER_GROUP_WAR = 2, + BATTLEFIELD_TIMER_GROUP_SAVE = 3, +}; + const uint32 BattlefieldFactions[PVP_TEAMS_COUNT] = { 1732, // Alliance @@ -401,18 +409,16 @@ protected: uint32 NoWarBattleTime; // Time between two battles uint32 RestartAfterCrash; // Delay to restart Wintergrasp if the server crashed during a running battle uint32 TimeForAcceptInvite; - uint32 KickDontAcceptTimer; WorldLocation KickPosition; // Position where players are teleported if they switch to afk during the battle or if they don't accept invitation - uint32 KickAfkPlayersTimer; // Timer for check Afk in war - // Graveyard variables GraveyardVect GraveyardList; // Vector which contains the different GY of the battle - uint32 LastResurrectTimer; // Timer for resurrect player every 30 sec uint32 StartGroupingTimer; // Timer for invite players in area 15 minutes before start battle bool StartGrouping; // bool for knowing if all players in area have been invited + TaskScheduler _scheduler; + GuidUnorderedSet Groups[PVP_TEAMS_COUNT]; // Contains different raid groups std::vector Data64; diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp index 2478162d7..f0f9ee4fc 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -77,8 +77,6 @@ bool BattlefieldWG::SetupBattlefield() Data32.resize(BATTLEFIELD_WG_DATA_MAX); - SaveTimer = 60000; - // Init Graveyards SetGraveyardNumber(BATTLEFIELD_WG_GRAVEYARD_MAX); @@ -189,37 +187,31 @@ bool BattlefieldWG::SetupBattlefield() } UpdateCounterVehicle(true); + + // Schedule always-running periodic timers + _scheduler.Schedule(Milliseconds(RESURRECTION_INTERVAL), + BATTLEFIELD_TIMER_GROUP_RESURRECT, [this](TaskContext context) + { + for (BfGraveyard* gy : GraveyardList) + if (gy) + gy->Resurrect(); + context.Repeat(); + }); + + _scheduler.Schedule(60s, BATTLEFIELD_TIMER_GROUP_SAVE, [this](TaskContext context) + { + sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_ACTIVE, Active); + sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_DEFENDER, DefenderTeam); + sWorldState->setWorldState(ClockWorldState[0], Timer); + context.Repeat(); + }); + return true; } bool BattlefieldWG::Update(uint32 diff) { - bool result = Battlefield::Update(diff); - if (SaveTimer <= diff) - { - sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_ACTIVE, Active); - sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_DEFENDER, DefenderTeam); - sWorldState->setWorldState(ClockWorldState[0], Timer); - SaveTimer = 60 * IN_MILLISECONDS; - } - else - SaveTimer -= diff; - - // Update Tenacity - if (IsWarTime()) - { - if (TenacityUpdateTimer <= diff) - { - TenacityUpdateTimer = 10000; - if (!UpdateTenacityList.empty()) - UpdateTenacity(); - UpdateTenacityList.clear(); - } - else - TenacityUpdateTimer -= diff; - } - - return result; + return Battlefield::Update(diff); } void BattlefieldWG::OnBattleStart() @@ -293,7 +285,14 @@ void BattlefieldWG::OnBattleStart() // Xinef: reset tenacity counter TenacityStack = 0; - TenacityUpdateTimer = 20000; + + _scheduler.Schedule(20s, BATTLEFIELD_TIMER_GROUP_WAR, [this](TaskContext context) + { + if (!UpdateTenacityList.empty()) + UpdateTenacity(); + UpdateTenacityList.clear(); + context.Repeat(10s); + }); if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE)) ChatHandler(nullptr).SendWorldText(BATTLEFIELD_WG_WORLD_START_MESSAGE); diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.h b/src/server/game/Battlefield/Zones/BattlefieldWG.h index 58d39e55f..8ea7d3ef3 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.h +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.h @@ -449,8 +449,6 @@ protected: GuidUnorderedSet UpdateTenacityList; int32 TenacityStack; - uint32 TenacityUpdateTimer; - uint32 SaveTimer; ObjectGuid TitansRelic; }; diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp index 5eda4152a..8a0145246 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.cpp +++ b/src/server/game/Battlegrounds/ArenaTeam.cpp @@ -132,6 +132,8 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid) if (sWorld->getIntConfig(CONFIG_ARENA_START_PERSONAL_RATING) > 0) personalRating = sWorld->getIntConfig(CONFIG_ARENA_START_PERSONAL_RATING); + else if (sArenaSeasonMgr->GetCurrentSeason() < 6) + personalRating = 1500; else if (GetRating() >= 1000) personalRating = 1000; diff --git a/src/server/game/Combat/CombatManager.cpp b/src/server/game/Combat/CombatManager.cpp index 64ba173e7..60bcd77be 100644 --- a/src/server/game/Combat/CombatManager.cpp +++ b/src/server/game/Combat/CombatManager.cpp @@ -427,6 +427,9 @@ bool CombatManager::UpdateOwnerCombatState() const _owner->AtExitCombat(); if (!_owner->IsCreature()) _owner->AtDisengage(); + + if (Player* player = _owner->ToPlayer()) + sScriptMgr->OnPlayerLeaveCombat(player); } if (Unit* master = _owner->GetCharmerOrOwner()) diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index ce37f961c..5559b8674 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -540,6 +540,11 @@ void ThreatManager::TauntUpdate() // taunt aura update also re-evaluates all suppressed states (retail behavior) EvaluateSuppressed(true); + + // immediately reselect victim so taunt takes effect without waiting + // for the next THREAT_UPDATE_INTERVAL (1 s) timer tick + UpdateVictim(); + _updateTimer = THREAT_UPDATE_INTERVAL; } void ThreatManager::SetTauntStateForTesting( diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h index 261679371..f111da619 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -182,6 +182,8 @@ public: // Test-only: directly set taunt state on a threat ref // Uses uint32 to avoid incomplete-type issue with ThreatReference void SetTauntStateForTesting(Unit* target, uint32 state); + // Immediately reselect victim (bypasses 1-second Update timer) + void UpdateVictimForTesting() { UpdateVictim(); } ///== REDIRECT SYSTEM == void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct); diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp index c02c7c681..812b9668d 100644 --- a/src/server/game/Conditions/DisableMgr.cpp +++ b/src/server/game/Conditions/DisableMgr.cpp @@ -17,7 +17,6 @@ #include "DisableMgr.h" #include "GameEventMgr.h" -#include "MMapFactory.h" #include "ObjectMgr.h" #include "OutdoorPvP.h" #include "Player.h" @@ -448,5 +447,17 @@ bool DisableMgr::IsPathfindingEnabled(Map const* map) if (!map) return false; - return !MMAP::MMapFactory::forbiddenMaps[map->GetId()] && (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena()); + // Still not an ideal solution but removes the extra awful forbiddenMaps hack + // int32 f[] = {616 /*EoE*/, 649 /*ToC25*/, 650 /*ToC5*/, -1}; + switch (map->GetId()) + { + case 616: // EoE + case 649: // ToC25 + case 650: // ToC5 + return false; + default: + break; + } + + return (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena()); } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 9f1d0b8d9..a7c80ef9a 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -388,6 +388,33 @@ void Creature::SearchFormation() } } +bool Creature::IsFormationLeader() const +{ + if (!m_formation) + return false; + + return m_formation->GetLeader() == this; +} + +void Creature::SignalFormationMovement() +{ + if (!m_formation) + return; + + if (!m_formation->GetLeader() || m_formation->GetLeader() != this) + return; + + m_formation->LeaderStartedMoving(); +} + +bool Creature::IsFormationLeaderMoveAllowed() const +{ + if (!m_formation) + return true; + + return m_formation->CanLeaderStartMoving(); +} + void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility) { if (getDeathState() != DeathState::Corpse) @@ -643,7 +670,7 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele SetControlled(true, UNIT_STATE_ROOT); UpdateMovementFlags(); - LoadSpellTemplateImmunity(); + LoadTemplateImmunities(cInfo->CreatureImmunitiesId); if (updateAI) { @@ -2132,39 +2159,55 @@ void Creature::InitializeReactState() SetReactState(REACT_AGGRESSIVE); } -bool Creature::HasMechanicTemplateImmunity(uint32 mask) const +bool Creature::HasMechanicTemplateImmunity(uint64 mask) const { - return !GetOwnerGUID().IsPlayer() && (GetCreatureTemplate()->MechanicImmuneMask & mask); + if (GetOwnerGUID().IsPlayer()) + return false; + + if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(_creatureImmunitiesId)) + return (immunities->Mechanic.to_ullong() & mask) != 0; + + // no custom immunity entry => no mechanic immunity + return false; } -void Creature::LoadSpellTemplateImmunity() +void Creature::LoadTemplateImmunities(int32 creatureImmunitiesId) { // uint32 max used for "spell id", the immunity system will not perform SpellInfo checks against invalid spells // used so we know which immunities were loaded from template - static uint32 const placeholderSpellId = std::numeric_limits::max(); + static uint32 constexpr placeholderSpellId = std::numeric_limits::max(); - // unapply template immunities (in case we're updating entry) - for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i) + auto applyCreatureImmunities = [this](CreatureImmunities const* immunities, bool apply) { - ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, i, false); - } + if (!immunities) + return; + for (std::size_t i = 0; i < immunities->School.size(); ++i) + if (immunities->School[i]) + ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, apply); + for (std::size_t i = 0; i < immunities->DispelType.size(); ++i) + if (immunities->DispelType[i]) + ApplySpellImmune(placeholderSpellId, IMMUNITY_DISPEL, i, apply); + for (std::size_t i = 0; i < immunities->Mechanic.size(); ++i) + if (immunities->Mechanic[i]) + ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, i, apply); + for (SpellEffects effect : immunities->Effect) + ApplySpellImmune(placeholderSpellId, IMMUNITY_EFFECT, effect, apply); + for (AuraType aura : immunities->Aura) + ApplySpellImmune(placeholderSpellId, IMMUNITY_STATE, aura, apply); + }; - // don't inherit immunities for hunter pets - if (GetOwnerGUID().IsPlayer() && IsHunterPet()) - { - return; - } + // unapply old template if any + if (CreatureImmunities const* oldImmunities = sSpellMgr->GetCreatureImmunities(_creatureImmunitiesId)) + applyCreatureImmunities(oldImmunities, false); - if (uint8 mask = GetCreatureTemplate()->SpellSchoolImmuneMask) + // apply requested immunities + if (CreatureImmunities const* newImmunities = sSpellMgr->GetCreatureImmunities(creatureImmunitiesId)) { - for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i) - { - if (mask & (1 << i)) - { - ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, true); - } - } + _creatureImmunitiesId = creatureImmunitiesId; + applyCreatureImmunities(newImmunities, true); } + else + _creatureImmunitiesId = 0; } bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) @@ -2179,11 +2222,13 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) // Xinef: this should exclude self casts... // Spells that don't have effectMechanics. - if (spellInfo->Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Mechanic - 1))) + if (spellInfo->Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(UI64LIT(1) << spellInfo->Mechanic)) return true; - // This check must be done instead of 'if (GetCreatureTemplate()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))' for not break - // the check of mechanic immunity on DB (tested) because GetCreatureTemplate()->MechanicImmuneMask and m_spellImmune[IMMUNITY_MECHANIC] don't have same data. + // The above helper uses the creature_immunities table rather than a + // simple mask on creature_template. We can't rely on the old mask field + // (which has been removed) because it no longer exists and did not always + // match the runtime immunity set stored in m_spellImmune. bool immunedToAllEffects = true; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (spellInfo->Effects[i].IsEffect() && !IsImmunedToSpellEffect(spellInfo, i)) @@ -2197,16 +2242,16 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) return Unit::IsImmunedToSpell(spellInfo, spell); } -bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const +bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const { // Xinef: this should exclude self casts... - if (spellInfo->Effects[index].Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Effects[index].Mechanic - 1))) + if (spellInfo->Effects[index].Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(UI64LIT(1) << spellInfo->Effects[index].Mechanic)) return true; if (GetCreatureTemplate()->type == CREATURE_TYPE_MECHANICAL && spellInfo->Effects[index].Effect == SPELL_EFFECT_HEAL) return true; - return Unit::IsImmunedToSpellEffect(spellInfo, index); + return Unit::IsImmunedToSpellEffect(spellInfo, index, caster); } SpellInfo const* Creature::reachWithSpellAttack(Unit* victim) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 0669d6e45..f95946ab9 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -105,12 +105,11 @@ public: bool isCanInteractWithBattleMaster(Player* player, bool msg) const; bool CanResetTalents(Player* player) const; bool CanCreatureAttack(Unit const* victim, bool skipDistCheck = false) const; - void LoadSpellTemplateImmunity(); bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr) override; - [[nodiscard]] bool HasMechanicTemplateImmunity(uint32 mask) const; + [[nodiscard]] bool HasMechanicTemplateImmunity(uint64 mask) const; // redefine Unit::IsImmunedToSpell - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const override; // redefine Unit::IsImmunedToSpellEffect [[nodiscard]] bool isElite() const { @@ -186,6 +185,8 @@ public: void UpdateAttackPowerAndDamage(bool ranged = false) override; void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override; + void LoadTemplateImmunities(int32 creatureImmunitiesId); + void LoadSparringPct(); [[nodiscard]] float GetSparringPct() const { return _sparringPct; } @@ -357,6 +358,14 @@ public: [[nodiscard]] uint32 GetCurrentWaypointID() const { return m_waypointID; } void UpdateWaypointID(uint32 wpID) { m_waypointID = wpID; } + // nodeId, pathId + std::pair GetCurrentWaypointInfo() const { return _currentWaypointNodeInfo; } + void UpdateCurrentWaypointInfo(uint32 nodeId, uint32 pathId) { _currentWaypointNodeInfo = { nodeId, pathId }; } + + bool IsFormationLeader() const; + void SignalFormationMovement(); + bool IsFormationLeaderMoveAllowed() const; + void SearchFormation(); [[nodiscard]] CreatureGroup const* GetFormation() const { return m_formation; } [[nodiscard]] CreatureGroup* GetFormation() { return m_formation; } @@ -486,6 +495,7 @@ protected: SpellSchoolMask m_meleeDamageSchoolMask; uint32 m_originalEntry; + int32 _creatureImmunitiesId; uint32 _gossipMenuId; bool m_moveInLineOfSightDisabled; @@ -517,6 +527,7 @@ private: // WaypointMovementGenerator variable uint32 m_waypointID; uint32 m_path_id; + std::pair _currentWaypointNodeInfo{0, 0}; // Formation variable CreatureGroup* m_formation; diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index fee46b126..616f2db62 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -22,6 +22,7 @@ #include "DatabaseEnv.h" #include "ItemTemplate.h" #include "LootMgr.h" +#include "SpawnData.h" #include "Unit.h" #include @@ -202,7 +203,6 @@ struct CreatureTemplate float speed_swim; float speed_flight; float detection_range; // Detection Range for Line of Sight aggro - float scale; uint32 rank; uint32 dmgschool; float DamageModifier; @@ -237,8 +237,7 @@ struct CreatureTemplate bool RacialLeader; uint32 movementId; bool RegenHealth; - uint32 MechanicImmuneMask; - uint8 SpellSchoolImmuneMask; + int32 CreatureImmunitiesId; uint32 flags_extra; uint32 ScriptID; WorldPacket queryData; // pussywizard @@ -367,33 +366,24 @@ typedef std::unordered_map EquipmentInfoContainerInternal; typedef std::unordered_map EquipmentInfoContainer; // from `creature` table -struct CreatureData +struct CreatureData : public SpawnData { - CreatureData() = default; + CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) {} ObjectGuid::LowType spawnId{ 0 }; // mod_playerbots uint32 id1{0}; // entry in creature_template uint32 id2{0}; // entry in creature_template uint32 id3{0}; // entry in creature_template - uint16 mapid{0}; - uint32 phaseMask{0}; uint32 displayid{0}; int8 equipmentId{0}; - float posX{0.0f}; - float posY{0.0f}; - float posZ{0.0f}; - float orientation{0.0f}; uint32 spawntimesecs{0}; float wander_distance{0.0f}; uint32 currentwaypoint{0}; uint32 curhealth{0}; uint32 curmana{0}; uint8 movementType{0}; - uint8 spawnMask{0}; uint32 npcflag{0}; uint32 unit_flags{0}; // enum UnitFlags mask values uint32 dynamicflags{0}; - uint32 ScriptId; - bool dbData{true}; }; struct CreatureModelInfo diff --git a/src/server/game/Entities/Creature/Trainer.cpp b/src/server/game/Entities/Creature/Trainer.cpp index c9d3d193c..58b61aabb 100644 --- a/src/server/game/Entities/Creature/Trainer.cpp +++ b/src/server/game/Entities/Creature/Trainer.cpp @@ -130,7 +130,7 @@ namespace Trainer return nullptr; } - bool Trainer::CanTeachSpell(Player const* player, Spell const* trainerSpell) + bool Trainer::CanTeachSpell(Player const* player, Spell const* trainerSpell) const { SpellState state = GetSpellState(player, trainerSpell); if (state != SpellState::Available) diff --git a/src/server/game/Entities/Creature/Trainer.h b/src/server/game/Entities/Creature/Trainer.h index 66de1154d..49e405603 100644 --- a/src/server/game/Entities/Creature/Trainer.h +++ b/src/server/game/Entities/Creature/Trainer.h @@ -70,7 +70,7 @@ namespace Trainer [[nodiscard]] Spell const* GetSpell(uint32 spellId) const; [[nodiscard]] std::vector const& GetSpells() const { return _spells; } void SendSpells(Creature* npc, Player* player, LocaleConstant locale) const; - bool CanTeachSpell(Player const* player, Spell const* trainerSpell); + bool CanTeachSpell(Player const* player, Spell const* trainerSpell) const; void TeachSpell(Creature* npc, Player* player, uint32 spellId); [[nodiscard]] Type GetTrainerType() const { return _type; } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 32e450030..7885b3bfc 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1703,7 +1703,7 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); if (info->camera.cinematicId) - player->SendCinematicStart(info->camera.cinematicId); + player->GetCinematicMgr().StartCinematic(info->camera.cinematicId); if (info->camera.eventID) { @@ -2142,7 +2142,32 @@ void GameObject::SendCustomAnim(uint32 anim) SendMessageToSet(&data, true); } -bool GameObject::IsInRange(float x, float y, float z, float radius) const +bool GameObject::IsInRange2d(float x, float y, float radius) const +{ + GameObjectDisplayInfoEntry const* info = sGameObjectDisplayInfoStore.LookupEntry(m_goInfo->displayId); + if (!info) + return IsWithinDist2d(x, y, radius); + + float sinA = std::sin(GetOrientation()); + float cosA = cos(GetOrientation()); + float dx = x - GetPositionX(); + float dy = y - GetPositionY(); + float dist = std::sqrt(dx * dx + dy * dy); + //! Check if the distance between the 2 objects is 0, can happen if both objects are on the same position. + //! The code below this check wont crash if dist is 0 because 0/0 in float operations is valid, and returns infinite + if (G3D::fuzzyEq(dist, 0.0f)) + return true; + + float scale = GetObjectScale(); + float sinB = dx / dist; + float cosB = dy / dist; + dx = dist * (cosA * cosB + sinA * sinB); + dy = dist * (cosA * sinB - sinA * cosB); + return dx < (info->maxX * scale) + radius && dx >(info->minX * scale) - radius + && dy < (info->maxY * scale) + radius && dy >(info->minY * scale) - radius; +} + +bool GameObject::IsInRange3d(float x, float y, float z, float radius) const { GameObjectDisplayInfoEntry const* info = sGameObjectDisplayInfoStore.LookupEntry(m_goInfo->displayId); if (!info) @@ -2217,6 +2242,11 @@ void GameObject::UpdatePackedRotation() m_packedRotation = z | (y << 21) | (x << 42); } +bool GameObject::IsWithinSightRange(Position const& pos, float dist) const +{ + return IsInRange2d(pos.GetPositionX(), pos.GetPositionY(), dist); +} + void GameObject::SetWorldRotation(G3D::Quat const& rot) { G3D::Quat rotation = rot; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index efcb66249..8c4a943e0 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -284,7 +284,8 @@ public: void CastSpell(Unit* target, uint32 spell); void SendCustomAnim(uint32 anim); - [[nodiscard]] bool IsInRange(float x, float y, float z, float radius) const; + bool IsInRange2d(float x, float y, float radius) const; + bool IsInRange3d(float x, float y, float z, float radius) const; void ModifyHealth(int32 change, Unit* attackerOrHealer = nullptr, uint32 spellId = 0); void SetDestructibleBuildingModifyState(bool allow) { m_allowModifyDestructibleBuilding = allow; } @@ -418,8 +419,11 @@ private: { //! Following check does check 3d distance dist2compare += obj->GetObjectSize(); - return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare); + return IsInRange3d(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare); } + + bool IsWithinSightRange(Position const& pos, float dist) const override; + GameObjectAI* m_AI; bool m_saveStateOnDb = false; diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h index 39c1a6e28..ab03b0098 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -19,6 +19,7 @@ #define GAMEOBJECTDATA_H #include "SharedDefines.h" +#include "SpawnData.h" #include #include #include @@ -710,24 +711,15 @@ enum GOState }; // from `gameobject` -struct GameObjectData +struct GameObjectData : public SpawnData { - explicit GameObjectData() = default; - uint32 id{ 0 }; // entry in gamobject_template - uint16 mapid{ 0 }; - uint32 phaseMask{ 0 }; - float posX{ 0.0f }; - float posY{ 0.0f }; - float posZ{ 0.0f }; - float orientation{ 0.0f }; + GameObjectData() : SpawnData(SPAWN_TYPE_GAMEOBJECT) {} + uint32 id{0}; // entry in gameobject_template G3D::Quat rotation; - int32 spawntimesecs{ 0 }; - uint32 ScriptId; - uint32 animprogress{ 0 }; - GOState go_state{ GO_STATE_ACTIVE }; - uint8 spawnMask{ 0 }; - uint8 artKit{ 0 }; - bool dbData{ true }; + int32 spawntimesecs{0}; + uint32 animprogress{0}; + GOState go_state{GO_STATE_ACTIVE}; + uint8 artKit{0}; }; #endif // GameObjectData_h__ diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 98e5b890a..9cd58f197 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1362,6 +1362,12 @@ bool WorldObject::IsWithinDist2d(const Position* pos, float dist) const return IsInDist2d(pos, dist + GetObjectSize()); } +// Visibility always uses 2d checks, factors in self-object size already. Gameobjects will override this for custom calc +bool WorldObject::IsWithinSightRange(Position const& pos, float dist) const +{ + return IsInDist2d(&pos, dist + GetObjectSize()); +} + // use only if you will sure about placing both object at same map bool WorldObject::IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D, bool incOwnRadius, bool incTargetRadius) const { @@ -1674,7 +1680,7 @@ float WorldObject::GetGridActivationRange() const { if (ToPlayer()) { - if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) + if (ToPlayer()->GetCinematicMgr().IsOnCinematic()) { return DEFAULT_VISIBILITY_INSTANCE; } @@ -1725,7 +1731,7 @@ float WorldObject::GetSightRange(WorldObject const* target) const return VISIBILITY_DIST_WINTERGRASP; else if (target->IsVisibilityOverridden()) return target->GetVisibilityOverrideDistance(); - else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) + else if (ToPlayer()->GetCinematicMgr().IsOnCinematic()) return DEFAULT_VISIBILITY_INSTANCE; else return GetMap()->GetVisibilityRange(); @@ -1794,10 +1800,10 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo if (distanceCheck) { bool corpseCheck = false; - WorldObject const* viewpoint = this; + Position const* sightPosition = this; if (Player const* thisPlayer = ToPlayer()) { - viewpoint = thisPlayer->GetSeer(); + sightPosition = &thisPlayer->GetSightPosition(); if (Creature const* creature = obj->ToCreature()) { @@ -1843,8 +1849,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo return false; } - // Xinef: check reversely obj vs viewpoint, object could be a gameObject which overrides _IsWithinDist function to include gameobject size - if (!corpseCheck && !viewpoint->IsWithinDist(obj, GetSightRange(obj), false)) + if (!corpseCheck && !obj->IsWithinSightRange(*sightPosition, GetSightRange(obj))) return false; } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index d2da4b523..4a635175d 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -543,6 +543,7 @@ public: bool IsWithinDist3d(const Position* pos, float dist) const; [[nodiscard]] bool IsWithinDist2d(float x, float y, float dist) const; bool IsWithinDist2d(const Position* pos, float dist) const; + virtual bool IsWithinSightRange(Position const& pos, float dist) const; // use only if you will sure about placing both object at same map bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D = true, bool incOwnRadius = true, bool incTargetRadius = true) const; bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true, bool incOwnRadius = true, bool incTargetRadius = true) const; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 95e4d878d..23df0fa0f 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -491,7 +491,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c } // must be after SetMinion (owner guid check) - //LoadTemplateImmunities(); + LoadTemplateImmunities(0); //LoadMechanicTemplateImmunity(); m_loading = false; }); diff --git a/src/server/game/Entities/Player/CinematicMgr.cpp b/src/server/game/Entities/Player/CinematicMgr.cpp index d8091d918..1cce91ed2 100644 --- a/src/server/game/Entities/Player/CinematicMgr.cpp +++ b/src/server/game/Entities/Player/CinematicMgr.cpp @@ -17,92 +17,84 @@ #include "CinematicMgr.h" #include "M2Stores.h" -#include "MotionMaster.h" #include "Player.h" -CinematicMgr::CinematicMgr(Player* playerref) +CinematicMgr::CinematicMgr(Player& player) : _player(player) { - player = playerref; - m_cinematicDiff = 0; - m_lastCinematicCheck = 0; - m_activeCinematicCameraId = 0; - m_cinematicLength = 0; - m_cinematicCamera = nullptr; - m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f); - m_CinematicObject = nullptr; + _cinematicDiff = 0; + _activeCinematicCameraId = 0; + _cinematicCamera = nullptr; + _remoteSightPosition = Position(0.0f, 0.0f, 0.0f); + _cinematicUpdateTimer.SetInterval(CINEMATIC_UPDATEDIFF); } -CinematicMgr::~CinematicMgr() +void CinematicMgr::StartCinematic(uint32 const cinematicSequenceId) { - if (m_cinematicCamera && m_activeCinematicCameraId) - EndCinematic(); + _player.SendCinematicStart(cinematicSequenceId); + if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(cinematicSequenceId)) + SetActiveCinematicCamera(sequence->cinematicCamera); } -void CinematicMgr::BeginCinematic() +void CinematicMgr::StartCinematicCamera() { // Sanity check for active camera set - if (m_activeCinematicCameraId == 0) + if (_activeCinematicCameraId == 0) return; - if (std::vector const* flyByCameras = GetFlyByCameras(m_activeCinematicCameraId)) + if (std::vector const* flyByCameras = GetFlyByCameras(_activeCinematicCameraId)) { // Initialize diff, and set camera - m_cinematicDiff = 0; - m_cinematicCamera = flyByCameras; + _cinematicDiff = 0; + _cinematicUpdateTimer.Reset(); + _cinematicCamera = flyByCameras; - auto camitr = m_cinematicCamera->begin(); - if (camitr != m_cinematicCamera->end()) + auto camitr = _cinematicCamera->begin(); + if (camitr != _cinematicCamera->end()) { Position const& pos = camitr->locations; if (!pos.IsPositionValid()) return; - player->GetMap()->LoadGrid(pos.GetPositionX(), pos.GetPositionY()); - m_CinematicObject = player->SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 120000); - if (m_CinematicObject) - { - m_CinematicObject->setActive(true); - player->SetViewpoint(m_CinematicObject, true); - } - - // Get cinematic length - m_cinematicLength = flyByCameras->back().timeStamp; + _player.GetMap()->LoadGridsInRange(pos, MAX_VISIBILITY_DISTANCE); + _remoteSightPosition.Relocate(pos); + _player.UpdateVisibilityForPlayer(); } } } void CinematicMgr::EndCinematic() { - if (m_activeCinematicCameraId == 0) + if (_activeCinematicCameraId == 0) return; - m_cinematicDiff = 0; - m_cinematicCamera = nullptr; - m_activeCinematicCameraId = 0; - if (m_CinematicObject) - { - if (WorldObject* vpObject = player->GetViewpoint()) - if (vpObject == m_CinematicObject) - player->SetViewpoint(m_CinematicObject, false); - - m_CinematicObject->AddObjectToRemoveList(); - } + _cinematicDiff = 0; + _cinematicUpdateTimer.Reset(); + _cinematicCamera = nullptr; + _activeCinematicCameraId = 0; + _player.UpdateVisibilityForPlayer(); } -void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/) +void CinematicMgr::UpdateCinematic(uint32 const diff) { - if (m_activeCinematicCameraId == 0 || !m_cinematicCamera || m_cinematicCamera->size() == 0) + if (_activeCinematicCameraId == 0 || !_cinematicCamera || _cinematicCamera->empty()) return; + _cinematicDiff += diff; + _cinematicUpdateTimer.Update(diff); + if (!_cinematicUpdateTimer.Passed()) + return; + + _cinematicUpdateTimer.Reset(); + Position lastPosition; uint32 lastTimestamp = 0; Position nextPosition; uint32 nextTimestamp = 0; // Obtain direction of travel - for (FlyByCamera cam : *m_cinematicCamera) + for (FlyByCamera const& cam : *_cinematicCamera) { - if (cam.timeStamp > m_cinematicDiff) + if (cam.timeStamp > _cinematicDiff) { nextPosition.Relocate(cam.locations); nextTimestamp = cam.timeStamp; @@ -113,26 +105,25 @@ void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/) } float angle = lastPosition.GetAbsoluteAngle(&nextPosition); angle -= lastPosition.GetOrientation(); - if (angle < 0) - angle += 2 * float(M_PI); + angle = Position::NormalizeOrientation(angle); // Look for position around 2 second ahead of us. - int32 workDiff = m_cinematicDiff; + int32 workDiff = _cinematicDiff; // Modify result based on camera direction (Humans for example, have the camera point behind) workDiff += static_cast(float(CINEMATIC_LOOKAHEAD) * cos(angle)); // Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end - auto endItr = m_cinematicCamera->rbegin(); - if (endItr != m_cinematicCamera->rend() && workDiff > static_cast(endItr->timeStamp)) + auto endItr = _cinematicCamera->rbegin(); + if (endItr != _cinematicCamera->rend() && workDiff > static_cast(endItr->timeStamp)) workDiff = endItr->timeStamp; // Never try to go back in time before the start of cinematic! if (workDiff < 0) - workDiff = m_cinematicDiff; + workDiff = _cinematicDiff; // Obtain the previous and next waypoint based on timestamp - for (FlyByCamera cam : *m_cinematicCamera) + for (FlyByCamera const& cam : *_cinematicCamera) { if (static_cast(cam.timeStamp) >= workDiff) { @@ -157,12 +148,15 @@ void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/) Position interPosition(lastPosition.m_positionX + (xDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionY + (yDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionZ + (zDiff * (float(interDiff) / float(timeDiff)))); - // Advance (at speed) to this position. The remote sight object is used - // to send update information to player in cinematic - if (m_CinematicObject && interPosition.IsPositionValid()) - m_CinematicObject->MonsterMoveWithSpeed(interPosition.m_positionX, interPosition.m_positionY, interPosition.m_positionZ, 500.0f); + // Advance _remoteSightPosition to new position + if (interPosition.IsPositionValid()) + { + _player.GetMap()->LoadGridsInRange(interPosition, MAX_VISIBILITY_DISTANCE); + _remoteSightPosition.Relocate(interPosition); + _player.UpdateVisibilityForPlayer(); + } // If we never received an end packet 10 seconds after the final timestamp then force an end - if (m_cinematicDiff > m_cinematicLength + 10 * IN_MILLISECONDS) + if (_cinematicDiff > _cinematicCamera->back().timeStamp + 10 * IN_MILLISECONDS) EndCinematic(); } diff --git a/src/server/game/Entities/Player/CinematicMgr.h b/src/server/game/Entities/Player/CinematicMgr.h index 1dffe9957..3409854d0 100644 --- a/src/server/game/Entities/Player/CinematicMgr.h +++ b/src/server/game/Entities/Player/CinematicMgr.h @@ -20,7 +20,7 @@ #include "Define.h" #include "Position.h" -#include "TemporarySummon.h" +#include "Timer.h" #include constexpr auto CINEMATIC_UPDATEDIFF = 500; @@ -31,31 +31,27 @@ struct FlyByCamera; class AC_GAME_API CinematicMgr { - friend class Player; public: - explicit CinematicMgr(Player* playerref); - ~CinematicMgr(); + explicit CinematicMgr(Player& player); + ~CinematicMgr() = default; // Cinematic camera data and remote sight functions - uint32 GetActiveCinematicCamera() const { return m_activeCinematicCameraId; } - void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; } - bool IsOnCinematic() const { return (m_cinematicCamera != nullptr); } - void BeginCinematic(); + void StartCinematic(uint32 const cinematicSequenceId); + uint32 GetActiveCinematicCamera() const { return _activeCinematicCameraId; } + void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { _activeCinematicCameraId = cinematicCameraId; } + bool IsOnCinematic() const { return (_cinematicCamera != nullptr); } + void StartCinematicCamera(); void EndCinematic(); - void UpdateCinematicLocation(uint32 diff); + void UpdateCinematic(uint32 const diff); + Position const& GetRemoteSightPosition() const { return _remoteSightPosition; } private: - // Remote location information - Player* player; - -protected: - uint32 m_cinematicDiff; - uint32 m_lastCinematicCheck; - uint32 m_activeCinematicCameraId; - uint32 m_cinematicLength; - std::vector const* m_cinematicCamera; - Position m_remoteSightPosition; - TempSummon* m_CinematicObject; + Player& _player; + uint32 _cinematicDiff; + uint32 _activeCinematicCameraId; + std::vector const* _cinematicCamera; + Position _remoteSightPosition; + IntervalTimer _cinematicUpdateTimer; }; #endif diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 49561765f..250a4d785 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -154,7 +154,7 @@ static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 }; #ifdef _MSC_VER #pragma warning(disable:4355) #endif -Player::Player(WorldSession* session): Unit(), m_mover(this) +Player::Player(WorldSession* session): Unit(), m_mover(this), _cinematicMgr(*this) { #ifdef _MSC_VER #pragma warning(default:4355) @@ -395,8 +395,6 @@ Player::Player(WorldSession* session): Unit(), m_mover(this) m_creationTime = 0s; - _cinematicMgr = new CinematicMgr(this); - m_achievementMgr = new AchievementMgr(this); m_reputationMgr = new ReputationMgr(this); @@ -462,7 +460,6 @@ Player::~Player() delete m_runes; delete m_achievementMgr; delete m_reputationMgr; - delete _cinematicMgr; sWorldSessionMgr->DecreasePlayerCount(); @@ -772,7 +769,9 @@ bool Player::IsImmuneToEnvironmentalDamage() uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) { - if (IsImmuneToEnvironmentalDamage()) + // DAMAGE_FALL_TO_VOID bypasses all immunities (e.g. Divine Shield) to prevent + // players from being stuck infinitely falling below the map + if (type != DAMAGE_FALL_TO_VOID && IsImmuneToEnvironmentalDamage()) return 0; // Absorb, resist some environmental damage type @@ -5697,10 +5696,6 @@ void Player::SendCinematicStart(uint32 CinematicSequenceId) const WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); data << uint32(CinematicSequenceId); SendDirectMessage(&data); - if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId)) - { - _cinematicMgr->SetActiveCinematicCamera(sequence->cinematicCamera); - } } void Player::SendMovieStart(uint32 MovieId) @@ -10457,7 +10452,7 @@ void Player::SendTaxiNodeStatusMultiple() if (!creature->HasNpcFlag(UNIT_NPC_FLAG_FLIGHTMASTER)) return; - uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetMapId(), GetTeamId()); + uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(*creature, GetTeamId(true)); if (!nearestNode) return; @@ -12515,21 +12510,52 @@ bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item cons if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i)) if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) return true; + + // Keep active non-passive auras (e.g. Bladestorm) when disarmed + if (!spellInfo->IsPassive()) + { + bool hasWeaponInSlot = false; + for (uint8 i = EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i) + if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) + { + hasWeaponInSlot = true; + break; + } + + if (!hasWeaponInSlot) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (spellInfo->Effects[i].IsAura()) + return true; + } + break; } case ITEM_CLASS_ARMOR: { + // most used check: shield only + if (spellInfo->EquippedItemSubClassMask & (1 << ITEM_SUBCLASS_ARMOR_SHIELD)) + { + if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) + if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) + return true; + + // Keep active non-passive auras (e.g. Shield Wall) when disarmed + Item* offhand = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if (!spellInfo->IsPassive() && offhand && offhand != ignoreItem) + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (spellInfo->Effects[i].IsAura()) + return true; + } + } + // tabard not have dependent spells for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_MAINHAND; ++i) if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i)) if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) return true; - // shields can be equipped to offhand slot - if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) - if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) - return true; - // ranged slot can have some armor subclasses if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo)) @@ -13210,6 +13236,14 @@ WorldObject* Player::GetViewpoint() const return nullptr; } +Position const& Player::GetSightPosition() const +{ + if (_cinematicMgr.IsOnCinematic()) + return _cinematicMgr.GetRemoteSightPosition(); + + return *m_seer; +} + bool Player::CanUseBattlegroundObject(GameObject* gameobject) const { // It is possible to call this method will a nullptr pointer, only skipping faction check. @@ -16279,7 +16313,10 @@ bool Player::IsWorldObjectOutOfSightRange(WorldObject const* target) const } // Check if out of range - return !m_seer->IsWithinDist(target, GetSightRange(target), false); + if (!target->IsWithinSightRange(GetSightPosition(), GetSightRange(target))) + return true; + + return false; } std::string Player::GetPlayerName() diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 420d60364..858b2a3c4 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1394,7 +1394,8 @@ public: [[nodiscard]] TradeData* GetTradeData() const { return m_trade; } void TradeCancel(bool sendback, TradeStatus status = TRADE_STATUS_TRADE_CANCELED); - CinematicMgr* GetCinematicMgr() const { return _cinematicMgr; } + CinematicMgr& GetCinematicMgr() { return _cinematicMgr; } + CinematicMgr const& GetCinematicMgr() const { return _cinematicMgr; } void UpdateEnchantTime(uint32 time); void UpdateSoulboundTradeItems(); @@ -2388,6 +2389,7 @@ public: WorldObject* GetSeer() const { return m_seer; } void SetViewpoint(WorldObject* target, bool apply); [[nodiscard]] WorldObject* GetViewpoint() const; + Position const& GetSightPosition() const; void StopCastingCharm(Aura* except = nullptr); void StopCastingBindSight(Aura* except = nullptr); @@ -2984,7 +2986,7 @@ private: Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update); Item* _LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields); - CinematicMgr* _cinematicMgr; + CinematicMgr _cinematicMgr; typedef GuidSet RefundableItemsSet; RefundableItemsSet m_refundableItems; diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 4ca2c096e..f664d98a3 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -68,14 +68,8 @@ void Player::Update(uint32 p_time) m_nextMailDelivereTime = time_t(0); } - // Update cinematic location, if 500ms have passed and we're doing a - // cinematic now. - _cinematicMgr->m_cinematicDiff += p_time; - if (_cinematicMgr->m_cinematicCamera && _cinematicMgr->m_activeCinematicCameraId && GetMSTimeDiffToNow(_cinematicMgr->m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF) - { - _cinematicMgr->m_lastCinematicCheck = getMSTime(); - _cinematicMgr->UpdateCinematicLocation(p_time); - } + // Update cinematic camera (if needed) + _cinematicMgr.UpdateCinematic(p_time); // used to implement delayed far teleports SetMustDelayTeleport(true); @@ -1609,8 +1603,8 @@ void Player::UpdateVisibilityForPlayer(bool mapChange) m_seer = this; Acore::VisibleNotifier notifier(*this, mapChange); - Cell::VisitObjects(m_seer, notifier, GetSightRange()); - Cell::VisitFarVisibleObjects(m_seer, notifier, VISIBILITY_DISTANCE_GIGANTIC); + Cell::VisitObjects(GetSightPosition().GetPositionX(), GetSightPosition().GetPositionY(), GetMap(), notifier, GetSightRange()); + Cell::VisitFarVisibleObjects(GetSightPosition().GetPositionX(), GetSightPosition().GetPositionY(), GetMap(), notifier, VISIBILITY_DISTANCE_GIGANTIC); notifier.SendToSelf(); if (mapChange) diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 9f28ac743..20253a0c3 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -179,7 +179,7 @@ void Totem::UnSummon(Milliseconds msTime) AddObjectToRemoveList(); } -bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const +bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const { // xinef: immune to all positive spells, except of stoneclaw totem absorb, sentry totem bind sight and intervene // totems positive spells have unit_caster target @@ -209,5 +209,5 @@ bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) con break; } - return Creature::IsImmunedToSpellEffect(spellInfo, index); + return Creature::IsImmunedToSpellEffect(spellInfo, index, caster); } diff --git a/src/server/game/Entities/Totem/Totem.h b/src/server/game/Entities/Totem/Totem.h index 3149e6398..08583d027 100644 --- a/src/server/game/Entities/Totem/Totem.h +++ b/src/server/game/Entities/Totem/Totem.h @@ -70,7 +70,7 @@ public: void UpdateAttackPowerAndDamage(bool /*ranged*/) override {} void UpdateDamagePhysical(WeaponAttackType /*attType*/) override {} - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const override; protected: TotemType m_type; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 49d45bcaa..191c2a547 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -72,6 +72,7 @@ #include "Vehicle.h" #include "World.h" #include "WorldPacket.h" +#include #include float baseMoveSpeed[MAX_MOVE_TYPE] = @@ -125,6 +126,28 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo, uint8 damageIndex) m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.attackType), m_absorb(dmgInfo.damages[damageIndex].absorb), m_resist(dmgInfo.damages[damageIndex].resist), m_block(dmgInfo.blocked_amount), m_cleanDamage(dmgInfo.cleanDamage), m_hitMask(0) { + switch (dmgInfo.TargetState) + { + case VICTIMSTATE_IS_IMMUNE: + m_hitMask |= PROC_HIT_IMMUNE; + break; + case VICTIMSTATE_BLOCKS: + m_hitMask |= PROC_HIT_FULL_BLOCK; + break; + } + + if (dmgInfo.HitInfo & (HITINFO_PARTIAL_ABSORB | HITINFO_FULL_ABSORB)) + m_hitMask |= PROC_HIT_ABSORB; + + if (dmgInfo.HitInfo & HITINFO_FULL_RESIST) + m_hitMask |= PROC_HIT_FULL_RESIST; + + if (m_block) + m_hitMask |= PROC_HIT_BLOCK; + + bool const damageNullified = (dmgInfo.HitInfo & (HITINFO_FULL_ABSORB | HITINFO_FULL_RESIST)) != 0 + || (m_hitMask & (PROC_HIT_IMMUNE | PROC_HIT_FULL_BLOCK)) != 0; + switch (dmgInfo.hitOutCome) { case MELEE_HIT_MISS: @@ -140,25 +163,19 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo, uint8 damageIndex) m_hitMask |= PROC_HIT_EVADE; break; case MELEE_HIT_BLOCK: - m_hitMask |= PROC_HIT_BLOCK; - [[fallthrough]]; case MELEE_HIT_CRUSHING: case MELEE_HIT_GLANCING: case MELEE_HIT_NORMAL: - m_hitMask |= PROC_HIT_NORMAL; + if (!damageNullified) + m_hitMask |= PROC_HIT_NORMAL; break; case MELEE_HIT_CRIT: - m_hitMask |= PROC_HIT_CRITICAL; + if (!damageNullified) + m_hitMask |= PROC_HIT_CRITICAL; break; default: break; } - - if (dmgInfo.damages[damageIndex].absorb) - m_hitMask |= PROC_HIT_ABSORB; - - if (dmgInfo.blocked_amount) - m_hitMask |= (dmgInfo.damages[damageIndex].damage == dmgInfo.blocked_amount) ? PROC_HIT_FULL_BLOCK : PROC_HIT_BLOCK; } DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask) @@ -372,9 +389,6 @@ Unit::Unit() : WorldObject(), m_transform = 0; m_canModifyStats = false; - for (uint8 i = 0; i < MAX_SPELL_IMMUNITY; ++i) - m_spellImmune[i].clear(); - for (uint8 i = 0; i < UNIT_MOD_END; ++i) { m_auraFlatModifiersGroup[i][BASE_VALUE] = 0.0f; @@ -891,6 +905,48 @@ bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint return false; } +bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster /*= nullptr*/) +{ + if (!spellInfo) + return false; + + bool immuneToAllEffects = true; + bool hasCheckedEffect = false; + + for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i) + { + if (!(effectMask & (1u << i))) + continue; + + if (!spellInfo->Effects[i].IsEffect()) + continue; + + hasCheckedEffect = true; + + if (IsImmunedToSpellEffect(spellInfo, i, caster)) + { + if (spellInfo->HasAttribute(SPELL_ATTR4_NO_PARTIAL_IMMUNITY)) + return true; + + continue; + } + + immuneToAllEffects = false; + break; + } + + if (hasCheckedEffect && immuneToAllEffects) + return true; + + if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) + { + if (IsImmunedToSchool(spellInfo)) + return true; + } + + return false; +} + bool Unit::HasBreakableByDamageAuraType(AuraType type, uint32 excludeAura) const { AuraEffectList const& auras = GetAuraEffectsByType(type); @@ -2075,7 +2131,7 @@ void Unit::DealDamageShieldDamage(Unit* victim) } // ...or immuned - if (IsImmunedToDamageOrSchool(i_spellProto)) + if (IsImmunedToDamage(victim, i_spellProto)) { victim->SendSpellDamageImmune(this, i_spellProto->Id); continue; @@ -3203,7 +3259,7 @@ void Unit::SendMeleeAttackStop(Unit* victim) bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType) { // These spells can't be blocked - if (spellProto && spellProto->HasAttribute(SPELL_ATTR0_NO_ACTIVE_DEFENSE)) + if (spellProto && (spellProto->HasAttribute(SPELL_ATTR0_NO_ACTIVE_DEFENSE) || spellProto->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))) return false; if (victim->HasIgnoreHitDirectionAura() || victim->HasInArc(M_PI, this)) @@ -3571,6 +3627,9 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Resist SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool CanReflect) { + if (spell->HasAttribute(SPELL_ATTR3_ALWAYS_HIT)) + return SPELL_MISS_NONE; + // Check for immune if (victim->IsImmunedToSpell(spell)) return SPELL_MISS_IMMUNE; @@ -3581,11 +3640,6 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool Ca && (!IsHostileTo(victim))) // prevent from affecting enemy by "positive" spell return SPELL_MISS_NONE; - // Check for immune - // xinef: check for school immunity only - if (victim->IsImmunedToSchool(spell)) - return SPELL_MISS_IMMUNE; - if (this == victim) return SPELL_MISS_NONE; @@ -3650,13 +3704,17 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, Spell const* spell, bool CanRef return SPELL_MISS_NONE; } - // Check for immune - // xinef: check for school immunity only - if (victim->IsImmunedToSchool(spell)) + // Check for immune to spell effects (includes school, mechanics, state, dispel immunities) + if (victim->IsImmunedToSpell(spellInfo, MAX_EFFECT_MASK, this)) { return SPELL_MISS_IMMUNE; } + // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply + // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit + if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(this, spellInfo)) + return SPELL_MISS_IMMUNE; + if (this == victim) { return SPELL_MISS_NONE; @@ -5447,7 +5505,7 @@ void Unit::RemoveMovementImpairingAuras(bool withRoot) } } -void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode, uint32 except) +void Unit::RemoveAurasWithMechanic(uint64 mechanic_mask, AuraRemoveMode removemode, uint32 except) { for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();) { @@ -5466,7 +5524,7 @@ void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemo void Unit::RemoveAurasByShapeShift() { - uint32 mechanic_mask = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT); + uint64 mechanic_mask = (UI64LIT(1) << MECHANIC_SNARE) | (UI64LIT(1) << MECHANIC_ROOT); for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();) { Aura const* aura = iter->second->GetBase(); @@ -6015,17 +6073,17 @@ bool Unit::HasNegativeAuraWithAttribute(uint32 flag, ObjectGuid guid) return false; } -bool Unit::HasAuraWithMechanic(uint32 mechanicMask) const +bool Unit::HasAuraWithMechanic(uint64 mechanicMask) const { for (AuraApplicationMap::const_iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter) { SpellInfo const* spellInfo = iter->second->GetBase()->GetSpellInfo(); - if (spellInfo->Mechanic && (mechanicMask & (1 << spellInfo->Mechanic))) + if (spellInfo->Mechanic && (mechanicMask & (UI64LIT(1) << spellInfo->Mechanic))) return true; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (iter->second->HasEffect(i) && spellInfo->Effects[i].Effect && spellInfo->Effects[i].Mechanic) - if (mechanicMask & (1 << spellInfo->Effects[i].Mechanic)) + if (mechanicMask & (UI64LIT(1) << spellInfo->Effects[i].Mechanic)) return true; } @@ -7339,19 +7397,22 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) // set position before any AI calls/assistance //if (IsCreature()) // ToCreature()->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ()); - if (creature && !IsControlledByPlayer()) + if (creature) { EngageWithTarget(victim); - creature->SendAIReaction(AI_REACTION_HOSTILE); + if (!IsControlledByPlayer()) + { + creature->SendAIReaction(AI_REACTION_HOSTILE); - /// @todo: Implement aggro range, detection range and assistance range templates - if (!(creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE))) - creature->CallAssistance(); + /// @todo: Implement aggro range, detection range and assistance range templates + if (!(creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE))) + creature->CallAssistance(); - creature->SetAssistanceTimer(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD)); + creature->SetAssistanceTimer(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD)); - SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + } } // delay offhand weapon attack by 50% of the base attack time @@ -8836,7 +8897,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui }); } - if (uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask()) + if (uint64 mechanicMask = spellProto->GetAllEffectsMechanicMask()) { int32 modifierMax = 0; int32 modifierMin = 0; @@ -8851,7 +8912,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui if (!caster || caster->GetGUID() != (*i)->GetCasterGUID()) continue; - if (mechanicMask & uint32(1 << (*i)->GetMiscValue())) + if (mechanicMask & uint64(UI64LIT(1) << (*i)->GetMiscValue())) { if ((*i)->GetAmount() > 0) { @@ -9742,107 +9803,90 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) return AdvertisedBenefit; } -bool Unit::IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const +uint32 Unit::GetDamageImmunityMask() const { - if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE) - { - return false; - } - - // If m_immuneToDamage type contain magic, IMMUNE damage. - SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; - for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr) - if ((itr->type & meleeSchoolMask) == meleeSchoolMask) - return true; - - return false; + uint32 mask = 0; + SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; + for (auto const& [immunitySchoolMask, _] : damageList) + mask |= immunitySchoolMask; + return mask; } -bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const +bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const { - if (!spellInfo) - { - return false; - } - - if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura()) - { - return false; - } - - if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) - { - return false; - } - - uint32 schoolMask = spellInfo->GetSchoolMask(); if (schoolMask == SPELL_SCHOOL_MASK_NONE) - { return false; - } - // If m_immuneToDamage type contain magic, IMMUNE damage. - SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; - for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr) - if ((itr->type & schoolMask) == schoolMask) + // If m_immuneToSchool type contains all requested schools, IMMUNE damage. + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto const& itr : schoolList) + if (IsImmuneMaskFully(SpellSchoolMask(itr.first), schoolMask)) return true; - return false; + // If m_immuneToDamage type contains all requested schools, IMMUNE damage. + SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; + return std::ranges::any_of(damageList, [schoolMask](auto const& immune) + { + return Unit::IsImmuneMaskFully(SpellSchoolMask(immune.first), schoolMask); + }); } -bool Unit::IsImmunedToDamage(Spell const* spell) const +bool Unit::IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const { - SpellInfo const* spellInfo = spell->GetSpellInfo(); if (!spellInfo) - { return false; - } - if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura()) - { + if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) return false; - } - if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) - { - return false; - } - - uint32 schoolMask = spell->GetSpellSchoolMask(); + SpellSchoolMask schoolMask = SpellSchoolMask(spellInfo->GetSchoolMask()); if (schoolMask == SPELL_SCHOOL_MASK_NONE) - { return false; - } - // If m_immuneToDamage type contain magic, IMMUNE damage. - SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE]; - for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr) + auto hasImmunity = [&](SpellImmuneContainer const& container) { - if ((itr->type & schoolMask) == schoolMask) + uint32 schoolImmunityMask = 0; + for (auto const& [immunitySchoolMask, immunityAuraId] : container) { - return true; + SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId); + if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this)) + continue; + + if (immuneAuraInfo && spellInfo->CanPierceImmuneAura(immuneAuraInfo)) + continue; + + schoolImmunityMask |= immunitySchoolMask; } - } - return false; -} - -bool Unit::IsImmunedToSchool(SpellSchoolMask meleeSchoolMask) const -{ - if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE) - { - return false; - } + // We need to be immune to all types + return (schoolImmunityMask & schoolMask) == schoolMask; + }; // If m_immuneToSchool type contain this school type, IMMUNE damage. - SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr) - if ((itr->type & meleeSchoolMask) == meleeSchoolMask) - return true; + if (hasImmunity(m_spellImmune[IMMUNITY_SCHOOL])) + return true; + + // If m_immuneToDamage type contain magic, IMMUNE damage. + if (hasImmunity(m_spellImmune[IMMUNITY_DAMAGE])) + return true; return false; } +bool Unit::IsImmunedToSchool(SpellSchoolMask schoolMask) const +{ + if (schoolMask == SPELL_SCHOOL_MASK_NONE) + return false; + + // Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered by at least one immunity + // (e.g., Anti-Magic Shell provides school immunity to all magic schools) + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + return std::ranges::any_of(schoolList, [schoolMask](auto const& immune) + { + return (immune.first & schoolMask) == schoolMask; + }); +} + bool Unit::IsImmunedToSchool(SpellInfo const* spellInfo) const { if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura()) @@ -9856,10 +9900,10 @@ bool Unit::IsImmunedToSchool(SpellInfo const* spellInfo) const if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471) { - // If m_immuneToSchool type contain this school type, IMMUNE damage. - SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr) - if ((itr->type & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId))) + // Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered and spell can't pierce + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) + if ((itr->first & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->second))) return true; } @@ -9882,11 +9926,11 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471) { - // If m_immuneToSchool type contain this school type, IMMUNE damage. - SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr) + // Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered and spell can't pierce + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) { - if ((itr->type & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId))) + if ((itr->first & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->second))) { return true; } @@ -9896,19 +9940,49 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const return false; } -bool Unit::IsImmunedToDamageOrSchool(SpellSchoolMask meleeSchoolMask) const +bool Unit::IsImmunedToDamageOrSchool(SpellSchoolMask schoolMask) const { - if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE) + if (schoolMask == SPELL_SCHOOL_MASK_NONE) { return false; } - return IsImmunedToDamage(meleeSchoolMask) || IsImmunedToSchool(meleeSchoolMask); + return IsImmunedToDamage(schoolMask) || IsImmunedToSchool(schoolMask); } -bool Unit::IsImmunedToDamageOrSchool(SpellInfo const* spellInfo) const +bool Unit::IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spellInfo) const { - return IsImmunedToDamage(spellInfo) || IsImmunedToSchool(spellInfo); + if (!spellInfo) + return false; + + if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) + return false; + + SpellSchoolMask schoolMask = SpellSchoolMask(spellInfo->GetSchoolMask()); + if (schoolMask == SPELL_SCHOOL_MASK_NONE) + return false; + + auto hasImmunity = [&](SpellImmuneContainer const& container) + { + uint32 schoolImmunityMask = 0; + for (auto const& [immunitySchoolMask, immunityAuraId] : container) + { + SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId); + if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this)) + continue; + + schoolImmunityMask |= immunitySchoolMask; + } + + // We need to be immune to all types + return (schoolImmunityMask & schoolMask) == schoolMask; + }; + + // If m_immuneToSchool type contain this school type, IMMUNE periodic tick. + if (hasImmunity(m_spellImmune[IMMUNITY_SCHOOL])) + return true; + + return false; } bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) @@ -9917,39 +9991,35 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) return false; // Single spell immunity. - SpellImmuneList const& idList = m_spellImmune[IMMUNITY_ID]; - for (SpellImmuneList::const_iterator itr = idList.begin(); itr != idList.end(); ++itr) - if (itr->type == spellInfo->Id) - return true; + SpellImmuneContainer const& idList = m_spellImmune[IMMUNITY_ID]; + if (idList.count(spellInfo->Id) > 0) + return true; // xinef: my special immunity, if spellid is not on this list it means npc is immune - SpellImmuneList const& allowIdList = m_spellImmune[IMMUNITY_ALLOW_ID]; + SpellImmuneContainer const& allowIdList = m_spellImmune[IMMUNITY_ALLOW_ID]; if (!allowIdList.empty()) { - for (SpellImmuneList::const_iterator itr = allowIdList.begin(); itr != allowIdList.end(); ++itr) - if (itr->type == spellInfo->Id) - return false; - return true; + if (allowIdList.count(spellInfo->Id) == 0) + return true; + return false; } if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura()) return false; - if (spellInfo->Dispel) + if (uint32 dispel = spellInfo->Dispel) { - SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL]; - for (SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr) - if (itr->type == spellInfo->Dispel) - return true; + SpellImmuneContainer const& dispelList = m_spellImmune[IMMUNITY_DISPEL]; + if (dispelList.count(dispel) > 0) + return true; } // Spells that don't have effectMechanics. - if (spellInfo->Mechanic) + if (uint32 mechanic = spellInfo->Mechanic) { - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - if (itr->type == spellInfo->Mechanic) - return true; + SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + if (mechanicList.count(mechanic) > 0) + return true; } bool immuneToAllEffects = true; @@ -9974,7 +10044,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects. return true; - if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471) + if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) { SpellSchoolMask spellSchoolMask = spellInfo->GetSchoolMask(); if (spell) @@ -9984,16 +10054,24 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) if (spellSchoolMask != SPELL_SCHOOL_MASK_NONE) { - SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr) + SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; + for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) { - SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->spellId); - if (((itr->type & spellSchoolMask) == spellSchoolMask) - && (!immuneSpellInfo || immuneSpellInfo->IsPositive()) && !spellInfo->IsPositive() - && !spellInfo->CanPierceImmuneAura(immuneSpellInfo)) + SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second); + if (!(itr->first & spellSchoolMask)) + continue; + + if (immuneSpellInfo && !immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)) { - return true; + Unit const* spellCaster = spell ? spell->GetCaster() : nullptr; + if (spellCaster && spellCaster->IsFriendlyTo(this)) + continue; } + + if (spellInfo->CanPierceImmuneAura(immuneSpellInfo)) + continue; + + return true; } } } @@ -10001,7 +10079,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) return false; } -bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const +bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const { if (!spellInfo || !spellInfo->Effects[index].IsEffect()) return false; @@ -10013,12 +10091,16 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura()) return false; + // Special-case handling for Vezax's Aura of Despair. + static constexpr uint32 SPELL_AURA_OF_DESPAIR_1 = 62692; + static constexpr uint32 SPELL_AURA_OF_DESPAIR_2 = 64848; + //If m_immuneToEffect type contain this effect type, IMMUNE effect. uint32 effect = spellInfo->Effects[index].Effect; - SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT]; - for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr) + SpellImmuneContainer const& effectList = m_spellImmune[IMMUNITY_EFFECT]; + for (SpellImmuneContainer::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr) { - if (itr->type == effect && (itr->spellId != 62692 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo)))) + if (itr->first == effect && (itr->second != SPELL_AURA_OF_DESPAIR_1 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo)))) { return true; } @@ -10026,41 +10108,32 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons if (uint32 mechanic = spellInfo->Effects[index].Mechanic) { - SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - if (itr->type == mechanic) - return true; + auto const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; + if (mechanicList.count(mechanic) > 0) + return true; } - if (uint32 aura = spellInfo->Effects[index].ApplyAuraName) + if (!spellInfo->HasAttribute(SPELL_ATTR3_ALWAYS_HIT)) { - SpellImmuneList const& list = m_spellImmune[IMMUNITY_STATE]; - for (SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr) + if (uint32 aura = spellInfo->Effects[index].ApplyAuraName) { - if (itr->type == aura && (itr->spellId != 64848 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo)))) + SpellImmuneContainer const& list = m_spellImmune[IMMUNITY_STATE]; + for (SpellImmuneContainer::const_iterator itr = list.begin(); itr != list.end(); ++itr) { - if (!spellInfo->HasAttribute(SPELL_ATTR3_ALWAYS_HIT)) - { - if (itr->blockType == SPELL_BLOCK_TYPE_ALL || spellInfo->IsPositive()) // xinef: added for pet scaling - { - return true; - } - } - } - } - - if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) - { - // Check for immune to application of harmful magical effects - AuraEffectList const& immuneAuraApply = GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL); - for (AuraEffectList::const_iterator iter = immuneAuraApply.begin(); iter != immuneAuraApply.end(); ++iter) - { - if (/*(spellInfo->Dispel == DISPEL_MAGIC || spellInfo->Dispel == DISPEL_CURSE || spellInfo->Dispel == DISPEL_DISEASE) &&*/ // Magic debuff, xinef: all kinds? - ((*iter)->GetMiscValue() & spellInfo->GetSchoolMask()) && // Check school - !spellInfo->IsPositiveEffect(index) && // Harmful - spellInfo->Effects[index].Effect != SPELL_EFFECT_PERSISTENT_AREA_AURA) // Not Persistent area auras - { + if (itr->first == aura && (itr->second != SPELL_AURA_OF_DESPAIR_2 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo)))) return true; + } + + if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) + { + // Check for immune to application of harmful magical effects + for (AuraEffect* immuneAuraApply : GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL)) + { + if (!(immuneAuraApply->GetMiscValue() & spellInfo->GetSchoolMask())) // Check school + continue; + + if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || (caster && !IsFriendlyTo(caster))) // Harmful + return true; } } } @@ -10289,17 +10362,17 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT }); // Mod damage from spell mechanic - uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask(); + uint64 mechanicMask = spellProto->GetAllEffectsMechanicMask(); // Shred, Maul - "Effects which increase Bleed damage also increase Shred damage" if (spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[0] & 0x00008800) - mechanicMask |= (1 << MECHANIC_BLEED); + mechanicMask |= (UI64LIT(1) << MECHANIC_BLEED); if (mechanicMask) { TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT, [mechanicMask](AuraEffect const* aurEff) -> bool { - if (mechanicMask & uint32(1 << (aurEff->GetMiscValue()))) + if (mechanicMask & uint64(UI64LIT(1) << (aurEff->GetMiscValue()))) return true; return false; }); @@ -10367,51 +10440,17 @@ private: uint32 _type; }; -void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType) +void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType /*blockType*/) { if (apply) - { - // xinef: immunities with spellId 0 are intended to be applied only once (script purposes mosty) - if (spellId == 0 && std::find_if(m_spellImmune[op].begin(), m_spellImmune[op].end(), spellIdImmunityPredicate(type)) != m_spellImmune[op].end()) - return; - - SpellImmune immune; - immune.spellId = spellId; - immune.type = type; - immune.blockType = blockType; - m_spellImmune[op].push_back(std::move(immune)); - } + m_spellImmune[op].emplace(type, spellId); else { - for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr) + auto bounds = m_spellImmune[op].equal_range(type); + for (auto itr = bounds.first; itr != bounds.second;) { - if (itr->spellId == spellId && itr->type == type) - { - m_spellImmune[op].erase(itr); - break; - } - } - } -} - -void Unit::ApplySpellDispelImmunity(SpellInfo const* spellProto, DispelType type, bool apply) -{ - ApplySpellImmune(spellProto->Id, IMMUNITY_DISPEL, type, apply); - - if (apply && spellProto->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) - { - // Create dispel mask by dispel type - uint32 dispelMask = SpellInfo::GetDispelMask(type); - // Dispel all existing auras vs current dispel type - AuraApplicationMap& auras = GetAppliedAuras(); - for (AuraApplicationMap::iterator itr = auras.begin(); itr != auras.end();) - { - SpellInfo const* spell = itr->second->GetBase()->GetSpellInfo(); - if (spell->GetDispelMask() & dispelMask) - { - // Dispel aura - RemoveAura(itr); - } + if (itr->second == spellId) + itr = m_spellImmune[op].erase(itr); else ++itr; } @@ -11177,8 +11216,9 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) { if (Creature* creature = ToCreature()) { - uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask; - if (immuneMask & (1 << (MECHANIC_SNARE - 1)) || immuneMask & (1 << (MECHANIC_DAZE - 1))) + // use creature helper which now consults creature_immunities table + if (creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_SNARE) || + creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_DAZE)) break; } @@ -11199,7 +11239,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced) if (creature && !IsPet() && !(IsControlledByPlayer() && IsVehicle()) - && !(creature->HasMechanicTemplateImmunity(MECHANIC_SNARE)) + && !(creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_SNARE)) && !(creature->IsDungeonBoss())) { // 1.6% for each % under 30. @@ -11395,6 +11435,8 @@ void Unit::AtTargetAttacked(Unit* target, bool canInitialAggro) target->EngageWithTarget(this); if (Unit* targetOwner = target->GetCharmerOrOwner()) targetOwner->EngageWithTarget(this); + if (Unit* myOwner = GetCharmerOrOwner()) + target->EngageWithTarget(myOwner); // Patch 3.0.8: All player spells which cause a creature to become aggressive // to you will now also immediately cause the creature to be tapped. @@ -11422,12 +11464,8 @@ Unit* Creature::SelectVictim() Unit* target = nullptr; - // Taunt handling is now done inside ThreatManager via TauntState if (CanHaveThreatList()) - { - if (!m_threatManager.IsThreatListEmpty()) - target = m_threatManager.GetCurrentVictim(); - } + target = m_threatManager.GetCurrentVictim(); else if (!HasReactState(REACT_PASSIVE)) { // we have player pet probably @@ -15087,7 +15125,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target) { if (!(effMask & (1 << i))) continue; - if (target->IsImmunedToSpellEffect(spellInfo, i)) + if (target->IsImmunedToSpellEffect(spellInfo, i, this)) effMask &= ~(1 << i); } @@ -15739,6 +15777,12 @@ void Unit::_ExitVehicle(Position const* exitPosition) pos.RelocateOffset({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO }); else if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamDest) pos.Relocate({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO }); + + bool isInLoS = GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing); + float floorZ = GetMapHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); + + if (!isInLoS || pos.GetPositionZ() < floorZ) + pos = vehicleBase->GetPosition(); } } @@ -16344,8 +16388,8 @@ void Unit::ExecuteDelayedUnitRelocationEvent() GetMap()->LoadGridsInRange(*player, MAX_VISIBILITY_DISTANCE); Acore::PlayerRelocationNotifier notifier(*player); - Cell::VisitObjects(viewPoint, notifier, player->GetSightRange()); - Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC); + Cell::VisitObjects(player->GetSightPosition().GetPositionX(), player->GetSightPosition().GetPositionY(), player->GetMap(), notifier, player->GetSightRange()); + Cell::VisitFarVisibleObjects(player->GetSightPosition().GetPositionX(), player->GetSightPosition().GetPositionY(), player->GetMap(), notifier, VISIBILITY_DISTANCE_GIGANTIC); notifier.SendToSelf(); this->AddToNotify(NOTIFY_AI_RELOCATION); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 0cedfb64c..f9cedb9dd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1408,7 +1408,7 @@ public: void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = 0, bool isAutoshot = false); void RemoveAurasWithAttribute(uint32 flags); void RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID); - void RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode = AURA_REMOVE_BY_DEFAULT, uint32 except = 0); + void RemoveAurasWithMechanic(uint64 mechanic_mask, AuraRemoveMode removemode = AURA_REMOVE_BY_DEFAULT, uint32 except = 0); void RemoveMovementImpairingAuras(bool withRoot); void RemoveAurasByShapeShift(); @@ -1500,7 +1500,7 @@ public: bool HasNegativeAuraWithInterruptFlag(uint32 flag, ObjectGuid guid = ObjectGuid::Empty); [[nodiscard]] bool HasVisibleAuraType(AuraType auraType) const; bool HasNegativeAuraWithAttribute(uint32 flag, ObjectGuid guid = ObjectGuid::Empty); - [[nodiscard]] bool HasAuraWithMechanic(uint32 mechanicMask) const; + [[nodiscard]] bool HasAuraWithMechanic(uint64 mechanicMask) const; [[nodiscard]] bool HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const; @@ -1636,17 +1636,22 @@ public: // Spells immunities void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL); - void ApplySpellDispelImmunity(SpellInfo const* spellProto, DispelType type, bool apply); virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr); - [[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const; - [[nodiscard]] bool IsImmunedToDamage(SpellInfo const* spellInfo) const; - [[nodiscard]] bool IsImmunedToDamage(Spell const* spell) const; - [[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask meleeSchoolMask) const; + bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster = nullptr); + [[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const; + [[nodiscard]] bool IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const; + [[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask schoolMask) const; + + static bool IsImmuneMaskFully(SpellSchoolMask immuneMask, SpellSchoolMask schoolMask) { return (immuneMask & schoolMask) == schoolMask; } + + [[nodiscard]] uint32 GetSchoolImmunityMask() const; + [[nodiscard]] uint32 GetDamageImmunityMask() const; + [[nodiscard]] bool IsImmunedToSchool(SpellInfo const* spellInfo) const; [[nodiscard]] bool IsImmunedToSchool(Spell const* spell) const; - [[nodiscard]] bool IsImmunedToDamageOrSchool(SpellSchoolMask meleeSchoolMask) const; - bool IsImmunedToDamageOrSchool(SpellInfo const* spellInfo) const; - virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const; + [[nodiscard]] bool IsImmunedToDamageOrSchool(SpellSchoolMask schoolMask) const; + [[nodiscard]] bool IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spellInfo) const; + virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const; // Critic chances bool isBlockCritical(); @@ -2082,7 +2087,8 @@ public: float m_modAttackSpeedPct[3]; - SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY]; + typedef std::unordered_multimap SpellImmuneContainer; + SpellImmuneContainer m_spellImmune[MAX_SPELL_IMMUNITY]; uint32 m_lastSanctuaryTime; // pet auras diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b3bcd7fac..f1e100bba 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -310,6 +310,9 @@ ObjectMgr::ObjectMgr(): { _playerClassInfo[i] = nullptr; } + + // Initialize default spawn group + _spawnGroupDataStore[0] = {0, "Default Group", 0, SpawnGroupFlags(SPAWNGROUP_FLAG_SYSTEM)}; } ObjectMgr::~ObjectMgr() @@ -522,18 +525,18 @@ void ObjectMgr::LoadCreatureTemplates() // 0 1 2 3 4 5 6 7 8 QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, " -// 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - "gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, " -// 23 24 25 26 27 28 29 30 31 32 +// 9 10 11 12 13 14 15 16 17 18 19 20 21 + "gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, " +// 22 23 24 25 26 27 28 29 30 31 "DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, " -// 33 34 35 36 37 +// 32 33 34 35 36 "type, type_flags, lootid, pickpocketloot, skinloot, " -// 38 39 40 41 42 43 44 45 46 47 +// 37 38 39 40 41 42 43 44 45 46 "PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, " -// 48 49 50 51 52 53 54 55 56 57 58 59 - "ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, " -// 60 61 62 - "spell_school_immune_mask, flags_extra, ScriptName " +// 47 48 49 50 51 52 53 54 55 56 57 + "ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, " +// 58 59 60 + "CreatureImmunitiesId, flags_extra, ScriptName " "FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId ORDER BY entry DESC;"); if (!result) @@ -619,24 +622,23 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook) creatureTemplate.speed_swim = fields[17].Get(); creatureTemplate.speed_flight = fields[18].Get(); creatureTemplate.detection_range = fields[19].Get(); - creatureTemplate.scale = fields[20].Get(); - creatureTemplate.rank = uint32(fields[21].Get()); - creatureTemplate.dmgschool = uint32(fields[22].Get()); - creatureTemplate.DamageModifier = fields[23].Get(); - creatureTemplate.BaseAttackTime = fields[24].Get(); - creatureTemplate.RangeAttackTime = fields[25].Get(); - creatureTemplate.BaseVariance = fields[26].Get(); - creatureTemplate.RangeVariance = fields[27].Get(); - creatureTemplate.unit_class = uint32(fields[28].Get()); - creatureTemplate.unit_flags = fields[29].Get(); - creatureTemplate.unit_flags2 = fields[30].Get(); - creatureTemplate.dynamicflags = fields[31].Get(); - creatureTemplate.family = uint32(fields[32].Get()); - creatureTemplate.type = uint32(fields[33].Get()); - creatureTemplate.type_flags = fields[34].Get(); - creatureTemplate.lootid = fields[35].Get(); - creatureTemplate.pickpocketLootId = fields[36].Get(); - creatureTemplate.SkinLootId = fields[37].Get(); + creatureTemplate.rank = uint32(fields[20].Get()); + creatureTemplate.dmgschool = uint32(fields[21].Get()); + creatureTemplate.DamageModifier = fields[22].Get(); + creatureTemplate.BaseAttackTime = fields[23].Get(); + creatureTemplate.RangeAttackTime = fields[24].Get(); + creatureTemplate.BaseVariance = fields[25].Get(); + creatureTemplate.RangeVariance = fields[26].Get(); + creatureTemplate.unit_class = uint32(fields[27].Get()); + creatureTemplate.unit_flags = fields[28].Get(); + creatureTemplate.unit_flags2 = fields[29].Get(); + creatureTemplate.dynamicflags = fields[30].Get(); + creatureTemplate.family = uint32(fields[31].Get()); + creatureTemplate.type = uint32(fields[32].Get()); + creatureTemplate.type_flags = fields[33].Get(); + creatureTemplate.lootid = fields[34].Get(); + creatureTemplate.pickpocketLootId = fields[35].Get(); + creatureTemplate.SkinLootId = fields[36].Get(); for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) { @@ -648,49 +650,56 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook) creatureTemplate.spells[i] = 0; } - creatureTemplate.PetSpellDataId = fields[38].Get(); - creatureTemplate.VehicleId = fields[39].Get(); - creatureTemplate.mingold = fields[40].Get(); - creatureTemplate.maxgold = fields[41].Get(); - creatureTemplate.AIName = fields[42].Get(); - creatureTemplate.MovementType = uint32(fields[43].Get()); - if (!fields[44].IsNull()) + creatureTemplate.PetSpellDataId = fields[37].Get(); + creatureTemplate.VehicleId = fields[38].Get(); + creatureTemplate.mingold = fields[39].Get(); + creatureTemplate.maxgold = fields[40].Get(); + creatureTemplate.AIName = fields[41].Get(); + creatureTemplate.MovementType = uint32(fields[42].Get()); + if (!fields[43].IsNull()) { - creatureTemplate.Movement.Ground = static_cast(fields[44].Get()); + creatureTemplate.Movement.Ground = static_cast(fields[43].Get()); } - creatureTemplate.Movement.Swim = fields[45].Get(); - if (!fields[46].IsNull()) + creatureTemplate.Movement.Swim = fields[44].Get(); + if (!fields[45].IsNull()) { - creatureTemplate.Movement.Flight = static_cast(fields[46].Get()); + creatureTemplate.Movement.Flight = static_cast(fields[45].Get()); } - creatureTemplate.Movement.Rooted = fields[47].Get(); + creatureTemplate.Movement.Rooted = fields[46].Get(); + if (!fields[47].IsNull()) + { + creatureTemplate.Movement.Chase = static_cast(fields[47].Get()); + } if (!fields[48].IsNull()) { - creatureTemplate.Movement.Chase = static_cast(fields[48].Get()); + creatureTemplate.Movement.Random = static_cast(fields[48].Get()); } if (!fields[49].IsNull()) { - creatureTemplate.Movement.Random = static_cast(fields[49].Get()); - } - if (!fields[50].IsNull()) - { - creatureTemplate.Movement.InteractionPauseTimer = fields[50].Get(); + creatureTemplate.Movement.InteractionPauseTimer = fields[49].Get(); } - creatureTemplate.HoverHeight = fields[51].Get(); - creatureTemplate.ModHealth = fields[52].Get(); - creatureTemplate.ModMana = fields[53].Get(); - creatureTemplate.ModArmor = fields[54].Get(); - creatureTemplate.ModExperience = fields[55].Get(); - creatureTemplate.RacialLeader = fields[56].Get(); - creatureTemplate.movementId = fields[57].Get(); - creatureTemplate.RegenHealth = fields[58].Get(); - creatureTemplate.MechanicImmuneMask = fields[59].Get(); - creatureTemplate.SpellSchoolImmuneMask = fields[60].Get(); - creatureTemplate.flags_extra = fields[61].Get(); - creatureTemplate.ScriptID = GetScriptId(fields[62].Get()); + creatureTemplate.HoverHeight = fields[50].Get(); + creatureTemplate.ModHealth = fields[51].Get(); + creatureTemplate.ModMana = fields[52].Get(); + creatureTemplate.ModArmor = fields[53].Get(); + creatureTemplate.ModExperience = fields[54].Get(); + creatureTemplate.RacialLeader = fields[55].Get(); + creatureTemplate.movementId = fields[56].Get(); + creatureTemplate.RegenHealth = fields[57].Get(); + creatureTemplate.CreatureImmunitiesId = fields[58].Get(); + creatureTemplate.flags_extra = fields[59].Get(); + creatureTemplate.ScriptID = GetScriptId(fields[60].Get()); + + // Warn about deprecated immunity flags that should be moved to `creature_immunities` table + if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT) + LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit NO_TAUNT (0x100) set. This will be migrated to the `creature_immunities` table in a future update.", entry); + if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_AVOID_AOE) + LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit AVOID_AOE (0x400000) set. This will be migrated to the `creature_immunities` table in a future update.", entry); + if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK) + LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit IMMUNITY_KNOCKBACK (0x40000000) set. This will be migrated to the `creature_immunities` table in a future update.", entry); // useful if the creature template load is being triggered from outside this class if (triggerHook) @@ -2394,6 +2403,7 @@ void ObjectMgr::LoadCreatures() data.unit_flags = fields[21].Get(); data.dynamicflags = fields[22].Get(); data.ScriptId = GetScriptId(fields[23].Get()); + data.spawnGroupId = 0; if (!data.ScriptId) data.ScriptId = cInfo->ScriptID; @@ -2572,6 +2582,7 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI creatureData.unit_flags = fields[19].Get(); creatureData.dynamicflags = fields[20].Get(); creatureData.ScriptId = GetScriptId(fields[21].Get()); + creatureData.spawnGroupId = 0; if (!creatureData.ScriptId) creatureData.ScriptId = cInfo->ScriptID; @@ -2754,6 +2765,7 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl data.phaseMask = PHASEMASK_NORMAL; data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0; data.dbData = false; + data.spawnGroupId = 0; AddGameobjectToGrid(spawnId, &data); @@ -2909,6 +2921,7 @@ void ObjectMgr::LoadGameobjects() data.rotation.w = fields[10].Get(); data.spawntimesecs = fields[11].Get(); data.ScriptId = GetScriptId(fields[18].Get()); + data.spawnGroupId = 0; if (!data.ScriptId) data.ScriptId = gInfo->ScriptId; @@ -7160,6 +7173,11 @@ void ObjectMgr::LoadAreaTriggerScripts() LOG_INFO("server.loading", " "); } +uint32 ObjectMgr::GetNearestTaxiNode(WorldLocation const& loc, uint32 teamId) +{ + return GetNearestTaxiNode(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetMapId(), teamId); +} + uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 teamId) { bool found = false; @@ -8672,6 +8690,19 @@ void ObjectMgr::DeleteGOData(ObjectGuid::LowType guid) _gameObjectDataStore.erase(guid); } +SpawnData const* ObjectMgr::GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const +{ + switch (type) + { + case SPAWN_TYPE_CREATURE: + return GetCreatureData(spawnId); + case SPAWN_TYPE_GAMEOBJECT: + return GetGameObjectData(spawnId); + default: + return nullptr; + } +} + void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, std::string const& table, bool starter, bool go) { uint32 oldMSTime = getMSTime(); @@ -9719,6 +9750,7 @@ void ObjectMgr::LoadTrainers() // For reload case _trainers.clear(); + _classTrainers.clear(); std::unordered_map> spellsByTrainer; if (QueryResult trainerSpellsResult = WorldDatabase.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell")) @@ -9795,7 +9827,18 @@ void ObjectMgr::LoadTrainers() spellsByTrainer.erase(spellsItr); } - _trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, requirement, std::move(greeting), std::move(spells))); + auto [it, isNew] = _trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, requirement, std::move(greeting), std::move(spells))); + ASSERT(isNew); + if (trainerType == Trainer::Type::Class) + { + if (!requirement || requirement >= MAX_CLASSES) + LOG_ERROR("sql.sql", "Table `trainer` has invalid class requirement for trainer {}, ignoring"); + else + { + uint8 classId = static_cast(requirement); + _classTrainers[classId].push_back(&it->second); + } + } } while (trainersResult->NextRow()); } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 00400e456..8a4fbbef2 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -506,6 +506,7 @@ struct AcoreString typedef std::map LinkedRespawnContainer; typedef std::unordered_map CreatureDataContainer; typedef std::unordered_map GameObjectDataContainer; +typedef std::unordered_map SpawnGroupDataContainer; typedef std::map > TempSummonDataContainer; typedef std::map > GameObjectSummonDataContainer; typedef std::unordered_map CreatureLocaleContainer; @@ -811,6 +812,7 @@ public: void GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const; uint32 GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 teamId); + uint32 GetNearestTaxiNode(WorldLocation const& loc, uint32 teamId); void GetTaxiPath(uint32 source, uint32 destination, uint32& path, uint32& cost); uint32 GetTaxiMountDisplayId(uint32 id, TeamId teamId, bool allowed_alt_team = false); @@ -1269,6 +1271,14 @@ public: if (itr == _gameObjectDataStore.end()) return nullptr; return &itr->second; } + [[nodiscard]] SpawnData const* GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const; + + [[nodiscard]] SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const + { + auto itr = _spawnGroupDataStore.find(groupId); + return itr != _spawnGroupDataStore.end() ? &itr->second : nullptr; + } + [[nodiscard]] CreatureLocale const* GetCreatureLocale(uint32 entry) const { CreatureLocaleContainer::const_iterator itr = _creatureLocaleStore.find(entry); @@ -1419,6 +1429,7 @@ public: bool DeleteGameTele(std::string_view name); Trainer::Trainer* GetTrainer(uint32 creatureId); + std::vector const& GetClassTrainers(uint8 classId) const { return _classTrainers.at(classId); } [[nodiscard]] VendorItemData const* GetNpcVendorItemList(uint32 entry) const { @@ -1642,6 +1653,7 @@ private: LinkedRespawnContainer _linkedRespawnStore; CreatureLocaleContainer _creatureLocaleStore; GameObjectDataContainer _gameObjectDataStore; + SpawnGroupDataContainer _spawnGroupDataStore; GameObjectLocaleContainer _gameObjectLocaleStore; GameObjectTemplateContainer _gameObjectTemplateStore; GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore; @@ -1667,6 +1679,7 @@ private: CacheVendorItemContainer _cacheVendorItemStore; std::unordered_map _trainers; + std::unordered_map> _classTrainers; std::unordered_map _creatureDefaultTrainers; std::set _difficultyEntries[MAX_DIFFICULTY - 1]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate diff --git a/src/server/game/Grids/Cells/Cell.h b/src/server/game/Grids/Cells/Cell.h index 72097e7e7..d077a19c6 100644 --- a/src/server/game/Grids/Cells/Cell.h +++ b/src/server/game/Grids/Cells/Cell.h @@ -107,6 +107,7 @@ struct Cell template static void VisitObjects(float x, float y, Map* map, T& visitor, float radius); template static void VisitFarVisibleObjects(WorldObject const* obj, T& visitor, float radius); + template static void VisitFarVisibleObjects(float x, float y, Map* map, T& visitor, float radius); private: template void VisitCircle(TypeContainerVisitor&, Map&, CellCoord const&, CellCoord const&) const; diff --git a/src/server/game/Grids/Cells/CellImpl.h b/src/server/game/Grids/Cells/CellImpl.h index 4bc1a66ed..3fe62948d 100644 --- a/src/server/game/Grids/Cells/CellImpl.h +++ b/src/server/game/Grids/Cells/CellImpl.h @@ -164,11 +164,7 @@ inline void Cell::VisitCircle(TypeContainerVisitor& visitor, Map& template inline void Cell::VisitObjects(WorldObject const* center_obj, T& visitor, float radius) { - CellCoord p(Acore::ComputeCellCoord(center_obj->GetPositionX(), center_obj->GetPositionY())); - Cell cell(p); - - TypeContainerVisitor gnotifier(visitor); - cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius); + VisitObjects(center_obj->GetPositionX(), center_obj->GetPositionY(), center_obj->GetMap(), visitor, radius); } template @@ -184,11 +180,17 @@ inline void Cell::VisitObjects(float x, float y, Map* map, T& visitor, float rad template inline void Cell::VisitFarVisibleObjects(WorldObject const* center_obj, T& visitor, float radius) { - CellCoord p(Acore::ComputeCellCoord(center_obj->GetPositionX(), center_obj->GetPositionY())); + VisitFarVisibleObjects(center_obj->GetPositionX(), center_obj->GetPositionY(), center_obj->GetMap(), visitor, radius); +} + +template +inline void Cell::VisitFarVisibleObjects(float x, float y, Map* map, T& visitor, float radius) +{ + CellCoord p(Acore::ComputeCellCoord(x, y)); Cell cell(p); TypeContainerVisitor gnotifier(visitor); - cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius); + cell.Visit(p, gnotifier, *map, x, y, radius); } #endif diff --git a/src/server/game/Grids/GridTerrainLoader.cpp b/src/server/game/Grids/GridTerrainLoader.cpp index ecf9b0941..5b9598522 100644 --- a/src/server/game/Grids/GridTerrainLoader.cpp +++ b/src/server/game/Grids/GridTerrainLoader.cpp @@ -1,6 +1,6 @@ -#include "DisableMgr.h" #include "GridTerrainLoader.h" -#include "MMapFactory.h" +#include "IVMapMgr.h" +#include "Map.h" #include "MMapMgr.h" #include "ScriptMgr.h" #include "VMapFactory.h" @@ -9,6 +9,7 @@ void GridTerrainLoader::LoadTerrain() { LoadMap(); + if (_map->GetInstanceId() == 0) { LoadVMap(); @@ -18,13 +19,10 @@ void GridTerrainLoader::LoadTerrain() void GridTerrainLoader::LoadMap() { - // Instances will point to the parent maps terrain data + // Instances will point to the parent maps terrain data, no need to load anything. if (_map->GetInstanceId() != 0) { - // load grid map for base map Map* parentMap = const_cast(_map->GetParent()); - - // GetGridTerrainData will create the parent map grid _grid.SetTerrainData(parentMap->GetGridTerrainDataSharedPtr(GridCoord(_grid.GetX(), _grid.GetY()))); return; } @@ -51,7 +49,7 @@ void GridTerrainLoader::LoadMap() void GridTerrainLoader::LoadVMap() { - int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapMgr()->loadMap((sWorld->GetDataPath() + "vmaps").c_str(), _map->GetId(), _grid.GetX(), _grid.GetY()); + int const vmapLoadResult = _map->GetMapCollisionData().LoadVMapTile(_grid.GetX(), _grid.GetY()); switch (vmapLoadResult) { case VMAP::VMAP_LOAD_RESULT_OK: @@ -66,15 +64,14 @@ void GridTerrainLoader::LoadVMap() LOG_DEBUG("maps", "Ignored VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})", _map->GetMapName(), _map->GetId(), _grid.GetX(), _grid.GetY(), _grid.GetX(), _grid.GetY()); break; + default: + break; } } void GridTerrainLoader::LoadMMap() { - if (!DisableMgr::IsPathfindingEnabled(_map)) - return; - - int mmapLoadResult = MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(_map->GetId(), _grid.GetX(), _grid.GetY()); + int const mmapLoadResult = _map->GetMapCollisionData().LoadMMapTile(_grid.GetX(), _grid.GetY()); switch (mmapLoadResult) { case MMAP::MMAP_LOAD_RESULT_OK: @@ -89,6 +86,8 @@ void GridTerrainLoader::LoadMMap() LOG_DEBUG("maps", "Ignored MMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})", _map->GetMapName(), _map->GetId(), _grid.GetX(), _grid.GetY(), _grid.GetX(), _grid.GetY()); break; + default: + break; } } @@ -145,13 +144,3 @@ bool GridTerrainLoader::ExistVMap(uint32 mapid, int gx, int gy) return true; } - -void GridTerrainUnloader::UnloadTerrain() -{ - // Only parent maps manage terrain data - if (_map->GetInstanceId() != 0) - return; - - VMAP::VMapFactory::createOrGetVMapMgr()->unloadMap(_map->GetId(), _grid.GetX(), _grid.GetY()); - MMAP::MMapFactory::createOrGetMMapMgr()->unloadMap(_map->GetId(), _grid.GetX(), _grid.GetY()); -} diff --git a/src/server/game/Grids/GridTerrainLoader.h b/src/server/game/Grids/GridTerrainLoader.h index e1d6019bf..99b4b89ef 100644 --- a/src/server/game/Grids/GridTerrainLoader.h +++ b/src/server/game/Grids/GridTerrainLoader.h @@ -20,6 +20,8 @@ #include "GridDefines.h" +class Map; + class GridTerrainLoader { public: @@ -40,17 +42,4 @@ private: Map* _map; }; -class GridTerrainUnloader -{ -public: - GridTerrainUnloader(MapGridType& grid, Map* map) - : _grid(grid), _map(map) { } - - void UnloadTerrain(); - -private: - MapGridType& _grid; - Map* _map; -}; - #endif diff --git a/src/server/game/Grids/MapGridManager.cpp b/src/server/game/Grids/MapGridManager.cpp index 21ca5bfed..7821bfe2d 100644 --- a/src/server/game/Grids/MapGridManager.cpp +++ b/src/server/game/Grids/MapGridManager.cpp @@ -8,9 +8,19 @@ void MapGridManager::CreateGrid(uint16 const x, uint16 const y) if (IsGridCreated(x, y)) return; + // If we are an instance, ensure parent map has already created the grid before proceeding. + // Note: grid loading is locked and is safe across multiple map threads in the + // event of multiple child maps attempting to create the same parent map grid + if (_map->GetInstanceId() != 0) + { + Map* parentMap = const_cast(_map->GetParent()); + parentMap->EnsureGridCreated(GridCoord(x, y)); + } + std::unique_ptr grid = std::make_unique(x, y); grid->link(_map); + // Terrain is loading during create (should/can we move this to LoadGrid?) GridTerrainLoader loader(*grid, _map); loader.LoadTerrain(); @@ -19,6 +29,7 @@ void MapGridManager::CreateGrid(uint16 const x, uint16 const y) ++_createdGridsCount; } +// Loads objects, terrain already loaded in CreateGrid bool MapGridManager::LoadGrid(uint16 const x, uint16 const y) { MapGridType* grid = GetGrid(x, y); @@ -55,9 +66,6 @@ void MapGridManager::UnloadGrid(uint16 const x, uint16 const y) grid->VisitAllCells(visitor); } - GridTerrainUnloader terrainUnloader(*grid, _map); - terrainUnloader.UnloadTerrain(); - _mapGrid[x][y] = nullptr; } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 36ed17744..28f87f286 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -192,7 +192,7 @@ void MessageDistDeliverer::Visit(VisiblePlayersMap const& m) for (auto const& kvPair : m) { Player const* target = kvPair.second; - if (i_distSq != 0.0f && target->m_seer->GetExactDist2dSq(i_source) > i_distSq) + if (i_distSq != 0.0f && target->GetSightPosition().GetExactDist2dSq(i_source) > i_distSq) continue; // @todo: Might not need this check anymore diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index e3271e305..838ed4048 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -1384,7 +1384,7 @@ namespace Acore AnyPlayerExactPositionInGameObjectRangeCheck(GameObject const* go, float range) : _go(go), _range(range) {} bool operator()(Player* u) { - if (!_go->IsInRange(u->GetPositionX(), u->GetPositionY(), u->GetPositionZ(), _range)) + if (!_go->IsInRange3d(u->GetPositionX(), u->GetPositionY(), u->GetPositionZ(), _range)) return false; return true; @@ -1604,7 +1604,7 @@ namespace Acore bool operator() (GameObject* go) { if (!entry || (go->GetGOInfo() && go->GetGOInfo()->entry == entry)) - return go->IsInRange(x, y, z, range); + return go->IsInRange3d(x, y, z, range); else return false; } private: diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 66e8cb4bc..0683a1450 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -868,9 +868,9 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder) if (ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass())) { if (cEntry->CinematicSequence) - pCurrChar->SendCinematicStart(cEntry->CinematicSequence); + pCurrChar->GetCinematicMgr().StartCinematic(cEntry->CinematicSequence); else if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace())) - pCurrChar->SendCinematicStart(rEntry->CinematicSequence); + pCurrChar->GetCinematicMgr().StartCinematic(rEntry->CinematicSequence); // send new char string if not empty std::string_view newCharString = sWorld->getStringConfig(CONFIG_NEW_CHAR_STRING); diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index e6e51086a..c2cc245b6 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -874,7 +874,11 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry) // Stop the npc if moving if (uint32 pause = vendor->GetMovementTemplate().GetInteractionPauseTimer()) vendor->PauseMovement(pause); - vendor->SetHomePosition(vendor->GetPosition()); + + // Update home position for patrolling NPCs only (prevents drift for stationary NPCs) + if (vendor->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE || + vendor->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + vendor->SetHomePosition(vendor->GetPosition()); SetCurrentVendor(vendorEntry); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index c90c3ff0e..e5ae429c0 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -732,7 +732,8 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data) if (player->isDebugAreaTriggers) ChatHandler(this).PSendSysMessage(LANG_DEBUG_AREATRIGGER_REACHED, triggerId); - if (sScriptMgr->OnAreaTrigger(player, atEntry)) + // Skip areatrigger scripts for GMs unless debug areatriggers is enabled + if ((!player->IsGameMaster() || player->isDebugAreaTriggers) && sScriptMgr->OnAreaTrigger(player, atEntry)) return; if (player->IsAlive()) @@ -947,14 +948,14 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data) void WorldSession::HandleCompleteCinematic(WorldPacket& /*recv_data*/) { - // If player has sight bound to visual waypoint NPC we should remove it - GetPlayer()->GetCinematicMgr()->EndCinematic(); + // End the current cinematic and restore the normal player view + GetPlayer()->GetCinematicMgr().EndCinematic(); } void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recv_data*/) { // Sent by client when cinematic actually begun. So we begin the server side process - GetPlayer()->GetCinematicMgr()->BeginCinematic(); + GetPlayer()->GetCinematicMgr().StartCinematicCamera(); } void WorldSession::HandleSetActionBarToggles(WorldPacket& recv_data) diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 8323cde9e..b355b1bcf 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -162,7 +162,11 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData) // Stop the npc if moving if (uint32 pause = unit->GetMovementTemplate().GetInteractionPauseTimer()) unit->PauseMovement(pause); - unit->SetHomePosition(unit->GetPosition()); + + // Update home position for patrolling NPCs only (prevents drift for stationary NPCs) + if (unit->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE || + unit->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + unit->SetHomePosition(unit->GetPosition()); // If spiritguide, no need for gossip menu, just put player into resurrect queue if (unit->IsSpiritGuide()) diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 25ebc3d32..33c4aeeda 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -96,7 +96,11 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData) // Stop the npc if moving if (uint32 pause = creature->GetMovementTemplate().GetInteractionPauseTimer()) creature->PauseMovement(pause); - creature->SetHomePosition(creature->GetPosition()); + + // Update home position for patrolling NPCs only (prevents drift for stationary NPCs) + if (creature->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE || + creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + creature->SetHomePosition(creature->GetPosition()); if (sScriptMgr->OnGossipHello(_player, creature)) return; diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index 4fd089b6b..c4f21afed 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -43,7 +43,7 @@ void WorldSession::SendTaxiStatus(ObjectGuid guid) } // find taxi node - uint32 nearest = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), player->GetTeamId()); + uint32 nearest = sObjectMgr->GetNearestTaxiNode(*unit, player->GetTeamId(true)); if (!nearest) { return; @@ -84,7 +84,7 @@ void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket& recvData) void WorldSession::SendTaxiMenu(Creature* unit) { // find current node - uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeamId()); + uint32 curloc = sObjectMgr->GetNearestTaxiNode(*unit, GetPlayer()->GetTeamId(true)); if (curloc == 0) return; @@ -127,7 +127,7 @@ void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathN bool WorldSession::SendLearnNewTaxiNode(Creature* unit) { // find current node - uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeamId()); + uint32 curloc = sObjectMgr->GetNearestTaxiNode(*unit, GetPlayer()->GetTeamId(true)); if (curloc == 0) return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result. diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 5b72b9464..94b367e61 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -134,6 +134,11 @@ void InstanceScript::HandleGameObject(ObjectGuid GUID, bool open, GameObject* go } } +void InstanceScript::HandleGameObject(uint32 type, bool open) +{ + HandleGameObject(ObjectGuid::Empty, open, GetGameObject(type)); +} + bool InstanceScript::IsEncounterInProgress() const { for (std::vector::const_iterator itr = bosses.begin(); itr != bosses.end(); ++itr) diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index b10db66e9..ba05588ee 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -193,11 +193,21 @@ public: //Called when a player enters/leaves water bodies. virtual void OnPlayerInWaterStateUpdate(Player* /*player*/, bool /*inWater*/) {} - //Handle open / close objects - //use HandleGameObject(ObjectGuid::Empty, boolen, GO); in OnObjectCreate in instance scripts - //use HandleGameObject(GUID, boolen, nullptr); in any other script + /** + * @brief Open or close a GameObject by GUID. + * @param guid The GUID of the GameObject. Pass ObjectGuid::Empty when providing the go pointer directly. + * @param open true to open (GO_STATE_ACTIVE), false to close (GO_STATE_READY). + * @param go Optional pointer to the GameObject. If nullptr, the object is looked up by GUID. + */ void HandleGameObject(ObjectGuid guid, bool open, GameObject* go = nullptr); + /** + * @brief Open or close a GameObject registered via LoadObjectData. + * @param type The ObjectData type constant (e.g. GO_FRONT_DOOR) used in the gameObjectData array. + * @param open true to open (GO_STATE_ACTIVE), false to close (GO_STATE_READY). + */ + void HandleGameObject(uint32 type, bool open); + //change active state of doors or buttons void DoUseDoorOrButton(ObjectGuid guid, uint32 withRestoreTime = 0, bool useAlternativeState = false); diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 74e8b7575..2ef13a484 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -418,83 +418,34 @@ bool LootItem::AllowedForPlayer(Player const* player, ObjectGuid source) const { ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); if (!pProto) - { return false; - } if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_LOOT, itemid, nullptr)) - { return false; - } - bool isMasterLooter = player->GetGroup() && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID(); - bool itemVisibleForMasterLooter = !needs_quest && (!follow_loot_rules || !is_underthreshold); - - // DB conditions check if (!sConditionMgr->IsObjectMeetToConditions(const_cast(player), conditions)) - { - // Master Looter can see conditioned recipes - if (isMasterLooter && itemVisibleForMasterLooter) - { - if (pProto->HasFlag(ITEM_FLAG_HIDE_UNUSABLE_RECIPE) || (pProto->Class == ITEM_CLASS_RECIPE && pProto->Bonding == BIND_WHEN_PICKED_UP && pProto->Spells[1].SpellId != 0)) - { - return true; - } - } - return false; - } // not show loot for not own team if (pProto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && player->GetTeamId(true) != TEAM_HORDE) - { return false; - } if (pProto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeamId(true) != TEAM_ALLIANCE) - { return false; - } - // Master looter can see all items even if the character can't loot them - if (isMasterLooter && itemVisibleForMasterLooter) - { - return true; - } - - // Don't allow loot for players without profession or those who already know the recipe + // profession / recipe checks if (pProto->HasFlag(ITEM_FLAG_HIDE_UNUSABLE_RECIPE) && (!player->HasSkill(pProto->RequiredSkill) || player->HasSpell(pProto->Spells[1].SpellId))) - { return false; - } - // Don't allow to loot soulbound recipes that the player has already learned if (pProto->Class == ITEM_CLASS_RECIPE && pProto->Bonding == BIND_WHEN_PICKED_UP && pProto->Spells[1].SpellId != 0 && player->HasSpell(pProto->Spells[1].SpellId)) - { return false; - } // check quest requirements - if (!pProto->HasFlagCu(ITEM_FLAGS_CU_IGNORE_QUEST_STATUS)) - { - // Don't drop quest items if the player is missing the relevant quest - if (needs_quest && !player->HasQuestForItem(itemid)) - return false; - - // for items that start quests - if (pProto->StartQuest) - { - // Don't drop the item if the player has already finished the quest OR player already has the item in their inventory, and that item is unique OR the player has not finished a prerequisite quest - uint32 prevQuestId = sObjectMgr->GetQuestTemplate(pProto->StartQuest) ? sObjectMgr->GetQuestTemplate(pProto->StartQuest)->GetPrevQuestId() : 0; - if (player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE || (player->HasItemCount(itemid, pProto->MaxCount) && pProto->MaxCount) || (prevQuestId && !player->GetQuestRewardStatus(prevQuestId))) - return false; - } - } + if (needs_quest && !pProto->HasFlagCu(ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && !player->HasQuestForItem(itemid)) + return false; if (!sScriptMgr->OnAllowedForPlayerLootCheck(player, source)) - { return false; - } return true; } @@ -691,7 +642,7 @@ QuestItemList* Loot::FillQuestLoot(Player* player) QuestItemList* ql = new QuestItemList(); - Player* lootOwner = (roundRobinPlayer) ? ObjectAccessor::FindPlayer(roundRobinPlayer) : player; + bool isMasterLooter = player->GetGroup() && player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID(); for (uint8 i = 0; i < quest_items.size(); ++i) { @@ -699,32 +650,33 @@ QuestItemList* Loot::FillQuestLoot(Player* player) sScriptMgr->OnPlayerBeforeFillQuestLootItem(player, item); - // Quest item is not free for all and is already assigned to another player - // or player doesn't need it - if (item.is_blocked || !item.AllowedForPlayer(player, sourceWorldObjectGUID)) - { - continue; - } + bool allowed = item.AllowedForPlayer(player, sourceWorldObjectGUID); - // Player is not the loot owner, and loot owner still needs this quest item - if (!item.freeforall && - lootOwner && lootOwner != player && - item.AllowedForPlayer(lootOwner, sourceWorldObjectGUID)) - { + if (!allowed && !isMasterLooter) continue; - } ql->push_back(QuestItem(i)); - ++unlootedCount; - if (!item.freeforall) + // Only add "allowed looter" if you are actually allowed to loot. + if (allowed) { - item.is_blocked = true; + item.AddAllowedLooter(player); + + if (item.freeforall) + { + ++unlootedCount; + } + else if (!item.is_counted) + { + ++unlootedCount; + item.is_counted = true; + } } if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS) break; } + if (ql->empty()) { delete ql; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 3a3098330..3a427312c 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -32,7 +32,6 @@ #include "MapInstanced.h" #include "Metric.h" #include "MiscPackets.h" -#include "MMapFactory.h" #include "Object.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" @@ -62,13 +61,12 @@ Map::~Map() if (!m_scriptSchedule.empty()) sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size()); - - MMAP::MMapFactory::createOrGetMMapMgr()->unloadMapInstance(GetId(), i_InstanceId); } Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : - _mapGridManager(this), i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), - m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0), + _mapGridManager(this), i_mapEntry(sMapStore.LookupEntry(id)), _mapCollisionData(*this, _parent), + i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0), + m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0), _transportsUpdateIter(_transports.end()), i_scriptLock(false), _defaultLight(GetDefaultMapLight(id)) { m_parentMap = (_parent ? _parent : this); @@ -432,7 +430,7 @@ void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone) void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) { if (t_diff) - _dynamicTree.update(t_diff); + _mapCollisionData.GetDynamicTree().update(t_diff); // Update world sessions and players for (m_mapRefIter = m_mapRefMgr.begin(); m_mapRefIter != m_mapRefMgr.end(); ++m_mapRefIter) @@ -1037,9 +1035,10 @@ void Map::UnloadAll() for (GridRefMgr::iterator i = GridRefMgr::begin(); i != GridRefMgr::end();) { - MapGridType& grid(*i->GetSource()); + MapGridType* grid = i->GetSource(); ++i; - UnloadGrid(grid); // deletes the grid and removes it from the GridRefMgr + if (grid) + UnloadGrid(*grid); } // pussywizard: crashfix, some npc can be left on transport (not a default passenger) @@ -1073,9 +1072,9 @@ void Map::UnloadAll() std::shared_ptr Map::GetGridTerrainDataSharedPtr(GridCoord const& gridCoord) { - // ensure GridMap is created - EnsureGridCreated(gridCoord); - return _mapGridManager.GetGrid(gridCoord.x_coord, gridCoord.y_coord)->GetTerrainDataSharedPtr(); + MapGridType* grid = _mapGridManager.GetGrid(gridCoord.x_coord, gridCoord.y_coord); + ASSERT(grid); // Grid should always exist during this call + return grid->GetTerrainDataSharedPtr(); } GridTerrainData* Map::GetGridTerrainData(GridCoord const& gridCoord) @@ -1152,10 +1151,7 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float float vmapHeight = VMAP_INVALID_HEIGHT_VALUE; if (checkVMap) - { - VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); - vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // look from a bit higher pos to find the floor - } + vmapHeight = _mapCollisionData.GetStaticTree().getHeight(x, y, z, maxSearchDist); // look from a bit higher pos to find the floor // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT // vmapheight set for any under Z value or <= INVALID_HEIGHT @@ -1203,12 +1199,11 @@ static inline bool IsInWMOInterior(uint32 mogpFlags) bool Map::GetAreaInfo(uint32 phaseMask, float x, float y, float z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const { float check_z = z; - VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); VMAP::AreaAndLiquidData vdata; VMAP::AreaAndLiquidData ddata; - bool hasVmapAreaInfo = vmgr->GetAreaAndLiquidData(GetId(), x, y, z, {}, vdata) && vdata.areaInfo.has_value(); - bool hasDynamicAreaInfo = _dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, {}, ddata) && ddata.areaInfo.has_value(); + bool hasVmapAreaInfo = _mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, {}, vdata) && vdata.areaInfo.has_value(); + bool hasDynamicAreaInfo = _mapCollisionData.GetDynamicTree().GetAreaAndLiquidData(x, y, z, phaseMask, {}, ddata) && ddata.areaInfo.has_value(); auto useVmap = [&] { check_z = vdata.floorZ; groupId = vdata.areaInfo->groupId; adtId = vdata.areaInfo->adtId; rootId = vdata.areaInfo->rootId; flags = vdata.areaInfo->mogpFlags; }; auto useDyn = [&] { check_z = ddata.floorZ; groupId = ddata.areaInfo->groupId; adtId = ddata.areaInfo->adtId; rootId = ddata.areaInfo->rootId; flags = ddata.areaInfo->mogpFlags; }; if (hasVmapAreaInfo) @@ -1299,10 +1294,9 @@ LiquidData const Map::GetLiquidData(uint32 phaseMask, float x, float y, float z, LiquidData liquidData; liquidData.Status = LIQUID_MAP_NO_WATER; - VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); VMAP::AreaAndLiquidData vmapData; bool useGridLiquid = true; - if (vmgr->GetAreaAndLiquidData(GetId(), x, y, z, ReqLiquidType, vmapData) && vmapData.liquidInfo) + if (_mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, ReqLiquidType, vmapData) && vmapData.liquidInfo) { useGridLiquid = !vmapData.areaInfo || !IsInWMOInterior(vmapData.areaInfo->mogpFlags); LOG_DEBUG("maps", "GetLiquidStatus(): vmap liquid level: {} ground: {} type: {}", vmapData.liquidInfo->level, vmapData.floorZ, vmapData.liquidInfo->type); @@ -1382,11 +1376,10 @@ void Map::GetFullTerrainStatusForPosition(uint32 /*phaseMask*/, float x, float y { GridTerrainData* gmap = GetGridTerrainData(x, y); - VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); VMAP::AreaAndLiquidData vmapData; // VMAP::AreaAndLiquidData dynData; VMAP::AreaAndLiquidData* wmoData = nullptr; - vmgr->GetAreaAndLiquidData(GetId(), x, y, z, reqLiquidType, vmapData); + _mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, reqLiquidType, vmapData); // _dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, reqLiquidType, dynData); uint32 gridAreaId = 0; @@ -1547,7 +1540,7 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa } } - if ((checks & LINEOFSIGHT_CHECK_VMAP) && !VMAP::VMapFactory::createOrGetVMapMgr()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2, ignoreFlags)) + if ((checks & LINEOFSIGHT_CHECK_VMAP) && !_mapCollisionData.GetStaticTree().isInLineOfSight(x1, y1, z1, x2, y2, z2, ignoreFlags)) { return false; } @@ -1560,7 +1553,7 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa ignoreFlags = VMAP::ModelIgnoreFlags::M2; } - if (!_dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask, ignoreFlags)) + if (!_mapCollisionData.GetDynamicTree().isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask, ignoreFlags)) { return false; } @@ -1569,25 +1562,11 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa return true; } -bool Map::GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) -{ - G3D::Vector3 startPos(x1, y1, z1); - G3D::Vector3 dstPos(x2, y2, z2); - - G3D::Vector3 resultPos; - bool result = _dynamicTree.GetObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist); - - rx = resultPos.x; - ry = resultPos.y; - rz = resultPos.z; - return result; -} - float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const { float h1, h2; h1 = GetHeight(x, y, z, vmap, maxSearchDist); - h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); + h2 = _mapCollisionData.GetDynamicTree().getHeight(x, y, z, maxSearchDist, phasemask); return std::max(h1, h2); } @@ -3056,8 +3035,7 @@ bool Map::CheckCollisionAndGetValidCoords(WorldObject const* source, float start // Unit is not on the ground, check for potential collision via vmaps if (notOnGround) { - bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(source->GetMapId(), - startX, startY, startZ + halfHeight, + bool col = _mapCollisionData.GetStaticTree().GetObjectHitPos(startX, startY, startZ + halfHeight, destX, destY, destZ + halfHeight, destX, destY, destZ, -CONTACT_DISTANCE); @@ -3071,7 +3049,7 @@ bool Map::CheckCollisionAndGetValidCoords(WorldObject const* source, float start } // check dynamic collision - bool col = source->GetMap()->GetObjectHitPos(source->GetPhaseMask(), + bool col = _mapCollisionData.GetDynamicTree().GetObjectHitPos(source->GetPhaseMask(), startX, startY, startZ + halfHeight, destX, destY, destZ + halfHeight, destX, destY, destZ, -CONTACT_DISTANCE); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 035ba0dce..e6027a717 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -27,6 +27,7 @@ #include "GameObjectModel.h" #include "GridDefines.h" #include "GridRefMgr.h" +#include "MapCollisionData.h" #include "MapGridManager.h" #include "MapRefMgr.h" #include "ObjectDefines.h" @@ -230,8 +231,6 @@ public: [[nodiscard]] Map const* GetParent() const { return m_parentMap; } - // pussywizard: movemaps, mmaps - [[nodiscard]] std::shared_mutex& GetMMapLock() const { return *(const_cast(&MMapLock)); } // pussywizard: std::unordered_set i_objectsForDelayedVisibility; void HandleDelayedVisibility(); @@ -389,15 +388,14 @@ public: bool CanReachPositionAndGetValidCoords(WorldObject const* source, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, float startX, float startY, float startZ, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; bool CheckCollisionAndGetValidCoords(WorldObject const* source, float startX, float startY, float startZ, float &destX, float &destY, float &destZ, bool failOnCollision = true) const; - void Balance() { _dynamicTree.balance(); } - void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); } - void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); } - [[nodiscard]] bool ContainsGameObjectModel(const GameObjectModel& model) const { return _dynamicTree.contains(model);} - [[nodiscard]] DynamicMapTree const& GetDynamicMapTree() const { return _dynamicTree; } - bool GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist); + void Balance() { _mapCollisionData.GetDynamicTree().balance(); } + void RemoveGameObjectModel(const GameObjectModel& model) { _mapCollisionData.GetDynamicTree().remove(model); } + void InsertGameObjectModel(const GameObjectModel& model) { _mapCollisionData.GetDynamicTree().insert(model); } + [[nodiscard]] bool ContainsGameObjectModel(const GameObjectModel& model) const { return _mapCollisionData.GetDynamicTree().contains(model);} + [[nodiscard]] DynamicMapTree const& GetDynamicMapTree() const { return _mapCollisionData.GetDynamicTree(); } [[nodiscard]] float GetGameObjectFloor(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { - return _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); + return _mapCollisionData.GetDynamicTree().getHeight(x, y, z, maxSearchDist, phasemask); } /* RESPAWN TIMES @@ -524,6 +522,9 @@ public: return 0; }; + MapCollisionData& GetMapCollisionData() { return _mapCollisionData; } + MapCollisionData const& GetMapCollisionData() const { return _mapCollisionData; } + private: template void InitializeObject(T* obj); @@ -551,15 +552,14 @@ protected: void AddToGrid(T* object, Cell const& cell); std::mutex Lock; - std::shared_mutex MMapLock; MapGridManager _mapGridManager; MapEntry const* i_mapEntry; + MapCollisionData _mapCollisionData; uint8 i_spawnMode; uint32 i_InstanceId; uint32 m_unloadTimer; float m_VisibleDistance; - DynamicMapTree _dynamicTree; time_t _instanceResetPeriod; // pussywizard MapRefMgr m_mapRefMgr; diff --git a/src/server/game/Maps/MapCollisionData.cpp b/src/server/game/Maps/MapCollisionData.cpp new file mode 100644 index 000000000..bdce5edb2 --- /dev/null +++ b/src/server/game/Maps/MapCollisionData.cpp @@ -0,0 +1,183 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#include "DBCStores.h" +#include "DetourNavMeshQuery.h" +#include "DisableMgr.h" +#include "MapCollisionData.h" +#include "MapTree.h" +#include "ModelInstance.h" +#include "VMapFactory.h" +#include "VMapMgr2.h" +#include "World.h" +#include "WorldModel.h" + +#include + +MapCollisionData::MapCollisionData(Map const& map, Map const* parentMap) : + _map(map), _staticVMapData(map.GetId()) +{ + if (parentMap) + { + // If we have a parent map, point our static tree and mmap nav mesh to the parent maps + _staticVMapData._staticTree = parentMap->GetMapCollisionData().GetStaticTreeSharedPtr(); + _mmapData._navMesh = parentMap->GetMapCollisionData().GetMMapNavMeshSharedPtr(); + } + else + { + // If we are a base map create a new static tree and mmap nav mesh + std::string const mapFileName = VMAP::VMapMgr2::getMapFileName(map.GetId()); + std::shared_ptr newTree = std::make_shared(map.GetId(), (sWorld->GetDataPath() + "vmaps")); + if (newTree->InitMap(mapFileName)) + _staticVMapData._staticTree = newTree; + + _mmapData._navMesh = MMAP::MMapMgr::LoadNavMesh(map.GetId()); + } +} + +int MapCollisionData::LoadVMapTile(uint32 tileX, uint32 tileY) +{ + if (!VMAP::VMapFactory::createOrGetVMapMgr()->isMapLoadingEnabled() || !_staticVMapData._staticTree) + return VMAP::VMAP_LOAD_RESULT_IGNORED; + + if (!_staticVMapData._staticTree->LoadMapTile(tileX, tileY)) + return VMAP::VMAP_LOAD_RESULT_ERROR; + + return VMAP::VMAP_LOAD_RESULT_OK; +} + +int MapCollisionData::LoadMMapTile(uint32 tileX, uint32 tileY) +{ + if (!DisableMgr::IsPathfindingEnabled(&_map) || !_mmapData._navMesh) + return MMAP::MMAP_LOAD_RESULT_IGNORED; + + return MMAP::MMapMgr::LoadTile(_mmapData._navMesh.get(), _map.GetId(), tileX, tileY); +} + +bool StaticVMapCollisionData::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const +{ +#if defined(ENABLE_VMAP_CHECKS) + if (!sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_LOS) || DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LOS)) + return true; +#endif + + if (!_staticTree) + return true; + + G3D::Vector3 const pos1 = VMAP::VMapMgr2::convertPositionToInternalRep(x1, y1, z1); + G3D::Vector3 const pos2 = VMAP::VMapMgr2::convertPositionToInternalRep(x2, y2, z2); + if (pos1 != pos2) + return _staticTree->isInLineOfSight(pos1, pos2, ignoreFlags); + + return true; +} + +bool StaticVMapCollisionData::GetObjectHitPos(float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const +{ +#if defined(ENABLE_VMAP_CHECKS) + if (sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_LOS) && !DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LOS)) +#endif + { + if (_staticTree) + { + G3D::Vector3 const pos1 = VMAP::VMapMgr2::convertPositionToInternalRep(x1, y1, z1); + G3D::Vector3 const pos2 = VMAP::VMapMgr2::convertPositionToInternalRep(x2, y2, z2); + G3D::Vector3 resultPos; + bool result = _staticTree->GetObjectHitPos(pos1, pos2, resultPos, modifyDist); + resultPos = VMAP::VMapMgr2::convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z); + rx = resultPos.x; + ry = resultPos.y; + rz = resultPos.z; + return result; + } + } + + rx = x2; + ry = y2; + rz = z2; + + return false; +} + +float StaticVMapCollisionData::getHeight(float x, float y, float z, float maxSearchDist) const +{ +#if defined(ENABLE_VMAP_CHECKS) + if (sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_HEIGHT) && !DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_HEIGHT)) +#endif + { + if (_staticTree) + { + G3D::Vector3 const pos = VMAP::VMapMgr2::convertPositionToInternalRep(x, y, z); + float height = _staticTree->getHeight(pos, maxSearchDist); + if (height >= G3D::finf()) + return VMAP_INVALID_HEIGHT_VALUE; // No height + + return height; + } + } + + return VMAP_INVALID_HEIGHT_VALUE; +} + +bool StaticVMapCollisionData::GetAreaAndLiquidData(float x, float y, float z, Optional reqLiquidType, VMAP::AreaAndLiquidData& data) const +{ + if (_staticTree) + { + VMAP::LocationInfo info; + G3D::Vector3 const pos = VMAP::VMapMgr2::convertPositionToInternalRep(x, y, z); + if (_staticTree->GetLocationInfo(pos, info)) + { + data.floorZ = info.ground_Z; + if (!DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LIQUIDSTATUS)) + { + uint32 liquidType = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc + float liquidLevel; + if (!reqLiquidType || (GetLiquidFlags(liquidType) & *reqLiquidType)) + if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel)) + data.liquidInfo.emplace(liquidType, liquidLevel); + } + + if (!DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_AREAFLAG)) + data.areaInfo.emplace(info.hitModel->GetWmoID(), info.hitInstance->adtId, info.rootId, info.hitModel->GetMogpFlags(), info.hitInstance->ID); + return true; + } + } + + return false; +} + +bool DynamicVMapCollisionData::GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const +{ + G3D::Vector3 startPos(x1, y1, z1); + G3D::Vector3 dstPos(x2, y2, z2); + + G3D::Vector3 resultPos; + bool result = DynamicMapTree::GetObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist); + + rx = resultPos.x; + ry = resultPos.y; + rz = resultPos.z; + return result; +} + +dtNavMeshQuery const* MMapData::GetNavMeshQuery() +{ + if (_navMesh && !_navMeshQuery) + _navMeshQuery = MMAP::MMapMgr::CreateNavMeshQuery(_navMesh.get()); + + return _navMeshQuery.get(); +} diff --git a/src/server/game/Maps/MapCollisionData.h b/src/server/game/Maps/MapCollisionData.h new file mode 100644 index 000000000..bb8a3cdac --- /dev/null +++ b/src/server/game/Maps/MapCollisionData.h @@ -0,0 +1,100 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#ifndef MAP_COLLISION_DATA_H +#define MAP_COLLISION_DATA_H + +#include "DynamicTree.h" +#include "MapTree.h" +#include "MMapMgr.h" +#include "IVMapMgr.h" + +class Map; + +namespace VMAP +{ + enum class ModelIgnoreFlags : uint32; +} + +// Simple wrappers for vmap static & dynamic trees to enable clear separation of use for increased readability +class StaticVMapCollisionData +{ + friend class MapCollisionData; + +public: + StaticVMapCollisionData(uint32 mapId) : _mapId(mapId) {} + + bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const; + bool GetObjectHitPos(float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const; + float getHeight(float x, float y, float z, float maxSearchDist) const; + bool GetAreaAndLiquidData(float x, float y, float z, Optional reqLiquidType, VMAP::AreaAndLiquidData& data) const; +protected: + // _staticTree is a shared_ptr as it will point to a parent maps static tree (if exists) to save on memory + std::shared_ptr _staticTree; + uint32 _mapId; +}; + +class DynamicVMapCollisionData : public DynamicMapTree +{ +public: + bool GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const; +}; + +class MMapData +{ + friend class MapCollisionData; + +public: + dtNavMesh const* GetNavMesh() const { return _navMesh.get(); } + dtNavMeshQuery const* GetNavMeshQuery(); + +protected: + // _navMesh is a shared_ptr as it will point to a parent maps nav mesh (if exists) to save on memory + std::shared_ptr _navMesh; + // navMeshQuery is not thread safe and needs its own instance per map + MMAP::ManagedNavMeshQuery _navMeshQuery; +}; + +// Map collision data holders (dynamic&static vmap, mmaps) +class MapCollisionData +{ +public: + MapCollisionData(Map const& map, Map const* parentMap); + ~MapCollisionData() = default; + + int LoadVMapTile(uint32 tileX, uint32 tileY); + int LoadMMapTile(uint32 tileX, uint32 tileY); + + DynamicVMapCollisionData& GetDynamicTree() { return _dynamicVMapData; } + DynamicVMapCollisionData const& GetDynamicTree() const { return _dynamicVMapData; } + StaticVMapCollisionData& GetStaticTree() { return _staticVMapData; } + StaticVMapCollisionData const& GetStaticTree() const { return _staticVMapData; } + MMapData& GetMMapData() { return _mmapData; } + MMapData const& GetMMapData() const { return _mmapData; } + + std::shared_ptr const GetStaticTreeSharedPtr() const { return _staticVMapData._staticTree; } + std::shared_ptr const GetMMapNavMeshSharedPtr() const { return _mmapData._navMesh; } + +private: + Map const& _map; + + DynamicVMapCollisionData _dynamicVMapData; + StaticVMapCollisionData _staticVMapData; + MMapData _mmapData; +}; + +#endif diff --git a/src/server/game/Maps/SpawnData.h b/src/server/game/Maps/SpawnData.h new file mode 100644 index 000000000..b1136203d --- /dev/null +++ b/src/server/game/Maps/SpawnData.h @@ -0,0 +1,79 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#ifndef AZEROTHCORE_SPAWNDATA_H +#define AZEROTHCORE_SPAWNDATA_H + +#include "Define.h" +#include + +enum SpawnObjectType : uint8 +{ + SPAWN_TYPE_CREATURE = 0, + SPAWN_TYPE_GAMEOBJECT = 1, + + SPAWN_TYPE_MAX +}; + +enum SpawnObjectTypeMask : uint8 +{ + SPAWN_TYPEMASK_CREATURE = (1 << SPAWN_TYPE_CREATURE), + SPAWN_TYPEMASK_GAMEOBJECT = (1 << SPAWN_TYPE_GAMEOBJECT), + + SPAWN_TYPEMASK_ALL = SPAWN_TYPEMASK_CREATURE | SPAWN_TYPEMASK_GAMEOBJECT +}; + +enum SpawnGroupFlags : uint32 +{ + SPAWNGROUP_FLAG_NONE = 0x00, + SPAWNGROUP_FLAG_SYSTEM = 0x01, + SPAWNGROUP_FLAG_MANUAL_SPAWN = 0x04, + SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE = 0x08, + SPAWNGROUP_FLAG_ESCORTQUESTNPC = 0x10, + + SPAWNGROUP_FLAG_ALL = SPAWNGROUP_FLAG_SYSTEM | + SPAWNGROUP_FLAG_MANUAL_SPAWN | SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE | + SPAWNGROUP_FLAG_ESCORTQUESTNPC +}; + +struct SpawnGroupTemplateData +{ + uint32 groupId; + std::string name; + uint16 mapid; + SpawnGroupFlags flags; +}; + +struct SpawnData +{ + SpawnObjectType const type; + uint16 mapid{0}; + uint32 phaseMask{0}; + float posX{0.0f}; + float posY{0.0f}; + float posZ{0.0f}; + float orientation{0.0f}; + uint8 spawnMask{0}; + uint32 ScriptId{0}; + bool dbData{true}; + uint32 spawnGroupId{0}; + +protected: + SpawnData(SpawnObjectType t) : type(t) {} +}; + +#endif // AZEROTHCORE_SPAWNDATA_H diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 70ff2a0ff..5fc80fca9 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -815,6 +815,7 @@ enum AcoreStrings LANG_NO_REASON = 35407, LANG_UNAUTHORIZED = 35408, LANG_PINFO_CHR_MAP_WITH_AREA = 35409, + LANG_PINFO_CHR_ONLINETIME = 35410, LANG_CHARACTER_GENDER_MALE = 855, LANG_CHARACTER_GENDER_FEMALE = 856, @@ -1202,7 +1203,30 @@ enum AcoreStrings LANG_BF_QUEUE_PLAYER_QUEUE = 5123, LANG_BF_QUEUE_PLAYER_INVITED = 5124, LANG_BF_QUEUE_PLAYER_WAR = 5125, - // Room for more strings 5126-9999 + + // Autobroadcast commands + LANG_AUTOBROADCAST_LIST_HEADER = 5126, + LANG_AUTOBROADCAST_LIST_ENTRY = 5127, + LANG_AUTOBROADCAST_LIST_EMPTY = 5128, + LANG_AUTOBROADCAST_ADD_SUCCESS = 5129, + LANG_AUTOBROADCAST_REMOVE_SUCCESS = 5130, + LANG_AUTOBROADCAST_NOT_FOUND = 5131, + LANG_AUTOBROADCAST_LOCALE_SUCCESS = 5132, + LANG_AUTOBROADCAST_LOCALE_ENTRY = 5133, + LANG_AUTOBROADCAST_INVALID_LOCALE = 5134, + + // Mail commands + LANG_MAIL_LIST_HEADER = 5135, + LANG_MAIL_LIST_ENTRY = 5136, + LANG_MAIL_LIST_EMPTY = 5137, + LANG_MAIL_RETURN_SUCCESS = 5138, + LANG_MAIL_RETURN_NOT_FOUND = 5139, + LANG_MAIL_RETURN_NOT_NORMAL = 5140, + LANG_MAIL_RETURN_NO_SENDER = 5141, + LANG_MAIL_RETURN_ALREADY_RETURNED = 5142, + LANG_MAIL_RETURN_HOOK_BLOCKED = 5143, + + // Room for more strings 5144-9999 // Level requirement notifications LANG_SAY_REQ = 6604, @@ -1390,6 +1414,37 @@ enum AcoreStrings LANG_DEBUG_LFG_ON = 30096, LANG_DEBUG_LFG_OFF = 30097, - LANG_DEBUG_LFG_CONF = 30098 + LANG_DEBUG_LFG_CONF = 30098, + + // debug loot command + LANG_DEBUG_LOOT_HEADER = 30099, + LANG_DEBUG_LOOT_ITEM = 30100, + LANG_DEBUG_LOOT_ITEM_QUEST = 30101, + LANG_DEBUG_LOOT_GOLD = 30102, + LANG_DEBUG_LOOT_EMPTY = 30103, + LANG_DEBUG_LOOT_INVALID_TYPE = 30104, + LANG_DEBUG_LOOT_NO_TEMPLATE = 30105, + LANG_DEBUG_LOOT_HEADER_MULTI = 30106, + LANG_DEBUG_LOOT_ITEM_MULTI = 30107, + LANG_DEBUG_LOOT_ITEM_QUEST_MULTI = 30108, + LANG_DEBUG_LOOT_GOLD_MULTI = 30109, + + // debug factionchange command (30110-30125) + LANG_DEBUG_FACTIONCHANGE_HEADER = 30110, + LANG_DEBUG_FACTIONCHANGE_FLAG_FACTION = 30111, + LANG_DEBUG_FACTIONCHANGE_FLAG_RACE = 30112, + LANG_DEBUG_FACTIONCHANGE_FLAG_NONE = 30113, + LANG_DEBUG_FACTIONCHANGE_GUILD_FAIL = 30114, + LANG_DEBUG_FACTIONCHANGE_GUILD_OK = 30115, + LANG_DEBUG_FACTIONCHANGE_ARENA_CAPTAIN_FAIL = 30116, + LANG_DEBUG_FACTIONCHANGE_ARENA_CAPTAIN_OK = 30117, + LANG_DEBUG_FACTIONCHANGE_MAIL_FAIL = 30118, + LANG_DEBUG_FACTIONCHANGE_MAIL_OK = 30119, + LANG_DEBUG_FACTIONCHANGE_AUCTION_FAIL = 30120, + LANG_DEBUG_FACTIONCHANGE_AUCTION_OK = 30121, + LANG_DEBUG_FACTIONCHANGE_GOLD_FAIL = 30122, + LANG_DEBUG_FACTIONCHANGE_GOLD_OK = 30123, + LANG_DEBUG_FACTIONCHANGE_GOLD_NOLIMIT = 30124, + LANG_DEBUG_FACTIONCHANGE_NA = 30125 }; #endif diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index ff8343dc6..aacb78458 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -538,9 +538,9 @@ void MotionMaster::MovePath(uint32 path_id, ForcedMovement forcedMovement, PathS } Movement::PointsArray points; - for (auto& point : *path) + for (auto const& node : path->Nodes) { - points.push_back(G3D::Vector3(point.second.x, point.second.y, point.second.z)); + points.push_back(G3D::Vector3(node.X, node.Y, node.Z)); } // pass the new PointsArray* to the appropriate MoveSplinePath function @@ -879,10 +879,10 @@ void MotionMaster::MoveDistract(uint32 timer) void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot) { + bool const delayed = (_cleanFlag & MMCF_UPDATE); + while (MovementGenerator* curr = Impl[slot]) { - bool delayed = (_top == slot && (_cleanFlag & MMCF_UPDATE)); - // clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete Impl[slot] = nullptr; while (!empty() && !top()) @@ -918,7 +918,7 @@ void MotionMaster::MoveWaypoint(uint32 path_id, bool repeatable, PathSource path if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; - Mutate(new WaypointMovementGenerator(path_id, pathSource, repeatable), MOTION_SLOT_IDLE); + Mutate(new WaypointMovementGenerator(path_id, repeatable, pathSource), MOTION_SLOT_IDLE); LOG_DEBUG("movement.motionmaster", "{} ({}) start moving over path(Id:{}, repeatable: {})", _owner->IsPlayer() ? "Player" : "Creature", _owner->GetGUID().ToString(), path_id, repeatable ? "YES" : "NO"); diff --git a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp index 5ddbc2c45..607428cb6 100644 --- a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp @@ -20,7 +20,6 @@ #include "DetourCommon.h" #include "Geometry.h" #include "Log.h" -#include "MMapFactory.h" #include "MMapMgr.h" #include "Map.h" #include "Metric.h" @@ -34,12 +33,10 @@ PathGenerator::PathGenerator(WorldObject const* owner) : { memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs)); - uint32 mapId = _source->GetMapId(); //if (sDisableMgr->IsPathfindingEnabled(_sourceUnit->FindMap())) { - MMAP::MMapMgr* mmap = MMAP::MMapFactory::createOrGetMMapMgr(); - _navMesh = mmap->GetNavMesh(mapId); - _navMeshQuery = mmap->GetNavMeshQuery(mapId, _source->GetInstanceId()); + _navMesh = _source->GetMap()->GetMapCollisionData().GetMMapData().GetNavMesh(); + _navMeshQuery = _source->GetMap()->GetMapCollisionData().GetMMapData().GetNavMeshQuery(); } CreateFilter(); diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 457698f18..188578ea0 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -259,12 +259,7 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) owner->SetInFront(target); if (cOwner) - { - if (cOwner->IsWithinMeleeRange(target)) - cOwner->SetCannotReachTarget(); - else - cOwner->SetCannotReachTarget(target->GetGUID()); - } + cOwner->SetCannotReachTarget(); MovementInform(owner); } diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 47a83f5ae..272a1f823 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -27,45 +27,69 @@ #include "Player.h" #include "Spell.h" #include "Transport.h" -#include "World.h" #include "SmartScriptMgr.h" +#include "World.h" -void WaypointMovementGenerator::LoadPath(Creature* creature) +inline G3D::Vector3 PositionToVector3(Position const& p) { return { p.GetPositionX(), p.GetPositionY(), p.GetPositionZ() }; } + +WaypointMovementGenerator::WaypointMovementGenerator(uint32 pathId, bool repeating, PathSource pathSource) : PathMovementBase((WaypointPath const*)nullptr), + _lastSplineId(0), _pathId(pathId), _waypointDelay(0), + _waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(true), _stalled(false), _hasBeenStalled(false), _done(false), _pathSource(pathSource), + _smoothSplineLaunched(false), _lastPassedSplineIdx(0) { - switch (i_pathSource) - { - case PathSource::WAYPOINT_MGR: - { - if (!path_id) - path_id = creature->GetWaypointPath(); +} - i_path = sWaypointMgr->GetPath(path_id); - break; - } - case PathSource::SMART_WAYPOINT_MGR: +WaypointMovementGenerator::WaypointMovementGenerator(WaypointPath& path, bool repeating) : PathMovementBase((WaypointPath const*)nullptr), + _lastSplineId(0), _pathId(0), _waypointDelay(0), + _waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(false), _stalled(false), _hasBeenStalled(false), _done(false), _pathSource(PathSource::WAYPOINT_MGR), + _smoothSplineLaunched(false), _lastPassedSplineIdx(0) +{ + i_path = &path; +} + +void WaypointMovementGenerator::DoInitialize(Creature* creature) +{ + _done = false; + + if (_loadedFromDB) + { + if (!_pathId) + _pathId = creature->GetWaypointPath(); + + switch (_pathSource) { - i_path = sSmartWaypointMgr->GetPath(path_id); - break; + default: + case PathSource::WAYPOINT_MGR: + i_path = sWaypointMgr->GetPath(_pathId); + break; + case PathSource::SMART_WAYPOINT_MGR: + i_path = sSmartWaypointMgr->GetPath(_pathId); + break; } } if (!i_path) { - // No movement found for entry - LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature {} ({}) doesn't have waypoint path id: {}", - creature->GetName(), creature->GetGUID().ToString(), path_id); + LOG_ERROR("sql.sql", "WaypointMovementGenerator::DoInitialize: creature {} ({}) doesn't have waypoint path id: {}", + creature->GetName(), creature->GetGUID().ToString(), _pathId); return; } - i_currentNode = i_path->begin()->first; + // Determine our first waypoint from the creature's stored waypoint + if (CreatureData const* creatureData = creature->GetCreatureData()) + { + if (i_path->Nodes.size() > creatureData->currentwaypoint) + { + creature->UpdateCurrentWaypointInfo(creatureData->currentwaypoint, i_path->Id); + i_currentNode = creatureData->currentwaypoint; + } + } - StartMoveNow(creature); -} - -void WaypointMovementGenerator::DoInitialize(Creature* creature) -{ - LoadPath(creature); creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); + + // Inform AI + if (CreatureAI* AI = creature->AI()) + AI->WaypointPathStarted(i_path->Id); } void WaypointMovementGenerator::DoFinalize(Creature* creature) @@ -75,133 +99,219 @@ void WaypointMovementGenerator::DoFinalize(Creature* creature) void WaypointMovementGenerator::DoReset(Creature* creature) { - if (stalled) + // We did not reach our last waypoint before reset, treat this scenario as resuming movement. + if (!_done && !_waypointReached) + _hasBeenStalled = true; + else if (_done) { - return; - } - creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); - StartMoveNow(creature); -} - -void WaypointMovementGenerator::OnArrived(Creature* creature) -{ - if (!i_path || i_path->empty()) - return; - if (m_isArrivalDone) - return; - - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - m_isArrivalDone = true; - - auto currentNodeItr = i_path->find(i_currentNode); - - if (currentNodeItr->second.event_id && urand(0, 99) < currentNodeItr->second.event_chance) - { - LOG_DEBUG("maps.script", "Creature movement start script {} at point {} for {}.", - currentNodeItr->second.event_id, i_currentNode, creature->GetGUID().ToString()); - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - creature->GetMap()->ScriptsStart(sWaypointScripts, currentNodeItr->second.event_id, creature, nullptr); - } - - // Inform script - MovementInform(creature); - creature->UpdateWaypointID(i_currentNode); - - if (currentNodeItr->second.delay) - { - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - Stop(currentNodeItr->second.delay); + // mimic IdleMovementGenerator + if (!creature->IsStopped()) + creature->StopMoving(); } } -bool WaypointMovementGenerator::StartMove(Creature* creature) +inline void UpdateHomePosition(Creature* creature, WaypointNode const& waypointNode) { - if (!i_path || i_path->empty()) - return false; - - // Xinef: Dont allow dead creatures to move - if (!creature->IsAlive()) - return false; - - if (Stopped()) - return true; + float x = waypointNode.X; + float y = waypointNode.Y; + float z = waypointNode.Z; + float o = creature->GetOrientation(); bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); - - if (m_isArrivalDone) + if (!transportPath) + creature->SetHomePosition(x, y, z, o); + else { + if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : nullptr)) { - auto currentNodeItr = i_path->find(i_currentNode); - float x = currentNodeItr->second.x; - float y = currentNodeItr->second.y; - float z = currentNodeItr->second.z; - float o = creature->GetOrientation(); + o -= trans->GetOrientation(); + creature->SetTransportHomePosition(x, y, z, o); + trans->CalculatePassengerPosition(x, y, z, &o); + creature->SetHomePosition(x, y, z, o); + } + } +} - if (!transportPath) - creature->SetHomePosition(x, y, z, o); - else +void WaypointMovementGenerator::ProcessWaypointArrival(Creature* creature, WaypointNode const& waypoint) +{ + if (_waypointReached) + return; + + if (waypoint.Delay > 0) + { + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + _waypointDelay = waypoint.Delay; + } + + bool pathEnded = (i_currentNode == i_path->Nodes.size() - 1) && !_repeating && !_done; + if (pathEnded) + _done = true; + + UpdateHomePosition(creature, waypoint); + + if (waypoint.EventId && urand(0, 99) < waypoint.EventChance) + { + LOG_DEBUG("maps.script", "Creature movement start script {} at point {} for {}.", + waypoint.EventId, i_currentNode, creature->GetGUID().ToString()); + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.EventId, creature, nullptr); + } + + // scripts can invalidate current path, store what we need + uint32 const waypointId = waypoint.Id; + uint32 const pathId = i_path->Id; + + creature->UpdateWaypointID(waypointId); + creature->UpdateCurrentWaypointInfo(waypointId, pathId); + + // Inform AI + if (CreatureAI* AI = creature->AI()) + { + AI->MovementInform(WAYPOINT_MOTION_TYPE, waypointId); + AI->WaypointReached(waypointId, pathId); + } + + if (Unit* owner = creature->GetCharmerOrOwner()) + { + if (UnitAI* AI = owner->GetAI()) + AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId); + } + else + { + if (TempSummon* tempSummon = creature->ToTempSummon()) + if (Unit* owner2 = tempSummon->GetSummonerUnit()) + if (UnitAI* AI = owner2->GetAI()) + AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId); + } + + // Path end notifications fire after WaypointReached so that m_path_id + // is still valid when SmartAI checks it for SMART_EVENT_WAYPOINT_REACHED. + if (pathEnded) + { + creature->UpdateCurrentWaypointInfo(0, 0); + + if (CreatureAI* AI = creature->AI()) + AI->PathEndReached(pathId); + + // Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI + if (CreatureAI* AI = creature->AI()) + AI->WaypointPathEnded(waypointId, pathId); + } + + // All hooks called and infos updated. Time to increment the waypoint node id + if (i_path && !i_path->Nodes.empty()) // ensure that the path has not been changed in one of the hooks. + i_currentNode = (i_currentNode + 1) % i_path->Nodes.size(); + + _waypointReached = true; +} + +void WaypointMovementGenerator::StartMove(Creature* creature, bool relaunch /*= false*/) +{ + // Formation checks. Do not launch a new spline when one of our formation members is currently in combat. + if (!relaunch) + { + if (!IsAllowedToMove(creature) || (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed())) + { + _waypointDelay = 1000; + return; + } + } + + // Dont allow dead creatures to move + if (!creature->IsAlive()) + return; + + // Step two: node selection is done, build spline data + creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); + WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode); + bool const useTransportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); + + Movement::MoveSplineInit init(creature); + //! If the creature is on transport, we assume waypoints set in DB are already transport offsets + if (useTransportPath) + init.DisableTransportPathTransformations(); + + if (waypoint.SmoothTransition && i_path->Nodes.size() > 2) + { + // Build a catmullrom spline segment, stopping at delay waypoints + init.Path().push_back(PositionToVector3(creature->GetPosition())); + + bool hasDelayInSegment = false; + uint32 segmentNodes = 0; + for (uint32 i = 0; i < i_path->Nodes.size(); ++i) + { + uint32 idx = (i_currentNode + i) % i_path->Nodes.size(); + WaypointNode const& node = i_path->Nodes.at(idx); + init.Path().push_back(G3D::Vector3(node.X, node.Y, node.Z)); + segmentNodes++; + + // Stop the segment at a waypoint with a delay + if (node.Delay > 0) { - if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : nullptr)) - { - o -= trans->GetOrientation(); - creature->SetTransportHomePosition(x, y, z, o); - trans->CalculatePassengerPosition(x, y, z, &o); - creature->SetHomePosition(x, y, z, o); - } - else - transportPath = false; - // else if (vehicle) - this should never happen, vehicle offsets are const + hasDelayInSegment = true; + break; } } - // Xinef: moved the upper IF here - uint32 lastPoint = i_path->rbegin()->first; - if ((i_currentNode == lastPoint) && !repeating) // If that's our last waypoint + // If no delays found and repeating, add wrap-around points for seamless loop + if (!hasDelayInSegment && _repeating) { - creature->AI()->PathEndReached(path_id); - creature->GetMotionMaster()->Initialize(); - return false; + for (uint32 i = 0; i < std::min(3, i_path->Nodes.size()); ++i) + { + uint32 idx = (i_currentNode + i) % i_path->Nodes.size(); + WaypointNode const& node = i_path->Nodes.at(idx); + init.Path().push_back(G3D::Vector3(node.X, node.Y, node.Z)); + } } - ++i_currentNode; - if (lastPoint < i_currentNode) - i_currentNode = i_path->begin()->first; + // Need at least 3 waypoints for a meaningful catmullrom spline + if (segmentNodes >= 3) + { + init.SetFirstPointId(i_currentNode); + init.SetSmooth(); + _smoothSplineLaunched = true; + _lastPassedSplineIdx = i_currentNode; + } + else + { + // Too few points for catmullrom, fall back to linear point-to-point + init.Path().clear(); + init.MoveTo(G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); + } } - - // xinef: do not initialize motion if we got stunned in movementinform - if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) + else if (!waypoint.SplinePoints.empty()) { - return true; + // We have spline points in waypoint_data_addon table + int32 splineIndex = 0; + + auto itr = waypoint.SplinePoints.begin(); + if (splineIndex) + std::advance(itr, splineIndex); + + init.Path().reserve(waypoint.SplinePoints.size() - splineIndex); + std::copy(itr, waypoint.SplinePoints.end(), std::back_inserter(init.Path())); + + // Add starting vertex and destination + init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition())); + init.Path().insert(init.Path().end(), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); } - - auto currentNodeItr = i_path->find(i_currentNode); - WaypointData const& node = currentNodeItr->second; - - m_isArrivalDone = false; - - creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); - - Movement::Location formationDest(node.x, node.y, node.z, 0.0f); - Movement::MoveSplineInit init(creature); - - //! If creature is on transport, we assume waypoints set in DB are already transport offsets - if (transportPath) + else { - init.DisableTransportPathTransformations(); - if (TransportBase* trans = creature->GetDirectTransport()) - trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); + // Smooth transition for short paths (<=2 nodes): use previous spline endpoint as start + if (waypoint.SmoothTransition && !creature->movespline->Finalized() && _lastSplineId == creature->movespline->GetId()) + { + init.MoveTo(creature->movespline->FinalDestination(), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); + if (!init.Path().empty()) + init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition())); + } + else + init.MoveTo(PositionToVector3(creature->GetPosition()), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z)); } - float z = node.z; - creature->UpdateAllowedPositionZ(node.x, node.y, z); - //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call - //! but formationDest contains global coordinates - init.MoveTo(node.x, node.y, z, true, true); + if (waypoint.Orientation.has_value() && waypoint.Delay > 0) + init.SetFacing(*waypoint.Orientation); - if (node.orientation.has_value() && node.delay > 0) - init.SetFacing(*node.orientation); - - switch (node.move_type) + switch (waypoint.MoveType) { case WAYPOINT_MOVE_TYPE_LAND: init.SetAnimation(AnimTier::Ground); @@ -219,89 +329,214 @@ bool WaypointMovementGenerator::StartMove(Creature* creature) break; } + if (creature->CanFly()) + init.SetFly(); + + if (waypoint.Velocity > 0.f) + init.SetVelocity(waypoint.Velocity); + init.Launch(); - //Call for creature group update - if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature && creature->GetFormation()->CanLeaderStartMoving()) - creature->GetFormation()->LeaderStartedMoving(); + if (!creature->movespline->Finalized()) + _lastSplineId = creature->movespline->GetId(); - return true; + // Inform formation + creature->SignalFormationMovement(); + + // Inform AI + if (!relaunch) + if (CreatureAI* AI = creature->AI()) + AI->WaypointStarted(waypoint.Id, i_path->Id); + + _waypointReached = false; + _recalculateSpeed = false; + _hasBeenStalled = false; } bool WaypointMovementGenerator::DoUpdate(Creature* creature, uint32 diff) { - // Waypoint movement can be switched on/off - // This is quite handy for escort quests and other stuff - if (stalled) - { - Stop(1000); + if (!creature || !creature->IsAlive()) return true; - } - if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) + + if (_done || !i_path || i_path->Nodes.empty()) + return true; + + // Stop movement if paused, rooted, or casting + if (!IsAllowedToMove(creature) && !creature->movespline->Finalized()) { creature->StopMoving(); - Stop(1000); + _lastSplineId = 0; + _smoothSplineLaunched = false; + } + + // Set home position to current position. + if (!creature->movespline->Finalized()) + { + bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); + if (!transportPath) + creature->SetHomePosition(creature->GetPosition()); + } + + // Smooth spline: track waypoint passages without rebuilding + if (_smoothSplineLaunched && creature->movespline->GetId() == _lastSplineId) + { + int32 currentIdx = creature->movespline->currentPathIdx(); + + // Process passed waypoints + while (_lastPassedSplineIdx < currentIdx) + { + _lastPassedSplineIdx++; + WaypointNode const& passedWp = i_path->Nodes.at(i_currentNode); + + UpdateHomePosition(creature, passedWp); + + // Save data before AI callbacks — they can invalidate the reference + uint32 const wpId = passedWp.Id; + uint32 const wpPathId = i_path->Id; + uint32 const wpDelay = passedWp.Delay; + std::optional const wpOrientation = passedWp.Orientation; + + creature->UpdateWaypointID(wpId); + creature->UpdateCurrentWaypointInfo(wpId, wpPathId); + + if (passedWp.EventId && urand(0, 99) < passedWp.EventChance) + { + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + creature->GetMap()->ScriptsStart(sWaypointScripts, passedWp.EventId, creature, nullptr); + } + + if (CreatureAI* AI = creature->AI()) + { + AI->MovementInform(WAYPOINT_MOTION_TYPE, wpId); + AI->WaypointReached(wpId, wpPathId); + } + + // Advance node + if (i_path && !i_path->Nodes.empty()) + i_currentNode = (i_currentNode + 1) % i_path->Nodes.size(); + + // If this waypoint has a delay, stop the spline and pause + if (wpDelay > 0) + { + creature->StopMoving(); + creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + _waypointDelay = wpDelay; + _waypointReached = true; + _smoothSplineLaunched = false; + if (wpOrientation.has_value()) + creature->SetFacingTo(*wpOrientation); + + return true; + } + } + + if (creature->movespline->Finalized()) + { + if (!_repeating) + { + // Path ended + uint32 const endWpId = i_path->Nodes.at(i_currentNode).Id; + uint32 const endPathId = i_path->Id; + _done = true; + _smoothSplineLaunched = false; + creature->UpdateCurrentWaypointInfo(0, 0); + if (CreatureAI* AI = creature->AI()) + AI->PathEndReached(endPathId); + + // Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI + if (CreatureAI* AI = creature->AI()) + AI->WaypointPathEnded(endWpId, endPathId); + } + else + { + // Repeating: rebuild spline + _smoothSplineLaunched = false; + StartMove(creature); + } + } + return true; } - // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) - return false; + // Non-smooth: per-waypoint logic + WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode); + UpdateWaypointState(creature, waypoint); - // Xinef: Dont allow dead creatures to move - if (!creature->IsAlive()) - return false; + // Process movement preventing timers + if (_waypointDelay > 0) + { + _waypointDelay -= diff; + if (_waypointDelay > 0) + return true; + } - if (Stopped()) + if (_pauseTime.has_value()) { - if (CanMove(diff)) - return StartMove(creature); - } - else - { - if (creature->movespline->Finalized()) - { - OnArrived(creature); - return StartMove(creature); - } + *_pauseTime -= diff; + if (*_pauseTime > 0) + return true; + else + _pauseTime.reset(); } + + // Timers are ready, let's try to move + if (IsAllowedToMove(creature) && (_waypointReached || _recalculateSpeed || _hasBeenStalled)) + StartMove(creature, _recalculateSpeed || _hasBeenStalled); + return true; } -void WaypointMovementGenerator::MovementInform(Creature* creature) +void WaypointMovementGenerator::Pause(uint32 timer /*= 0*/) { - if (creature->AI()) - creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); - - if (Unit* owner = creature->GetCharmerOrOwner()) - { - if (UnitAI* AI = owner->GetAI()) - AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode); - } - else - { - if (TempSummon* tempSummon = creature->ToTempSummon()) - if (Unit* owner = tempSummon->GetSummonerUnit()) - if (UnitAI* AI = owner->GetAI()) - AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode); - } + _stalled = timer ? false : true; + _hasBeenStalled = !_waypointReached; + _pauseTime = timer; } -void WaypointMovementGenerator::Pause(uint32 timer) +void WaypointMovementGenerator::Resume(uint32 overrideTimer /*= 0*/) { - if (timer) - i_nextMoveTime.Reset(timer); - else - { - // No timer? Will be paused forever until ::Resume is called - stalled = true; - i_nextMoveTime.Reset(1); - } + _hasBeenStalled = !_waypointReached; + _stalled = false; + if (overrideTimer) + _pauseTime = overrideTimer; } -void WaypointMovementGenerator::Resume(uint32 /*overrideTimer/*/) +bool WaypointMovementGenerator::GetResetPosition(float& x, float& y, float& z) { - stalled = false; + // prevent a crash at empty waypoint path. + if (!i_path || i_path->Nodes.empty()) + return false; + + ASSERT(i_currentNode < i_path->Nodes.size(), "WaypointMovementGenerator::GetResetPos: tried to reference a node id ({}) which is not included in path ({})", i_currentNode, i_path->Id); + WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode); + + x = waypoint.X; + y = waypoint.Y; + z = waypoint.Z; + return true; +} + +bool WaypointMovementGenerator::IsAllowedToMove(Creature* creature) const +{ + if (_stalled || _done) + return false; + + if (_pauseTime.has_value()) + return false; + + if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) + return false; + + return true; +} + +void WaypointMovementGenerator::UpdateWaypointState(Creature* creature, WaypointNode const& waypointNode) +{ + if (creature->movespline->GetId() != _lastSplineId) + return; + + if (creature->movespline->Finalized()) + ProcessWaypointArrival(creature, waypointNode); } //----------------------------------------------------// diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index 44194581e..12479820a 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -54,49 +54,43 @@ class WaypointMovementGenerator : public MovementGeneratorMedium< Crea public PathMovementBase { public: - WaypointMovementGenerator(uint32 _path_id = 0, PathSource pathSource = PathSource::WAYPOINT_MGR, bool _repeating = true, bool _stalled = false) - : PathMovementBase((WaypointPath const*)nullptr), i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating), stalled(_stalled), i_pathSource(pathSource) {} + explicit WaypointMovementGenerator(uint32 pathId = 0, bool repeating = true, PathSource pathSource = PathSource::WAYPOINT_MGR); + explicit WaypointMovementGenerator(WaypointPath& path, bool repeating = true); ~WaypointMovementGenerator() { i_path = nullptr; } + void DoInitialize(Creature*); void DoFinalize(Creature*); void DoReset(Creature*); bool DoUpdate(Creature*, uint32 diff); - void Pause(uint32 timer = 0); - void Resume(uint32 overrideTimer/* = 0*/); - void MovementInform(Creature*); + void unitSpeedChanged() override { _recalculateSpeed = true; } + void Pause(uint32 timer = 0) override; + void Resume(uint32 overrideTimer = 0) override; + bool GetResetPosition(float& x, float& y, float& z) override; - MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; } - - // now path movement implmementation - void LoadPath(Creature*); + MovementGeneratorType GetMovementGeneratorType() override { return WAYPOINT_MOTION_TYPE; } private: - void Stop(int32 time) { i_nextMoveTime.Reset(time);} + void ProcessWaypointArrival(Creature*, WaypointNode const&); + void StartMove(Creature*, bool relaunch = false); + bool IsAllowedToMove(Creature*) const; + void UpdateWaypointState(Creature*, WaypointNode const&); - bool Stopped() { return !i_nextMoveTime.Passed();} + uint32 _lastSplineId; + uint32 _pathId; + int32 _waypointDelay; + std::optional _pauseTime; + bool _waypointReached; - bool CanMove(int32 diff) - { - i_nextMoveTime.Update(diff); - return i_nextMoveTime.Passed(); - } - - void OnArrived(Creature*); - bool StartMove(Creature*); - - void StartMoveNow(Creature* creature) - { - i_nextMoveTime.Reset(0); - StartMove(creature); - } - - TimeTrackerSmall i_nextMoveTime; - bool m_isArrivalDone; - uint32 path_id; - bool repeating; - bool stalled; - PathSource i_pathSource; + bool _recalculateSpeed; + bool _repeating; + bool _loadedFromDB; + bool _stalled; + bool _hasBeenStalled; + bool _done; + PathSource _pathSource; + bool _smoothSplineLaunched; + int32 _lastPassedSplineIdx; }; /** FlightPathMovementGenerator generates movement of the player for the paths diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h index 3b134d44e..07cfcb41f 100644 --- a/src/server/game/Movement/Spline/MoveSpline.h +++ b/src/server/game/Movement/Spline/MoveSpline.h @@ -122,6 +122,7 @@ namespace Movement [[nodiscard]] Vector3 FinalDestination() const { return Initialized() ? spline.getPoint(spline.last()) : Vector3(); } [[nodiscard]] Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1) : Vector3(); } [[nodiscard]] int32 currentPathIdx() const; + [[nodiscard]] int32 MaxPathIdx() const { return spline.last() - 1; } [[nodiscard]] bool HasAnimation() const { return splineflags.animation; } [[nodiscard]] uint8 GetAnimationType() const { return splineflags.animId; } diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp index 4ae646e37..16b58a45d 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.cpp +++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp @@ -199,6 +199,25 @@ namespace Movement args.flags.EnableFacingAngle(); } + void MoveSplineInit::MoveTo(Vector3 const& start, Vector3 const& dest, bool generatePath, bool forceDestination) + { + if (generatePath) + { + PathGenerator path(unit); + bool result = path.CalculatePath(start.x, start.y, start.z, dest.x, dest.y, dest.z, forceDestination); + if (result && !(path.GetPathType() & PATHFIND_NOPATH)) + { + MovebyPath(path.GetPath()); + return; + } + } + + args.path_Idx_offset = 0; + args.path.resize(2); + TransportPathTransform transform(unit, args.TransformForTransport); + args.path[1] = transform(dest); + } + void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination) { if (generatePath) diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h index 5e26dad04..16120d9bb 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.h +++ b/src/server/game/Movement/Spline/MoveSplineInit.h @@ -99,6 +99,7 @@ namespace Movement /* Initializes simple A to B motion, A is current unit's position, B is destination */ + void MoveTo(Vector3 const& start, Vector3 const& destination, bool generatePath = true, bool forceDestination = false); void MoveTo(const Vector3& destination, bool generatePath = false, bool forceDestination = false); void MoveTo(float x, float y, float z, bool generatePath = false, bool forceDestination = false); diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h new file mode 100644 index 000000000..6b55ab5af --- /dev/null +++ b/src/server/game/Movement/Waypoints/WaypointDefines.h @@ -0,0 +1,72 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#ifndef ACORE_WAYPOINTDEFINES_H +#define ACORE_WAYPOINTDEFINES_H + +#include "Define.h" +#include "G3D/Vector3.h" +#include +#include + +enum WaypointMoveType +{ + WAYPOINT_MOVE_TYPE_WALK, + WAYPOINT_MOVE_TYPE_RUN, + WAYPOINT_MOVE_TYPE_LAND, + WAYPOINT_MOVE_TYPE_TAKEOFF, + + WAYPOINT_MOVE_TYPE_MAX +}; + +struct WaypointNode +{ + WaypointNode() : Id(0), X(0.f), Y(0.f), Z(0.f), Velocity(0.f), Delay(0), EventId(0), MoveType(WAYPOINT_MOVE_TYPE_RUN), EventChance(0), SmoothTransition(false) { } + WaypointNode(uint32 id, float x, float y, float z, std::optional orientation = { }, float velocity = 0.f, uint32 delay = 0, bool smoothTransition = false) : + Id(id), X(x), Y(y), Z(z), Orientation(orientation), Velocity(velocity), Delay(delay), SmoothTransition(smoothTransition) + { + EventId = 0; + MoveType = WAYPOINT_MOVE_TYPE_WALK; + EventChance = 100; + } + + uint32 Id; + float X, Y, Z; + std::optional Orientation; + float Velocity; + uint32 Delay; + uint32 EventId; + uint32 MoveType; + uint8 EventChance; + bool SmoothTransition; + std::vector SplinePoints; +}; + +struct WaypointPath +{ + WaypointPath() : Id(0) { } + WaypointPath(uint32 _id, std::vector&& _nodes) + { + Id = _id; + Nodes = _nodes; + } + + std::vector Nodes; + uint32 Id; +}; + +#endif diff --git a/src/server/game/Movement/Waypoints/WaypointMgr.cpp b/src/server/game/Movement/Waypoints/WaypointMgr.cpp index 5d673b35a..0d0334d73 100644 --- a/src/server/game/Movement/Waypoints/WaypointMgr.cpp +++ b/src/server/game/Movement/Waypoints/WaypointMgr.cpp @@ -22,20 +22,6 @@ #include "QueryResult.h" #include "Timer.h" -WaypointMgr::WaypointMgr() -{ -} - -WaypointMgr::~WaypointMgr() -{ - for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr) - { - itr->second.clear(); - } - - _waypointStore.clear(); -} - WaypointMgr* WaypointMgr::instance() { static WaypointMgr instance; @@ -46,8 +32,8 @@ void WaypointMgr::Load() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 - QueryResult result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data ORDER BY id, point"); + // 0 1 2 3 4 5 6 7 8 9 10 11 + QueryResult result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, orientation, velocity, delay, smoothTransition, move_type, action, action_chance FROM waypoint_data ORDER BY id, point"); if (!result) { @@ -61,66 +47,113 @@ void WaypointMgr::Load() do { Field* fields = result->Fetch(); - WaypointData data; - uint32 pathId = fields[0].Get(); - WaypointPath& path = _waypointStore[pathId]; - float x = fields[2].Get(); float y = fields[3].Get(); float z = fields[4].Get(); - std::optional o; + std::optional o; if (!fields[5].IsNull()) o = fields[5].Get(); + float velocity = fields[6].Get(); + Acore::NormalizeMapCoord(x); Acore::NormalizeMapCoord(y); - data.id = fields[1].Get(); - data.x = x; - data.y = y; - data.z = z; - data.orientation = o; - data.move_type = fields[6].Get(); + WaypointNode waypoint; + waypoint.Id = fields[1].Get(); + waypoint.X = x; + waypoint.Y = y; + waypoint.Z = z; + if (o.has_value()) + waypoint.Orientation = o; + waypoint.Velocity = velocity; + waypoint.Delay = fields[7].Get(); + waypoint.SmoothTransition = fields[8].Get(); + waypoint.MoveType = fields[9].Get(); - if (data.move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.MoveType >= WAYPOINT_MOVE_TYPE_MAX) { - //LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", wp->id); + LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", waypoint.Id); continue; } - data.delay = fields[7].Get(); - data.event_id = fields[8].Get(); - data.event_chance = fields[9].Get(); + waypoint.EventId = fields[10].Get(); + waypoint.EventChance = fields[11].Get(); - path.emplace(data.id, data); + WaypointPath& path = _waypointStore[pathId]; + path.Id = pathId; + path.Nodes.push_back(std::move(waypoint)); ++count; } while (result->NextRow()); - for (auto itr = _waypointStore.begin(); itr != _waypointStore.end(); ) - { - uint32 first = itr->second.begin()->first; - uint32 last = itr->second.rbegin()->first; - if (last - first + 1 != itr->second.size()) - { - LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has non-contiguous pointids, skipping", itr->first); - itr = _waypointStore.erase(itr); - } - else - ++itr; - } - LOG_INFO("server.loading", ">> Loaded {} waypoints in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } +void WaypointMgr::LoadWaypointAddons() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT PathID, PointID, SplinePointIndex, PositionX, PositionY, PositionZ FROM waypoint_data_addon ORDER BY PathID, PointID, SplinePointIndex"); + + if (!result) + { + LOG_INFO("server.loading", ">> Loaded 0 waypoint addon data. DB table `waypoint_data_addon` is empty!"); + LOG_INFO("server.loading", " "); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 pathId = fields[0].Get(); + + auto it = _waypointStore.find(pathId); + if (it == _waypointStore.end()) + { + LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PathID {} but there is no such path in waypoint_data. Ignoring.", pathId); + continue; + } + + WaypointPath& path = it->second; + uint32 pointId = fields[1].Get(); + + auto itr = std::find_if(path.Nodes.begin(), path.Nodes.end(), [pointId](WaypointNode const& node) + { + return node.Id == pointId; + }); + + if (itr == path.Nodes.end()) + { + LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PointID {} of PathID {} but there is no such point in waypoint_data. Ignoring.", pointId, pathId); + continue; + } + + float x = fields[3].Get(); + float y = fields[4].Get(); + float z = fields[5].Get(); + + Acore::NormalizeMapCoord(x); + Acore::NormalizeMapCoord(y); + + itr->SplinePoints.push_back(G3D::Vector3(x, y, z)); + + ++count; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} waypoint addon data in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); +} + void WaypointMgr::ReloadPath(uint32 id) { - WaypointPathContainer::iterator itr = _waypointStore.find(id); + auto itr = _waypointStore.find(id); if (itr != _waypointStore.end()) - { _waypointStore.erase(itr); - } WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID); @@ -131,13 +164,10 @@ void WaypointMgr::ReloadPath(uint32 id) if (!result) return; - WaypointPath& path = _waypointStore[id]; - + std::vector values; do { Field* fields = result->Fetch(); - WaypointData data; - float x = fields[1].Get(); float y = fields[2].Get(); float z = fields[3].Get(); @@ -145,26 +175,34 @@ void WaypointMgr::ReloadPath(uint32 id) if (!fields[4].IsNull()) o = fields[4].Get(); + float velocity = fields[5].Get(); + Acore::NormalizeMapCoord(x); Acore::NormalizeMapCoord(y); - data.id = fields[0].Get(); - data.x = x; - data.y = y; - data.z = z; - data.orientation = o; - data.move_type = fields[5].Get(); + WaypointNode waypoint; + waypoint.Id = fields[0].Get(); + waypoint.X = x; + waypoint.Y = y; + waypoint.Z = z; + if (o.has_value()) + waypoint.Orientation = o; + waypoint.Velocity = velocity; + waypoint.Delay = fields[6].Get(); + waypoint.SmoothTransition = fields[7].Get(); + waypoint.MoveType = fields[8].Get(); - if (data.move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.MoveType >= WAYPOINT_MOVE_TYPE_MAX) { - //LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", wp->id); + LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", waypoint.Id); continue; } - data.delay = fields[6].Get(); - data.event_id = fields[7].Get(); - data.event_chance = fields[8].Get(); + waypoint.EventId = fields[9].Get(); + waypoint.EventChance = fields[10].Get(); - path.emplace(data.id, data); + values.push_back(std::move(waypoint)); } while (result->NextRow()); + + _waypointStore[id] = WaypointPath(id, std::move(values)); } diff --git a/src/server/game/Movement/Waypoints/WaypointMgr.h b/src/server/game/Movement/Waypoints/WaypointMgr.h index f9c0d59df..d6bb3ec21 100644 --- a/src/server/game/Movement/Waypoints/WaypointMgr.h +++ b/src/server/game/Movement/Waypoints/WaypointMgr.h @@ -18,35 +18,8 @@ #ifndef ACORE_WAYPOINTMANAGER_H #define ACORE_WAYPOINTMANAGER_H -#include "Define.h" -#include +#include "WaypointDefines.h" #include -#include -#include - -enum WaypointMoveType -{ - WAYPOINT_MOVE_TYPE_WALK, - WAYPOINT_MOVE_TYPE_RUN, - WAYPOINT_MOVE_TYPE_LAND, - WAYPOINT_MOVE_TYPE_TAKEOFF, - - WAYPOINT_MOVE_TYPE_MAX -}; - -struct WaypointData -{ - uint32 id; - float x, y, z; - std::optional orientation; - uint32 delay; - uint32 event_id = 0; - uint32 move_type = 0; - uint8 event_chance = 0; -}; - -typedef std::map WaypointPath; -typedef std::unordered_map WaypointPathContainer; class WaypointMgr { @@ -59,10 +32,13 @@ public: // Loads all paths from database, should only run on startup void Load(); + // Loads additional path data for waypoints from database. Should only be called on startup. + void LoadWaypointAddons(); + // Returns the path from a given id WaypointPath const* GetPath(uint32 id) const { - WaypointPathContainer::const_iterator itr = _waypointStore.find(id); + auto itr = _waypointStore.find(id); if (itr != _waypointStore.end()) return &itr->second; @@ -70,10 +46,9 @@ public: } private: - WaypointMgr(); - ~WaypointMgr(); + WaypointMgr() { } - WaypointPathContainer _waypointStore; + std::unordered_map _waypointStore; }; #define sWaypointMgr WaypointMgr::instance() diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.h b/src/server/game/OutdoorPvP/OutdoorPvP.h index 25a82959b..774244a14 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.h +++ b/src/server/game/OutdoorPvP/OutdoorPvP.h @@ -137,6 +137,8 @@ public: { return _value; } + [[nodiscard]] float GetMaxValue() const { return _maxValue; } + [[nodiscard]] float GetMinValue() const { return _minValue; } protected: bool AddObject(uint32 type, uint32 entry, uint32 map, float x, float y, float z, float o, diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 2e85d484f..55113a806 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -379,8 +379,8 @@ void OpcodeTable::Initialize() /*0x0F8*/ DEFINE_HANDLER(CMSG_TRIGGER_CINEMATIC_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x0F9*/ DEFINE_HANDLER(CMSG_OPENING_CINEMATIC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x0FA*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER); - /*0x0FB*/ DEFINE_HANDLER(CMSG_NEXT_CINEMATIC_CAMERA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleNextCinematicCamera ); - /*0x0FC*/ DEFINE_HANDLER(CMSG_COMPLETE_CINEMATIC, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCompleteCinematic ); + /*0x0FB*/ DEFINE_HANDLER(CMSG_NEXT_CINEMATIC_CAMERA, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleNextCinematicCamera ); + /*0x0FC*/ DEFINE_HANDLER(CMSG_COMPLETE_CINEMATIC, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleCompleteCinematic ); /*0x0FD*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_TUTORIAL_FLAGS, STATUS_NEVER); /*0x0FE*/ DEFINE_HANDLER(CMSG_TUTORIAL_FLAG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialFlag ); /*0x0FF*/ DEFINE_HANDLER(CMSG_TUTORIAL_CLEAR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialClear ); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index e7c030cc3..5506a9461 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1223,7 +1223,7 @@ bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventIn case SPELL_AURA_MECHANIC_IMMUNITY: case SPELL_AURA_MOD_MECHANIC_RESISTANCE: // compare mechanic - if (!spellInfo || !(spellInfo->GetAllEffectsMechanicMask() & (1 << GetMiscValue()))) + if (!spellInfo || !(spellInfo->GetAllEffectsMechanicMask() & (UI64LIT(1) << GetMiscValue()))) return false; break; case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK: @@ -3933,312 +3933,7 @@ void AuraEffect::HandleModStateImmunityMask(AuraApplication const* aurApp, uint8 return; Unit* target = aurApp->GetTarget(); - std::list aura_immunity_list; - uint32 mechanic_immunity_list = 0; - int32 miscVal = GetMiscValue(); - - switch (miscVal) - { - case 96: - case 1615: - { - if (!GetAmount()) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_CHARM); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 679: - { - if (GetId() == 57742) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 1557: - { - if (GetId() == 64187) - { - mechanic_immunity_list = (1 << MECHANIC_STUN); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - } - else - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 1614: - case 1694: - { - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT); - break; - } - case 1630: - { - if (!GetAmount()) - { - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT); - } - else - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 477: - case 1733: - case 1632: - { - if (!GetAmount()) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT) - | (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM) - | (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR) - | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED) - | (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN) | (1 << MECHANIC_BANISH); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_BANISH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - break; - } - case 878: - { - if (GetAmount() == 1) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_STUN) - | (1 << MECHANIC_DISORIENTED) | (1 << MECHANIC_FREEZE); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - } - break; - } - default: - break; - } - - if (aura_immunity_list.empty()) - { - // Roots, OK - if (GetMiscValue() & (1 << 0)) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT); - } - // Taunt, OK - if (GetMiscValue() & (1 << 1)) - { - aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT); - } - // Crowd-Control auras? - if (GetMiscValue() & (1 << 2)) - { - mechanic_immunity_list = (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE); - } - // Interrupt, OK - if (GetMiscValue() & (1 << 3)) - { - mechanic_immunity_list = (1 << MECHANIC_INTERRUPT); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_INTERRUPT, apply); - } - // Transform? - if (GetMiscValue() & (1 << 4)) - { - aura_immunity_list.push_back(SPELL_AURA_TRANSFORM); - } - // Stun auras breakable by damage (Incapacitate effects), OK - if (GetMiscValue() & (1 << 5)) - { - mechanic_immunity_list = (1 << MECHANIC_KNOCKOUT); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply); - } - // // Slowing effects - if (GetMiscValue() & (1 << 6)) - { - mechanic_immunity_list = (1 << MECHANIC_SNARE); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED); - } - // Charm auras?, 90% - if ((GetMiscValue() & (1 << 7))) - { - mechanic_immunity_list = (1 << MECHANIC_CHARM); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_CHARM); - aura_immunity_list.push_back(SPELL_AURA_MOD_POSSESS); - } - // UNK - // if ((GetMiscValue() & (1 << 8))) - // { - // } - // Fear, OK - if (GetMiscValue() & (1 << 9)) - { - mechanic_immunity_list = (1 << MECHANIC_FEAR); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR); - } - // Stuns, OK - if (GetMiscValue() & (1 << 10)) - { - mechanic_immunity_list = (1 << MECHANIC_STUN); - - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - aura_immunity_list.push_back(SPELL_AURA_MOD_STUN); - } - } - - // apply immunities - for (std::list ::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter) - target->ApplySpellImmune(GetId(), IMMUNITY_STATE, *iter, apply); - - // Patch 3.0.3 Bladestorm now breaks all snares and roots on the warrior when activated. - if (GetId() == 46924) - { - // Knockback and hex - target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, apply); - } - - if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) - { - target->RemoveAurasWithMechanic(mechanic_immunity_list, AURA_REMOVE_BY_DEFAULT, GetId()); - for (std::list ::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter) - target->RemoveAurasByType(*iter); - } + m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply); } void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4247,67 +3942,7 @@ void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 return; Unit* target = aurApp->GetTarget(); - uint32 mechanic = 0; - - switch (GetId()) - { - case 46924: // BladeStorm - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - break; - case 34471: // The Beast Within - case 19574: // Bestial Wrath - case 38484: // Bestial Wrath - case 40081: // Free friend (Black Temple) - mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_BANISH, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SHACKLE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DAZE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply); - break; - case 42292: // PvP trinket - case 59752: // Every Man for Himself - case 65547: // PvP trinket for Faction Champions (ToC 25) - case 53490: // Bullheaded - case 46227: // Medalion of Immunity - mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; - target->RemoveAurasByType(SPELL_AURA_PREVENTS_FLEEING); // xinef: Patch 2.3.0 PvP Trinkets: Insignia of the Alliance, Insignia of the Horde, Medallion of the Alliance, and Medallion of the Horde now clear the debuff from Judgement of Justice. - // Actually we should apply immunities here, too, but the aura has only 100 ms duration, so there is practically no point - break; - case 54508: // Demonic Empowerment - mechanic = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT); - target->RemoveAurasByType(SPELL_AURA_MOD_STUN); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); - break; - default: - if (GetMiscValue() < 1) - return; - mechanic = 1 << GetMiscValue(); - target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, GetMiscValue(), apply); - break; - } - - if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) - { - // Xinef: exception for purely snare mechanic (eg. hands of freedom)! - if (mechanic == (1 << MECHANIC_SNARE)) - target->RemoveMovementImpairingAuras(false); - else - target->RemoveAurasWithMechanic(mechanic, AURA_REMOVE_BY_DEFAULT, GetId()); - } + m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply); } void AuraEffect::HandleAuraModEffectImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4415,7 +4050,7 @@ void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 m Unit* target = aurApp->GetTarget(); - target->ApplySpellImmune(GetId(), IMMUNITY_DAMAGE, GetMiscValue(), apply); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply); if (apply) target->GetThreatMgr().EvaluateSuppressed(); @@ -4427,8 +4062,7 @@ void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint return; Unit* target = aurApp->GetTarget(); - - target->ApplySpellDispelImmunity(m_spellInfo, DispelType(GetMiscValue()), (apply)); + m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply); } /*********************************************************/ @@ -6643,7 +6277,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const if (!target->IsAlive()) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()) || target->IsTotem()) + if (target->IsImmunedToDamage(caster, GetSpellInfo()) || target->IsTotem()) { SendTickImmune(target, caster); return; @@ -6795,7 +6429,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c if (!target->IsAlive()) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo())) + if (target->IsImmunedToDamage(caster, GetSpellInfo())) { SendTickImmune(target, caster); return; @@ -6905,7 +6539,7 @@ void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster) if (!caster || !caster->IsAlive() || !target->IsAlive()) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED)) + if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo())) { SendTickImmune(target, caster); return; @@ -6934,7 +6568,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const if (!target->IsAlive()) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED)) + if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo())) { SendTickImmune(target, caster); return; @@ -7073,7 +6707,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con if (!caster || !caster->IsAlive() || !target->IsAlive() || !target->HasActivePowerType(PowerType)) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo())) + if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo())) { SendTickImmune(target, caster); return; @@ -7148,7 +6782,7 @@ void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const if (!target->IsAlive() || !target->GetMaxPower(PowerType)) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED)) + if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo())) { SendTickImmune(target, caster); return; @@ -7181,7 +6815,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons if (!target->IsAlive() || !target->GetMaxPower(PowerType)) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED)) + if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo())) { SendTickImmune(target, caster); return; @@ -7212,7 +6846,7 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con if (!caster || !target->IsAlive() || !target->HasActivePowerType(PowerType)) return; - if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo())) + if (target->IsImmunedToDamage(caster, GetSpellInfo())) { SendTickImmune(target, caster); return; @@ -7306,7 +6940,8 @@ void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEv Unit* triggerTarget = target == eventInfo.GetActor() ? eventInfo.GetActionTarget() : eventInfo.GetActor(); if (!triggerTarget) return; - if (triggerTarget->HasUnitState(UNIT_STATE_ISOLATED) || triggerTarget->IsImmunedToDamageOrSchool(GetSpellInfo())) + + if (triggerTarget->IsImmunedToDamage(target, GetSpellInfo())) { SendTickImmune(triggerTarget, target); return; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index e4b59781e..3ca928b47 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -552,7 +552,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) if (IsArea()) for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) { - if ((existing->second & (1 << effIndex)) && existing->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex)) + if ((existing->second & (1 << effIndex)) && existing->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster())) existing->second &= ~(1 << effIndex); } @@ -594,7 +594,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) // check target immunities for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) { - if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex)) + if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster())) itr->second &= ~(1 << effIndex); } if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo()) || !CanBeAppliedOn(itr->first)) @@ -2822,7 +2822,7 @@ void UnitAura::FillTargetMap(std::map& targets, Unit* caster) { float radius = GetSpellInfo()->Effects[effIndex].CalcRadius(caster); - if (!GetUnitOwner()->HasUnitState(UNIT_STATE_ISOLATED)) + if (!GetUnitOwner()->HasAuraState(AURA_STATE_BANISHED, GetSpellInfo(), caster)) { switch (GetSpellInfo()->Effects[effIndex].Effect) { diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 63e2c5c0b..69a795012 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1315,7 +1315,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge // Xinef: the distance should be increased by caster size, it is neglected in latter calculations std::list targets; float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod; - SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions); + SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions, Acore::WorldObjectSpellAreaTargetSearchReason::Area); CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); @@ -1414,7 +1414,6 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici { float distance = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); Map* map = m_caster->GetMap(); - uint32 mapid = m_caster->GetMapId(); uint32 phasemask = m_caster->GetPhaseMask(); float collisionHeight = m_caster->GetCollisionHeight(); float destz = 0.0f, startx = 0.0f, starty = 0.0f, startz = 0.0f, starto = 0.0f; @@ -1556,9 +1555,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici //LOG_ERROR("spells", "total path > than distance in 3D , need to move back a bit for save distance, total path = {}, overdistance = {}", totalpath, overdistance); } - bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(mapid, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f); + bool col = m_caster->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos(prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f); // check dynamic collision - bool dcol = m_caster->GetMap()->GetObjectHitPos(phasemask, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f); + bool dcol = m_caster->GetMap()->GetMapCollisionData().GetDynamicTree().GetObjectHitPos(phasemask, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f); // collision occured if (col || dcol || (overdistance > 0.0f && !map->IsInWater(phasemask, tstX, tstY, ground, collisionHeight)) || (fabs(prevZ - tstZ) > maxtravelDistZ && (tstZ > prevZ))) @@ -1620,9 +1619,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici else { float z = pos.GetPositionZ(); - bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(mapid, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f); + bool col = m_caster->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos(pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f); // check dynamic collision - bool dcol = m_caster->GetMap()->GetObjectHitPos(phasemask, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f); + bool dcol = m_caster->GetMap()->GetMapCollisionData().GetDynamicTree().GetObjectHitPos(phasemask, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f); // collision occured if (col || dcol) @@ -2091,12 +2090,12 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec return target; } -void Spell::SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList) +void Spell::SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason) { uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); if (!containerTypeMask) return; - Acore::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList); + Acore::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList, searchReason); Acore::WorldObjectListSearcher searcher(m_caster, targets, check, containerTypeMask); SearchTargets > (searcher, containerTypeMask, m_caster, position, range); } @@ -2142,7 +2141,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list tempTargets; - SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList); + SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList, Acore::WorldObjectSpellAreaTargetSearchReason::Chain); tempTargets.remove(target); // remove targets which are always invalid for chain spells @@ -2310,7 +2309,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= // Check for effect immune skip if immuned for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - if (target->IsImmunedToSpellEffect(m_spellInfo, effIndex)) + if (target->IsImmunedToSpellEffect(m_spellInfo, effIndex, m_caster)) effectMask &= ~(1 << effIndex); ObjectGuid targetGUID = target->GetGUID(); @@ -2593,6 +2592,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) m_spellAura = nullptr; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied + if (m_originalCaster && missInfo != SPELL_MISS_EVADE && !m_originalCaster->IsFriendlyTo(effectUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || effectUnit->IsEngaged())) + effectUnit->SetInCombatWith(m_originalCaster); + PrepareScriptHitHandlers(); CallScriptBeforeHitHandlers(missInfo); @@ -2774,83 +2776,93 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo, m_spellSchoolMask); - // Add bonuses and fill damageInfo struct - // Dancing Rune Weapon... - if (m_caster->GetEntry() == 27893) + // Check damage immunity + if (unitTarget->IsImmunedToDamage(caster, m_spellInfo)) { - if (Unit* owner = m_caster->GetOwner()) - owner->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); + m_damage = 0; + + // no packet found in sniffs } else - caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); - - // xinef: override miss info after absorb / block calculations - if (missInfo == SPELL_MISS_NONE && damageInfo.damage == 0) { - //if (damageInfo.absorb > 0) - // missInfo = SPELL_MISS_ABSORB; - if (damageInfo.blocked) - missInfo = SPELL_MISS_BLOCK; - } - - // Xinef: override with forced crit, only visual result - if (GetSpellValue()->ForcedCritResult) - { - damageInfo.HitInfo |= SPELL_HIT_TYPE_CRIT; - } - - Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); - - // xinef: health leech handling - if (m_spellInfo->HasEffect(SPELL_EFFECT_HEALTH_LEECH)) - { - uint8 effIndex = EFFECT_0; - for (; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_HEALTH_LEECH) - break; - - float healMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this); - - // get max possible damage, don't count overkill for heal - uint32 healthGain = uint32(-unitTarget->GetHealthGain(-int32(damageInfo.damage)) * healMultiplier); - - if (m_caster->IsAlive()) + // Add bonuses and fill damageInfo struct + // Dancing Rune Weapon... + if (m_caster->GetEntry() == 27893) { - healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL, effIndex); - healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL); - - HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellInfo->GetSchoolMask()); - m_caster->HealBySpell(healInfo); + if (Unit* owner = m_caster->GetOwner()) + owner->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); } + else + caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit); + + // xinef: override miss info after absorb / block calculations + if (missInfo == SPELL_MISS_NONE && damageInfo.damage == 0) + { + //if (damageInfo.absorb > 0) + // missInfo = SPELL_MISS_ABSORB; + if (damageInfo.blocked) + missInfo = SPELL_MISS_BLOCK; + } + + // Xinef: override with forced crit, only visual result + if (GetSpellValue()->ForcedCritResult) + { + damageInfo.HitInfo |= SPELL_HIT_TYPE_CRIT; + } + + Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); + + // xinef: health leech handling + if (m_spellInfo->HasEffect(SPELL_EFFECT_HEALTH_LEECH)) + { + uint8 effIndex = EFFECT_0; + for (; effIndex < MAX_SPELL_EFFECTS; ++effIndex) + if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_HEALTH_LEECH) + break; + + float healMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this); + + // get max possible damage, don't count overkill for heal + uint32 healthGain = uint32(-unitTarget->GetHealthGain(-int32(damageInfo.damage)) * healMultiplier); + + if (m_caster->IsAlive()) + { + healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL, effIndex); + healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL); + + HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellInfo->GetSchoolMask()); + m_caster->HealBySpell(healInfo); + } + } + + // Send log damage message to client + caster->SendSpellNonMeleeDamageLog(&damageInfo); + // Xinef: send info to target about reflect + if (reflectedSpell) + effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit); + + procVictim |= PROC_FLAG_TAKEN_DAMAGE; + + caster->DealSpellDamage(&damageInfo, true, this); + + // do procs after damage, eg healing effects + // no need to check if target is alive, done in procdamageandspell + + // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) + if (canEffectTrigger) + { + DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, missInfo); + uint32 hitMask = m_procEx | dmgInfo.GetHitMask(); + Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo, + m_triggeredByAuraSpell.effectIndex, this, &dmgInfo); + + if (caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 && + m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) + caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, dmgInfo.GetHitMask()); + } + + m_damage = damageInfo.damage; } - - // Send log damage message to client - caster->SendSpellNonMeleeDamageLog(&damageInfo); - // Xinef: send info to target about reflect - if (reflectedSpell) - effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit); - - procVictim |= PROC_FLAG_TAKEN_DAMAGE; - - caster->DealSpellDamage(&damageInfo, true, this); - - // do procs after damage, eg healing effects - // no need to check if target is alive, done in procdamageandspell - - // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) - if (canEffectTrigger) - { - DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, missInfo); - uint32 hitMask = m_procEx | dmgInfo.GetHitMask(); - Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo, - m_triggeredByAuraSpell.effectIndex, this, &dmgInfo); - - if (caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 && - m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)) - caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, dmgInfo.GetHitMask()); - } - - m_damage = damageInfo.damage; } // Passive spell hits/misses or active spells only misses (only triggers) else @@ -2887,16 +2899,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (!m_triggeredByAuraSpell.spellInfo || m_damage || (!(m_triggeredByAuraSpell.spellInfo->Effects[m_triggeredByAuraSpell.effectIndex].TriggerSpell == m_spellInfo->Id) && !(m_triggeredByAuraSpell.spellInfo->IsAuraEffectEqual(m_spellInfo)))) m_caster->AtTargetAttacked(effectUnit, !(m_spellInfo->HasAttribute(SPELL_ATTR1_NO_THREAT) || m_spellInfo->HasAttribute(SPELL_ATTR3_SUPPRESS_TARGET_PROCS))); - // Unsure if there are more spells that are not supposed to stop enemy from - // regenerating HP from food, so for now it stays as an ID. - const uint32 SPELL_PREMEDITATION = 14183; - if (m_spellInfo->Id != SPELL_PREMEDITATION) - { - if (!effectUnit->IsStandState()) - { - effectUnit->SetStandState(UNIT_STAND_STATE_STAND); - } - } + // Spells that don't initiate combat shouldn't affect stand state (e.g. Premeditation, Storm Giant/Felreaver Passive) + if (m_spellInfo->HasInitialAggro() && !effectUnit->IsStandState()) + effectUnit->SetStandState(UNIT_STAND_STATE_STAND); } } @@ -2943,7 +2948,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA return SPELL_MISS_EVADE; // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case - if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(this)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this))) + if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(m_caster, m_spellInfo)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this))) { return SPELL_MISS_IMMUNE; } @@ -2954,7 +2959,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA { if (effectMask & (1 << effectNumber)) { - if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber)) + if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber, m_caster)) effectMask &= ~(1 << effectNumber); // Xinef: Buggs out polymorph // Xinef: And this is checked in MagicSpellHitResult, why we check resistance twice? @@ -3100,14 +3105,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA if (m_spellAura) { - // Prevent aura application if target is banished and immuned - if (m_targets.GetUnitTarget() && m_targets.GetUnitTarget()->IsImmunedToDamageOrSchool(m_spellAura->GetSpellInfo()) - && m_targets.GetUnitTarget()->HasUnitState(UNIT_STATE_ISOLATED)) - { - m_spellAura->Remove(); - return SPELL_MISS_IMMUNE; - } - // Set aura stack amount to desired value if (m_spellValue->AuraStackAmount > 1) { @@ -5610,7 +5607,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT } } -SpellCastResult Spell::CheckCast(bool strict) +SpellCastResult Spell::CheckCast(bool strict, uint32* /*param1*/, uint32* /*param2*/) { // check death state if (!m_caster->IsAlive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !(m_spellInfo->HasAttribute(SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) @@ -6019,10 +6016,9 @@ SpellCastResult Spell::CheckCast(bool strict) // xinef: Enraged Regeneration: While this is active, the warrior is blocked from using abilities that trigger being enraged (which would do nothing and waste the cooldowns). if (m_spellInfo->Mechanic && m_spellInfo->IsSelfCast()) { - SpellImmuneList const& mechanicList = m_caster->m_spellImmune[IMMUNITY_MECHANIC]; - for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr) - if (itr->type == m_spellInfo->Mechanic) - return SPELL_FAILED_DAMAGE_IMMUNE; + auto const& mechanicList = m_caster->m_spellImmune[IMMUNITY_MECHANIC]; + if (mechanicList.count(m_spellInfo->Mechanic) > 0) + return SPELL_FAILED_DAMAGE_IMMUNE; } } @@ -7083,18 +7079,18 @@ SpellCastResult Spell::CheckRange(bool strict) else if (!m_caster->IsWithinCombatRange(target, max_range)) return SPELL_FAILED_OUT_OF_RANGE; //0x5A; - if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED && range_type == SPELL_RANGE_RANGED) - { - if (m_caster->IsWithinMeleeRange(target)) - return SPELL_FAILED_TOO_CLOSE; - } - if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target) && !m_caster->IsWithinBoundaryRadius(target)) return SPELL_FAILED_UNIT_NOT_INFRONT; } - // Xinef: check min range for self casts - if (min_range && range_type != SPELL_RANGE_RANGED && m_caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0 + // Check min range - for ranged spells, min range is the spell's min range + melee range (no leeway) + if (range_type == SPELL_RANGE_RANGED) + { + float minRangeCombined = min_range + m_caster->GetMeleeRange(target); + if (m_caster->IsWithinRange(target, minRangeCombined)) + return SPELL_FAILED_TOO_CLOSE; + } + else if (min_range > 0 && m_caster->IsWithinCombatRange(target, min_range)) return SPELL_FAILED_TOO_CLOSE; } @@ -7162,7 +7158,7 @@ SpellCastResult Spell::CheckPower() return SPELL_CAST_OK; } -SpellCastResult Spell::CheckItems() +SpellCastResult Spell::CheckItems(uint32* param1, uint32* param2) { Player* player = m_caster->ToPlayer(); if (!player) @@ -7570,21 +7566,29 @@ SpellCastResult Spell::CheckItems() } case SPELL_EFFECT_PROSPECTING: { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_CANT_BE_PROSPECTED; //ensure item is a prospectable ore - if (!(m_targets.GetItemTarget()->GetTemplate()->HasFlag(ITEM_FLAG_IS_PROSPECTABLE))) + if (!(item->GetTemplate()->HasFlag(ITEM_FLAG_IS_PROSPECTABLE))) return SPELL_FAILED_CANT_BE_PROSPECTED; //prevent prospecting in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != m_caster->GetGUID()) return SPELL_FAILED_CANT_BE_PROSPECTED; //Check for enough skill in jewelcrafting - uint32 item_prospectingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank; + uint32 item_prospectingskilllevel = item->GetTemplate()->RequiredSkillRank; if (item_prospectingskilllevel > player->GetSkillValue(SKILL_JEWELCRAFTING)) return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required ores in inventory - if (m_targets.GetItemTarget()->GetCount() < 5) + if (item->GetCount() < 5) + { + if (param1 && param2) + { + *param1 = item->GetEntry(); + *param2 = 5; + } return SPELL_FAILED_NEED_MORE_ITEMS; + } if (!LootTemplates_Prospecting.HaveLootFor(m_targets.GetItemTargetEntry())) return SPELL_FAILED_CANT_BE_PROSPECTED; @@ -7593,21 +7597,29 @@ SpellCastResult Spell::CheckItems() } case SPELL_EFFECT_MILLING: { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_CANT_BE_MILLED; //ensure item is a millable herb - if (!(m_targets.GetItemTarget()->GetTemplate()->HasFlag(ITEM_FLAG_IS_MILLABLE))) + if (!(item->GetTemplate()->HasFlag(ITEM_FLAG_IS_MILLABLE))) return SPELL_FAILED_CANT_BE_MILLED; //prevent milling in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != m_caster->GetGUID()) return SPELL_FAILED_CANT_BE_MILLED; //Check for enough skill in inscription - uint32 item_millingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank; + uint32 item_millingskilllevel = item->GetTemplate()->RequiredSkillRank; if (item_millingskilllevel > player->GetSkillValue(SKILL_INSCRIPTION)) return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required herbs in inventory - if (m_targets.GetItemTarget()->GetCount() < 5) + if (item->GetCount() < 5) + { + if (param1 && param2) + { + *param1 = item->GetEntry(); + *param2 = 5; + } return SPELL_FAILED_NEED_MORE_ITEMS; + } if (!LootTemplates_Milling.HaveLootFor(m_targets.GetItemTargetEntry())) return SPELL_FAILED_CANT_BE_MILLED; @@ -8296,6 +8308,13 @@ void Spell::HandleLaunchPhase() } } + if (m_originalCaster && target.missCondition != SPELL_MISS_EVADE) + { + Unit* targetUnit = m_caster->GetGUID() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID); + if (targetUnit && !m_originalCaster->IsFriendlyTo(targetUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || targetUnit->IsEngaged())) + m_originalCaster->SetInCombatWith(targetUnit, true); + } + DoAllEffectOnLaunchTarget(target, multiplier); } @@ -9060,8 +9079,8 @@ namespace Acore } WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList) - : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position) + Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason) + : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position), _searchReason(searchReason) { } @@ -9069,13 +9088,32 @@ namespace Acore { if (target->IsGameObject()) { - if (!target->ToGameObject()->IsInRange(_position->GetPositionX(), _position->GetPositionY(), _position->GetPositionZ(), _range)) + if (!target->ToGameObject()->IsInRange3d(_position->GetPositionX(), _position->GetPositionY(), _position->GetPositionZ(), _range)) return false; } else if (!target->IsWithinDist3d(_position, _range)) return false; - else if (target->IsCreature() && target->ToCreature()->IsAvoidingAOE()) // pussywizard - return false; + else if (Creature* c = target->ToCreature()) + { + if (c->IsAvoidingAOE()) // pussywizard + return false; + if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(c->GetCreatureTemplate()->CreatureImmunitiesId)) + { + switch (_searchReason) + { + case Acore::WorldObjectSpellAreaTargetSearchReason::Area: + if (immunities->ImmuneAoE) + return false; + break; + case Acore::WorldObjectSpellAreaTargetSearchReason::Chain: + if (immunities->ImmuneChain) + return false; + break; + default: + break; + } + } + } return WorldObjectSpellTargetCheck::operator ()(target); } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 700137f17..40ebfaa6f 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -38,6 +38,15 @@ class SpellEvent; class ByteBuffer; class BasicEvent; +namespace Acore +{ + enum class WorldObjectSpellAreaTargetSearchReason + { + Area, + Chain + }; +} + #define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILLISECONDS) #define TRAJECTORY_MISSILE_SIZE 3.0f @@ -444,7 +453,7 @@ public: template void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius); WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList = nullptr); - void SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList); + void SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason = Acore::WorldObjectSpellAreaTargetSearchReason::Area); void SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellTargetSelectionCategories selectCategory, ConditionList* condList, bool isChainHeal); SpellCastResult prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura = nullptr); @@ -460,7 +469,7 @@ public: void TakeReagents(); void TakeCastItem(); - SpellCastResult CheckCast(bool strict); + SpellCastResult CheckCast(bool strict, uint32* param1 = nullptr, uint32* param2 = nullptr); SpellCastResult CheckPetCast(Unit* target); // handlers @@ -472,7 +481,7 @@ public: void OnSpellLaunch(); - SpellCastResult CheckItems(); + SpellCastResult CheckItems(uint32* param1 = nullptr, uint32* param2 = nullptr); SpellCastResult CheckSpellFocus(); SpellCastResult CheckRange(bool strict); SpellCastResult CheckPower(); @@ -827,8 +836,9 @@ namespace Acore { float _range; Position const* _position; + Acore::WorldObjectSpellAreaTargetSearchReason _searchReason; WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList); + Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason = Acore::WorldObjectSpellAreaTargetSearchReason::Area); bool operator()(WorldObject* target); }; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 7c47c9d41..40edc0576 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -856,7 +856,7 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo(); // Pounce Bleed shouldn't be removed by Cloak of Shadows. - if (spell->GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED) + if (spell->GetAllEffectsMechanicMask() & (UI64LIT(1) << MECHANIC_BLEED)) return; bool dmgClassNone = false; @@ -1889,7 +1889,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) if (!unitTarget->IsAlive()) return; - if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED)) + if (unitTarget->IsImmunedToAuraPeriodicTick(m_caster, m_spellInfo)) { m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id); return; @@ -3681,7 +3681,7 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) if (!unitTarget || !unitTarget->IsAlive()) return; - if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED)) + if (unitTarget->IsImmunedToAuraPeriodicTick(m_caster, m_spellInfo)) { m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id); return; @@ -5152,7 +5152,7 @@ void Spell::EffectDispelMechanic(SpellEffIndex effIndex) continue; if (roll_chance_i(aura->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster)))) { - if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (1 << mechanic))) + if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (UI64LIT(1) << mechanic))) { dispel_list.push(std::make_pair(aura->GetId(), aura->GetCasterGUID())); @@ -5943,6 +5943,9 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* if (caster->IsTotem()) caster = caster->ToTotem()->GetOwner(); + else if (caster->IsPet()) + if (Unit* owner = caster->GetOwner()) + caster = owner; // in another case summon new uint8 summonLevel = caster->GetLevel(); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 86eb2d4dd..1775e9ec1 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -605,6 +605,14 @@ SpellTargetObjectTypes SpellEffectInfo::GetUsedTargetObjectType() const return _data[Effect].UsedTargetObjectType; } +ImmunityInfo const* SpellEffectInfo::GetImmunityInfo() const +{ + if (!_spellInfo) + return nullptr; + + return _spellInfo->GetImmunityInfo(EffectIndex); +} + std::array SpellEffectInfo::_data = { { // implicit target type used target object type @@ -915,6 +923,29 @@ bool SpellInfo::HasAreaAuraEffect() const return false; } +bool SpellInfo::HasOnlyDamageEffects() const +{ + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (Effects[i].IsEffect()) + { + switch (Effects[i].Effect) + { + case SPELL_EFFECT_SCHOOL_DAMAGE: + case SPELL_EFFECT_HEALTH_LEECH: + case SPELL_EFFECT_POWER_DRAIN: + case SPELL_EFFECT_POWER_BURN: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + continue; + default: + return false; + } + } + } + return true; +} + bool SpellInfo::IsExplicitDiscovery() const { return ((Effects[0].Effect == SPELL_EFFECT_CREATE_RANDOM_ITEM @@ -1328,54 +1359,25 @@ bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const return IsAffected(affectSpell->SpellFamilyName, mod->mask); } -bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const +bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const { - // aura can't be pierced - if (!aura || aura->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) - { - return false; - } - - // these spells pierce all avalible spells (Resurrection Sickness for example) - if (HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) + // Dispels other auras on immunity, check if this spell makes the unit immune to aura + if (HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT) && CanSpellProvideImmunityAgainstAura(auraSpellInfo)) return true; - // these spells (Cyclone for example) can pierce all... - if (HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) - { - if (aura->Mechanic != MECHANIC_IMMUNE_SHIELD && - aura->Mechanic != MECHANIC_INVULNERABILITY && - aura->Mechanic != MECHANIC_BANISH) - { - return true; - } - - } - return false; } -bool SpellInfo::CanDispelAura(SpellInfo const* aura) const +bool SpellInfo::CanDispelAura(SpellInfo const* auraSpellInfo) const { // Xinef: Passive auras cannot be dispelled - if (aura->IsPassive()) + if (auraSpellInfo->IsPassive()) return false; // These auras (like Divine Shield) can't be dispelled - if (aura->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) + if (auraSpellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) return false; - // These spells (like Mass Dispel) can dispell all auras - if (HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) - return true; - - // These auras (Cyclone for example) are not dispelable - if ((aura->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && aura->Mechanic != MECHANIC_NONE) - || aura->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) - { - return false; - } - return true; } @@ -1666,6 +1668,12 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta if (AttributesEx & SPELL_ATTR1_ONLY_PEACEFUL_TARGETS && (unitTarget->IsInCombat() || unitTarget->IsPetInCombat())) return SPELL_FAILED_TARGET_AFFECTING_COMBAT; + if (HasAttribute(SPELL_ATTR3_NOT_ON_AOE_IMMUNE)) + if (auto creature = unitTarget->ToCreature()) + if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(creature->GetCreatureTemplate()->CreatureImmunitiesId)) + if (immunities->ImmuneAoE) + return SPELL_FAILED_BAD_TARGETS; + // only spells with SPELL_ATTR3_ONLY_ON_GHOSTS can target ghosts if (IsRequiringDeadTarget()) { @@ -1888,35 +1896,35 @@ SpellSchoolMask SpellInfo::GetSchoolMask() const return SpellSchoolMask(SchoolMask); } -uint32 SpellInfo::GetAllEffectsMechanicMask() const +uint64 SpellInfo::GetAllEffectsMechanicMask() const { - uint32 mask = 0; + uint64 mask = 0; if (Mechanic) - mask |= 1 << Mechanic; + mask |= UI64LIT(1) << Mechanic; for (int i = 0; i < MAX_SPELL_EFFECTS; ++i) if (Effects[i].IsEffect() && Effects[i].Mechanic) - mask |= 1 << Effects[i].Mechanic; + mask |= UI64LIT(1) << Effects[i].Mechanic; return mask; } -uint32 SpellInfo::GetEffectMechanicMask(uint8 effIndex) const +uint64 SpellInfo::GetEffectMechanicMask(uint8 effIndex) const { - uint32 mask = 0; + uint64 mask = 0; if (Mechanic) - mask |= 1 << Mechanic; + mask |= UI64LIT(1) << Mechanic; if (Effects[effIndex].IsEffect() && Effects[effIndex].Mechanic) - mask |= 1 << Effects[effIndex].Mechanic; + mask |= UI64LIT(1) << Effects[effIndex].Mechanic; return mask; } -uint32 SpellInfo::GetSpellMechanicMaskByEffectMask(uint32 effectMask) const +uint64 SpellInfo::GetSpellMechanicMaskByEffectMask(uint32 effectMask) const { - uint32 mask = 0; + uint64 mask = 0; if (Mechanic) - mask |= 1 << Mechanic; + mask |= UI64LIT(1) << Mechanic; for (int i = 0; i < MAX_SPELL_EFFECTS; ++i) if ((effectMask & (1 << i)) && Effects[i].Mechanic) - mask |= 1 << Effects[i].Mechanic; + mask |= UI64LIT(1) << Effects[i].Mechanic; return mask; } @@ -2000,9 +2008,13 @@ AuraStateType SpellInfo::LoadAuraState() const return AURA_STATE_ENRAGE; // Bleeding aura state - if (GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED) + if (GetAllEffectsMechanicMask() & (UI64LIT(1) << MECHANIC_BLEED)) return AURA_STATE_BLEEDING; + // Banished aura state + if (Mechanic == MECHANIC_BANISH) + return AURA_STATE_BANISHED; + if (GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (Effects[i].IsAura() && (Effects[i].ApplyAuraName == SPELL_AURA_MOD_STUN @@ -2213,6 +2225,492 @@ SpellSpecificType SpellInfo::LoadSpellSpecific() const return SPELL_SPECIFIC_NORMAL; } +// immunity helper functions moved from SpellAuraEffects and global switches +void SpellInfo::_LoadImmunityInfo() +{ + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + SpellEffectInfo& effectInfo = Effects[i]; + if (!effectInfo.Effect) + continue; + + uint32 schoolImmunityMask = 0; + uint32 applyHarmfulAuraImmunityMask = 0; + uint64 mechanicImmunityMask = 0; + uint32 dispelImmunityMask = 0; + uint32 damageImmunityMask = 0; + + int32 miscVal = effectInfo.MiscValue; + int32 amount = effectInfo.CalcValue(); + + ImmunityInfo& immuneInfo = _immunityInfo[i]; + + switch (effectInfo.ApplyAuraName) + { + case SPELL_AURA_MECHANIC_IMMUNITY_MASK: + { + switch (miscVal) + { + case 27: + mechanicImmunityMask |= (1ULL << MECHANIC_SILENCE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_SILENCE); + break; + case 96: + case 1615: + { + if (amount) + { + mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT) + | (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN) + | (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM) + | (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR) + | (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED) + | (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + } + break; + } + case 679: + { + if (Id == 57742) + { + mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT) + | (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN) + | (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM) + | (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR) + | (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED) + | (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + } + break; + } + case 1557: + { + if (Id == 64187) + { + mechanicImmunityMask |= (1ULL << MECHANIC_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + } + else + { + mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT) + | (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN) + | (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM) + | (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR) + | (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED) + | (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + } + break; + } + case 1614: + case 1694: + { + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT); + break; + } + case 1630: + { + if (Id == 64112) + { + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT); + } + else + { + mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT) + | (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN) + | (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM) + | (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR) + | (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED) + | (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + } + break; + } + case 477: + case 1733: + { + if (!amount) + { + mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT) + | (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN) + | (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM) + | (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR) + | (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED) + | (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN); + + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK); + immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK_DEST); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + } + break; + } + case 878: + { + if (Id == 66092) + { + mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_STUN) + | (1ULL << MECHANIC_DISORIENTED) | (1ULL << MECHANIC_FREEZE); + + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + } + break; + } + default: + break; + } + + if (immuneInfo.AuraTypeImmune.empty()) + { + if (miscVal & (1 << 10)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN); + if (miscVal & (1 << 1)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_TRANSFORM); + + if (miscVal & (1 << 6)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED); + if (miscVal & (1 << 0)) + { + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT); + } + if (miscVal & (1 << 2)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE); + if (miscVal & (1 << 9)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR); + if (miscVal & (1 << 7)) + immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DISARM); + } + break; + } + case SPELL_AURA_MECHANIC_IMMUNITY: + { + switch (Id) + { + case 34471: // The Beast Within + case 19574: // Bestial Wrath + case 42292: // PvP trinket + case 46227: // Medallion of Immunity + case 59752: // Every Man for Himself + case 53490: // Bullheaded + case 65547: // PvP Trinket + case 134946: // Supremacy of the Alliance + case 134956: // Supremacy of the Horde + case 195710: // Honorable Medallion + case 208683: // Gladiator's Medallion + mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + break; + case 54508: // Demonic Empowerment + mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT) | (1ULL << MECHANIC_STUN); + break; + default: + if (miscVal < 1) + break; + + mechanicImmunityMask |= 1ULL << miscVal; + break; + } + break; + } + case SPELL_AURA_EFFECT_IMMUNITY: + { + immuneInfo.SpellEffectImmune.insert(static_cast(miscVal)); + break; + } + case SPELL_AURA_STATE_IMMUNITY: + { + immuneInfo.AuraTypeImmune.insert(static_cast(miscVal)); + break; + } + case SPELL_AURA_SCHOOL_IMMUNITY: + { + schoolImmunityMask |= uint32(miscVal); + break; + } + case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: + { + applyHarmfulAuraImmunityMask |= uint32(miscVal); + break; + } + case SPELL_AURA_DAMAGE_IMMUNITY: + { + damageImmunityMask |= uint32(miscVal); + break; + } + case SPELL_AURA_DISPEL_IMMUNITY: + { + dispelImmunityMask = uint32(miscVal); + break; + } + default: + break; + } + + immuneInfo.SchoolImmuneMask = schoolImmunityMask; + immuneInfo.ApplyHarmfulAuraImmuneMask = applyHarmfulAuraImmunityMask; + immuneInfo.MechanicImmuneMask = mechanicImmunityMask; + immuneInfo.DispelImmuneMask = dispelImmunityMask; + immuneInfo.DamageSchoolMask = damageImmunityMask; + } +} + +void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const* effect, bool apply) const +{ + ImmunityInfo const* immuneInfo = nullptr; + if (effect) + { + uint8 effIndex = effect->EffectIndex; + if (effIndex < MAX_SPELL_EFFECTS) + immuneInfo = &_immunityInfo[effIndex]; + } + + if (!immuneInfo) + return; + + if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) + { + target->ApplySpellImmune(Id, IMMUNITY_SCHOOL, schoolImmunity, apply); + + if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) +{ + target->RemoveAppliedAuras([this, target, schoolImmunity](AuraApplication const* aurApp) -> bool + { + SpellInfo const* auraSpellInfo = aurApp->GetBase()->GetSpellInfo(); + if (auraSpellInfo->Id == Id) // Don't remove self + return false; + if (auraSpellInfo->IsPassive()) // Don't remove passive auras + return false; + if (!(auraSpellInfo->GetSchoolMask() & schoolImmunity)) // Check for school mask + return false; + if (!CanDispelAura(auraSpellInfo)) + return false; + if (!HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)) + { + Unit* existingAuraCaster = aurApp->GetBase()->GetCaster(); + if (existingAuraCaster && existingAuraCaster->IsFriendlyTo(target)) // Don't remove friendly auras + return false; + } + return true; + }); + } + } + + if (uint64 mechanicImmunity = immuneInfo->MechanicImmuneMask) + { + for (uint32 i = 0; i < MAX_MECHANIC; ++i) + if (mechanicImmunity & (1ULL << i)) + target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply); + + if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) + target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id); + } + + if (uint32 dispelImmunity = immuneInfo->DispelImmuneMask) + { + target->ApplySpellImmune(Id, IMMUNITY_DISPEL, dispelImmunity, apply); + + if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) + { + target->RemoveAppliedAuras([dispelImmunity](AuraApplication const* aurApp) -> bool + { + SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo(); + if (spellInfo->Dispel == dispelImmunity) + return true; + + return false; + }); + } + } + + if (uint32 damageImmunity = immuneInfo->DamageSchoolMask) + target->ApplySpellImmune(Id, IMMUNITY_DAMAGE, damageImmunity, apply); + + for (AuraType auraType : immuneInfo->AuraTypeImmune) + { + target->ApplySpellImmune(Id, IMMUNITY_STATE, auraType, apply); + if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) + { + target->RemoveAppliedAuras([this, auraType](AuraApplication const* aurApp) -> bool + { + Aura const* aura = aurApp->GetBase(); + if (!aura->GetSpellInfo()->HasAura(auraType)) + return false; + return CanDispelAura(aura->GetSpellInfo()); + }); + } + } + + for (SpellEffects effectType : immuneInfo->SpellEffectImmune) + target->ApplySpellImmune(Id, IMMUNITY_EFFECT, effectType, apply); +} + +bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInfo) const +{ + if (!auraSpellInfo) + return false; + + for (SpellEffectInfo const& effectInfo : Effects) + { + if (!effectInfo.Effect) + continue; + + ImmunityInfo const* immuneInfo = effectInfo.GetImmunityInfo(); + if (!immuneInfo) + continue; + + if (!auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) + { + if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) + if ((auraSpellInfo->SchoolMask & schoolImmunity) != 0) + return true; + } + + if (uint64 mechanicImmunity = immuneInfo->MechanicImmuneMask) + if ((mechanicImmunity & (1ULL << auraSpellInfo->Mechanic)) != 0) + return true; + + if (uint32 dispelImmunity = immuneInfo->DispelImmuneMask) + if (auraSpellInfo->Dispel == dispelImmunity) + return true; + + bool immuneToAllEffects = true; + for (SpellEffectInfo const& auraSpellEffectInfo : auraSpellInfo->Effects) + { + if (!auraSpellEffectInfo.Effect) + continue; + + uint32 effectName = auraSpellEffectInfo.Effect; + if (!effectName) + continue; + + if (immuneInfo->SpellEffectImmune.find(static_cast(effectName)) == immuneInfo->SpellEffectImmune.cend()) + { + immuneToAllEffects = false; + break; + } + + if (uint32 mechanic = auraSpellEffectInfo.Mechanic) + { + if (!(immuneInfo->MechanicImmuneMask & (1ULL << mechanic))) + { + immuneToAllEffects = false; + break; + } + } + + if (uint32 auraName = auraSpellEffectInfo.ApplyAuraName) + { + bool isImmuneToAuraEffectApply = false; + if (immuneInfo->AuraTypeImmune.find(static_cast(auraName)) != immuneInfo->AuraTypeImmune.cend()) + isImmuneToAuraEffectApply = true; + + if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(auraSpellEffectInfo.EffectIndex) && + !auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) + { + if (uint32 applyHarmfulAuraImmuneMask = immuneInfo->ApplyHarmfulAuraImmuneMask) + if ((auraSpellInfo->SchoolMask & applyHarmfulAuraImmuneMask) != 0) + isImmuneToAuraEffectApply = true; + } + + if (!isImmuneToAuraEffectApply) + { + immuneToAllEffects = false; + break; + } + } + } + + if (immuneToAllEffects) + return true; + } + + return false; +} + +// based on client sub_007FDFA0 +bool SpellInfo::CanSpellCastOverrideAuraEffect(AuraEffect const* aurEff) const +{ + if (!HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) + return false; + + if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) + return false; + + SpellEffectInfo const* aurEffInfo = &aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()]; + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + SpellEffectInfo const& effectInfo = Effects[i]; + if (!effectInfo.Effect) + continue; + + if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA) + continue; + + uint32 const miscValue = static_cast(effectInfo.MiscValue); + switch (effectInfo.ApplyAuraName) + { + case SPELL_AURA_STATE_IMMUNITY: + if (miscValue != aurEffInfo->ApplyAuraName) + continue; + break; + case SPELL_AURA_SCHOOL_IMMUNITY: + case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: + if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) || !(aurEff->GetSpellInfo()->SchoolMask & miscValue)) + continue; + break; + case SPELL_AURA_DISPEL_IMMUNITY: + if (miscValue != aurEff->GetSpellInfo()->Dispel) + continue; + break; + case SPELL_AURA_MECHANIC_IMMUNITY: + if (miscValue != aurEff->GetSpellInfo()->Mechanic) + { + if (miscValue != aurEffInfo->Mechanic) + continue; + } + break; + default: + continue; + } + + return true; + } + + return false; +} + float SpellInfo::GetMinRange(bool positive) const { if (!RangeEntry) diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 70f0097f7..b87b5e37d 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -22,6 +22,7 @@ #include "Object.h" #include "SharedDefines.h" #include "SpellAuraDefines.h" +#include #include "Util.h" class Unit; @@ -213,6 +214,26 @@ enum SpellCustomAttributes uint32 GetTargetFlagMask(SpellTargetObjectTypes objType); +struct SpellDiminishInfo +{ + DiminishingGroup DiminishGroup = DIMINISHING_NONE; + DiminishingReturnsType DiminishReturnType = DRTYPE_NONE; + int32 DiminishMaxLevel = 3; // DIMINISHING_LEVEL_IMMUNE + int32 DiminishDurationLimit = 0; +}; + +struct AC_GAME_API ImmunityInfo +{ + uint32 SchoolImmuneMask = 0; + uint32 ApplyHarmfulAuraImmuneMask = 0; + uint64 MechanicImmuneMask = 0; + uint32 DispelImmuneMask = 0; + uint32 DamageSchoolMask = 0; + + boost::container::flat_set AuraTypeImmune; + boost::container::flat_set SpellEffectImmune; +}; + class SpellImplicitTargetInfo { private: @@ -302,6 +323,8 @@ public: SpellEffectImplicitTargetTypes GetImplicitTargetType() const; SpellTargetObjectTypes GetUsedTargetObjectType() const; + [[nodiscard]] ImmunityInfo const* GetImmunityInfo() const; + private: struct StaticData { @@ -310,6 +333,7 @@ private: }; static std::array _data; + }; class AC_GAME_API SpellInfo @@ -412,6 +436,7 @@ public: bool HasAura(AuraType aura) const; bool HasAnyAura() const; bool HasAreaAuraEffect() const; + bool HasOnlyDamageEffects() const; inline bool HasAttribute(SpellAttr0 attribute) const { return (Attributes & attribute) != 0; } inline bool HasAttribute(SpellAttr1 attribute) const { return (AttributesEx & attribute) != 0; } @@ -473,8 +498,12 @@ public: bool IsAffectedBySpellMods() const; bool IsAffectedBySpellMod(SpellModifier const* mod) const; - bool CanPierceImmuneAura(SpellInfo const* aura) const; - bool CanDispelAura(SpellInfo const* aura) const; + bool CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const; + bool CanDispelAura(SpellInfo const* auraSpellInfo) const; + + void ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const* effect, bool apply) const; + bool CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInfo) const; + bool CanSpellCastOverrideAuraEffect(AuraEffect const* aurEff) const; bool IsSingleTarget() const; bool IsAuraExclusiveBySpecificWith(SpellInfo const* spellInfo) const; @@ -490,11 +519,12 @@ public: bool ValidateAttribute6SpellDamageMods(Unit const* caster, const AuraEffect* auraEffect, bool isDot) const; SpellSchoolMask GetSchoolMask() const; - uint32 GetAllEffectsMechanicMask() const; - uint32 GetEffectMechanicMask(uint8 effIndex) const; - uint32 GetSpellMechanicMaskByEffectMask(uint32 effectMask) const; + uint64 GetAllEffectsMechanicMask() const; + uint64 GetEffectMechanicMask(uint8 effIndex) const; + uint64 GetSpellMechanicMaskByEffectMask(uint32 effectMask) const; Mechanics GetEffectMechanic(uint8 effIndex) const; bool HasAnyEffectMechanic() const; + [[nodiscard]] ImmunityInfo const* GetImmunityInfo(uint8 effIndex) const { return effIndex < MAX_SPELL_EFFECTS ? &_immunityInfo[effIndex] : nullptr; } uint32 GetDispelMask() const; static uint32 GetDispelMask(DispelType type); uint32 GetExplicitTargetMask() const; @@ -541,7 +571,13 @@ public: // unloading helpers void _UnloadImplicitTargetConditionLists(); -private: + // immunity helpers + void _LoadImmunityInfo(); + + SpellDiminishInfo _diminishInfoNonTriggered; + SpellDiminishInfo _diminishInfoTriggered; + + ImmunityInfo _immunityInfo[MAX_SPELL_EFFECTS]; std::array& _GetEffects() { return Effects; } SpellEffectInfo& _GetEffect(SpellEffIndex index) { ASSERT(index < Effects.size()); return Effects[index]; } }; diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index d19308e56..f5d1d83e2 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -1695,12 +1695,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[0].TargetB = SpellImplicitTargetInfo(); }); - // Flame Breath - ApplySpellFix({ 47592 }, [](SpellInfo* spellInfo) - { - spellInfo->Effects[EFFECT_0].Amplitude = 200; - }); - // Skarvald, Charge ApplySpellFix({ 43651 }, [](SpellInfo* spellInfo) { diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 42bf4ed7d..af70ec147 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -16,6 +16,7 @@ */ #include "SpellMgr.h" +#include "Log.h" #include "BattlefieldMgr.h" #include "BattlegroundIC.h" #include "Chat.h" @@ -29,6 +30,7 @@ #include "Spell.h" #include "SpellAuraDefines.h" #include "SpellInfo.h" +#include "Tokenize.h" #include "World.h" bool IsPrimaryProfessionSkill(uint32 skill) @@ -53,6 +55,53 @@ bool IsPartOfSkillLine(uint32 skillId, uint32 spellId) return false; } +CreatureImmunities const* SpellMgr::GetCreatureImmunities(int32 creatureImmunitiesId) const +{ + return Acore::Containers::MapGetValuePtr(mCreatureImmunities, creatureImmunitiesId); +} + +void SpellMgr::LoadCreatureImmunities() +{ + mCreatureImmunities.clear(); + if (QueryResult result = WorldDatabase.Query("SELECT ID, SchoolMask, DispelTypeMask, MechanicsMask, Effects, Auras, ImmuneAoE, ImmuneChain FROM creature_immunities")) + { + do + { + Field* fields = result->Fetch(); + int32 id = fields[0].Get(); + uint8 school = fields[1].Get(); + uint16 dispelType = fields[2].Get(); + uint64 mechanics = fields[3].Get(); + CreatureImmunities& immunities = mCreatureImmunities[id]; + immunities.School = school; + immunities.DispelType = dispelType; + immunities.Mechanic = mechanics; + immunities.ImmuneAoE = fields[6].Get(); + immunities.ImmuneChain = fields[7].Get(); + { + std::string effects = fields[4].Get(); + for (std::string_view token : Acore::Tokenize(effects, ',', false)) + { + if (Optional val = Acore::StringTo(token); val && *val < uint32(TOTAL_SPELL_EFFECTS)) + immunities.Effect.push_back(SpellEffects(*val)); + else + LOG_ERROR("sql.sql", "Invalid effect type in `Effects` {} for creature immunities {}, skipped", token, id); + } + } + { + std::string auras = fields[5].Get(); + for (std::string_view token : Acore::Tokenize(auras, ',', false)) + { + if (Optional val = Acore::StringTo(token); val && *val < TOTAL_AURAS) + immunities.Aura.push_back(AuraType(*val)); + else + LOG_ERROR("sql.sql", "Invalid aura type in `Auras` {} for creature immunities {}, skipped", token, id); + } + } + } while (result->NextRow()); + } +} + DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered) { if (spellproto->IsPositive()) @@ -212,29 +261,29 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, } // Lastly - Set diminishing depending on mechanic - uint32 mechanic = spellproto->GetAllEffectsMechanicMask(); - if (mechanic & (1 << MECHANIC_CHARM)) + uint64 mechanic = spellproto->GetAllEffectsMechanicMask(); + if (mechanic & (1ULL << MECHANIC_CHARM)) return DIMINISHING_MIND_CONTROL; - if (mechanic & (1 << MECHANIC_SILENCE)) + if (mechanic & (1ULL << MECHANIC_SILENCE)) return DIMINISHING_SILENCE; - if (mechanic & (1 << MECHANIC_SLEEP)) + if (mechanic & (1ULL << MECHANIC_SLEEP)) return DIMINISHING_SLEEP; - if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE))) + if (mechanic & ((1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_SHACKLE))) return DIMINISHING_DISORIENT; // Mechanic Knockout, except Blast Wave - if (mechanic & (1 << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292) + if (mechanic & (1ULL << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292) return DIMINISHING_DISORIENT; - if (mechanic & (1 << MECHANIC_DISARM)) + if (mechanic & (1ULL << MECHANIC_DISARM)) return DIMINISHING_DISARM; - if (mechanic & (1 << MECHANIC_FEAR)) + if (mechanic & (1ULL << MECHANIC_FEAR)) return DIMINISHING_FEAR; - if (mechanic & (1 << MECHANIC_STUN)) + if (mechanic & (1ULL << MECHANIC_STUN)) return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN; - if (mechanic & (1 << MECHANIC_BANISH)) + if (mechanic & (1ULL << MECHANIC_BANISH)) return DIMINISHING_BANISH; - if (mechanic & (1 << MECHANIC_ROOT)) + if (mechanic & (1ULL << MECHANIC_ROOT)) return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT; - if (mechanic & (1 << MECHANIC_HORROR)) + if (mechanic & (1ULL << MECHANIC_HORROR)) return DIMINISHING_HORROR; return DIMINISHING_NONE; @@ -2876,6 +2925,8 @@ void SpellMgr::LoadSpellInfoStore() } } + LoadCreatureImmunities(); + LOG_INFO("server.loading", ">> Loaded Spell Custom Attributes in {} ms", GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } @@ -3714,3 +3765,17 @@ void SpellMgr::LoadSpellInfoCustomAttributes() LOG_INFO("server.loading", ">> Loaded SpellInfo Custom Attributes in {} ms", GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } + +void SpellMgr::LoadSpellInfoImmunities() +{ + uint32 oldMSTime = getMSTime(); + + for (SpellInfo* spellInfo : mSpellInfoMap) + { + if (!spellInfo) + continue; + spellInfo->_LoadImmunityInfo(); + } + + LOG_INFO("server.loading", ">> Loaded SpellInfo immunity infos in {} ms", GetMSTimeDiffToNow(oldMSTime)); +} diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index ccedf1bff..32f34b030 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -25,6 +25,9 @@ #include "SharedDefines.h" #include "Unit.h" +#include +#include + class SpellInfo; class Player; class Unit; @@ -557,6 +560,18 @@ typedef std::multimap SpellsRequiringSpellMap; typedef std::pair SpellsRequiringSpellMapBounds; // Spell learning properties (accessed using SpellMgr functions) +struct CreatureImmunities +{ + std::bitset School; + std::bitset DispelType; + std::bitset Mechanic; + std::vector Effect; + std::vector Aura; + bool ImmuneAoE = false; + bool ImmuneChain = false; +}; + +typedef std::unordered_map CreatureImmunitiesMap; struct SpellLearnSkillNode { uint16 skill; @@ -634,6 +649,9 @@ private: public: static SpellMgr* instance(); + // creature immunity definitions loaded from DB + CreatureImmunities const* GetCreatureImmunities(int32 creatureImmunitiesId) const; + // Spell correctness for client using static bool ComputeIsSpellValid(SpellInfo const* spellInfo, bool msg = true); static bool IsSpellValid(SpellInfo const* spellInfo); @@ -772,10 +790,12 @@ public: void LoadPetDefaultSpells(); void LoadSpellAreas(); void LoadSpellInfoStore(); + void LoadCreatureImmunities(); void LoadSpellCooldownOverrides(); void UnloadSpellInfoStore(); void UnloadSpellInfoImplicitTargetConditionLists(); void LoadSpellInfoCustomAttributes(); + void LoadSpellInfoImmunities(); void LoadSpellInfoCorrections(); void LoadSpellSpecificAndAuraState(); void LoadSpellJumpDistances(); @@ -792,6 +812,7 @@ private: SpellGroupStackMap mSpellGroupStack; SameEffectStackMap mSpellSameEffectStack; SpellProcMap mSpellProcMap; + CreatureImmunitiesMap mCreatureImmunities; SpellBonusMap mSpellBonusMap; SpellThreatMap mSpellThreatMap; SpellMixologyMap mSpellMixologyMap; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 0e1295ddf..cc951abd0 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -60,7 +60,6 @@ #include "LootItemStorage.h" #include "LootMgr.h" #include "M2Stores.h" -#include "MMapFactory.h" #include "MapMgr.h" #include "Metric.h" #include "MotdMgr.h" @@ -140,7 +139,6 @@ World::~World() delete command; VMAP::VMapFactory::clear(); - MMAP::MMapFactory::clear(); } std::unique_ptr& getWorldInstance() @@ -285,9 +283,9 @@ void World::LoadConfigSettings(bool reload) } bool const enableIndoor = getBoolConfig(CONFIG_VMAP_INDOOR_CHECK); - bool const enableLOS = sConfigMgr->GetOption("vmap.enableLOS", true); + bool const enableLOS = getBoolConfig(CONFIG_VMAP_ENABLE_LOS); bool const enablePetLOS = getBoolConfig(CONFIG_PET_LOS); - bool const enableHeight = sConfigMgr->GetOption("vmap.enableHeight", true); + bool const enableHeight = getBoolConfig(CONFIG_VMAP_ENABLE_HEIGHT); if (!enableHeight) LOG_ERROR("server.loading", "VMap height checking disabled! Creatures movements and other various things WILL be broken! Expect no support."); @@ -295,8 +293,6 @@ void World::LoadConfigSettings(bool reload) VMAP::VMapFactory::createOrGetVMapMgr()->setEnableHeightCalc(enableHeight); LOG_INFO("server.loading", "WORLD: VMap support included. LineOfSight:{}, getHeight:{}, indoorCheck:{} PetLOS:{}", enableLOS, enableHeight, enableIndoor, enablePetLOS); - MMAP::MMapFactory::InitializeDisabledMaps(); - // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload); } @@ -393,17 +389,6 @@ void World::SetInitialWorldSettings() // Load IP Location Database sIPLocation->Load(); - std::vector mapIds; - for (auto const& map : sMapStore) - { - mapIds.emplace_back(map->MapID); - } - - vmmgr2->InitializeThreadUnsafe(mapIds); - - MMAP::MMapMgr* mmmgr = MMAP::MMapFactory::createOrGetMMapMgr(); - mmmgr->InitializeThreadUnsafe(mapIds); - LOG_INFO("server.loading", "Loading Game Graveyard..."); sGraveyard->LoadGraveyardFromDB(); @@ -437,6 +422,9 @@ void World::SetInitialWorldSettings() LOG_INFO("server.loading", "Loading Spell Jump Distances..."); sSpellMgr->LoadSpellJumpDistances(); + LOG_INFO("server.loading", "Loading SpellInfo Immunity infos..."); + sSpellMgr->LoadSpellInfoImmunities(); + LOG_INFO("server.loading", "Loading Player Totem models..."); sObjectMgr->LoadPlayerTotemModels(); @@ -792,6 +780,9 @@ void World::SetInitialWorldSettings() LOG_INFO("server.loading", "Loading Waypoints..."); sWaypointMgr->Load(); + LOG_INFO("server.loading", "Loading Waypoint Addons..."); + sWaypointMgr->LoadWaypointAddons(); + LOG_INFO("server.loading", "Loading SmartAI Waypoints..."); sSmartWaypointMgr->LoadFromDB(); diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index b30587974..131f734ad 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -523,6 +523,8 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT, "Respawn.DynamicMinimumGameObject", 10); SetConfigValue(CONFIG_VMAP_INDOOR_CHECK, "vmap.enableIndoorCheck", true); + SetConfigValue(CONFIG_VMAP_ENABLE_LOS, "vmap.enableLOS", true); + SetConfigValue(CONFIG_VMAP_ENABLE_HEIGHT, "vmap.enableHeight", true); SetConfigValue(CONFIG_PET_LOS, "vmap.petLOS", true); SetConfigValue(CONFIG_VMAP_BLIZZLIKE_PVP_LOS, "vmap.BlizzlikePvPLOS", true); @@ -591,6 +593,8 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_WINTERGRASP_NOBATTLETIME, "Wintergrasp.NoBattleTimer", 150); SetConfigValue(CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, "Wintergrasp.CrashRestartTimer", 10); + SetConfigValue(CONFIG_WINTERGRASP_SKIP_BATTLE_SESSION_COUNT, "Wintergrasp.SkipBattleSessionCount", 3500); + SetConfigValue(CONFIG_BIRTHDAY_TIME, "BirthdayTime", 1222964635); SetConfigValue(CONFIG_MINIGOB_MANABONK, "Minigob.Manabonk.Enable", true); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 0e4480af2..9626be8bf 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -80,6 +80,8 @@ enum ServerConfigs CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, CONFIG_CREATURE_REPOSITION_AGAINST_NPCS, CONFIG_VMAP_INDOOR_CHECK, + CONFIG_VMAP_ENABLE_LOS, + CONFIG_VMAP_ENABLE_HEIGHT, CONFIG_PET_LOS, CONFIG_START_CUSTOM_SPELLS, CONFIG_START_ALL_EXPLORED, @@ -327,6 +329,7 @@ enum ServerConfigs CONFIG_WINTERGRASP_BATTLETIME, CONFIG_WINTERGRASP_NOBATTLETIME, CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, + CONFIG_WINTERGRASP_SKIP_BATTLE_SESSION_COUNT, CONFIG_PACKET_SPOOF_BANMODE, CONFIG_PACKET_SPOOF_BANDURATION, CONFIG_WARDEN_CLIENT_RESPONSE_DELAY, diff --git a/src/server/scripts/Commands/cs_autobroadcast.cpp b/src/server/scripts/Commands/cs_autobroadcast.cpp new file mode 100644 index 000000000..27b5a8c20 --- /dev/null +++ b/src/server/scripts/Commands/cs_autobroadcast.cpp @@ -0,0 +1,208 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#include "AutobroadcastMgr.h" +#include "Chat.h" +#include "CommandScript.h" +#include "Config.h" +#include "Language.h" + +using namespace Acore::ChatCommands; + +class autobroadcast_commandscript : public CommandScript +{ +public: + autobroadcast_commandscript() : CommandScript("autobroadcast_commandscript") { } + + ChatCommandTable GetCommands() const override + { + static ChatCommandTable autobroadcastCommandTable = + { + { "list", HandleAutobroadcastListCommand, SEC_GAMEMASTER, Console::Yes }, + { "add", HandleAutobroadcastAddCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "locale", HandleAutobroadcastLocaleCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "remove", HandleAutobroadcastRemoveCommand, SEC_ADMINISTRATOR, Console::Yes } + }; + + static ChatCommandTable commandTable = + { + { "autobroadcast", autobroadcastCommandTable } + }; + + return commandTable; + } + + static bool HandleAutobroadcastListCommand(ChatHandler* handler) + { + uint32 realmId = sConfigMgr->GetOption("RealmID", 0); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST); + stmt->SetData(0, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (!result) + { + handler->SendSysMessage(LANG_AUTOBROADCAST_LIST_EMPTY); + return true; + } + + // Prefetch all locales and group by id + LoginDatabasePreparedStatement* localeStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED); + localeStmt->SetData(0, realmId); + PreparedQueryResult localeResult = LoginDatabase.Query(localeStmt); + + std::unordered_map>> localeMap; + if (localeResult) + { + do + { + Field* localeFields = localeResult->Fetch(); + uint32 localeId = localeFields[0].Get(); + std::string locale = localeFields[1].Get(); + std::string localeText = localeFields[2].Get(); + localeMap[localeId].emplace_back(std::move(locale), std::move(localeText)); + } while (localeResult->NextRow()); + } + + handler->SendSysMessage(LANG_AUTOBROADCAST_LIST_HEADER); + + do + { + Field* fields = result->Fetch(); + uint32 id = fields[0].Get(); + uint8 weight = fields[1].Get(); + std::string text = fields[2].Get(); + + handler->PSendSysMessage(LANG_AUTOBROADCAST_LIST_ENTRY, id, weight, text); + + auto itr = localeMap.find(id); + if (itr != localeMap.end()) + for (auto const& [locale, localeText] : itr->second) + handler->PSendSysMessage(LANG_AUTOBROADCAST_LOCALE_ENTRY, locale, localeText); + } while (result->NextRow()); + + return true; + } + + static bool HandleAutobroadcastAddCommand(ChatHandler* handler, uint8 weight, Tail text) + { + if (text.empty()) + return false; + + uint32 realmId = sConfigMgr->GetOption("RealmID", 0); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_AUTOBROADCAST); + stmt->SetData(0, realmId); + stmt->SetData(1, weight); + stmt->SetData(2, std::string(text)); + LoginDatabase.DirectExecute(stmt); + + // Retrieve the newly inserted ID + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST_MAX_ID); + stmt->SetData(0, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + uint32 newId = 0; + if (result) + newId = result->Fetch()[0].Get(); + + sAutobroadcastMgr->LoadAutobroadcasts(); + sAutobroadcastMgr->LoadAutobroadcastsLocalized(); + + handler->PSendSysMessage(LANG_AUTOBROADCAST_ADD_SUCCESS, newId); + return true; + } + + static bool HandleAutobroadcastLocaleCommand(ChatHandler* handler, uint32 id, std::string locale, Tail text) + { + if (text.empty()) + return false; + + if (!IsLocaleValid(locale)) + { + handler->SendErrorMessage(LANG_AUTOBROADCAST_INVALID_LOCALE); + return true; + } + + uint32 realmId = sConfigMgr->GetOption("RealmID", 0); + + // Verify the autobroadcast entry exists + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST_BY_ID); + stmt->SetData(0, id); + stmt->SetData(1, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (!result) + { + handler->SendErrorMessage(LANG_AUTOBROADCAST_NOT_FOUND, id); + return true; + } + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_AUTOBROADCAST_LOCALE); + stmt->SetData(0, realmId); + stmt->SetData(1, id); + stmt->SetData(2, locale); + stmt->SetData(3, std::string(text)); + LoginDatabase.DirectExecute(stmt); + + sAutobroadcastMgr->LoadAutobroadcasts(); + sAutobroadcastMgr->LoadAutobroadcastsLocalized(); + + handler->PSendSysMessage(LANG_AUTOBROADCAST_LOCALE_SUCCESS, locale, id); + return true; + } + + static bool HandleAutobroadcastRemoveCommand(ChatHandler* handler, uint32 id) + { + uint32 realmId = sConfigMgr->GetOption("RealmID", 0); + + // Verify the autobroadcast entry exists + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST_BY_ID); + stmt->SetData(0, id); + stmt->SetData(1, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (!result) + { + handler->SendErrorMessage(LANG_AUTOBROADCAST_NOT_FOUND, id); + return true; + } + + // Delete the autobroadcast entry + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_AUTOBROADCAST); + stmt->SetData(0, id); + stmt->SetData(1, realmId); + LoginDatabase.DirectExecute(stmt); + + // Delete associated locale entries + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_AUTOBROADCAST_LOCALE); + stmt->SetData(0, id); + stmt->SetData(1, realmId); + LoginDatabase.DirectExecute(stmt); + + sAutobroadcastMgr->LoadAutobroadcasts(); + sAutobroadcastMgr->LoadAutobroadcastsLocalized(); + + handler->PSendSysMessage(LANG_AUTOBROADCAST_REMOVE_SUCCESS, id); + return true; + } +}; + +void AddSC_autobroadcast_commandscript() +{ + new autobroadcast_commandscript(); +} diff --git a/src/server/scripts/Commands/cs_cheat.cpp b/src/server/scripts/Commands/cs_cheat.cpp index 84d22411e..2b067ab78 100644 --- a/src/server/scripts/Commands/cs_cheat.cpp +++ b/src/server/scripts/Commands/cs_cheat.cpp @@ -19,7 +19,6 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" -#include "WorldSession.h" using namespace Acore::ChatCommands; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index cd1fc6702..8c80529d9 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -15,25 +15,35 @@ * with this program. If not, see . */ +#include "ArenaTeamMgr.h" +#include "AuctionHouseMgr.h" #include "Bag.h" #include "BattlegroundMgr.h" #include "CellImpl.h" #include "Channel.h" +#include "CharacterCache.h" +#include "DatabaseEnv.h" #include "Chat.h" #include "CommandScript.h" #include "GridNotifiersImpl.h" +#include "GuildMgr.h" +#include "ItemTemplate.h" #include "LFGMgr.h" #include "Language.h" #include "Log.h" +#include "LootMgr.h" #include "M2Stores.h" #include "MapMgr.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "PoolMgr.h" +#include "RaceMgr.h" #include "ScriptMgr.h" #include "Transport.h" #include "Warden.h" +#include #include +#include #include using namespace Acore::ChatCommands; @@ -96,6 +106,7 @@ public: { "itemexpire", HandleDebugItemExpireCommand, SEC_ADMINISTRATOR, Console::No }, { "areatriggers", HandleDebugAreaTriggersCommand, SEC_ADMINISTRATOR, Console::No }, { "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::Yes}, + { "loot", HandleDebugLootCommand, SEC_GAMEMASTER, Console::Yes}, { "los", HandleDebugLoSCommand, SEC_ADMINISTRATOR, Console::No }, { "moveflags", HandleDebugMoveflagsCommand, SEC_ADMINISTRATOR, Console::No }, { "unitstate", HandleDebugUnitStateCommand, SEC_ADMINISTRATOR, Console::No }, @@ -104,6 +115,7 @@ public: { "mapdata", HandleDebugMapDataCommand, SEC_ADMINISTRATOR, Console::No }, { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No }, { "visibilitydata", HandleDebugVisibilityDataCommand, SEC_ADMINISTRATOR, Console::No }, + { "factionchange", HandleDebugFactionChangeCommand, SEC_ADMINISTRATOR, Console::Yes}, { "zonestats", HandleDebugZoneStatsCommand, SEC_MODERATOR, Console::Yes} }; static ChatCommandTable commandTable = @@ -137,7 +149,7 @@ public: handler->PSendSysMessage("{} waypoints dumped", flyByCameras->size()); } - handler->GetPlayer()->SendCinematicStart(cinematicId); + handler->GetPlayer()->GetCinematicMgr().StartCinematic(cinematicId); return true; } @@ -1030,6 +1042,22 @@ public: } handler->SendSysMessage("End of threatened by me list."); + + // Also show combat refs that may not appear in the threat list + // (e.g. creatures without threat lists like triggers/environmental hazards) + handler->PSendSysMessage("Combat refs (InCombat: {} | HasCombat: {})", target->IsInCombat(), target->GetCombatManager().HasCombat()); + for (auto const& ref : target->GetCombatManager().GetPvECombatRefs()) + { + Unit* unit = ref.second->GetOther(target); + handler->PSendSysMessage(" [PvE] {} ({}) Entry: {}", unit->GetName(), unit->GetGUID().ToString(), + unit->IsCreature() ? unit->ToCreature()->GetEntry() : 0); + } + for (auto const& ref : target->GetCombatManager().GetPvPCombatRefs()) + { + Unit* unit = ref.second->GetOther(target); + handler->PSendSysMessage(" [PvP] {} ({})", unit->GetName(), unit->GetGUID().ToString()); + } + return true; } @@ -1607,6 +1635,352 @@ public: handler->PSendSysMessage("Player count in zone {} ({}): {}.", zoneId, (zoneEntry ? zoneEntry->area_name[LOCALE_enUS] : ""), player->GetMap()->GetPlayerCountInZone(zoneId)); return true; } + + static std::string GetLootSourceName(std::string const& type, uint32 lootId) + { + if (type == "creature" || type == "skinning" || type == "pickpocketing") + { + if (CreatureTemplate const* ct = sObjectMgr->GetCreatureTemplate(lootId)) + return ct->Name; + } + else if (type == "gameobject") + { + if (GameObjectTemplate const* gt = sObjectMgr->GetGameObjectTemplate(lootId)) + return gt->name; + } + else if (type == "item" || type == "disenchant" || type == "prospecting" || type == "milling") + { + if (ItemTemplate const* it = sObjectMgr->GetItemTemplate(lootId)) + return it->Name1; + } + else if (type == "fishing") + { + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(lootId)) + return area->area_name[LOCALE_enUS]; + } + + return ""; + } + + static char const* GetItemQualityName(uint32 quality) + { + static char const* const qualityNames[MAX_ITEM_QUALITY] = + { + "Poor", "Normal", "Uncommon", "Rare", + "Epic", "Legendary", "Artifact", "Heirloom" + }; + + if (quality < MAX_ITEM_QUALITY) + return qualityNames[quality]; + return "Unknown"; + } + + static void GenerateLoot(Loot& loot, LootTemplate const* tab, + LootStore const& store, Player* player, std::string const& type, uint32 lootId) + { + loot.clear(); + loot.items.reserve(MAX_NR_LOOT_ITEMS); + loot.quest_items.reserve(MAX_NR_QUEST_ITEMS); + tab->Process(loot, store, LOOT_MODE_DEFAULT, player); + + if (type == "creature") + { + if (CreatureTemplate const* ct = sObjectMgr->GetCreatureTemplate(lootId)) + loot.generateMoneyLoot(ct->mingold, ct->maxgold); + } + else if (type == "gameobject") + { + if (GameObjectTemplateAddon const* addon = sObjectMgr->GetGameObjectTemplateAddon(lootId)) + loot.generateMoneyLoot(addon->mingold, addon->maxgold); + } + else if (type == "item") + { + if (ItemTemplate const* it = sObjectMgr->GetItemTemplate(lootId)) + loot.generateMoneyLoot(it->MinMoneyLoot, it->MaxMoneyLoot); + } + } + + static bool HandleDebugLootCommand(ChatHandler* handler, std::string type, uint32 lootId, Optional count) + { + static std::unordered_map const lootStoreMap = + { + { "creature", &LootTemplates_Creature }, + { "gameobject", &LootTemplates_Gameobject }, + { "fishing", &LootTemplates_Fishing }, + { "item", &LootTemplates_Item }, + { "pickpocketing", &LootTemplates_Pickpocketing }, + { "skinning", &LootTemplates_Skinning }, + { "disenchant", &LootTemplates_Disenchant }, + { "prospecting", &LootTemplates_Prospecting }, + { "milling", &LootTemplates_Milling }, + { "spell", &LootTemplates_Spell }, + { "reference", &LootTemplates_Reference }, + { "mail", &LootTemplates_Mail }, + { "player", &LootTemplates_Player } + }; + + // Lowercase the type for case-insensitive matching + std::transform(type.begin(), type.end(), type.begin(), ::tolower); + + auto itr = lootStoreMap.find(type); + if (itr == lootStoreMap.end()) + { + handler->SendErrorMessage(LANG_DEBUG_LOOT_INVALID_TYPE, type); + return false; + } + + LootStore const* store = itr->second; + + LootTemplate const* tab = store->GetLootFor(lootId); + if (!tab) + { + handler->SendErrorMessage(LANG_DEBUG_LOOT_NO_TEMPLATE, type, lootId); + return false; + } + + uint32 iterations = std::min(count.value_or(1), uint32(100)); + if (iterations == 0) + iterations = 1; + + Player* player = handler->GetPlayer(); + std::string sourceName = GetLootSourceName(type, lootId); + + // Single iteration - original behavior + if (iterations == 1) + { + Loot loot; + GenerateLoot(loot, tab, *store, player, type, lootId); + + handler->PSendSysMessage(LANG_DEBUG_LOOT_HEADER, type, sourceName, lootId); + + if (loot.items.empty() && loot.quest_items.empty() && loot.gold == 0) + { + handler->PSendSysMessage(LANG_DEBUG_LOOT_EMPTY); + return true; + } + + for (LootItem const& li : loot.items) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(li.itemid); + std::string name = proto ? proto->Name1 : "Unknown"; + char const* qualityName = GetItemQualityName(proto ? proto->Quality : 0); + handler->PSendSysMessage(LANG_DEBUG_LOOT_ITEM, + li.itemid, uint32(li.count), name, qualityName, + li.randomPropertyId, li.randomSuffix); + } + + for (LootItem const& li : loot.quest_items) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(li.itemid); + std::string name = proto ? proto->Name1 : "Unknown"; + char const* qualityName = GetItemQualityName(proto ? proto->Quality : 0); + handler->PSendSysMessage(LANG_DEBUG_LOOT_ITEM_QUEST, + li.itemid, uint32(li.count), name, qualityName); + } + + if (loot.gold > 0) + { + uint32 gold = loot.gold / 10000; + uint32 silver = (loot.gold % 10000) / 100; + uint32 copper = loot.gold % 100; + handler->PSendSysMessage(LANG_DEBUG_LOOT_GOLD, + loot.gold, gold, silver, copper); + } + + return true; + } + + // Multi iteration - aggregate results + struct ItemStats + { + uint32 totalCount = 0; + uint32 timesDropped = 0; + bool questItem = false; + }; + + std::map itemStats; + uint64 totalGold = 0; + + for (uint32 i = 0; i < iterations; ++i) + { + Loot loot; + GenerateLoot(loot, tab, *store, player, type, lootId); + + std::set seenThisRun; + + for (LootItem const& li : loot.items) + { + itemStats[li.itemid].totalCount += li.count; + if (seenThisRun.insert(li.itemid).second) + itemStats[li.itemid].timesDropped++; + } + + for (LootItem const& li : loot.quest_items) + { + itemStats[li.itemid].totalCount += li.count; + itemStats[li.itemid].questItem = true; + if (seenThisRun.insert(li.itemid).second) + itemStats[li.itemid].timesDropped++; + } + + totalGold += loot.gold; + } + + handler->PSendSysMessage(LANG_DEBUG_LOOT_HEADER_MULTI, + type, sourceName, lootId, iterations); + + if (itemStats.empty() && totalGold == 0) + { + handler->PSendSysMessage(LANG_DEBUG_LOOT_EMPTY); + return true; + } + + for (auto const& [itemId, stats] : itemStats) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + std::string name = proto ? proto->Name1 : "Unknown"; + char const* qualityName = GetItemQualityName(proto ? proto->Quality : 0); + uint32 dropPct = (stats.timesDropped * 10000) / iterations; + + if (stats.questItem) + handler->PSendSysMessage(LANG_DEBUG_LOOT_ITEM_QUEST_MULTI, + itemId, stats.totalCount, name, qualityName, + stats.timesDropped, iterations, dropPct / 100, dropPct % 100); + else + handler->PSendSysMessage(LANG_DEBUG_LOOT_ITEM_MULTI, + itemId, stats.totalCount, name, qualityName, + stats.timesDropped, iterations, dropPct / 100, dropPct % 100); + } + + if (totalGold > 0) + { + uint32 avgGold = static_cast(totalGold / iterations); + uint32 gold = avgGold / 10000; + uint32 silver = (avgGold % 10000) / 100; + uint32 copper = avgGold % 100; + handler->PSendSysMessage(LANG_DEBUG_LOOT_GOLD_MULTI, + avgGold, gold, silver, copper, iterations); + } + + return true; + } + + static bool HandleDebugFactionChangeCommand(ChatHandler* handler, Optional playerTarget) + { + if (!playerTarget) + playerTarget = PlayerIdentifier::FromTarget(handler); + + if (!playerTarget) + { + handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND); + return false; + } + + ObjectGuid guid = playerTarget->GetGUID(); + std::string name = playerTarget->GetName(); + + CharacterCacheEntry const* playerData = sCharacterCache->GetCharacterCacheByGuid(guid); + if (!playerData) + { + handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND); + return false; + } + + // Query at_login flags and money from the database (synchronous) + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY); + stmt->SetData(0, guid.GetCounter()); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + + if (!result) + { + handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND); + return false; + } + + Field* fields = result->Fetch(); + uint32 atLoginFlags = fields[0].Get(); + uint32 money = fields[2].Get(); + + // Header + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_HEADER, name, guid.GetCounter()); + + // AT_LOGIN flags + bool hasFactionFlag = (atLoginFlags & AT_LOGIN_CHANGE_FACTION) != 0; + bool hasRaceFlag = (atLoginFlags & AT_LOGIN_CHANGE_RACE) != 0; + + if (hasFactionFlag) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_FLAG_FACTION); + if (hasRaceFlag) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_FLAG_RACE); + if (!hasFactionFlag && !hasRaceFlag) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_FLAG_NONE); + + // Faction-change-only checks (guild, arena captain, mail, auctions) + if (hasFactionFlag) + { + // Guild check + if (playerData->GuildId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) + { + std::string guildName = sGuildMgr->GetGuildNameById(playerData->GuildId); + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_GUILD_FAIL, guildName); + } + else + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_GUILD_OK); + + // Arena team captain check + if (sArenaTeamMgr->GetArenaTeamByCaptain(guid)) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_ARENA_CAPTAIN_FAIL); + else + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_ARENA_CAPTAIN_OK); + + // Mail check + if (playerData->MailCount) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_MAIL_FAIL, playerData->MailCount); + else + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_MAIL_OK); + + // Auction check - mirrors CharacterHandler.cpp logic + // AH faction IDs: 0 = neutral, 12 = alliance, 29 = horde + bool hasAuctions = false; + for (uint8 i = 0; i < 2; ++i) + { + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap( + i == 0 ? 0 : (((1 << (playerData->Race - 1)) & sRaceMgr->GetAllianceRaceMask()) ? 12 : 29)); + + for (auto const& [auID, Aentry] : auctionHouse->GetAuctions()) + { + if (Aentry && (Aentry->owner == guid || Aentry->bidder == guid)) + { + hasAuctions = true; + break; + } + } + + if (hasAuctions) + break; + } + + if (hasAuctions) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_AUCTION_FAIL); + else + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_AUCTION_OK); + } + else + { + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_NA); + } + + // Gold limit check + uint32 maxMoney = sWorld->getIntConfig(CONFIG_CHANGE_FACTION_MAX_MONEY); + if (maxMoney && money > maxMoney) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_GOLD_FAIL, money, maxMoney); + else if (maxMoney) + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_GOLD_OK, money, maxMoney); + else + handler->PSendSysMessage(LANG_DEBUG_FACTIONCHANGE_GOLD_NOLIMIT, money); + + return true; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp index f38e387d3..2846f59a4 100644 --- a/src/server/scripts/Commands/cs_disable.cpp +++ b/src/server/scripts/Commands/cs_disable.cpp @@ -21,7 +21,6 @@ #include "Language.h" #include "ObjectMgr.h" #include "OutdoorPvP.h" -#include "Player.h" #include "SpellMgr.h" using namespace Acore::ChatCommands; diff --git a/src/server/scripts/Commands/cs_gear.cpp b/src/server/scripts/Commands/cs_gear.cpp index 1916359d7..b67fc456d 100644 --- a/src/server/scripts/Commands/cs_gear.cpp +++ b/src/server/scripts/Commands/cs_gear.cpp @@ -19,7 +19,6 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" -#include "WorldSession.h" using namespace Acore::ChatCommands; diff --git a/src/server/scripts/Commands/cs_honor.cpp b/src/server/scripts/Commands/cs_honor.cpp index 32cbb26a2..af83d0b9f 100644 --- a/src/server/scripts/Commands/cs_honor.cpp +++ b/src/server/scripts/Commands/cs_honor.cpp @@ -19,7 +19,6 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" -#include "WorldSession.h" using namespace Acore::ChatCommands; diff --git a/src/server/scripts/Commands/cs_inventory.cpp b/src/server/scripts/Commands/cs_inventory.cpp index 50490bb62..43fae2e91 100644 --- a/src/server/scripts/Commands/cs_inventory.cpp +++ b/src/server/scripts/Commands/cs_inventory.cpp @@ -19,7 +19,6 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" -#include "WorldSession.h" constexpr std::array bagSpecsToString = { diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp index 1d1825fa8..1b5cc4b89 100644 --- a/src/server/scripts/Commands/cs_learn.cpp +++ b/src/server/scripts/Commands/cs_learn.cpp @@ -37,8 +37,9 @@ public: { { "class", HandleLearnAllMyClassCommand, SEC_GAMEMASTER, Console::No }, { "pettalents", HandleLearnAllMyPetTalentsCommand, SEC_GAMEMASTER, Console::No }, - { "spells", HandleLearnAllMySpellsCommand, SEC_GAMEMASTER, Console::No }, - { "talents", HandleLearnAllMyTalentsCommand, SEC_GAMEMASTER, Console::No } + { "trainer", HandleLearnAllMyTrainerSpellsCommand, SEC_GAMEMASTER, Console::No }, + { "talents", HandleLearnAllMyTalentsCommand, SEC_GAMEMASTER, Console::No }, + { "quest", HandleLearnAllMyQuestSpells, SEC_GAMEMASTER, Console::No } }; static ChatCommandTable learnAllCommandTable = @@ -98,52 +99,57 @@ public: static bool HandleLearnAllMyClassCommand(ChatHandler* handler) { - HandleLearnAllMySpellsCommand(handler); + HandleLearnAllMyTrainerSpellsCommand(handler); HandleLearnAllMyTalentsCommand(handler); + HandleLearnAllMyQuestSpells(handler); return true; } - static bool HandleLearnAllMySpellsCommand(ChatHandler* handler) + static bool HandleLearnAllMyQuestSpells(ChatHandler* handler) { - ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(handler->GetSession()->GetPlayer()->getClass()); - if (!classEntry) - return true; - uint32 family = classEntry->spellfamily; - - for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) + Player* player = handler->GetPlayer(); + for (auto const& questPair : sObjectMgr->GetQuestTemplates()) { - SkillLineAbilityEntry const* entry = sSkillLineAbilityStore.LookupEntry(i); - if (!entry) - continue; - - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry->Spell); - if (!spellInfo) - continue; - - // skip server-side/triggered spells - if (spellInfo->SpellLevel == 0) - continue; - - // skip wrong class/race skills - if (!handler->GetSession()->GetPlayer()->IsSpellFitByClassAndRace(spellInfo->Id)) - continue; - - // skip other spell families - if (spellInfo->SpellFamilyName != family) - continue; - - // skip spells with first rank learned as talent (and all talents then also) - uint32 firstRank = sSpellMgr->GetFirstSpellInChain(spellInfo->Id); - if (GetTalentSpellCost(firstRank) > 0) - continue; - - // skip broken spells - if (!SpellMgr::IsSpellValid(spellInfo)) - continue; - - handler->GetSession()->GetPlayer()->learnSpell(spellInfo->Id); + Quest const* quest = questPair.second; + if (quest->GetRequiredClasses() && player->SatisfyQuestClass(quest, false)) + player->learnQuestRewardedSpells(quest); } + return true; + } + + static bool HandleLearnAllMyTrainerSpellsCommand(ChatHandler* handler) + { + if (!sChrClassesStore.LookupEntry(handler->GetSession()->GetPlayer()->getClass())) + return true; + + Player* player = handler->GetPlayer(); + std::vector const& trainers = sObjectMgr->GetClassTrainers(player->getClass()); + + bool hadNew; + do + { + hadNew = false; + for (Trainer::Trainer const* trainer : trainers) + { + if (!trainer->IsTrainerValidForPlayer(player)) + continue; + + for (Trainer::Spell const& trainerSpell : trainer->GetSpells()) + { + if (!trainer->CanTeachSpell(player, &trainerSpell)) + continue; + + if (trainerSpell.IsCastable()) + player->CastSpell(player, trainerSpell.SpellId, true); + else + player->learnSpell(trainerSpell.SpellId, false); + + hadNew = true; + } + } + } while (hadNew); + handler->SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS); return true; } diff --git a/src/server/scripts/Commands/cs_mail.cpp b/src/server/scripts/Commands/cs_mail.cpp new file mode 100644 index 000000000..6f622e2b3 --- /dev/null +++ b/src/server/scripts/Commands/cs_mail.cpp @@ -0,0 +1,389 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#include "Bag.h" +#include "CharacterCache.h" +#include "Chat.h" +#include "CommandScript.h" +#include "DatabaseEnv.h" +#include "GameTime.h" +#include "Language.h" +#include "Mail.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "Timer.h" + +using namespace Acore::ChatCommands; + +class mail_commandscript : public CommandScript +{ +public: + mail_commandscript() : CommandScript("mail_commandscript") { } + + ChatCommandTable GetCommands() const override + { + static ChatCommandTable mailCommandTable = + { + { "list", HandleMailListCommand, SEC_GAMEMASTER, Console::Yes }, + { "return", HandleMailReturnCommand, SEC_GAMEMASTER, Console::Yes } + }; + + static ChatCommandTable commandTable = + { + { "mail", mailCommandTable } + }; + + return commandTable; + } + + static char const* GetMailTypeString(uint8 messageType) + { + switch (messageType) + { + case MAIL_NORMAL: return "Normal"; + case MAIL_AUCTION: return "Auction"; + case MAIL_CREATURE: return "Creature"; + case MAIL_GAMEOBJECT: return "GameObject"; + case MAIL_CALENDAR: return "Calendar"; + default: return "Unknown"; + } + } + + static char const* GetMailStationeryString(uint8 stationery) + { + switch (stationery) + { + case MAIL_STATIONERY_TEST: return "Test"; + case MAIL_STATIONERY_DEFAULT: return "Default"; + case MAIL_STATIONERY_GM: return "GM"; + case MAIL_STATIONERY_AUCTION: return "Auction"; + case MAIL_STATIONERY_VAL: return "Valentine"; + case MAIL_STATIONERY_CHR: return "Christmas"; + case MAIL_STATIONERY_ORP: return "Orphan"; + default: return "Unknown"; + } + } + + static std::string GetItemListString(std::vector const& items) + { + if (items.empty()) + return "none"; + + std::string result; + for (size_t i = 0; i < items.size(); ++i) + { + if (i > 0) + result += ", "; + + result += Acore::StringFormat("{}(guid:{})", items[i].item_template, items[i].item_guid); + } + return result; + } + + static bool HandleMailListCommand(ChatHandler* handler, Optional target) + { + if (!target) + target = PlayerIdentifier::FromTargetOrSelf(handler); + + if (!target) + return false; + + Player* player = target->GetConnectedPlayer(); + + if (player) + { + PlayerMails const& mails = player->GetMails(); + + if (mails.empty()) + { + handler->PSendSysMessage(LANG_MAIL_LIST_EMPTY, handler->playerLink(target->GetName())); + return true; + } + + handler->PSendSysMessage(LANG_MAIL_LIST_HEADER, handler->playerLink(target->GetName())); + + for (Mail const* mail : mails) + { + std::string senderName; + if (mail->messageType == MAIL_NORMAL) + sCharacterCache->GetCharacterNameByGuid(ObjectGuid(HighGuid::Player, mail->sender), senderName); + + std::string expireStr = Acore::Time::TimeToTimestampStr(Seconds(mail->expire_time)); + std::string deliverStr = Acore::Time::TimeToTimestampStr(Seconds(mail->deliver_time)); + + handler->PSendSysMessage(LANG_MAIL_LIST_ENTRY, + mail->messageID, GetMailTypeString(mail->messageType), GetMailStationeryString(mail->stationery), + mail->mailTemplateId, mail->sender, senderName, mail->receiver, + expireStr, deliverStr, mail->money, mail->COD, mail->checked, GetItemListString(mail->items)); + } + } + else + { + ObjectGuid::LowType lowGuid = target->GetGUID().GetCounter(); + + QueryResult result = CharacterDatabase.Query( + "SELECT id, messageType, sender, receiver, subject, body, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId" + " FROM mail WHERE receiver = {} AND deliver_time <= {} ORDER BY id DESC", + lowGuid, GameTime::GetGameTime().count()); + + if (!result) + { + handler->PSendSysMessage(LANG_MAIL_LIST_EMPTY, handler->playerLink(target->GetName())); + return true; + } + + handler->PSendSysMessage(LANG_MAIL_LIST_HEADER, handler->playerLink(target->GetName())); + + do + { + Field* fields = result->Fetch(); + uint32 messageID = fields[0].Get(); + uint8 messageType = fields[1].Get(); + uint32 sender = fields[2].Get(); + uint32 receiver = fields[3].Get(); + // fields[4] = subject (skipped) + // fields[5] = body (skipped) + uint32 expireTime = fields[6].Get(); + uint32 deliverTime = fields[7].Get(); + uint32 money = fields[8].Get(); + uint32 cod = fields[9].Get(); + uint32 checked = fields[10].Get(); + uint8 stationery = fields[11].Get(); + uint16 mailTemplate = fields[12].Get(); + + std::string senderName; + if (messageType == MAIL_NORMAL) + sCharacterCache->GetCharacterNameByGuid(ObjectGuid(HighGuid::Player, sender), senderName); + + std::string expireStr = Acore::Time::TimeToTimestampStr(Seconds(expireTime)); + std::string deliverStr = Acore::Time::TimeToTimestampStr(Seconds(deliverTime)); + + // For offline players we don't have item details loaded + handler->PSendSysMessage(LANG_MAIL_LIST_ENTRY, + messageID, GetMailTypeString(messageType), GetMailStationeryString(stationery), + mailTemplate, sender, senderName, receiver, + expireStr, deliverStr, money, cod, checked, "N/A (offline)"); + } while (result->NextRow()); + } + + return true; + } + + static bool HandleMailReturnCommand(ChatHandler* handler, PlayerIdentifier target, uint32 mailId) + { + // Query mail data from DB so this works for offline players + QueryResult result = CharacterDatabase.Query( + "SELECT messageType, sender, receiver, subject, body, money, mailTemplateId, checked, deliver_time" + " FROM mail WHERE id = {}", mailId); + + if (!result) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_NOT_FOUND, mailId); + return true; + } + + Field* fields = result->Fetch(); + uint8 messageType = fields[0].Get(); + uint32 sender = fields[1].Get(); + uint32 receiver = fields[2].Get(); + std::string subject = fields[3].Get(); + std::string body = fields[4].Get(); + uint32 money = fields[5].Get(); + uint16 mailTemplate = fields[6].Get(); + uint32 checked = fields[7].Get(); + uint32 deliverTime = fields[8].Get(); + + // Reject undelivered mail, same as the core handler + if (deliverTime > GameTime::GetGameTime().count()) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_NOT_FOUND, mailId); + return true; + } + + // Verify the mail belongs to the target player + if (receiver != target.GetGUID().GetCounter()) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_NOT_FOUND, mailId); + return true; + } + + if (messageType != MAIL_NORMAL) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_NOT_NORMAL); + return true; + } + + if (!sender) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_NO_SENDER); + return true; + } + + if (checked & MAIL_CHECK_MASK_RETURNED) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_ALREADY_RETURNED); + return true; + } + + Player* player = target.GetConnectedPlayer(); + + // Run the same script hook as the client return handler, failing early before any deletions + if (player) + { + Mail* m = player->GetMail(mailId); + if (m) + { + ObjectGuid senderGuid = ObjectGuid(HighGuid::Player, sender); + + if (m->HasItems()) + { + for (auto const& itemInfo : m->items) + { + Item* item = player->GetMItem(itemInfo.item_guid); + if (item && !sScriptMgr->OnPlayerCanSendMail(player, senderGuid, ObjectGuid::Empty, subject, body, money, 0, item)) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_HOOK_BLOCKED); + return true; + } + } + } + else if (!sScriptMgr->OnPlayerCanSendMail(player, senderGuid, ObjectGuid::Empty, subject, body, money, 0, nullptr)) + { + handler->SendErrorMessage(LANG_MAIL_RETURN_HOOK_BLOCKED); + return true; + } + } + } + + // Same logic as WorldSession::HandleReturnToSender + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID); + stmt->SetData(0, mailId); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID); + stmt->SetData(0, mailId); + trans->Append(stmt); + + MailDraft draft(subject, body); + if (mailTemplate) + draft = MailDraft(mailTemplate, false); + + if (player) + { + // Online: same logic as WorldSession::HandleReturnToSender + // Get pointer before RemoveMail (which removes from deque but does not delete the object) + Mail* m = player->GetMail(mailId); + + player->RemoveMail(mailId); + + if (m && m->HasItems()) + { + for (auto const& itemInfo : m->items) + { + if (Item* item = player->GetMItem(itemInfo.item_guid)) + draft.AddItem(item); + + player->RemoveMItem(itemInfo.item_guid); + } + } + + delete m; + } + else + { + // Offline: load Item* objects from DB using same query shape as CHAR_SEL_MAILITEMS + // (LEFT JOIN to handle dangling mail_items) and same logic as Player::_LoadMailedItem + QueryResult itemResult = CharacterDatabase.Query( + "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments," + " randomPropertyId, durability, playedTime, text, mi.item_guid, itemEntry, ii.owner_guid" + " FROM mail_items mi LEFT JOIN item_instance ii ON mi.item_guid = ii.guid" + " WHERE mi.mail_id = {}", mailId); + + if (itemResult) + { + do + { + Field* itemFields = itemResult->Fetch(); + uint32 itemGuid = itemFields[11].Get(); + uint32 itemEntry = itemFields[12].Get(); + + // Handle dangling mail_items (missing item_instance) + if (!itemEntry) + { + LOG_ERROR("misc", "cs_mail: Mail #{} has dangling mail_items row for item_guid {}. Cleaning up.", mailId, itemGuid); + + CharacterDatabasePreparedStatement* delStmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM); + delStmt->SetData(0, itemGuid); + trans->Append(delStmt); + continue; + } + + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry); + if (!proto) + { + LOG_ERROR("misc", "cs_mail: Mail #{} has unknown item (entry: {}, guid: {}). Cleaning up.", mailId, itemEntry, itemGuid); + + CharacterDatabasePreparedStatement* delStmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM); + delStmt->SetData(0, itemGuid); + trans->Append(delStmt); + continue; + } + + Item* item = NewItemOrBag(proto); + ObjectGuid ownerGuid = itemFields[13].Get() + ? ObjectGuid::Create(itemFields[13].Get()) + : ObjectGuid::Empty; + + if (!item->LoadFromDB(itemGuid, ownerGuid, itemFields, itemEntry)) + { + LOG_ERROR("misc", "cs_mail: Item (GUID: {}) in mail #{} failed to load. Cleaning up.", itemGuid, mailId); + + CharacterDatabasePreparedStatement* delStmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM); + delStmt->SetData(0, itemGuid); + trans->Append(delStmt); + + item->FSetState(ITEM_REMOVED); + CharacterDatabaseTransaction nullTrans = CharacterDatabaseTransaction(nullptr); + item->SaveToDB(nullTrans); + return true; + } + + draft.AddItem(item); + } while (itemResult->NextRow()); + } + } + + uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(ObjectGuid(HighGuid::Player, receiver)); + draft.AddMoney(money).SendReturnToSender(accountId, receiver, sender, trans); + + CharacterDatabase.CommitTransaction(trans); + + sCharacterCache->DecreaseCharacterMailCount(ObjectGuid(HighGuid::Player, receiver)); + + handler->PSendSysMessage(LANG_MAIL_RETURN_SUCCESS, mailId, handler->playerLink(target.GetName())); + return true; + } +}; + +void AddSC_mail_commandscript() +{ + new mail_commandscript(); +} diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index cd543488a..bc7307086 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -36,7 +36,6 @@ #include "Language.h" #include "MapMgr.h" #include "MiscPackets.h" -#include "MMapFactory.h" #include "MovementGenerator.h" #include "ObjectAccessor.h" #include "Pet.h" @@ -630,7 +629,7 @@ public: uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; uint32 haveVMap = GridTerrainLoader::ExistVMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; - uint32 haveMMAP = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()) ? 1 : 0; + uint32 haveMMAP = handler->GetSession()->GetPlayer()->GetMap()->GetMapCollisionData().GetMMapData().GetNavMesh() ? 1 : 0; if (haveVMap) { @@ -2418,6 +2417,13 @@ public: // Output XX. LANG_PINFO_CHR_PLAYEDTIME handler->PSendSysMessage(LANG_PINFO_CHR_PLAYEDTIME, (secsToTimeString(totalPlayerTime, true))); + // Output XXI. LANG_PINFO_CHR_ONLINETIME (only for online players) + if (playerTarget) + { + uint32 onlineTime = uint32(GameTime::GetGameTime().count() - playerTarget->m_logintime); + handler->PSendSysMessage(LANG_PINFO_CHR_ONLINETIME, secsToTimeString(onlineTime, true)); + } + // Mail Data - an own query, because it may or may not be useful. // SQL: "SELECT SUM(CASE WHEN (checked & 1) THEN 1 ELSE 0 END) AS 'readmail', COUNT(*) AS 'totalmail' FROM mail WHERE `receiver` = ?" CharacterDatabasePreparedStatement* mailQuery = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PINFO_MAILS); diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp index f41c9cb1e..91a164a11 100644 --- a/src/server/scripts/Commands/cs_mmaps.cpp +++ b/src/server/scripts/Commands/cs_mmaps.cpp @@ -28,7 +28,6 @@ #include "CommandScript.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" -#include "MMapFactory.h" #include "MMapMgr.h" #include "Map.h" #include "PathGenerator.h" @@ -62,7 +61,8 @@ public: static bool HandleMmapPathCommand(ChatHandler* handler, Optional param) { - if (!MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) + Map* map = handler->GetSession()->GetPlayer()->GetMap(); + if (!map->GetMapCollisionData().GetMMapData().GetNavMesh()) { handler->PSendSysMessage("NavMesh not loaded for current map."); return true; @@ -173,9 +173,11 @@ public: handler->PSendSysMessage("gridloc [{}, {}]", gridCoord.x_coord, gridCoord.y_coord); + Map* map = handler->GetSession()->GetPlayer()->GetMap(); + // calculate navmesh tile location - dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); - dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId()); + dtNavMesh const* navmesh = map->GetMapCollisionData().GetMMapData().GetNavMesh(); + dtNavMeshQuery const* navmeshquery = map->GetMapCollisionData().GetMMapData().GetNavMeshQuery(); if (!navmesh || !navmeshquery) { handler->PSendSysMessage("NavMesh not loaded for current map."); @@ -225,9 +227,9 @@ public: static bool HandleMmapLoadedTilesCommand(ChatHandler* handler) { - uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId(); - dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMesh(mapid); - dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapMgr()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId()); + Map* map = handler->GetSession()->GetPlayer()->GetMap(); + dtNavMesh const* navmesh = map->GetMapCollisionData().GetMMapData().GetNavMesh(); + dtNavMeshQuery const* navmeshquery = map->GetMapCollisionData().GetMMapData().GetNavMeshQuery(); if (!navmesh || !navmeshquery) { handler->PSendSysMessage("NavMesh not loaded for current map."); @@ -250,12 +252,14 @@ public: static bool HandleMmapStatsCommand(ChatHandler* handler) { - handler->PSendSysMessage("mmap stats:"); - //handler->PSendSysMessage(" global mmap pathfinding is {}abled", sDisableMgr->IsPathfindingEnabled(mapId) ? "en" : "dis"); - MMAP::MMapMgr* manager = MMAP::MMapFactory::createOrGetMMapMgr(); - handler->PSendSysMessage(" {} maps loaded with {} tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount()); + Map* map = handler->GetSession()->GetPlayer()->GetMap(); - dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); + //handler->PSendSysMessage("mmap stats:"); + //handler->PSendSysMessage(" global mmap pathfinding is {}abled", sDisableMgr->IsPathfindingEnabled(mapId) ? "en" : "dis"); + //MMAP::MMapMgr* manager = MMAP::MMapFactory::createOrGetMMapMgr(); + //handler->PSendSysMessage(" {} maps loaded with {} tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount()); + + dtNavMesh const* navmesh = map->GetMapCollisionData().GetMMapData().GetNavMesh(); if (!navmesh) { handler->PSendSysMessage("NavMesh not loaded for current map."); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index ce6b78213..6eb781317 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -655,8 +655,17 @@ public: CreatureTemplate const* cInfo = target->GetCreatureTemplate(); uint32 faction = target->GetFaction(); uint32 npcflags = target->GetNpcFlags(); - uint32 mechanicImmuneMask = cInfo->MechanicImmuneMask; - uint32 spellSchoolImmuneMask = cInfo->SpellSchoolImmuneMask; + uint64 mechanicImmuneMask = 0; + uint32 spellSchoolImmuneMask = 0; + + if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(cInfo->CreatureImmunitiesId)) + { + mechanicImmuneMask = immunities->Mechanic.to_ullong(); + for (std::size_t j = 0; j < immunities->School.size(); ++j) + if (immunities->School[j]) + spellSchoolImmuneMask |= (1u << j); + } + uint32 displayid = target->GetDisplayId(); uint32 nativeid = target->GetNativeDisplayId(); uint32 entry = target->GetEntry(); @@ -673,6 +682,7 @@ public: int64 curRespawnDelay = target->GetRespawnTimeEx() - GameTime::GetGameTime().count(); if (curRespawnDelay < 0) curRespawnDelay = 0; + std::string curRespawnDelayStr = secsToTimeString(uint64(curRespawnDelay), true); std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(), true); @@ -689,31 +699,19 @@ public: handler->PSendSysMessage(LANG_NPCINFO_POSITION, float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ())); handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName(), target->GetScriptName()); - for (uint8 i = 0; i < NPCFLAG_COUNT; i++) - { - if (npcflags & npcFlagTexts[i].flag) - { - handler->PSendSysMessage(npcFlagTexts[i].text, npcFlagTexts[i].flag); - } - } + for (auto npcFlagText : npcFlagTexts) + if (npcflags & npcFlagText.flag) + handler->PSendSysMessage(npcFlagText.text, npcFlagText.flag); - handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, mechanicImmuneMask); + handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, Acore::StringFormat("0x{:X}", mechanicImmuneMask).c_str()); for (uint8 i = 1; i < MAX_MECHANIC; ++i) - { - if (mechanicImmuneMask & (1 << (mechanicImmunes[i].flag - 1))) - { + if (mechanicImmuneMask & (UI64LIT(1) << i)) handler->PSendSysMessage(mechanicImmunes[i].text, mechanicImmunes[i].flag); - } - } handler->PSendSysMessage(LANG_NPCINFO_SPELL_SCHOOL_IMMUNE, spellSchoolImmuneMask); - for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) - { - if (spellSchoolImmuneMask & (1 << spellSchoolImmunes[i].flag)) - { - handler->PSendSysMessage(spellSchoolImmunes[i].text, spellSchoolImmunes[i].flag); - } - } + for (auto spellSchoolImmune : spellSchoolImmunes) + if (spellSchoolImmuneMask & (1 << spellSchoolImmune.flag)) + handler->PSendSysMessage(spellSchoolImmune.text, spellSchoolImmune.flag); return true; } diff --git a/src/server/scripts/Commands/cs_script_loader.cpp b/src/server/scripts/Commands/cs_script_loader.cpp index 68bd683f7..a3e68d004 100644 --- a/src/server/scripts/Commands/cs_script_loader.cpp +++ b/src/server/scripts/Commands/cs_script_loader.cpp @@ -19,6 +19,7 @@ void AddSC_account_commandscript(); void AddSC_achievement_commandscript(); void AddSC_arena_commandscript(); +void AddSC_autobroadcast_commandscript(); void AddSC_bag_commandscript(); void AddSC_ban_commandscript(); void AddSC_bf_commandscript(); @@ -42,6 +43,7 @@ void AddSC_learn_commandscript(); void AddSC_lfg_commandscript(); void AddSC_list_commandscript(); void AddSC_lookup_commandscript(); +void AddSC_mail_commandscript(); void AddSC_message_commandscript(); void AddSC_misc_commandscript(); void AddSC_mmaps_commandscript(); @@ -73,6 +75,7 @@ void AddCommandsScripts() AddSC_account_commandscript(); AddSC_achievement_commandscript(); AddSC_arena_commandscript(); + AddSC_autobroadcast_commandscript(); AddSC_bag_commandscript(); AddSC_ban_commandscript(); AddSC_bf_commandscript(); @@ -96,6 +99,7 @@ void AddCommandsScripts() AddSC_lfg_commandscript(); AddSC_list_commandscript(); AddSC_lookup_commandscript(); + AddSC_mail_commandscript(); AddSC_message_commandscript(); AddSC_misc_commandscript(); AddSC_mmaps_commandscript(); diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp b/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp index 7020a484f..c823bb061 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp +++ b/src/server/scripts/EasternKingdoms/AlteracValley/boss_balinda.cpp @@ -43,12 +43,7 @@ enum Creatures struct boss_balinda : public ScriptedAI { boss_balinda(Creature* creature) : ScriptedAI(creature), summons(me), _hasCastIceBlock(false) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp index 14ab0e15e..55f33f152 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp @@ -18,6 +18,7 @@ #include "blackrock_depths.h" #include "AreaTriggerScript.h" #include "CreatureScript.h" +#include "GameObjectAI.h" #include "GameObjectScript.h" #include "GameTime.h" #include "Player.h" @@ -34,93 +35,76 @@ enum IronhandData constexpr Milliseconds IRONHAND_FLAMES_TIMER = 16s; constexpr Milliseconds IRONHAND_FLAMES_TIMER_RAND = 3s; -class go_shadowforge_brazier : public GameObjectScript +struct go_shadowforge_brazier : public GameObjectAI { -public: - go_shadowforge_brazier() : GameObjectScript("go_shadowforge_brazier") {} + go_shadowforge_brazier(GameObject* go) : GameObjectAI(go) {} - bool OnGossipHello(Player* /*player*/, GameObject* go) override + bool GossipHello(Player* /*player*/, bool /*reportUse*/) override { - if (InstanceScript* instance = go->GetInstanceScript()) + if (InstanceScript* instance = me->GetInstanceScript()) { - GameObject* northBrazier = ObjectAccessor::GetGameObject(*go, instance->GetGuidData(DATA_SF_BRAZIER_N)); - GameObject* southBrazier = ObjectAccessor::GetGameObject(*go, instance->GetGuidData(DATA_SF_BRAZIER_S)); + GameObject* northBrazier = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_SF_BRAZIER_N)); + GameObject* southBrazier = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_SF_BRAZIER_S)); if (!northBrazier || !southBrazier) - { return false; - } // should only happen on first brazier if (instance->GetData(TYPE_LYCEUM) == NOT_STARTED) - { instance->SetData(TYPE_LYCEUM, IN_PROGRESS); - } // Check if the opposite brazier is lit - if it is, open the gates. - if ((go->GetGUID() == northBrazier->GetGUID() && southBrazier->GetGoState() == GO_STATE_ACTIVE) || (go->GetGUID() == southBrazier->GetGUID() && northBrazier->GetGoState() == GO_STATE_ACTIVE)) + if ((me->GetGUID() == northBrazier->GetGUID() && southBrazier->GetGoState() == GO_STATE_ACTIVE) || (me->GetGUID() == southBrazier->GetGUID() && northBrazier->GetGoState() == GO_STATE_ACTIVE)) { instance->SetData(TYPE_LYCEUM, DONE); } return false; } return false; - }; + } }; -class ironhand_guardian : public CreatureScript +struct brd_ironhand_guardian : public CreatureAI { -public: - ironhand_guardian() : CreatureScript("brd_ironhand_guardian") {} + brd_ironhand_guardian(Creature* creature) : CreatureAI(creature) {} - CreatureAI* GetAI(Creature* creature) const override + void SetData(uint32 id, uint32 value) override { - return GetBlackrockDepthsAI(creature); + if (id == 0) + if (value == 0 || value == 1) + { + _flamesEnabled = (bool) (value); + events.ScheduleEvent(SPELL_GOUT_OF_FLAMES, urand(1, IRONHAND_N_GROUPS) * IRONHAND_FLAMES_TIMER / IRONHAND_N_GROUPS); + } } - struct ironhand_guardianAI : public CreatureAI + void UpdateAI(uint32 diff) override { - ironhand_guardianAI(Creature* creature) : CreatureAI(creature) {} - bool flames_enabled = false; + events.Update(diff); - void SetData(uint32 id, uint32 value) override + if (_flamesEnabled) { - if (id == 0) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - if (value == 0 || value == 1) + switch (eventId) { - flames_enabled = (bool) (value); - events.ScheduleEvent(SPELL_GOUT_OF_FLAMES, urand(1, IRONHAND_N_GROUPS) * IRONHAND_FLAMES_TIMER / IRONHAND_N_GROUPS); + case SPELL_GOUT_OF_FLAMES: + DoCast(SPELL_GOUT_OF_FLAMES); + events.RescheduleEvent(SPELL_GOUT_OF_FLAMES, IRONHAND_FLAMES_TIMER - IRONHAND_FLAMES_TIMER_RAND, IRONHAND_FLAMES_TIMER + IRONHAND_FLAMES_TIMER_RAND); + break; + default: + break; } } } + } - void UpdateAI(uint32 diff) override - { - events.Update(diff); - - if (flames_enabled) - { - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_GOUT_OF_FLAMES: - DoCast(SPELL_GOUT_OF_FLAMES); - events.RescheduleEvent(SPELL_GOUT_OF_FLAMES, IRONHAND_FLAMES_TIMER - IRONHAND_FLAMES_TIMER_RAND, IRONHAND_FLAMES_TIMER + IRONHAND_FLAMES_TIMER_RAND); - break; - default: - break; - } - } - } - } - EventMap events; - }; +private: + bool _flamesEnabled = false; + EventMap events; }; struct WaveCreature @@ -159,13 +143,10 @@ public: { time_t now = GameTime::GetGameTime().count(); if (instance->GetData(TYPE_RING_OF_LAW) == IN_PROGRESS || instance->GetData(TYPE_RING_OF_LAW) == DONE) - { return false; - } + if (now - instance->GetData(DATA_TIME_RING_FAIL) < 2 * 60) // in case of wipe, so people can rez. - { return false; - } instance->SetData(TYPE_RING_OF_LAW, IN_PROGRESS); return true; @@ -185,249 +166,233 @@ enum GrimstoneTexts SAY_TEXT6 = 5 }; -class npc_grimstone : public CreatureScript +struct npc_grimstone : public npc_escortAI { -public: - npc_grimstone() : CreatureScript("npc_grimstone") { } - - CreatureAI* GetAI(Creature* creature) const override + npc_grimstone(Creature* creature) : npc_escortAI(creature), summons(me) { - return GetBlackrockDepthsAI(creature); + instance = creature->GetInstanceScript(); + MobSpawnId = instance ? instance->GetData(DATA_ARENA_MOBS) : urand(0, 5); + BossSpawnId = instance ? instance->GetData(DATA_ARENA_BOSS) : urand(0, 5); + eventPhase = 0; + eventTimer = 1000; + resetTimer = 0; + theldrenEvent = false; + summons.DespawnAll(); } - struct npc_grimstoneAI : public npc_escortAI + InstanceScript* instance; + SummonList summons; + + uint8 eventPhase; + uint32 eventTimer; + uint32 resetTimer; + uint8 MobSpawnId; + uint8 BossSpawnId; + bool theldrenEvent; + + void Reset() override { - npc_grimstoneAI(Creature* creature) : npc_escortAI(creature), summons(me) + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + if (Unit* target = SelectTargetFromPlayerList(100.0f)) + summon->AI()->AttackStart(target); + } + + void SummonedCreatureDies(Creature* summon, Unit*) override + { + summons.Despawn(summon); + // All Summons killed, next phase + if (summons.empty()) { - instance = creature->GetInstanceScript(); - MobSpawnId = instance ? instance->GetData(DATA_ARENA_MOBS) : urand(0, 5); - BossSpawnId = instance ? instance->GetData(DATA_ARENA_BOSS) : urand(0, 5); - eventPhase = 0; - eventTimer = 1000; resetTimer = 0; - theldrenEvent = false; - summons.DespawnAll(); + eventTimer = 5000; } + } - InstanceScript* instance; - SummonList summons; - - uint8 eventPhase; - uint32 eventTimer; - uint32 resetTimer; - uint8 MobSpawnId; - uint8 BossSpawnId; - bool theldrenEvent; - - void Reset() override + using CreatureAI::WaypointReached; + void WaypointReached(uint32 waypointId) override + { + switch (waypointId) { - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (Unit* target = SelectTargetFromPlayerList(100.0f)) - summon->AI()->AttackStart(target); - } - - void SummonedCreatureDies(Creature* summon, Unit*) override - { - summons.Despawn(summon); - // All Summons killed, next phase - if (summons.empty()) - { - resetTimer = 0; + case 0: + Talk(SAY_TEXT1); + SetEscortPaused(true); eventTimer = 5000; - } + break; + case 1: + Talk(SAY_TEXT2); + SetEscortPaused(true); + eventTimer = 5000; + break; + case 2: + SetEscortPaused(true); + break; + case 3: + Talk(SAY_TEXT3); + break; + case 4: + Talk(SAY_TEXT4); + SetEscortPaused(true); + eventTimer = 5000; + break; + case 5: + if (instance) + { + me->GetMap()->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_GRIMSTONE, me); + instance->SetData(TYPE_RING_OF_LAW, DONE); + } + break; } + } - void WaypointReached(uint32 waypointId) override + void HandleGameObject(uint32 id, bool open) + { + instance->HandleGameObject(instance->GetGuidData(id), open); + } + + void SummonBoss() + { + if (me->FindNearestGameObject(GO_BANNER_OF_PROVOCATION, 100.0f)) { - switch (waypointId) + theldrenEvent = true; + me->SummonCreature(NPC_THELDREN, 644.300f, -175.989f, -53.739f, 3.418f, TEMPSUMMON_DEAD_DESPAWN, 0); + uint8 rand = urand(0, 4); + for (uint8 i = rand; i < rand + 4; ++i) + me->SummonCreature(theldrenTeam[i], 644.300f, -175.989f, -53.739f, 3.418f, TEMPSUMMON_DEAD_DESPAWN, 0); + } + else + me->SummonCreature(RingBoss[BossSpawnId], 644.300f, -175.989f, -53.739f, 3.418f, TEMPSUMMON_DEAD_DESPAWN, 0); + resetTimer = 30000; + } + + bool updateReset(uint32 diff) + { + // as long as the summoned creatures have someone to attack, we reset the timer. + // once they don't find anyone, the timer will count down until it is smaller than diff and reset. + bool doReset = false; + if (resetTimer > 0) + { + for (auto const& sum : summons) { - case 0: - Talk(SAY_TEXT1); - SetEscortPaused(true); - eventTimer = 5000; - break; - case 1: - Talk(SAY_TEXT2); - SetEscortPaused(true); - eventTimer = 5000; - break; - case 2: - SetEscortPaused(true); - break; - case 3: - Talk(SAY_TEXT3); - break; - case 4: - Talk(SAY_TEXT4); - SetEscortPaused(true); - eventTimer = 5000; - break; - case 5: - if (instance) + if (Creature* creature = ObjectAccessor::GetCreature(*me, sum)) + { + if (creature->IsAlive() && creature->GetVictim()) { - me->GetMap()->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, NPC_GRIMSTONE, me); - instance->SetData(TYPE_RING_OF_LAW, DONE); + resetTimer = 30000; + break; // only need to find one. } - break; + } } + + resetTimer -= diff; + if (resetTimer <= diff) + doReset = true; + } + return doReset; + } + + void SpawnWave(uint32 mobId) + { + for (uint32 i = 0; i < RingMobs[mobId].amount; i++) + me->SummonCreature(RingMobs[mobId].entry, 608.960f + 0.4f * i, -235.322f, -53.907f, 1.857f, TEMPSUMMON_DEAD_DESPAWN, 0); + resetTimer = 30000; + } + + void UpdateEscortAI(uint32 diff) override + { + if (!instance) + return; + + // reset if our mobs don't have a target. + if (updateReset(diff)) + { + summons.DespawnAll(); + HandleGameObject(DATA_ARENA4, true); + HandleGameObject(DATA_ARENA3, false); + HandleGameObject(DATA_ARENA2, false); + HandleGameObject(DATA_ARENA1, false); + instance->SetData(TYPE_RING_OF_LAW, FAIL); } - void HandleGameObject(uint32 id, bool open) + if (eventTimer) { - instance->HandleGameObject(instance->GetGuidData(id), open); - } - - void SummonBoss() - { - if (me->FindNearestGameObject(GO_BANNER_OF_PROVOCATION, 100.0f)) + if (eventTimer <= diff) { - theldrenEvent = true; - me->SummonCreature(NPC_THELDREN, 644.300f, -175.989f, -53.739f, 3.418f, TEMPSUMMON_DEAD_DESPAWN, 0); - uint8 rand = urand(0, 4); - for (uint8 i = rand; i < rand + 4; ++i) - me->SummonCreature(theldrenTeam[i], 644.300f, -175.989f, -53.739f, 3.418f, TEMPSUMMON_DEAD_DESPAWN, 0); + switch (eventPhase) + { + case 0: + Talk(SAY_TEXT5); + HandleGameObject(DATA_ARENA4, false); + me->SetWalk(true); + Start(false); + eventTimer = 0; + break; + case 1: + SetEscortPaused(false); + eventTimer = 0; + break; + case 2: + eventTimer = 2000; + break; + case 3: + HandleGameObject(DATA_ARENA1, true); + eventTimer = 3000; + break; + case 4: + SetEscortPaused(false); + me->SetVisible(false); + SpawnWave(MobSpawnId); // wave 1 + eventTimer = 15000; + break; + case 5: + SpawnWave(MobSpawnId); // wave 2 + eventTimer = 0; // will be set from SummonedCreatureDies + break; + case 6: + me->SetVisible(true); + HandleGameObject(DATA_ARENA1, false); + Talk(SAY_TEXT6); + SetEscortPaused(false); + eventTimer = 0; + break; + case 7: + HandleGameObject(DATA_ARENA2, true); + eventTimer = 5000; + break; + case 8: + me->SetVisible(false); + SummonBoss(); + eventTimer = 0; + break; + case 9: + if (theldrenEvent) + { + // All objects are removed from world once tempsummons despawn, so have a player spawn it instead. + Player* player = me->SelectNearestPlayer(100.0f); + if (GameObject* go = player->SummonGameObject(GO_ARENA_SPOILS, 596.48f, -187.91f, -54.14f, 4.9f, 0.0f, 0.0f, 0.0f, 0.0f, 300)) + go->SetOwnerGUID(ObjectGuid::Empty); + + Map::PlayerList const& pl = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) + itr->GetSource()->KilledMonsterCredit(16166); + } + + HandleGameObject(DATA_ARENA2, false); + HandleGameObject(DATA_ARENA3, true); + HandleGameObject(DATA_ARENA4, true); + SetEscortPaused(false); + break; + } + ++eventPhase; } else - me->SummonCreature(RingBoss[BossSpawnId], 644.300f, -175.989f, -53.739f, 3.418f, TEMPSUMMON_DEAD_DESPAWN, 0); - resetTimer = 30000; + eventTimer -= diff; } - - bool updateReset(uint32 diff) - { - // as long as the summoned creatures have someone to attack, we reset the timer. - // once they don't find anyone, the timer will count down until it is smaller than diff and reset. - bool doReset = false; - if (resetTimer > 0) - { - for (auto const& sum : summons) - { - if (Creature* creature = ObjectAccessor::GetCreature(*me, sum)) - { - if (creature->IsAlive() && creature->GetVictim()) - { - resetTimer = 30000; - break; // only need to find one. - } - } - } - - resetTimer -= diff; - if (resetTimer <= diff) - { - doReset = true; - } - } - return doReset; - } - - void SpawnWave(uint32 mobId) - { - for (uint32 i = 0; i < RingMobs[mobId].amount; i++) - { - me->SummonCreature(RingMobs[mobId].entry, 608.960f + 0.4f * i, -235.322f, -53.907f, 1.857f, TEMPSUMMON_DEAD_DESPAWN, 0); - } - resetTimer = 30000; - } - - void UpdateEscortAI(uint32 diff) override - { - if (!instance) - return; - - // reset if our mobs don't have a target. - if (updateReset(diff)) - { - summons.DespawnAll(); - HandleGameObject(DATA_ARENA4, true); - HandleGameObject(DATA_ARENA3, false); - HandleGameObject(DATA_ARENA2, false); - HandleGameObject(DATA_ARENA1, false); - instance->SetData(TYPE_RING_OF_LAW, FAIL); - } - - if (eventTimer) - { - if (eventTimer <= diff) - { - switch (eventPhase) - { - case 0: - Talk(SAY_TEXT5); - HandleGameObject(DATA_ARENA4, false); - me->SetWalk(true); - Start(false); - eventTimer = 0; - break; - case 1: - SetEscortPaused(false); - eventTimer = 0; - break; - case 2: - eventTimer = 2000; - break; - case 3: - HandleGameObject(DATA_ARENA1, true); - eventTimer = 3000; - break; - case 4: - SetEscortPaused(false); - me->SetVisible(false); - SpawnWave(MobSpawnId); // wave 1 - eventTimer = 15000; - break; - case 5: - SpawnWave(MobSpawnId); // wave 2 - eventTimer = 0; // will be set from SummonedCreatureDies - break; - case 6: - me->SetVisible(true); - HandleGameObject(DATA_ARENA1, false); - Talk(SAY_TEXT6); - SetEscortPaused(false); - eventTimer = 0; - break; - case 7: - HandleGameObject(DATA_ARENA2, true); - eventTimer = 5000; - break; - case 8: - me->SetVisible(false); - SummonBoss(); - eventTimer = 0; - break; - case 9: - if (theldrenEvent) - { - // All objects are removed from world once tempsummons despawn, so have a player spawn it instead. - Player* player = me->SelectNearestPlayer(100.0f); - if (GameObject* go = player->SummonGameObject(GO_ARENA_SPOILS, 596.48f, -187.91f, -54.14f, 4.9f, 0.0f, 0.0f, 0.0f, 0.0f, 300)) - { - go->SetOwnerGUID(ObjectGuid::Empty); - } - - Map::PlayerList const& pl = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - itr->GetSource()->KilledMonsterCredit(16166); - } - - HandleGameObject(DATA_ARENA2, false); - HandleGameObject(DATA_ARENA3, true); - HandleGameObject(DATA_ARENA4, true); - SetEscortPaused(false); - break; - } - ++eventPhase; - } - else - eventTimer -= diff; - } - } - }; + } }; // npc_phalanx @@ -438,67 +403,53 @@ enum PhalanxSpells SPELL_MIGHTYBLOW = 14099 }; -class npc_phalanx : public CreatureScript +struct npc_phalanx : public ScriptedAI { -public: - npc_phalanx() : CreatureScript("npc_phalanx") { } + npc_phalanx(Creature* creature) : ScriptedAI(creature) { } - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackrockDepthsAI(creature); + _thunderClapTimer = 12000; + _fireballVolleyTimer = 0; + _mightyBlowTimer = 15000; } - struct npc_phalanxAI : public ScriptedAI + void UpdateAI(uint32 diff) override { - npc_phalanxAI(Creature* creature) : ScriptedAI(creature) { } + if (!UpdateVictim()) + return; - uint32 ThunderClap_Timer; - uint32 FireballVolley_Timer; - uint32 MightyBlow_Timer; - - void Reset() override + if (_thunderClapTimer <= diff) { - ThunderClap_Timer = 12000; - FireballVolley_Timer = 0; - MightyBlow_Timer = 15000; + DoCastVictim(SPELL_THUNDERCLAP); + _thunderClapTimer = 10000; + } + else _thunderClapTimer -= diff; + + if (HealthBelowPct(51)) + { + if (_fireballVolleyTimer <= diff) + { + DoCastVictim(SPELL_FIREBALLVOLLEY); + _fireballVolleyTimer = 15000; + } + else _fireballVolleyTimer -= diff; } - void UpdateAI(uint32 diff) override + if (_mightyBlowTimer <= diff) { - //Return since we have no target - if (!UpdateVictim()) - return; - - //ThunderClap_Timer - if (ThunderClap_Timer <= diff) - { - DoCastVictim(SPELL_THUNDERCLAP); - ThunderClap_Timer = 10000; - } - else ThunderClap_Timer -= diff; - - //FireballVolley_Timer - if (HealthBelowPct(51)) - { - if (FireballVolley_Timer <= diff) - { - DoCastVictim(SPELL_FIREBALLVOLLEY); - FireballVolley_Timer = 15000; - } - else FireballVolley_Timer -= diff; - } - - //MightyBlow_Timer - if (MightyBlow_Timer <= diff) - { - DoCastVictim(SPELL_MIGHTYBLOW); - MightyBlow_Timer = 10000; - } - else MightyBlow_Timer -= diff; - - DoMeleeAttackIfReady(); + DoCastVictim(SPELL_MIGHTYBLOW); + _mightyBlowTimer = 10000; } - }; + else _mightyBlowTimer -= diff; + + DoMeleeAttackIfReady(); + } + +private: + uint32 _thunderClapTimer; + uint32 _fireballVolleyTimer; + uint32 _mightyBlowTimer; }; // npc_lokhtos_darkbargainer @@ -518,12 +469,11 @@ enum LokhtosSpells SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND = 23059 }; -class npc_lokhtos_darkbargainer : public CreatureScript +struct npc_lokhtos_darkbargainer : public ScriptedAI { -public: - npc_lokhtos_darkbargainer() : CreatureScript("npc_lokhtos_darkbargainer") { } + npc_lokhtos_darkbargainer(Creature* creature) : ScriptedAI(creature) { } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override + void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override { ClearGossipMenuFor(player); if (action == GOSSIP_ACTION_INFO_DEF + 1) @@ -532,32 +482,26 @@ public: player->CastSpell(player, SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND, false); } if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; + player->GetSession()->SendListInventory(me->GetGUID()); } - bool OnGossipHello(Player* player, Creature* creature) override + void sGossipHello(Player* player) override { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); + if (me->IsQuestGiver()) + player->PrepareQuestMenu(me->GetGUID()); - if (creature->IsVendor() && player->GetReputationRank(59) >= REP_FRIENDLY) + if (me->IsVendor() && player->GetReputationRank(59) >= REP_FRIENDLY) AddGossipItemFor(player, 4781, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); if (player->GetQuestRewardStatus(QUEST_A_BINDING_CONTRACT) != 1 && !player->HasItemCount(ITEM_THRORIUM_BROTHERHOOD_CONTRACT, 1, true) && player->HasItemCount(ITEM_SULFURON_INGOT)) - { AddGossipItemFor(player, 4781, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - } if (player->GetReputationRank(59) < REP_FRIENDLY) - SendGossipMenuFor(player, 3673, creature->GetGUID()); + SendGossipMenuFor(player, 3673, me->GetGUID()); else - SendGossipMenuFor(player, 3677, creature->GetGUID()); - - return true; + SendGossipMenuFor(player, 3677, me->GetGUID()); } }; @@ -577,19 +521,29 @@ enum RocknotQuests QUEST_ALE = 4295 }; -class npc_rocknot : public CreatureScript +struct npc_rocknot : public npc_escortAI { -public: - npc_rocknot() : CreatureScript("npc_rocknot") { } - - bool OnQuestReward(Player* /*player*/, Creature* creature, Quest const* quest, uint32 /*item*/) override + npc_rocknot(Creature* creature) : npc_escortAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() override + { + if (HasEscortState(STATE_ESCORT_ESCORTING)) + return; + + _breakKegTimer = 0; + _breakDoorTimer = 0; + } + + void sQuestReward(Player* /*player*/, Quest const* quest, uint32 /*opt*/) override { - InstanceScript* instance = creature->GetInstanceScript(); if (!instance) - return true; + return; if (instance->GetData(TYPE_BAR) == DONE || instance->GetData(TYPE_BAR) == SPECIAL) - return true; + return; if (quest->GetQuestId() == QUEST_ALE) { @@ -601,120 +555,93 @@ public: //keep track of amount in instance script, returns SPECIAL if amount ok and event in progress if (instance->GetData(TYPE_BAR) == SPECIAL) { - creature->AI()->Talk(SAY_GOT_BEER); - creature->CastSpell(creature, SPELL_DRUNKEN_RAGE, false); - - if (npc_escortAI* escortAI = CAST_AI(npc_rocknot::npc_rocknotAI, creature->AI())) - { - creature->SetWalk(true); - escortAI->Start(false); - } + Talk(SAY_GOT_BEER); + me->CastSpell(me, SPELL_DRUNKEN_RAGE, false); + me->SetWalk(true); + Start(false); } } - - return true; } - CreatureAI* GetAI(Creature* creature) const override + void DoGo(uint32 id, uint32 state) { - return GetBlackrockDepthsAI(creature); + if (GameObject* go = instance->instance->GetGameObject(instance->GetGuidData(id))) + go->SetGoState((GOState)state); } - struct npc_rocknotAI : public npc_escortAI + using CreatureAI::WaypointReached; + void WaypointReached(uint32 waypointId) override { - npc_rocknotAI(Creature* creature) : npc_escortAI(creature) + switch (waypointId) { - instance = creature->GetInstanceScript(); + case 1: + me->HandleEmoteCommand(EMOTE_ONESHOT_KICK); + break; + case 2: + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED); + break; + case 3: + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED); + break; + case 4: + me->HandleEmoteCommand(EMOTE_ONESHOT_KICK); + break; + case 5: + me->HandleEmoteCommand(EMOTE_ONESHOT_KICK); + _breakKegTimer = 2000; + break; } + } - InstanceScript* instance; - - uint32 BreakKeg_Timer; - uint32 BreakDoor_Timer; - - void Reset() override + void UpdateAI(uint32 diff) override + { + if (_breakKegTimer) { - if (HasEscortState(STATE_ESCORT_ESCORTING)) - return; - - BreakKeg_Timer = 0; - BreakDoor_Timer = 0; - } - - void DoGo(uint32 id, uint32 state) - { - if (GameObject* go = instance->instance->GetGameObject(instance->GetGuidData(id))) - go->SetGoState((GOState)state); - } - - void WaypointReached(uint32 waypointId) override - { - switch (waypointId) + if (_breakKegTimer <= diff) { - case 1: - me->HandleEmoteCommand(EMOTE_ONESHOT_KICK); - break; - case 2: - me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED); - break; - case 3: - me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK_UNARMED); - break; - case 4: - me->HandleEmoteCommand(EMOTE_ONESHOT_KICK); - break; - case 5: - me->HandleEmoteCommand(EMOTE_ONESHOT_KICK); - BreakKeg_Timer = 2000; - break; + DoGo(DATA_GO_BAR_KEG, 0); + _breakKegTimer = 0; + _breakDoorTimer = 1000; } + else _breakKegTimer -= diff; } - void UpdateAI(uint32 diff) override + if (_breakDoorTimer) { - if (BreakKeg_Timer) + if (_breakDoorTimer <= diff) { - if (BreakKeg_Timer <= diff) - { - DoGo(DATA_GO_BAR_KEG, 0); - BreakKeg_Timer = 0; - BreakDoor_Timer = 1000; - } - else BreakKeg_Timer -= diff; + DoGo(DATA_GO_BAR_DOOR, 2); + DoGo(DATA_GO_BAR_KEG_TRAP, 0); //doesn't work very well, leaving code here for future + //spell by trap has effect61, this indicate the bar go hostile + + if (Unit* tmp = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PHALANX))) + tmp->SetFaction(FACTION_MONSTER); + + //for later, this event(s) has alot more to it. + //optionally, DONE can trigger bar to go hostile. + instance->SetData(TYPE_BAR, DONE); + + _breakDoorTimer = 0; } - - if (BreakDoor_Timer) - { - if (BreakDoor_Timer <= diff) - { - DoGo(DATA_GO_BAR_DOOR, 2); - DoGo(DATA_GO_BAR_KEG_TRAP, 0); //doesn't work very well, leaving code here for future - //spell by trap has effect61, this indicate the bar go hostile - - if (Unit* tmp = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PHALANX))) - tmp->SetFaction(FACTION_MONSTER); - - //for later, this event(s) has alot more to it. - //optionally, DONE can trigger bar to go hostile. - instance->SetData(TYPE_BAR, DONE); - - BreakDoor_Timer = 0; - } - else BreakDoor_Timer -= diff; - } - - npc_escortAI::UpdateAI(diff); + else _breakDoorTimer -= diff; } - }; + + npc_escortAI::UpdateAI(diff); + } + +private: + InstanceScript* instance; + uint32 _breakKegTimer; + uint32 _breakDoorTimer; }; void AddSC_blackrock_depths() { - new go_shadowforge_brazier(); + RegisterBlackrockDepthsGameObjectAI(go_shadowforge_brazier); new at_ring_of_law(); - new npc_grimstone(); - new npc_phalanx(); - new npc_lokhtos_darkbargainer(); - new npc_rocknot(); - new ironhand_guardian(); + RegisterBlackrockDepthsCreatureAI(npc_grimstone); + RegisterBlackrockDepthsCreatureAI(npc_phalanx); + RegisterBlackrockDepthsCreatureAI(npc_lokhtos_darkbargainer); + RegisterBlackrockDepthsCreatureAI(npc_rocknot); + RegisterBlackrockDepthsCreatureAI(brd_ironhand_guardian); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h index 82c587049..03d6aac22 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h @@ -151,4 +151,7 @@ inline AI* GetBlackrockDepthsAI(T* obj) return GetInstanceAI(obj, BRDScriptName); } +#define RegisterBlackrockDepthsCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetBlackrockDepthsAI) +#define RegisterBlackrockDepthsGameObjectAI(ai_name) RegisterGameObjectAIWithFactory(ai_name, GetBlackrockDepthsAI) + #endif diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp index e60657290..e76062bd7 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp @@ -50,219 +50,184 @@ const Position SummonPositions[7] = std::vector gobjectDwarfRunesEntry { 170578, 170579, 170580, 170581, 170582, 170583, 170584 }; -class boss_ambassador_flamelash : public CreatureScript +struct boss_ambassador_flamelash : public BossAI { -public: - boss_ambassador_flamelash() : CreatureScript("boss_ambassador_flamelash") { } + boss_ambassador_flamelash(Creature* creature) : BossAI(creature, BOSS_AMBASSADOR_FLAMELASH) { } - CreatureAI* GetAI(Creature* creature) const override + void DoAction(int32 param) override { - return GetBlackrockDepthsAI(creature); + switch (param) + { + case EVENT_SUMMON_SPIRITS: + events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 12s, 14s); + break; + } } - struct boss_ambassador_flamelashAI : public BossAI + void Reset() override { - boss_ambassador_flamelashAI(Creature* creature) : BossAI(creature, BOSS_AMBASSADOR_FLAMELASH), summons(me) { } + _Reset(); + TurnRunes(false); + _foundValidPosition = false; + _validPosition.clear(); + } - EventMap _events; + void TurnRunes(bool mode) + { + // Active makes the runes burn, ready turns them off + GOState state = mode ? GO_STATE_ACTIVE : GO_STATE_READY; - // This will help reseting the boss - SummonList summons; + for (int RuneEntry : gobjectDwarfRunesEntry) + if (GameObject* dwarfRune = me->FindNearestGameObject(RuneEntry, 200.0f)) + dwarfRune->SetGoState(state); + } - // This will allow to find a valid position to spawn them - std::vector validPosition; - bool foundValidPosition = false; + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_SPELL_FIREBLAST, 2s); - void JustSummoned(Creature* cr) override { summons.Summon(cr); } + // Spawn 7 Embers initially + for (int i = 0; i < 4; ++i) + events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 4s); - void DoAction(int32 param) override + // Activate the runes (Start burning) + TurnRunes(true); + + Talk(AGGRO_TEXT); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + TurnRunes(false); + } + + int getValidRandomPosition() + { + /* Generate a random position which + * have not been used in 4 summonings. + * Since we are calling the event whenever the Spirit + * dies and not all at the time, we need to save at + * least 4 positions until reseting the vector + */ + + // Searching a new position so reset this bool check + _foundValidPosition = false; + int randomPosition; + + while (!_foundValidPosition) { - switch (param) + /* When we have summoned 4 creatures, reset the vector + * so we can summon new spirits in other positions.*/ + if (_validPosition.size() == 4) + _validPosition.clear(); + + // The random ranges from the position 0 to the position 6 + randomPosition = urand(0, 6); + + // When we have an empty vector we can use any random position generated. + if (_validPosition.empty()) + _foundValidPosition = true; + + /* This check is done to avoid running the vector + * when it is empty. Because if it is empty, then any + * position can be used to summon Spirits. + */ + if (!_foundValidPosition) { - case EVENT_SUMMON_SPIRITS: - _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 12s, 14s); - break; - } - } - - void Reset() override - { - _events.Reset(); - summons.DespawnAll(); - TurnRunes(false); - foundValidPosition = false; - validPosition.clear(); - } - - void TurnRunes(bool mode) - { - // Active makes the runes burn, ready turns them off - GOState state = mode ? GO_STATE_ACTIVE : GO_STATE_READY; - - for (int RuneEntry : gobjectDwarfRunesEntry) - if (GameObject* dwarfRune = me->FindNearestGameObject(RuneEntry, 200.0f)) - dwarfRune->SetGoState(state); - } - - void JustEngagedWith(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_SPELL_FIREBLAST, 2s); - - // Spawn 7 Embers initially - for (int i = 0; i < 4; ++i) - _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 4s); - - // Activate the runes (Start burning) - TurnRunes(true); - - Talk(AGGRO_TEXT); - } - - void JustDied(Unit* /*killer*/) override - { - TurnRunes(false); - _events.Reset(); - summons.DespawnAll(); - } - - int getValidRandomPosition() - { - /* Generate a random position which - * have not been used in 4 summonings. - * Since we are calling the event whenever the Spirit - * dies and not all at the time, we need to save at - * least 4 positions until reseting the vector - */ - - // Searching a new position so reset this bool check - foundValidPosition = false; - int randomPosition; - - while (!foundValidPosition) - { - /* When we have summoned 4 creatures, reset the vector - * so we can summon new spirits in other positions.*/ - if (validPosition.size() == 4) - validPosition.clear(); - - // The random ranges from the position 0 to the position 6 - randomPosition = urand(0, 6); - - // When we have an empty vector we can use any random position generated. - if (validPosition.empty()) - foundValidPosition = true; - - /* This check is done to avoid running the vector - * when it is empty. Because if it is empty, then any - * position can be used to summon Spirits. - */ - if (!foundValidPosition) + // Check every position inside the vector + for (int pos : _validPosition) { - // Check every position inside the vector - for (int pos : validPosition) + // If the random is different, we found a temporary true, + // until we find one that is equal, which means it has been used. + if (pos != randomPosition) + _foundValidPosition = true; + else { - // If the random is different, we found a temporary true, - // until we find one that is equal, which means it has been used. - if (pos != randomPosition) - foundValidPosition = true; - else - { - foundValidPosition = false; - break; - } + _foundValidPosition = false; + break; } } } - - // We found a valid position. Save it and return it to summon. - validPosition.emplace_back(randomPosition); - return randomPosition; } - void SummonSpirits() + // We found a valid position. Save it and return it to summon. + _validPosition.emplace_back(randomPosition); + return randomPosition; + } + + void SummonSpirits() + { + // Make the Spirits chase Ambassador Flamelash + me->SummonCreature(NPC_FIRE_SPIRIT, SummonPositions[getValidRandomPosition()], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 12s, 14s); + } + + void UpdateAI(uint32 diff) override + { + //Return since we have no target + if (!UpdateVictim()) + return; + + events.Update(diff); + + switch (events.ExecuteEvent()) { - // Make the Spirits chase Ambassador Flamelash - me->SummonCreature(NPC_FIRE_SPIRIT, SummonPositions[getValidRandomPosition()], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60 * IN_MILLISECONDS); - _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 12s, 14s); + case EVENT_SPELL_FIREBLAST: + DoCastVictim(SPELL_FIREBLAST); + events.ScheduleEvent(EVENT_SPELL_FIREBLAST, 7s); + break; + case EVENT_SUMMON_SPIRITS: + SummonSpirits(); + break; } - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; + DoMeleeAttackIfReady(); + } - _events.Update(diff); - - switch (_events.ExecuteEvent()) - { - case EVENT_SPELL_FIREBLAST: - DoCastVictim(SPELL_FIREBLAST); - _events.ScheduleEvent(EVENT_SPELL_FIREBLAST, 7s); - break; - case EVENT_SUMMON_SPIRITS: - SummonSpirits(); - break; - } - - DoMeleeAttackIfReady(); - } - }; +private: + // This will allow to find a valid position to spawn them + std::vector _validPosition; + bool _foundValidPosition = false; }; -class npc_burning_spirit : public CreatureScript +struct npc_burning_spirit : public ScriptedAI { -public: - npc_burning_spirit() : CreatureScript("npc_burning_spirit") { } + npc_burning_spirit(Creature* creature) : ScriptedAI(creature) {} - struct npc_burning_spiritAI : public ScriptedAI + void IsSummonedBy(WorldObject* summoner) override { - npc_burning_spiritAI(Creature* creature) : ScriptedAI(creature) {} + if (!summoner->IsCreature()) + return; - void IsSummonedBy(WorldObject* summoner) override - { - if (!summoner->IsCreature()) - { - return; - } - - _flamelasherGUID = summoner->GetGUID(); - me->GetMotionMaster()->MoveFollow(summoner->ToCreature(), 0.f, 0.f); - } - - void EnterEvadeMode(EvadeReason /*why*/) override - { - if (Creature* flamelasher = ObjectAccessor::GetCreature(*me, _flamelasherGUID)) - { - me->GetMotionMaster()->MoveFollow(flamelasher, 5.f, 0.f); - } - } - - void MovementInform(uint32 type, uint32 /*id*/) override - { - if (type != FOLLOW_MOTION_TYPE) - return; - - if (Creature* flamelasher = ObjectAccessor::GetCreature(*me, _flamelasherGUID)) - { - flamelasher->CastSpell(flamelasher, SPELL_BURNING_SPIRIT); - Unit::Kill(flamelasher, me); - } - } - - private: - EventMap _events; - ObjectGuid _flamelasherGUID; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockDepthsAI(creature); + _flamelasherGUID = summoner->GetGUID(); + me->GetMotionMaster()->MoveFollow(summoner->ToCreature(), 0.f, 0.f); } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* flamelasher = ObjectAccessor::GetCreature(*me, _flamelasherGUID)) + me->GetMotionMaster()->MoveFollow(flamelasher, 5.f, 0.f); + } + + void MovementInform(uint32 type, uint32 /*id*/) override + { + if (type != FOLLOW_MOTION_TYPE) + return; + + if (Creature* flamelasher = ObjectAccessor::GetCreature(*me, _flamelasherGUID)) + { + flamelasher->CastSpell(flamelasher, SPELL_BURNING_SPIRIT); + Unit::Kill(flamelasher, me); + } + } + +private: + ObjectGuid _flamelasherGUID; }; void AddSC_boss_ambassador_flamelash() { - new boss_ambassador_flamelash(); - new npc_burning_spirit(); + RegisterBlackrockDepthsCreatureAI(boss_ambassador_flamelash); + RegisterBlackrockDepthsCreatureAI(npc_burning_spirit); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp index f706bf5a0..1e7a9296a 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_anubshiah.cpp @@ -34,84 +34,67 @@ constexpr Milliseconds TIMER_CURSE_WEAKNESS = 12s; constexpr Milliseconds TIMER_DEMON_ARMOR = 3s; //virtually only cast once constexpr Milliseconds TIMER_ENVELOPING_WEB = 16s; -class boss_anubshiah : public CreatureScript +struct boss_anubshiah : public BossAI { -public: - boss_anubshiah() : CreatureScript("boss_anubshiah") { } + boss_anubshiah(Creature* creature) : BossAI(creature, DATA_ANUBSHIAH) { } - CreatureAI* GetAI(Creature* creature) const override + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockDepthsAI(creature); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_SHADOWBOLT, TIMER_SHADOWBOLT / 5); + events.ScheduleEvent(SPELL_CURSE_TONGUES, TIMER_CURSE_TONGUES / 5); + events.ScheduleEvent(SPELL_CURSE_WEAKNESS, TIMER_CURSE_WEAKNESS / 5); + events.ScheduleEvent(SPELL_DEMON_ARMOR, TIMER_DEMON_ARMOR / 5); + events.ScheduleEvent(SPELL_ENVELOPING_WEB, TIMER_ENVELOPING_WEB / 5); } - struct boss_anubshiahAI : public BossAI + void UpdateAI(uint32 diff) override { - boss_anubshiahAI(Creature* creature) : BossAI(creature, DATA_ANUBSHIAH) { } + //Return since we have no target + if (!UpdateVictim()) + return; - void JustEngagedWith(Unit* /*who*/) override + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_SHADOWBOLT, TIMER_SHADOWBOLT / 5); - events.ScheduleEvent(SPELL_CURSE_TONGUES, TIMER_CURSE_TONGUES / 5); - events.ScheduleEvent(SPELL_CURSE_WEAKNESS, TIMER_CURSE_WEAKNESS / 5); - events.ScheduleEvent(SPELL_DEMON_ARMOR, TIMER_DEMON_ARMOR / 5); - events.ScheduleEvent(SPELL_ENVELOPING_WEB, TIMER_ENVELOPING_WEB / 5); + switch (eventId) + { + case SPELL_SHADOWBOLT: + DoCastVictim(SPELL_SHADOWBOLT); + events.ScheduleEvent(SPELL_SHADOWBOLT, TIMER_SHADOWBOLT - 2s, TIMER_SHADOWBOLT + 2s); + break; + case SPELL_CURSE_TONGUES: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) + DoCast(target, SPELL_CURSE_TONGUES); + events.ScheduleEvent(SPELL_CURSE_TONGUES, TIMER_CURSE_TONGUES - 2s, TIMER_CURSE_TONGUES + 2s); + break; + case SPELL_CURSE_WEAKNESS: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) + DoCast(target, SPELL_CURSE_WEAKNESS); + events.ScheduleEvent(SPELL_CURSE_WEAKNESS, TIMER_CURSE_WEAKNESS - 2s, TIMER_CURSE_WEAKNESS + 2s); + break; + case SPELL_DEMON_ARMOR: + DoCast(me, SPELL_DEMON_ARMOR); + events.ScheduleEvent(SPELL_DEMON_ARMOR, TIMER_DEMON_ARMOR); + break; + case SPELL_ENVELOPING_WEB: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) + DoCast(target, SPELL_ENVELOPING_WEB); + events.ScheduleEvent(SPELL_ENVELOPING_WEB, TIMER_ENVELOPING_WEB - 2s, TIMER_ENVELOPING_WEB + 2s); + break; + default: + break; + } } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - { - return; - } - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_SHADOWBOLT: - DoCastVictim(SPELL_SHADOWBOLT); - events.ScheduleEvent(SPELL_SHADOWBOLT, TIMER_SHADOWBOLT - 2s, TIMER_SHADOWBOLT + 2s); - break; - case SPELL_CURSE_TONGUES: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - { - DoCast(target, SPELL_CURSE_TONGUES); - } - events.ScheduleEvent(SPELL_CURSE_TONGUES, TIMER_CURSE_TONGUES - 2s, TIMER_CURSE_TONGUES + 2s); - break; - case SPELL_CURSE_WEAKNESS: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - { - DoCast(target, SPELL_CURSE_WEAKNESS); - } - events.ScheduleEvent(SPELL_CURSE_WEAKNESS, TIMER_CURSE_WEAKNESS - 2s, TIMER_CURSE_WEAKNESS + 2s); - break; - case SPELL_DEMON_ARMOR: - DoCast(me, SPELL_DEMON_ARMOR); - events.ScheduleEvent(SPELL_DEMON_ARMOR, TIMER_DEMON_ARMOR); - break; - case SPELL_ENVELOPING_WEB: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - DoCast(target, SPELL_ENVELOPING_WEB); - events.ScheduleEvent(SPELL_ENVELOPING_WEB, TIMER_ENVELOPING_WEB - 2s, TIMER_ENVELOPING_WEB + 2s); - break; - default: - break; - } - } - DoMeleeAttackIfReady(); - } - }; + DoMeleeAttackIfReady(); + } }; void AddSC_boss_anubshiah() { - new boss_anubshiah(); + RegisterBlackrockDepthsCreatureAI(boss_anubshiah); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_emperor_dagran_thaurissan.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_emperor_dagran_thaurissan.cpp index cb97adeb7..2b725fef1 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_emperor_dagran_thaurissan.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_emperor_dagran_thaurissan.cpp @@ -34,106 +34,87 @@ enum Spells #define DATA_PERCENT_DEAD_SENATORS 0 -class boss_emperor_dagran_thaurissan : public CreatureScript +struct boss_emperor_dagran_thaurissan : public BossAI { -public: - boss_emperor_dagran_thaurissan() : CreatureScript("boss_emperor_dagran_thaurissan") { } + uint32 hasYelled = 0; + uint32 SenatorYells[5] = {3, 4, 5, 6, 7}; // IDs in creature_text database - CreatureAI* GetAI(Creature* creature) const override + boss_emperor_dagran_thaurissan(Creature* creature) : BossAI(creature, DATA_EMPEROR){} + + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockDepthsAI(creature); + if (hasYelled != 5) + Talk(YELL_SENATORS_ALIVE); + else + Talk(YELL_SENATORS_DEAD); + + me->CallForHelp(VISIBLE_RANGE); + events.ScheduleEvent(SPELL_HANDOFTHAURISSAN, 4s, 7s); + events.ScheduleEvent(SPELL_AVATAROFFLAME, 10s, 12s); } - struct boss_draganthaurissanAI : public BossAI + void KilledUnit(Unit* /*victim*/) override { - uint32 hasYelled = 0; - uint32 SenatorYells[5] = {3, 4, 5, 6, 7}; // IDs in creature_text database + Talk(SAY_SLAY); + } - boss_draganthaurissanAI(Creature* creature) : BossAI(creature, DATA_EMPEROR){} - - void JustEngagedWith(Unit* /*who*/) override + void SetData(uint32 type, uint32 data) override + { + if (type == DATA_PERCENT_DEAD_SENATORS) { - if (hasYelled != 5) + if (data >= 20 * (hasYelled + 1)) // map the 5 yells to %. Yell after 20,40,60,80,100% { - Talk(YELL_SENATORS_ALIVE); - } - else - { - Talk(YELL_SENATORS_DEAD); - } - - me->CallForHelp(VISIBLE_RANGE); - events.ScheduleEvent(SPELL_HANDOFTHAURISSAN, 4s, 7s); - events.ScheduleEvent(SPELL_AVATAROFFLAME, 10s, 12s); - } - - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_SLAY); - } - - void SetData(uint32 type, uint32 data) override - { - if (type == DATA_PERCENT_DEAD_SENATORS) - { - if (data >= 20 * (hasYelled + 1)) // map the 5 yells to %. Yell after 20,40,60,80,100% - { - if (hasYelled < 5) - { - if (me->IsAlive()) - { - Talk(SenatorYells[hasYelled]); - } - } - hasYelled++; - } + if (hasYelled < 5) + if (me->IsAlive()) + Talk(SenatorYells[hasYelled]); + hasYelled++; } } + } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* /*killer*/) override + { + if (Creature* Moira = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MOIRA))) { - if (Creature* Moira = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MOIRA))) + Moira->AI()->EnterEvadeMode(); + Moira->AI()->Talk(0); + Moira->SetFaction(FACTION_FRIENDLY); + } + } + + void UpdateAI(uint32 diff) override + { + //Return since we have no target + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - Moira->AI()->EnterEvadeMode(); - Moira->AI()->Talk(0); - Moira->SetFaction(FACTION_FRIENDLY); + case SPELL_HANDOFTHAURISSAN: + DoCast(SelectTarget(SelectTargetMethod::Random), SPELL_HANDOFTHAURISSAN); + //DoCastVictim(SPELL_HANDOFTHAURISSAN); + events.ScheduleEvent(SPELL_HANDOFTHAURISSAN, 4s, 7s); + break; + case SPELL_AVATAROFFLAME: + DoCastSelf(SPELL_AVATAROFFLAME); + events.ScheduleEvent(SPELL_AVATAROFFLAME, 23s, 27s); + break; + default: + break; } } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_HANDOFTHAURISSAN: - DoCast(SelectTarget(SelectTargetMethod::Random), SPELL_HANDOFTHAURISSAN); - //DoCastVictim(SPELL_HANDOFTHAURISSAN); - events.ScheduleEvent(SPELL_HANDOFTHAURISSAN, 4s, 7s); - break; - case SPELL_AVATAROFFLAME: - DoCastSelf(SPELL_AVATAROFFLAME); - events.ScheduleEvent(SPELL_AVATAROFFLAME, 23s, 27s); - break; - default: - break; - } - } - DoMeleeAttackIfReady(); - } - }; + DoMeleeAttackIfReady(); + } }; void AddSC_boss_draganthaurissan() { - new boss_emperor_dagran_thaurissan(); + RegisterBlackrockDepthsCreatureAI(boss_emperor_dagran_thaurissan); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_eviscerator.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_eviscerator.cpp index 3952b9f9c..b02b0ce31 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_eviscerator.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_eviscerator.cpp @@ -30,78 +30,65 @@ constexpr Milliseconds TIMER_SHADOWBOLT_VOLLEY = 7s; constexpr Milliseconds TIMER_REND = 20s; constexpr Milliseconds TIMER_SHIELD = 12s; -class boss_eviscerator : public CreatureScript +struct boss_eviscerator : public BossAI { -public: - boss_eviscerator() : CreatureScript("boss_eviscerator") {} + boss_eviscerator(Creature* creature) : BossAI(creature, DATA_EVISCERATOR) {} - CreatureAI* GetAI(Creature* creature) const override + bool SpellShieldReady = false; + + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockDepthsAI(creature); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_SHADOWBOLT_VOLLEY, TIMER_SHADOWBOLT_VOLLEY / 5); + events.ScheduleEvent(SPELL_REND, TIMER_REND / 5); + events.ScheduleEvent(SPELL_SHIELD, TIMER_SHIELD / 5); } - struct boss_evisceratorAI : public BossAI + void DamageTaken(Unit* /* doneBy */, uint32& /* damage */, DamageEffectType /* damagetype */, SpellSchoolMask damageSchoolMask) override { - boss_evisceratorAI(Creature* creature) : BossAI(creature, DATA_EVISCERATOR) {} - - bool SpellShieldReady = false; - - void JustEngagedWith(Unit* /*who*/) override + if ((damageSchoolMask & SPELL_SCHOOL_MASK_MAGIC) && SpellShieldReady) { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_SHADOWBOLT_VOLLEY, TIMER_SHADOWBOLT_VOLLEY / 5); - events.ScheduleEvent(SPELL_REND, TIMER_REND / 5); - events.ScheduleEvent(SPELL_SHIELD, TIMER_SHIELD / 5); + DoCast(SPELL_SHIELD); + SpellShieldReady = false; + events.ScheduleEvent(SPELL_SHIELD, TIMER_SHIELD); } + } - void DamageTaken(Unit* /* doneBy */, uint32& /* damage */, DamageEffectType /* damagetype */, SpellSchoolMask damageSchoolMask) override + void UpdateAI(uint32 diff) override + { + //Return since we have no target + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - if ((damageSchoolMask & SPELL_SCHOOL_MASK_MAGIC) && SpellShieldReady) + switch (eventId) { - DoCast(SPELL_SHIELD); - SpellShieldReady = false; - events.ScheduleEvent(SPELL_SHIELD, TIMER_SHIELD); + case SPELL_SHADOWBOLT_VOLLEY: + DoCastVictim(SPELL_SHADOWBOLT_VOLLEY); + events.ScheduleEvent(SPELL_SHADOWBOLT_VOLLEY, TIMER_SHADOWBOLT_VOLLEY - 2s, TIMER_SHADOWBOLT_VOLLEY + 2s); + break; + case SPELL_REND: + DoCastVictim(SPELL_REND); + events.ScheduleEvent(SPELL_REND, TIMER_REND - 2s, TIMER_REND + 2s); + break; + case SPELL_SHIELD: + SpellShieldReady = true; + break; + default: + break; } } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - { - return; - } - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_SHADOWBOLT_VOLLEY: - DoCastVictim(SPELL_SHADOWBOLT_VOLLEY); - events.ScheduleEvent(SPELL_SHADOWBOLT_VOLLEY, TIMER_SHADOWBOLT_VOLLEY - 2s, TIMER_SHADOWBOLT_VOLLEY + 2s); - break; - case SPELL_REND: - DoCastVictim(SPELL_REND); - events.ScheduleEvent(SPELL_REND, TIMER_REND - 2s, TIMER_REND + 2s); - break; - case SPELL_SHIELD: - SpellShieldReady = true; - break; - default: - break; - } - } - DoMeleeAttackIfReady(); - } - }; + DoMeleeAttackIfReady(); + } }; void AddSC_boss_eviscerator() { - new boss_eviscerator(); + RegisterBlackrockDepthsCreatureAI(boss_eviscerator); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_general_angerforge.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_general_angerforge.cpp index d04b97eca..21b6597df 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_general_angerforge.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_general_angerforge.cpp @@ -26,108 +26,94 @@ enum Spells SPELL_CLEAVE = 20691 }; -class boss_general_angerforge : public CreatureScript +struct boss_general_angerforge : public ScriptedAI { -public: - boss_general_angerforge() : CreatureScript("boss_general_angerforge") { } + boss_general_angerforge(Creature* creature) : ScriptedAI(creature) { } - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackrockDepthsAI(creature); + _mightyBlowTimer = 8000; + _hamStringTimer = 12000; + _cleaveTimer = 16000; + _addsTimer = 0; + _medics = false; } - struct boss_general_angerforgeAI : public ScriptedAI + void JustEngagedWith(Unit* /*who*/) override { } + + void SummonAdds(Unit* victim) { - boss_general_angerforgeAI(Creature* creature) : ScriptedAI(creature) { } + if (Creature* SummonedAdd = DoSpawnCreature(8901, float(irand(-14, 14)), float(irand(-14, 14)), 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000)) + SummonedAdd->AI()->AttackStart(victim); + } - uint32 MightyBlow_Timer; - uint32 HamString_Timer; - uint32 Cleave_Timer; - uint32 Adds_Timer; - bool Medics; + void SummonMedics(Unit* victim) + { + if (Creature* SummonedMedic = DoSpawnCreature(8894, float(irand(-9, 9)), float(irand(-9, 9)), 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000)) + SummonedMedic->AI()->AttackStart(victim); + } - void Reset() override + void UpdateAI(uint32 diff) override + { + //Return since we have no target + if (!UpdateVictim()) + return; + + if (_mightyBlowTimer <= diff) { - MightyBlow_Timer = 8000; - HamString_Timer = 12000; - Cleave_Timer = 16000; - Adds_Timer = 0; - Medics = false; + DoCastVictim(SPELL_MIGHTYBLOW); + _mightyBlowTimer = 18000; + } + else _mightyBlowTimer -= diff; + + if (_hamStringTimer <= diff) + { + DoCastVictim(SPELL_HAMSTRING); + _hamStringTimer = 15000; + } + else _hamStringTimer -= diff; + + if (_cleaveTimer <= diff) + { + DoCastVictim(SPELL_CLEAVE); + _cleaveTimer = 9000; + } + else _cleaveTimer -= diff; + + if (HealthBelowPct(21)) + { + if (_addsTimer <= diff) + { + // summon 3 Adds every 25s + SummonAdds(me->GetVictim()); + SummonAdds(me->GetVictim()); + SummonAdds(me->GetVictim()); + + _addsTimer = 25000; + } + else _addsTimer -= diff; } - void JustEngagedWith(Unit* /*who*/) override { } - - void SummonAdds(Unit* victim) + //Summon Medics + if (!_medics && HealthBelowPct(21)) { - if (Creature* SummonedAdd = DoSpawnCreature(8901, float(irand(-14, 14)), float(irand(-14, 14)), 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000)) - SummonedAdd->AI()->AttackStart(victim); + SummonMedics(me->GetVictim()); + SummonMedics(me->GetVictim()); + _medics = true; } - void SummonMedics(Unit* victim) - { - if (Creature* SummonedMedic = DoSpawnCreature(8894, float(irand(-9, 9)), float(irand(-9, 9)), 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000)) - SummonedMedic->AI()->AttackStart(victim); - } + DoMeleeAttackIfReady(); + } - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - //MightyBlow_Timer - if (MightyBlow_Timer <= diff) - { - DoCastVictim(SPELL_MIGHTYBLOW); - MightyBlow_Timer = 18000; - } - else MightyBlow_Timer -= diff; - - //HamString_Timer - if (HamString_Timer <= diff) - { - DoCastVictim(SPELL_HAMSTRING); - HamString_Timer = 15000; - } - else HamString_Timer -= diff; - - //Cleave_Timer - if (Cleave_Timer <= diff) - { - DoCastVictim(SPELL_CLEAVE); - Cleave_Timer = 9000; - } - else Cleave_Timer -= diff; - - //Adds_Timer - if (HealthBelowPct(21)) - { - if (Adds_Timer <= diff) - { - // summon 3 Adds every 25s - SummonAdds(me->GetVictim()); - SummonAdds(me->GetVictim()); - SummonAdds(me->GetVictim()); - - Adds_Timer = 25000; - } - else Adds_Timer -= diff; - } - - //Summon Medics - if (!Medics && HealthBelowPct(21)) - { - SummonMedics(me->GetVictim()); - SummonMedics(me->GetVictim()); - Medics = true; - } - - DoMeleeAttackIfReady(); - } - }; +private: + uint32 _mightyBlowTimer; + uint32 _hamStringTimer; + uint32 _cleaveTimer; + uint32 _addsTimer; + bool _medics; }; void AddSC_boss_general_angerforge() { - new boss_general_angerforge(); + RegisterBlackrockDepthsCreatureAI(boss_general_angerforge); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp index 43d93e397..449e7f801 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_gorosh_the_dervish.cpp @@ -30,78 +30,65 @@ constexpr Milliseconds TIMER_WHIRLWIND = 12s; constexpr Milliseconds TIMER_MORTAL = 22s; constexpr Milliseconds TIMER_BLOODLUST = 30s; -class boss_gorosh_the_dervish : public CreatureScript +struct boss_gorosh_the_dervish : public BossAI { -public: - boss_gorosh_the_dervish() : CreatureScript("boss_gorosh_the_dervish") { } + boss_gorosh_the_dervish(Creature* creature) : BossAI(creature, DATA_GOROSH) { } - CreatureAI* GetAI(Creature* creature) const override + Milliseconds nextWhirlwindTime; + + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockDepthsAI(creature); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_WHIRLWIND, TIMER_WHIRLWIND / 5); + events.ScheduleEvent(SPELL_MORTALSTRIKE, TIMER_MORTAL / 5); + events.ScheduleEvent(SPELL_BLOODLUST, TIMER_BLOODLUST / 5); } - struct boss_gorosh_the_dervishAI : public BossAI + void UpdateAI(uint32 diff) override { - boss_gorosh_the_dervishAI(Creature* creature) : BossAI(creature, DATA_GOROSH) { } + // Return since we have no target + if (!UpdateVictim()) + return; - Milliseconds nextWhirlwindTime; + events.Update(diff); - void JustEngagedWith(Unit* /*who*/) override + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_WHIRLWIND, TIMER_WHIRLWIND / 5); - events.ScheduleEvent(SPELL_MORTALSTRIKE, TIMER_MORTAL / 5); - events.ScheduleEvent(SPELL_BLOODLUST, TIMER_BLOODLUST / 5); - } - - void UpdateAI(uint32 diff) override - { - // Return since we have no target - if (!UpdateVictim()) + switch (eventId) { - return; - } - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) + case SPELL_WHIRLWIND: + if (me->GetDistance2d(me->GetVictim()) < 10.0f) { - case SPELL_WHIRLWIND: - if (me->GetDistance2d(me->GetVictim()) < 10.0f) - { - DoCastVictim(SPELL_WHIRLWIND); - nextWhirlwindTime = randtime(TIMER_WHIRLWIND - 2s, TIMER_WHIRLWIND + 2s); - } - else - { - // reschedule sooner - nextWhirlwindTime = randtime(TIMER_WHIRLWIND - 2s, TIMER_WHIRLWIND + 2s) / 3; - } - events.ScheduleEvent(SPELL_WHIRLWIND, nextWhirlwindTime); - break; - case SPELL_MORTALSTRIKE: - DoCastVictim(SPELL_MORTALSTRIKE); - events.ScheduleEvent(SPELL_MORTALSTRIKE, TIMER_MORTAL - 2s, TIMER_MORTAL + 2s); - break; - case SPELL_BLOODLUST: - DoCastSelf(SPELL_BLOODLUST); - events.ScheduleEvent(SPELL_BLOODLUST, TIMER_BLOODLUST - 2s, TIMER_BLOODLUST + 2s); - break; - default: - break; + DoCastVictim(SPELL_WHIRLWIND); + nextWhirlwindTime = randtime(TIMER_WHIRLWIND - 2s, TIMER_WHIRLWIND + 2s); } + else + { + // reschedule sooner + nextWhirlwindTime = randtime(TIMER_WHIRLWIND - 2s, TIMER_WHIRLWIND + 2s) / 3; + } + events.ScheduleEvent(SPELL_WHIRLWIND, nextWhirlwindTime); + break; + case SPELL_MORTALSTRIKE: + DoCastVictim(SPELL_MORTALSTRIKE); + events.ScheduleEvent(SPELL_MORTALSTRIKE, TIMER_MORTAL - 2s, TIMER_MORTAL + 2s); + break; + case SPELL_BLOODLUST: + DoCastSelf(SPELL_BLOODLUST); + events.ScheduleEvent(SPELL_BLOODLUST, TIMER_BLOODLUST - 2s, TIMER_BLOODLUST + 2s); + break; + default: + break; } - DoMeleeAttackIfReady(); } - }; + DoMeleeAttackIfReady(); + } }; void AddSC_boss_gorosh_the_dervish() { - new boss_gorosh_the_dervish(); + RegisterBlackrockDepthsCreatureAI(boss_gorosh_the_dervish); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp index eff584834..646f65992 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_grizzle.cpp @@ -29,74 +29,60 @@ enum Grizzle constexpr Milliseconds TIMER_GROUNDTREMOR = 10s; constexpr Milliseconds TIMER_FRENZY = 15s; -class boss_grizzle : public CreatureScript +struct boss_grizzle : public BossAI { -public: - boss_grizzle() : CreatureScript("boss_grizzle") { } + boss_grizzle(Creature* creature) : BossAI(creature, DATA_GRIZZLE) {} - CreatureAI* GetAI(Creature* creature) const override + Milliseconds nextTremorTime; + + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockDepthsAI(creature); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_GROUNDTREMOR, TIMER_GROUNDTREMOR / 5); + events.ScheduleEvent(SPELL_FRENZY, TIMER_FRENZY / 5); } - struct boss_grizzleAI : public BossAI + void UpdateAI(uint32 diff) override { - boss_grizzleAI(Creature* creature) : BossAI(creature, DATA_GRIZZLE) {} + //Return since we have no target + if (!UpdateVictim()) + return; - Milliseconds nextTremorTime; + events.Update(diff); - void JustEngagedWith(Unit* /*who*/) override + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_GROUNDTREMOR, TIMER_GROUNDTREMOR / 5); - events.ScheduleEvent(SPELL_FRENZY, TIMER_FRENZY / 5); - } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) + switch (eventId) { - return; - } - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) + case SPELL_GROUNDTREMOR: + if (me->GetDistance2d(me->GetVictim()) < 10.0f) { - case SPELL_GROUNDTREMOR: - if (me->GetDistance2d(me->GetVictim()) < 10.0f) - { - DoCastVictim(SPELL_GROUNDTREMOR); - nextTremorTime = randtime(TIMER_GROUNDTREMOR - 2s, TIMER_GROUNDTREMOR + 2s); - } - else - { - nextTremorTime = randtime(TIMER_GROUNDTREMOR - 2s, TIMER_GROUNDTREMOR + 2s) / 3; - } - events.ScheduleEvent(SPELL_GROUNDTREMOR, nextTremorTime); - break; - case SPELL_FRENZY: - DoCastSelf(SPELL_FRENZY); - events.ScheduleEvent(SPELL_FRENZY, TIMER_FRENZY - 2s, TIMER_FRENZY + 2s); - Talk(EMOTE_FRENZY_KILL); - break; - default: - break; + DoCastVictim(SPELL_GROUNDTREMOR); + nextTremorTime = randtime(TIMER_GROUNDTREMOR - 2s, TIMER_GROUNDTREMOR + 2s); } + else + { + nextTremorTime = randtime(TIMER_GROUNDTREMOR - 2s, TIMER_GROUNDTREMOR + 2s) / 3; + } + events.ScheduleEvent(SPELL_GROUNDTREMOR, nextTremorTime); + break; + case SPELL_FRENZY: + DoCastSelf(SPELL_FRENZY); + events.ScheduleEvent(SPELL_FRENZY, TIMER_FRENZY - 2s, TIMER_FRENZY + 2s); + Talk(EMOTE_FRENZY_KILL); + break; + default: + break; } - DoMeleeAttackIfReady(); } - }; + DoMeleeAttackIfReady(); + } }; void AddSC_boss_grizzle() { - new boss_grizzle(); + RegisterBlackrockDepthsCreatureAI(boss_grizzle); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_hedrum.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_hedrum.cpp index 787c72780..c12f84f18 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_hedrum.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_hedrum.cpp @@ -30,70 +30,55 @@ constexpr Milliseconds TIMER_PARALYZING = 20s; constexpr Milliseconds TIMER_BANEFUL = 24s; constexpr Milliseconds TIMER_WEB_EXPLOSION = 20s; -class boss_hedrum : public CreatureScript +struct boss_hedrum : public BossAI { -public: - boss_hedrum() : CreatureScript("boss_hedrum") {} + boss_hedrum(Creature* creature) : BossAI(creature, DATA_HEDRUM) {} - CreatureAI* GetAI(Creature* creature) const override + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockDepthsAI(creature); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_PARALYZING, TIMER_PARALYZING / 5); + events.ScheduleEvent(SPELL_BANEFUL, TIMER_BANEFUL / 5); + events.ScheduleEvent(SPELL_WEB_EXPLOSION, TIMER_WEB_EXPLOSION / 5); } - struct boss_hedrumAI : public BossAI + void UpdateAI(uint32 diff) override { - boss_hedrumAI(Creature* creature) : BossAI(creature, DATA_HEDRUM) {} + // Return since we have no target + if (!UpdateVictim()) + return; - void JustEngagedWith(Unit* /*who*/) override + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_PARALYZING, TIMER_PARALYZING / 5); - events.ScheduleEvent(SPELL_BANEFUL, TIMER_BANEFUL / 5); - events.ScheduleEvent(SPELL_WEB_EXPLOSION, TIMER_WEB_EXPLOSION / 5); + switch (eventId) + { + case SPELL_PARALYZING: + DoCastVictim(SPELL_PARALYZING); + events.ScheduleEvent(SPELL_PARALYZING, TIMER_PARALYZING - 2s, TIMER_PARALYZING + 2s); + break; + case SPELL_BANEFUL: + DoCastVictim(SPELL_BANEFUL); + events.ScheduleEvent(SPELL_BANEFUL, TIMER_BANEFUL - 2s, TIMER_BANEFUL + 2s); + break; + case SPELL_WEB_EXPLOSION: + if (me->GetDistance2d(me->GetVictim()) < 100.0f) + DoCast(SPELL_WEB_EXPLOSION); + events.ScheduleEvent(SPELL_WEB_EXPLOSION, TIMER_WEB_EXPLOSION - 2s, TIMER_WEB_EXPLOSION + 2s); + break; + default: + break; + } } - - void UpdateAI(uint32 diff) override - { - // Return since we have no target - if (!UpdateVictim()) - { - return; - } - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_PARALYZING: - DoCastVictim(SPELL_PARALYZING); - events.ScheduleEvent(SPELL_PARALYZING, TIMER_PARALYZING - 2s, TIMER_PARALYZING + 2s); - break; - case SPELL_BANEFUL: - DoCastVictim(SPELL_BANEFUL); - events.ScheduleEvent(SPELL_BANEFUL, TIMER_BANEFUL - 2s, TIMER_BANEFUL + 2s); - break; - case SPELL_WEB_EXPLOSION: - if (me->GetDistance2d(me->GetVictim()) < 100.0f) - { - DoCast(SPELL_WEB_EXPLOSION); - } - events.ScheduleEvent(SPELL_WEB_EXPLOSION, TIMER_WEB_EXPLOSION - 2s, TIMER_WEB_EXPLOSION + 2s); - break; - default: - break; - } - } - DoMeleeAttackIfReady(); - } - }; + DoMeleeAttackIfReady(); + } }; void AddSC_boss_hedrum() { - new boss_hedrum(); + RegisterBlackrockDepthsCreatureAI(boss_hedrum); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_high_interrogator_gerstahn.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_high_interrogator_gerstahn.cpp index 87202e6f0..dc6ada187 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_high_interrogator_gerstahn.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_high_interrogator_gerstahn.cpp @@ -27,81 +27,67 @@ enum Spells SPELL_SHADOWSHIELD = 22417 }; -class boss_high_interrogator_gerstahn : public CreatureScript +struct boss_high_interrogator_gerstahn : public ScriptedAI { -public: - boss_high_interrogator_gerstahn() : CreatureScript("boss_high_interrogator_gerstahn") { } + boss_high_interrogator_gerstahn(Creature* creature) : ScriptedAI(creature) { } - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackrockDepthsAI(creature); + _shadowWordPainTimer = 4000; + _manaBurnTimer = 14000; + _psychicScreamTimer = 32000; + _shadowShieldTimer = 8000; } - struct boss_high_interrogator_gerstahnAI : public ScriptedAI + void JustEngagedWith(Unit* /*who*/) override { } + + void UpdateAI(uint32 diff) override { - boss_high_interrogator_gerstahnAI(Creature* creature) : ScriptedAI(creature) { } + //Return since we have no target + if (!UpdateVictim()) + return; - uint32 ShadowWordPain_Timer; - uint32 ManaBurn_Timer; - uint32 PsychicScream_Timer; - uint32 ShadowShield_Timer; - - void Reset() override + if (_shadowWordPainTimer <= diff) { - ShadowWordPain_Timer = 4000; - ManaBurn_Timer = 14000; - PsychicScream_Timer = 32000; - ShadowShield_Timer = 8000; + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) + DoCast(target, SPELL_SHADOWWORDPAIN); + _shadowWordPainTimer = 7000; } + else _shadowWordPainTimer -= diff; - void JustEngagedWith(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override + if (_manaBurnTimer <= diff) { - //Return since we have no target - if (!UpdateVictim()) - return; - - //ShadowWordPain_Timer - if (ShadowWordPain_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - DoCast(target, SPELL_SHADOWWORDPAIN); - ShadowWordPain_Timer = 7000; - } - else ShadowWordPain_Timer -= diff; - - //ManaBurn_Timer - if (ManaBurn_Timer <= diff) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - DoCast(target, SPELL_MANABURN); - ManaBurn_Timer = 10000; - } - else ManaBurn_Timer -= diff; - - //PsychicScream_Timer - if (PsychicScream_Timer <= diff) - { - DoCastVictim(SPELL_PSYCHICSCREAM); - PsychicScream_Timer = 30000; - } - else PsychicScream_Timer -= diff; - - //ShadowShield_Timer - if (ShadowShield_Timer <= diff) - { - DoCast(me, SPELL_SHADOWSHIELD); - ShadowShield_Timer = 25000; - } - else ShadowShield_Timer -= diff; - - DoMeleeAttackIfReady(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) + DoCast(target, SPELL_MANABURN); + _manaBurnTimer = 10000; } - }; + else _manaBurnTimer -= diff; + + if (_psychicScreamTimer <= diff) + { + DoCastVictim(SPELL_PSYCHICSCREAM); + _psychicScreamTimer = 30000; + } + else _psychicScreamTimer -= diff; + + if (_shadowShieldTimer <= diff) + { + DoCast(me, SPELL_SHADOWSHIELD); + _shadowShieldTimer = 25000; + } + else _shadowShieldTimer -= diff; + + DoMeleeAttackIfReady(); + } + +private: + uint32 _shadowWordPainTimer; + uint32 _manaBurnTimer; + uint32 _psychicScreamTimer; + uint32 _shadowShieldTimer; }; void AddSC_boss_high_interrogator_gerstahn() { - new boss_high_interrogator_gerstahn(); + RegisterBlackrockDepthsCreatureAI(boss_high_interrogator_gerstahn); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_magmus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_magmus.cpp index 8551a20b1..3e326e817 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_magmus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_magmus.cpp @@ -25,66 +25,54 @@ enum Spells SPELL_WARSTOMP = 24375 }; -class boss_magmus : public CreatureScript +struct boss_magmus : public BossAI { -public: - boss_magmus() : CreatureScript("boss_magmus") { } + boss_magmus(Creature* creature) : BossAI(creature, TYPE_IRON_HALL) {} - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackrockDepthsAI(creature); + _Reset(); + instance->SetData(TYPE_IRON_HALL, NOT_STARTED); } - struct boss_magmusAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_magmusAI(Creature* creature) : BossAI(creature, TYPE_IRON_HALL) {} + instance->SetData(TYPE_IRON_HALL, IN_PROGRESS); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_WARSTOMP, 8s, 12s); + events.ScheduleEvent(SPELL_FIERYBURST, 4s, 8s); + } - void Reset() override - { - _Reset(); - instance->SetData(TYPE_IRON_HALL, NOT_STARTED); - } + void UpdateAI(uint32 diff) override + { + //Return since we have no target + if (!UpdateVictim()) + return; - void JustEngagedWith(Unit* /*who*/) override - { - instance->SetData(TYPE_IRON_HALL, IN_PROGRESS); - _JustEngagedWith(); - events.ScheduleEvent(SPELL_WARSTOMP, 8s, 12s); - events.ScheduleEvent(SPELL_FIERYBURST, 4s, 8s); - } + events.Update(diff); - void UpdateAI(uint32 diff) override + while (uint32 eventId = events.ExecuteEvent()) { - //Return since we have no target - if (!UpdateVictim()) + switch (eventId) { - return; + case SPELL_WARSTOMP: + DoCastVictim(SPELL_WARSTOMP); + events.ScheduleEvent(SPELL_WARSTOMP, 8s, 12s); + break; + case SPELL_FIERYBURST: + DoCastVictim(SPELL_FIERYBURST); + events.ScheduleEvent(SPELL_FIERYBURST, 4s, 8s); + break; + default: + break; } - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_WARSTOMP: - DoCastVictim(SPELL_WARSTOMP); - events.ScheduleEvent(SPELL_WARSTOMP, 8s, 12s); - break; - case SPELL_FIERYBURST: - DoCastVictim(SPELL_FIERYBURST); - events.ScheduleEvent(SPELL_FIERYBURST, 4s, 8s); - break; - default: - break; - } - } - - DoMeleeAttackIfReady(); } - }; + + DoMeleeAttackIfReady(); + } }; void AddSC_boss_magmus() { - new boss_magmus(); + RegisterBlackrockDepthsCreatureAI(boss_magmus); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_moira_bronzebeard.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_moira_bronzebeard.cpp index ddcbfaa9e..15804801a 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_moira_bronzebeard.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_moira_bronzebeard.cpp @@ -34,10 +34,10 @@ constexpr Milliseconds TIMER_RENEW = 12s; constexpr Milliseconds TIMER_SHADOWBOLT = 16s; constexpr Milliseconds TIMER_WORDPAIN = 12s; -struct boss_moira_bronzebeardAI : public BossAI +struct boss_moira_bronzebeard : public BossAI { // use a default value so we can inherit for priestess - boss_moira_bronzebeardAI(Creature* creature, uint32 data = DATA_MOIRA) : BossAI(creature, data) {} + boss_moira_bronzebeard(Creature* creature, uint32 data = DATA_MOIRA) : BossAI(creature, data) {} void JustEngagedWith(Unit* /*who*/) override { _JustEngagedWith(); @@ -49,15 +49,13 @@ struct boss_moira_bronzebeardAI : public BossAI 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) @@ -83,22 +81,18 @@ struct boss_moira_bronzebeardAI : public BossAI { Creature* emperor = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_EMPEROR)); if (emperor && emperor->HealthBelowPct(90)) - { DoCast(emperor, spell); - } else if (HealthBelowPct(90)) - { DoCastSelf(spell); - } events.ScheduleEvent(spell, timer - 2s, timer + 2s); } }; // high priestess should be fairly identical to Moira. // Running away when emperor dies is handled through GUID from emperor, therefore not relevant here. -struct boss_high_priestess_thaurissanAI : public boss_moira_bronzebeardAI +struct boss_high_priestess_thaurissan : public boss_moira_bronzebeard { - boss_high_priestess_thaurissanAI(Creature* creature) : boss_moira_bronzebeardAI(creature, DATA_PRIESTESS) {} + boss_high_priestess_thaurissan(Creature* creature) : boss_moira_bronzebeard(creature, DATA_PRIESTESS) {} void JustEngagedWith(Unit* /*who*/) override { @@ -110,18 +104,16 @@ struct boss_high_priestess_thaurissanAI : public boss_moira_bronzebeardAI events.ScheduleEvent(SPELL_SHADOWBOLT, TIMER_SHADOWBOLT / 2); } - void UpdateAI(uint32 diff) override + 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) @@ -148,34 +140,12 @@ struct boss_high_priestess_thaurissanAI : public boss_moira_bronzebeardAI } }; -class boss_moira_bronzebeard : public CreatureScript -{ -public: - boss_moira_bronzebeard() : CreatureScript("boss_moira_bronzebeard") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockDepthsAI(creature); - } -}; - -class boss_high_priestess_thaurissan : public CreatureScript -{ -public: - boss_high_priestess_thaurissan() : CreatureScript("boss_high_priestess_thaurissan") {} - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockDepthsAI(creature); - } -}; - void AddSC_boss_moira_bronzebeard() { - new boss_moira_bronzebeard(); + RegisterBlackrockDepthsCreatureAI(boss_moira_bronzebeard); } void AddSC_boss_high_priestess_thaurissan() { - new boss_high_priestess_thaurissan(); + RegisterBlackrockDepthsCreatureAI(boss_high_priestess_thaurissan); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_okthor.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_okthor.cpp index 0fd86d6be..688858ac0 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_okthor.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_okthor.cpp @@ -32,92 +32,74 @@ constexpr Milliseconds TIMER_ARCANE_EXPLOSION = 24s; constexpr Milliseconds TIMER_POLYMORPH = 12s; constexpr Milliseconds TIMER_SLOW = 15s; -class boss_okthor : public CreatureScript +struct boss_okthor : public BossAI { -public: - boss_okthor() : CreatureScript("boss_okthor") {} + boss_okthor(Creature* creature) : BossAI(creature, DATA_OKTHOR) {} - CreatureAI* GetAI(Creature* creature) const override + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockDepthsAI(creature); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_ARCANE_BOLT, TIMER_ARCANE_BOLT / 5); + events.ScheduleEvent(SPELL_ARCANE_EXPLOSION, TIMER_ARCANE_EXPLOSION / 5); + events.ScheduleEvent(SPELL_POLYMORPH, TIMER_POLYMORPH / 5); + events.ScheduleEvent(SPELL_SLOW, 500ms); } - struct boss_okthorAI : public BossAI + void UpdateAI(uint32 diff) override { - boss_okthorAI(Creature* creature) : BossAI(creature, DATA_OKTHOR) {} + //Return since we have no target + if (!UpdateVictim()) + return; - Milliseconds nextArcaneExplosionTime; + events.Update(diff); - void JustEngagedWith(Unit* /*who*/) override + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_ARCANE_BOLT, TIMER_ARCANE_BOLT / 5); - events.ScheduleEvent(SPELL_ARCANE_EXPLOSION, TIMER_ARCANE_EXPLOSION / 5); - events.ScheduleEvent(SPELL_POLYMORPH, TIMER_POLYMORPH / 5); - events.ScheduleEvent(SPELL_SLOW, 500ms); - } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) + switch (eventId) { - return; - } - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) + case SPELL_ARCANE_BOLT: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) + DoCast(target, SPELL_ARCANE_BOLT); + events.ScheduleEvent(SPELL_ARCANE_BOLT, TIMER_ARCANE_BOLT - 2s, TIMER_ARCANE_BOLT + 2s); + break; + case SPELL_ARCANE_EXPLOSION: + if (me->GetDistance2d(me->GetVictim()) < 50.0f) { - case SPELL_ARCANE_BOLT: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - { - DoCast(target, SPELL_ARCANE_BOLT); - } - events.ScheduleEvent(SPELL_ARCANE_BOLT, TIMER_ARCANE_BOLT - 2s, TIMER_ARCANE_BOLT + 2s); - break; - case SPELL_ARCANE_EXPLOSION: - if (me->GetDistance2d(me->GetVictim()) < 50.0f) - { - DoCast(SPELL_ARCANE_EXPLOSION); - nextArcaneExplosionTime = randtime(TIMER_ARCANE_EXPLOSION - 2s, TIMER_ARCANE_EXPLOSION + 2s); - } - else - { - nextArcaneExplosionTime = randtime(TIMER_ARCANE_EXPLOSION - 2s, TIMER_ARCANE_EXPLOSION + 2s) / 3; - } - events.ScheduleEvent(SPELL_ARCANE_EXPLOSION, nextArcaneExplosionTime); - break; - case SPELL_POLYMORPH: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - { - DoCast(target, SPELL_POLYMORPH); - } - events.ScheduleEvent(SPELL_POLYMORPH, TIMER_POLYMORPH - 2s, TIMER_POLYMORPH + 2s); - break; - case SPELL_SLOW: - if (me->GetDistance2d(me->GetVictim()) < 50.0f) - { - DoCast(SPELL_SLOW); - } - events.ScheduleEvent(SPELL_SLOW, TIMER_SLOW); - break; - - default: - break; + DoCast(SPELL_ARCANE_EXPLOSION); + _nextArcaneExplosionTime = randtime(TIMER_ARCANE_EXPLOSION - 2s, TIMER_ARCANE_EXPLOSION + 2s); } + else + { + _nextArcaneExplosionTime = randtime(TIMER_ARCANE_EXPLOSION - 2s, TIMER_ARCANE_EXPLOSION + 2s) / 3; + } + events.ScheduleEvent(SPELL_ARCANE_EXPLOSION, _nextArcaneExplosionTime); + break; + case SPELL_POLYMORPH: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) + DoCast(target, SPELL_POLYMORPH); + events.ScheduleEvent(SPELL_POLYMORPH, TIMER_POLYMORPH - 2s, TIMER_POLYMORPH + 2s); + break; + case SPELL_SLOW: + if (me->GetDistance2d(me->GetVictim()) < 50.0f) + DoCast(SPELL_SLOW); + events.ScheduleEvent(SPELL_SLOW, TIMER_SLOW); + break; + + default: + break; } - DoMeleeAttackIfReady(); } - }; + DoMeleeAttackIfReady(); + } + +private: + Milliseconds _nextArcaneExplosionTime; }; void AddSC_boss_okthor() { - new boss_okthor(); + RegisterBlackrockDepthsCreatureAI(boss_okthor); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp index 3feabc95f..758b41a99 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp @@ -23,20 +23,9 @@ enum Spells { - SPELL_SMELT_DARK_IRON = 14891, SPELL_LEARN_SMELT = 14894, }; -enum Quests -{ - QUEST_SPECTRAL_CHALICE = 4083 -}; - -enum Misc -{ - DATA_SKILLPOINT_MIN = 230 -}; - enum Says { SAY_START_FIGHT = 0 @@ -44,62 +33,32 @@ enum Says enum Gossip { - GOSSIP_TEXT_CONTINUE = 1828, // Continue... - GOSSIP_GROOMREL = 1945, // Option 1 : Before quest(4083) accepted, option 0 after quest(4083) accepted - GOSSIP_DOOMREL_START_COMBAT = 1947, // Your bondage is at an end, Doom'rel. I challenge you! - SAY_DOOMREL_HELLO = 2601, // Our fate is the doom of all who face the Great Fire. - SAY_QUEST_ACCEPTED = 2604, // You wish to learn the old craft? You wish to smelt dark iron?$B$BAppease me, $r. Show me a sacrifice and I will consider it! - SAY_QUEST_COMPLETED = 2605, // Your will is strong, and your intent is clear.$B$BPerhaps you are worthy... - SAY_QUEST_COMPLETED_END = 2606, // You have shown me your desire, and have payed with precious stone. I will teach you... + GOSSIP_GLOOMREL_CONTINUE_SMELT = 1952, + GOSSIP_GLOOMREL_CONTINUE_CHALICE = 1953, + GOSSIP_DOOMREL_CONTINUE = 1950, }; -class boss_gloomrel : public CreatureScript +struct boss_gloomrel : public ScriptedAI { -public: - boss_gloomrel() : CreatureScript("boss_gloomrel") { } + boss_gloomrel(Creature* creature) : ScriptedAI(creature) { } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { - ClearGossipMenuFor(player); - switch (action) + if (gossipListId != 0) + return; + + if (menuId == GOSSIP_GLOOMREL_CONTINUE_SMELT) { - case GOSSIP_ACTION_INFO_DEF+1: - AddGossipItemFor(player, GOSSIP_TEXT_CONTINUE, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); - SendGossipMenuFor(player, SAY_QUEST_COMPLETED_END, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+11: - CloseGossipMenuFor(player); - player->CastSpell(player, SPELL_LEARN_SMELT, false); - break; - case GOSSIP_ACTION_INFO_DEF+2: - AddGossipItemFor(player, GOSSIP_TEXT_CONTINUE, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 22); - SendGossipMenuFor(player, SAY_QUEST_ACCEPTED, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+22: - CloseGossipMenuFor(player); - if (InstanceScript* instance = creature->GetInstanceScript()) - { - //are 5 minutes expected? go template may have data to despawn when used at quest - instance->DoRespawnGameObject(instance->GetGuidData(DATA_GO_CHALICE), MINUTE * 5); - } - break; + CloseGossipMenuFor(player); + player->CastSpell(player, SPELL_LEARN_SMELT, false); } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetQuestRewardStatus(QUEST_SPECTRAL_CHALICE) == 1 && player->GetSkillValue(SKILL_MINING) >= DATA_SKILLPOINT_MIN && !player->HasSpell(SPELL_SMELT_DARK_IRON)) + else if (menuId == GOSSIP_GLOOMREL_CONTINUE_CHALICE) { - AddGossipItemFor(player, GOSSIP_GROOMREL, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, SAY_QUEST_COMPLETED, creature->GetGUID()); + CloseGossipMenuFor(player); + if (InstanceScript* instance = me->GetInstanceScript()) + //are 5 minutes expected? go template may have data to despawn when used at quest + instance->DoRespawnGameObject(instance->GetGuidData(DATA_GO_CHALICE), MINUTE * 5); } - - if (player->GetQuestRewardStatus(QUEST_SPECTRAL_CHALICE) == 0 && player->GetSkillValue(SKILL_MINING) >= DATA_SKILLPOINT_MIN) - AddGossipItemFor(player, GOSSIP_GROOMREL, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - return true; } }; @@ -120,145 +79,111 @@ enum DoomrelEvents EVENT_SPELL_SUMMON_VOIDWALKERS = 5, }; -class boss_doomrel : public CreatureScript +struct boss_doomrel : public ScriptedAI { -public: - boss_doomrel() : CreatureScript("boss_doomrel") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override + boss_doomrel(Creature* creature) : ScriptedAI(creature) { - ClearGossipMenuFor(player); - switch (action) + instance = creature->GetInstanceScript(); + } + + InstanceScript* instance; + EventMap _events; + bool Voidwalkers; + + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId == GOSSIP_DOOMREL_CONTINUE && gossipListId == 0) { - case GOSSIP_ACTION_INFO_DEF+1: - AddGossipItemFor(player, GOSSIP_TEXT_CONTINUE, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, SAY_QUEST_COMPLETED, creature->GetGUID()); + CloseGossipMenuFor(player); + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + // Start encounter + if (instance) + instance->SetData(TYPE_TOMB_OF_SEVEN, IN_PROGRESS); + Talk(SAY_START_FIGHT); + } + } + + void Reset() override + { + Voidwalkers = false; + me->SetFaction(FACTION_FRIENDLY); + + // was set before event start, so set again + me->SetImmuneToPC(true); + + if (instance->GetData(TYPE_TOMB_OF_SEVEN) == DONE) // what is this trying to do? Probably some kind of crash recovery + me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); + else + me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_GOSSIP); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_SPELL_SHADOWBOLTVOLLEY, 10s); + _events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 18s); + _events.ScheduleEvent(EVENT_SPELL_CURSEOFWEAKNESS, 5s); + _events.ScheduleEvent(EVENT_SPELL_DEMONARMOR, 16s); + _events.ScheduleEvent(EVENT_SPELL_SUMMON_VOIDWALKERS, 1s); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + me->RemoveAllAuras(); + me->GetThreatMgr().ClearAllThreat(); + me->CombatStop(true); + me->LoadCreaturesAddon(true); + if (me->IsAlive()) + me->GetMotionMaster()->MoveTargetedHome(); + me->SetLootRecipient(nullptr); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + switch (_events.ExecuteEvent()) + { + case EVENT_SPELL_SHADOWBOLTVOLLEY: + DoCastVictim(SPELL_SHADOWBOLTVOLLEY); + _events.ScheduleEvent(EVENT_SPELL_SHADOWBOLTVOLLEY, 12s); break; - case GOSSIP_ACTION_INFO_DEF+2: - CloseGossipMenuFor(player); - creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - // Start encounter - InstanceScript* instance = creature->GetInstanceScript(); - if (instance) + case EVENT_SPELL_IMMOLATE: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) { - instance->SetData(TYPE_TOMB_OF_SEVEN, IN_PROGRESS); + DoCast(target, SPELL_IMMOLATE); + _events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 25s); } - creature->AI()->Talk(SAY_START_FIGHT); + // Didn't get a target, try again in 1s + _events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 1s); + break; + case EVENT_SPELL_CURSEOFWEAKNESS: + DoCastVictim(SPELL_CURSEOFWEAKNESS); + _events.ScheduleEvent(EVENT_SPELL_CURSEOFWEAKNESS, 45s); + break; + case EVENT_SPELL_DEMONARMOR: + DoCast(me, SPELL_DEMONARMOR); + _events.ScheduleEvent(EVENT_SPELL_DEMONARMOR, 300s); + break; + case EVENT_SPELL_SUMMON_VOIDWALKERS: + if (!Voidwalkers && HealthBelowPct(51)) + { + DoCastVictim(SPELL_SUMMON_VOIDWALKERS, true); + Voidwalkers = true; + } + // Not ready yet, try again in 1s + _events.ScheduleEvent(EVENT_SPELL_SUMMON_VOIDWALKERS, 1s); break; } - return true; + + DoMeleeAttackIfReady(); } - - bool OnGossipHello(Player* player, Creature* creature) override - { - AddGossipItemFor(player, GOSSIP_DOOMREL_START_COMBAT, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, SAY_DOOMREL_HELLO, creature->GetGUID()); - - return true; - } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockDepthsAI(creature); - } - - struct boss_doomrelAI : public ScriptedAI - { - boss_doomrelAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - EventMap _events; - bool Voidwalkers; - - void Reset() override - { - Voidwalkers = false; - me->SetFaction(FACTION_FRIENDLY); - - // was set before event start, so set again - me->SetImmuneToPC(true); - - if (instance->GetData(TYPE_TOMB_OF_SEVEN) == DONE) // what is this trying to do? Probably some kind of crash recovery - { - me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); - } - else - { - me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_GOSSIP); - } - } - - void JustEngagedWith(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_SPELL_SHADOWBOLTVOLLEY, 10s); - _events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 18s); - _events.ScheduleEvent(EVENT_SPELL_CURSEOFWEAKNESS, 5s); - _events.ScheduleEvent(EVENT_SPELL_DEMONARMOR, 16s); - _events.ScheduleEvent(EVENT_SPELL_SUMMON_VOIDWALKERS, 1s); - } - - void EnterEvadeMode(EvadeReason /*why*/) override - { - me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); - me->CombatStop(true); - me->LoadCreaturesAddon(true); - if (me->IsAlive()) - me->GetMotionMaster()->MoveTargetedHome(); - me->SetLootRecipient(nullptr); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - _events.Update(diff); - - switch (_events.ExecuteEvent()) - { - case EVENT_SPELL_SHADOWBOLTVOLLEY: - DoCastVictim(SPELL_SHADOWBOLTVOLLEY); - _events.ScheduleEvent(EVENT_SPELL_SHADOWBOLTVOLLEY, 12s); - break; - case EVENT_SPELL_IMMOLATE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - { - DoCast(target, SPELL_IMMOLATE); - _events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 25s); - } - // Didn't get a target, try again in 1s - _events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 1s); - break; - case EVENT_SPELL_CURSEOFWEAKNESS: - DoCastVictim(SPELL_CURSEOFWEAKNESS); - _events.ScheduleEvent(EVENT_SPELL_CURSEOFWEAKNESS, 45s); - break; - case EVENT_SPELL_DEMONARMOR: - DoCast(me, SPELL_DEMONARMOR); - _events.ScheduleEvent(EVENT_SPELL_DEMONARMOR, 300s); - break; - case EVENT_SPELL_SUMMON_VOIDWALKERS: - if (!Voidwalkers && HealthBelowPct(51)) - { - DoCastVictim(SPELL_SUMMON_VOIDWALKERS, true); - Voidwalkers = true; - } - // Not ready yet, try again in 1s - _events.ScheduleEvent(EVENT_SPELL_SUMMON_VOIDWALKERS, 1s); - break; - } - - DoMeleeAttackIfReady(); - } - }; }; void AddSC_boss_tomb_of_seven() { - new boss_gloomrel(); - new boss_doomrel(); + RegisterBlackrockDepthsCreatureAI(boss_gloomrel); + RegisterBlackrockDepthsCreatureAI(boss_doomrel); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp index f6c487ba9..678885ce2 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp @@ -95,762 +95,715 @@ private: Creature* _boss; }; -class instance_blackrock_depths : public InstanceMapScript +struct instance_blackrock_depths : public InstanceScript { -public: - instance_blackrock_depths() : InstanceMapScript("instance_blackrock_depths", MAP_BLACKROCK_DEPTHS) { } - - InstanceScript* GetInstanceScript(InstanceMap* map) const override + instance_blackrock_depths(Map* map) : InstanceScript(map) { - return new instance_blackrock_depths_InstanceMapScript(map); + SetHeaders(DataHeader); } - struct instance_blackrock_depths_InstanceMapScript : public InstanceScript + uint32 encounter[MAX_ENCOUNTER]; + std::string str_data; + + ObjectGuid EmperorGUID; + ObjectGuid PhalanxGUID; + ObjectGuid MagmusGUID; + ObjectGuid MoiraGUID; + ObjectGuid PriestessGUID; + ObjectGuid IronhandGUID[6]; + ObjectGuid CorenGUID; + + ObjectGuid GoArena1GUID; + ObjectGuid GoArena2GUID; + ObjectGuid GoArena3GUID; + ObjectGuid GoArena4GUID; + ObjectGuid GoShadowLockGUID; + ObjectGuid GoShadowMechGUID; + ObjectGuid GoShadowGiantGUID; + ObjectGuid GoShadowDummyGUID; + ObjectGuid GoBarKegGUID; + ObjectGuid GoBarKegTrapGUID; + ObjectGuid GoBarDoorGUID; + ObjectGuid GoTombEnterGUID; + ObjectGuid GoTombExitGUID; + ObjectGuid GoLyceumGUID; + ObjectGuid GoSFSGUID; + ObjectGuid GoSFNGUID; + ObjectGuid GoGolemNGUID; + ObjectGuid GoGolemSGUID; + ObjectGuid GoThroneGUID; + ObjectGuid GoChestGUID; + ObjectGuid GoSpectralChaliceGUID; + + uint32 BarAleCount; + uint32 GhostKillCount; + ObjectGuid TombBossGUIDs[7]; + uint32 tombResetTimer; + uint32 TombTimer; + uint32 TombEventCounter; + uint32 OpenedCoofers; + uint32 IronhandCounter; + + GuidList ArgelmachAdds; + ObjectGuid ArgelmachGUID; + + TempSummon* TempSummonGrimstone = nullptr; + Position GrimstonePositon = Position(625.559f, -205.618f, -52.735f, 2.609f); + time_t timeRingFail = 0; + uint8 arenaMobsToSpawn; + uint8 arenaBossToSpawn; + + std::vector ArenaSpectators; + Position CenterOfRingOfLaw = Position(595.289, -186.56); + + ObjectGuid EmperorSenators[5]; + std::vector EmperorSenatorsVector; + Position EmperorSpawnPos = Position(1380.52, -831, 115); + + void OnPlayerEnter(Player* /* player */) override { - instance_blackrock_depths_InstanceMapScript(Map* map) : InstanceScript(map) + ReplaceMoiraIfSaved(); // In case a player joins the party during the run + // SetData(TYPE_RING_OF_LAW, DONE); + } + + void ReplaceMoiraIfSaved() + { + ObjectGuid* GUIDToReplace = &PriestessGUID; // default to having Moira + ObjectGuid* GUIDToSpawn = &MoiraGUID; + uint32 NPCEntry = NPC_MOIRA; + bool MoiraSaved = true; + + // check if all players saved her. + Map::PlayerList const& lPlayers = instance->GetPlayers(); + if (!lPlayers.IsEmpty()) { - SetHeaders(DataHeader); - } - - uint32 encounter[MAX_ENCOUNTER]; - std::string str_data; - - ObjectGuid EmperorGUID; - ObjectGuid PhalanxGUID; - ObjectGuid MagmusGUID; - ObjectGuid MoiraGUID; - ObjectGuid PriestessGUID; - ObjectGuid IronhandGUID[6]; - ObjectGuid CorenGUID; - - ObjectGuid GoArena1GUID; - ObjectGuid GoArena2GUID; - ObjectGuid GoArena3GUID; - ObjectGuid GoArena4GUID; - ObjectGuid GoShadowLockGUID; - ObjectGuid GoShadowMechGUID; - ObjectGuid GoShadowGiantGUID; - ObjectGuid GoShadowDummyGUID; - ObjectGuid GoBarKegGUID; - ObjectGuid GoBarKegTrapGUID; - ObjectGuid GoBarDoorGUID; - ObjectGuid GoTombEnterGUID; - ObjectGuid GoTombExitGUID; - ObjectGuid GoLyceumGUID; - ObjectGuid GoSFSGUID; - ObjectGuid GoSFNGUID; - ObjectGuid GoGolemNGUID; - ObjectGuid GoGolemSGUID; - ObjectGuid GoThroneGUID; - ObjectGuid GoChestGUID; - ObjectGuid GoSpectralChaliceGUID; - - uint32 BarAleCount; - uint32 GhostKillCount; - ObjectGuid TombBossGUIDs[7]; - uint32 tombResetTimer; - uint32 TombTimer; - uint32 TombEventCounter; - uint32 OpenedCoofers; - uint32 IronhandCounter; - - GuidList ArgelmachAdds; - ObjectGuid ArgelmachGUID; - - TempSummon* TempSummonGrimstone = nullptr; - Position GrimstonePositon = Position(625.559f, -205.618f, -52.735f, 2.609f); - time_t timeRingFail = 0; - uint8 arenaMobsToSpawn; - uint8 arenaBossToSpawn; - - std::vector ArenaSpectators; - Position CenterOfRingOfLaw = Position(595.289, -186.56); - - ObjectGuid EmperorSenators[5]; - std::vector EmperorSenatorsVector; - Position EmperorSpawnPos = Position(1380.52, -831, 115); - - void OnPlayerEnter(Player* /* player */) override - { - ReplaceMoiraIfSaved(); // In case a player joins the party during the run - // SetData(TYPE_RING_OF_LAW, DONE); - } - - void ReplaceMoiraIfSaved() - { - ObjectGuid* GUIDToReplace = &PriestessGUID; // default to having Moira - ObjectGuid* GUIDToSpawn = &MoiraGUID; - uint32 NPCEntry = NPC_MOIRA; - bool MoiraSaved = true; - - // check if all players saved her. - Map::PlayerList const& lPlayers = instance->GetPlayers(); - if (!lPlayers.IsEmpty()) + for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) { - for (Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr) + if (Player* player = itr->GetSource()) { - if (Player* player = itr->GetSource()) - { - // set to false if this player hasn't saved her. Another player can't put it to true. - MoiraSaved = MoiraSaved && ((player->GetQuestStatus(PRINCESS_QUEST_HORDE) == QUEST_STATUS_REWARDED) - || (player->GetQuestStatus(PRINCESS_QUEST_ALLIANCE) == QUEST_STATUS_REWARDED)); - } + // set to false if this player hasn't saved her. Another player can't put it to true. + MoiraSaved = MoiraSaved && ((player->GetQuestStatus(PRINCESS_QUEST_HORDE) == QUEST_STATUS_REWARDED) + || (player->GetQuestStatus(PRINCESS_QUEST_ALLIANCE) == QUEST_STATUS_REWARDED)); } } - - // assign correct GUIDs and spawn targets - if (MoiraSaved) - { - GUIDToReplace = &MoiraGUID; - GUIDToSpawn = &PriestessGUID; - NPCEntry = NPC_PRIESTESS; - } - - if (Creature* CreatureToReplace = instance->GetCreature(*GUIDToReplace)) - { - Creature* NewSpawn = instance->SummonCreature(NPCEntry, CreatureToReplace->GetPosition()); - CreatureToReplace->RemoveFromWorld(); - *GUIDToSpawn = NewSpawn->GetGUID(); - } } - void Initialize() override + // assign correct GUIDs and spawn targets + if (MoiraSaved) { - memset(&encounter, 0, sizeof(encounter)); - - BarAleCount = 0; - GhostKillCount = 0; - TombTimer = TIMER_TOMB_START; - TombEventCounter = 0; - tombResetTimer = 0; - OpenedCoofers = 0; - IronhandCounter = 0; - ArenaSpectators.clear(); - - // these are linked to the dungeon and not how many times the arena started. - arenaMobsToSpawn = urand(0, 5); - arenaBossToSpawn = urand(0, 5); + GUIDToReplace = &MoiraGUID; + GUIDToSpawn = &PriestessGUID; + NPCEntry = NPC_PRIESTESS; } - void OnCreatureCreate(Creature* creature) override + if (Creature* CreatureToReplace = instance->GetCreature(*GUIDToReplace)) { - switch (creature->GetEntry()) - { - case NPC_EMPEROR: - EmperorGUID = creature->GetGUID(); - break; - case NPC_PHALANX: - PhalanxGUID = creature->GetGUID(); - break; - case NPC_MOIRA: - MoiraGUID = creature->GetGUID(); - break; - case NPC_COREN_DIREBREW: - CorenGUID = creature->GetGUID(); - break; - case NPC_ANGERREL: - TombBossGUIDs[0] = creature->GetGUID(); - break; - case NPC_SEETHREL: - TombBossGUIDs[1] = creature->GetGUID(); - break; - case NPC_DOPEREL: - TombBossGUIDs[2] = creature->GetGUID(); - break; - case NPC_GLOOMREL: - TombBossGUIDs[3] = creature->GetGUID(); - break; - case NPC_VILEREL: - TombBossGUIDs[4] = creature->GetGUID(); - break; - case NPC_HATEREL: - TombBossGUIDs[5] = creature->GetGUID(); - break; - case NPC_DOOMREL: - TombBossGUIDs[6] = creature->GetGUID(); - break; - case NPC_MAGMUS: - MagmusGUID = creature->GetGUID(); - if (!creature->IsAlive()) - HandleGameObject(GoThroneGUID, true); // if Magmus is dead open door to last boss - break; - case NPC_WEAPON_TECHNICIAN: - case NPC_DOOMFORGE_ARCANASMITH: - case NPC_RAGEREAVER_GOLEM: - case NPC_WRATH_HAMMER_CONSTRUCT: - if (creature->IsAlive() && creature->GetPositionZ() < -51.5f && creature->GetPositionZ() > -55.f) - { - ArgelmachAdds.push_back(creature->GetGUID()); - } - break; - case NPC_GOLEM_LORD_ARGELMACH: - ArgelmachGUID = creature->GetGUID(); - break; - case NPC_IRONHAND_GUARDIAN: - IronhandGUID[IronhandCounter] = creature->GetGUID(); - IronhandCounter++; - break; - case NPC_ARENA_SPECTATOR: - ArenaSpectators.push_back(creature->GetGUID()); - if (encounter[TYPE_RING_OF_LAW] == DONE) // added for crashes - { - creature->SetFaction(FACTION_NEUTRAL); - creature->SetReactState(REACT_DEFENSIVE); - } - break; - case NPC_SHADOWFORGE_PEASANT: - case NPC_SHADOWFORCE_CITIZEN: // both do the same - if (creature->GetDistance2d(CenterOfRingOfLaw.GetPositionX(), CenterOfRingOfLaw.GetPositionY()) < (float)RADIUS_RING_OF_LAW) - { - ArenaSpectators.push_back(creature->GetGUID()); - } - if (encounter[TYPE_RING_OF_LAW] == DONE) // added for crashes - { - creature->SetFaction(FACTION_NEUTRAL); - creature->SetReactState(REACT_DEFENSIVE); - } - break; - case NPC_SHADOWFORGE_SENATOR: - // keep track of Senators that are not too far from emperor. Can't really use emperor as creature due to him possibly not being spawned. - // some senators spawn at ring of law - if (creature->GetDistance2d(EmperorSpawnPos.GetPositionX(), EmperorSpawnPos.GetPositionY()) < (float)DISTANCE_EMPEROR_ROOM) - { - EmperorSenatorsVector.push_back(creature->GetGUID()); - } - break; - default: - break; - } + Creature* NewSpawn = instance->SummonCreature(NPCEntry, CreatureToReplace->GetPosition()); + CreatureToReplace->RemoveFromWorld(); + *GUIDToSpawn = NewSpawn->GetGUID(); } + } - void OnGameObjectCreate(GameObject* go) override + void Initialize() override + { + memset(&encounter, 0, sizeof(encounter)); + + BarAleCount = 0; + GhostKillCount = 0; + TombTimer = TIMER_TOMB_START; + TombEventCounter = 0; + tombResetTimer = 0; + OpenedCoofers = 0; + IronhandCounter = 0; + ArenaSpectators.clear(); + + // these are linked to the dungeon and not how many times the arena started. + arenaMobsToSpawn = urand(0, 5); + arenaBossToSpawn = urand(0, 5); + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) { - switch (go->GetEntry()) - { - case GO_ARENA1: - GoArena1GUID = go->GetGUID(); - break; - case GO_ARENA2: - GoArena2GUID = go->GetGUID(); - break; - case GO_ARENA3: - GoArena3GUID = go->GetGUID(); - break; - case GO_ARENA4: - GoArena4GUID = go->GetGUID(); - break; - case GO_SHADOW_LOCK: - GoShadowLockGUID = go->GetGUID(); - break; - case GO_SHADOW_MECHANISM: - GoShadowMechGUID = go->GetGUID(); - break; - case GO_SHADOW_GIANT_DOOR: - GoShadowGiantGUID = go->GetGUID(); - break; - case GO_SHADOW_DUMMY: - GoShadowDummyGUID = go->GetGUID(); - break; - case GO_BAR_KEG_SHOT: - GoBarKegGUID = go->GetGUID(); - break; - case GO_BAR_KEG_TRAP: - GoBarKegTrapGUID = go->GetGUID(); - break; - case GO_BAR_DOOR: - GoBarDoorGUID = go->GetGUID(); - break; - case GO_TOMB_ENTER: - GoTombEnterGUID = go->GetGUID(); - break; - case GO_TOMB_EXIT: - GoTombExitGUID = go->GetGUID(); - if (GhostKillCount >= 7) - HandleGameObject(ObjectGuid::Empty, true, go); - else - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_LYCEUM: - GoLyceumGUID = go->GetGUID(); - break; - case GO_SF_S: - GoSFSGUID = go->GetGUID(); - break; - case GO_SF_N: - GoSFNGUID = go->GetGUID(); - break; - case GO_GOLEM_ROOM_N: - GoGolemNGUID = go->GetGUID(); - break; - case GO_GOLEM_ROOM_S: - GoGolemSGUID = go->GetGUID(); - break; - case GO_THRONE_ROOM: - GoThroneGUID = go->GetGUID(); - break; - case GO_CHEST_SEVEN: - GoChestGUID = go->GetGUID(); - break; - case GO_SPECTRAL_CHALICE: - GoSpectralChaliceGUID = go->GetGUID(); - break; - } - } - - void OnUnitDeath(Unit* unit) override - { - uint32 deadSenators = 0; - switch (unit->GetEntry()) - { - case NPC_WEAPON_TECHNICIAN: - case NPC_DOOMFORGE_ARCANASMITH: - case NPC_RAGEREAVER_GOLEM: - case NPC_WRATH_HAMMER_CONSTRUCT: - ArgelmachAdds.remove(unit->GetGUID()); - break; - case NPC_MAGMUS: - SetData(TYPE_IRON_HALL, DONE); - break; - case NPC_SHADOWFORGE_SENATOR: - deadSenators = 1; //hacky, but we cannot count the unit that just died through its state because OnUnitDeath() is called before the state is set. - for (const auto &senatorGUID: EmperorSenatorsVector) - { - if (Creature* senator = instance->GetCreature(senatorGUID)) - { - if (!senator->IsAlive() || senator->isDying()) - { - deadSenators++; - } - } - } - - if (Creature* emperor = instance->GetCreature(EmperorGUID)) - { - // send % of senators that died - emperor->AI()->SetData(0, (100 * deadSenators) / EmperorSenatorsVector.size()); - } - break; - case NPC_ANGERREL: - case NPC_DOPEREL: - case NPC_HATEREL: - case NPC_VILEREL: - case NPC_SEETHREL: - case NPC_GLOOMREL: - case NPC_DOOMREL: - GhostKillCount++; - if (GhostKillCount >= 7) - { - SetData(TYPE_TOMB_OF_SEVEN, DONE); - } - break; - default: - break; - } - } - - void SetData(uint32 type, uint32 data) override - { - LOG_DEBUG("scripts.ai", "Instance Blackrock Depths: SetData update (Type: {} Data {})", type, data); - - switch (type) - { - case TYPE_RING_OF_LAW: - encounter[0] = data; - switch (data) - { - case IN_PROGRESS: - TempSummonGrimstone = instance->SummonCreature(NPC_GRIMSTONE, GrimstonePositon); - break; - case FAIL: - if (TempSummonGrimstone) - { - TempSummonGrimstone->RemoveFromWorld(); - TempSummonGrimstone = nullptr; - timeRingFail = GameTime::GetGameTime().count(); - } - SetData(TYPE_RING_OF_LAW, NOT_STARTED); - break; - case DONE: - for (auto const& itr : ArenaSpectators) - { - if (Creature* spectator = instance->GetCreature(itr)) - { - spectator->SetFaction(FACTION_NEUTRAL); - spectator->SetReactState(REACT_DEFENSIVE); - } - } - break; - default: - break; - } - break; - case TYPE_VAULT: - encounter[1] = data; - break; - case TYPE_BAR: - if (data == SPECIAL) - ++BarAleCount; - else - encounter[2] = data; - break; - case TYPE_TOMB_OF_SEVEN: - encounter[3] = data; - switch (data) - { - case IN_PROGRESS: - HandleGameObject(GoTombExitGUID, false); - HandleGameObject(GoTombEnterGUID, false); - break; - case DONE: - DoRespawnGameObject(GoChestGUID, DAY); - HandleGameObject(GoTombExitGUID, true); - HandleGameObject(GoTombEnterGUID, true); - break; - } - break; - case TYPE_LYCEUM: - encounter[4] = data; - if (data == DONE) - { - HandleGameObject(GetGuidData(DATA_GOLEM_DOOR_N), true); - HandleGameObject(GetGuidData(DATA_GOLEM_DOOR_S), true); - if (Creature* magmus = instance->GetCreature(MagmusGUID)) - { - magmus->AI()->Talk(0); - } - ReplaceMoiraIfSaved(); // Need to place the correct final boss, but we need her to be spawned first. - } - break; - case TYPE_IRON_HALL: - encounter[5] = data; - switch (data) - { - case NOT_STARTED: - case IN_PROGRESS: - for (int i = 0; i < 6; i++) - { - if (Creature* ironhand = instance->GetCreature(IronhandGUID[i])) - { - ironhand->AI()->SetData(0, data == IN_PROGRESS); - } - } - break; - case DONE: - HandleGameObject(GetGuidData(DATA_THRONE_DOOR), true); - break; - default: - break; - } - break; - case DATA_OPEN_COFFER_DOORS: - OpenedCoofers += 1; - if (OpenedCoofers == 12) - { - Position pos = {812.15f, -348.91f, -50.579f, 0.7f}; - if (TempSummon* summon = instance->SummonCreature(NPC_WATCHMAN_DOOMGRIP, pos)) - summon->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN); - } - break; - case DATA_GOLEM_LORD_ARGELMACH_INIT: + case NPC_EMPEROR: + EmperorGUID = creature->GetGUID(); + break; + case NPC_PHALANX: + PhalanxGUID = creature->GetGUID(); + break; + case NPC_MOIRA: + MoiraGUID = creature->GetGUID(); + break; + case NPC_COREN_DIREBREW: + CorenGUID = creature->GetGUID(); + break; + case NPC_ANGERREL: + TombBossGUIDs[0] = creature->GetGUID(); + break; + case NPC_SEETHREL: + TombBossGUIDs[1] = creature->GetGUID(); + break; + case NPC_DOPEREL: + TombBossGUIDs[2] = creature->GetGUID(); + break; + case NPC_GLOOMREL: + TombBossGUIDs[3] = creature->GetGUID(); + break; + case NPC_VILEREL: + TombBossGUIDs[4] = creature->GetGUID(); + break; + case NPC_HATEREL: + TombBossGUIDs[5] = creature->GetGUID(); + break; + case NPC_DOOMREL: + TombBossGUIDs[6] = creature->GetGUID(); + break; + case NPC_MAGMUS: + MagmusGUID = creature->GetGUID(); + if (!creature->IsAlive()) + HandleGameObject(GoThroneGUID, true); // if Magmus is dead open door to last boss + break; + case NPC_WEAPON_TECHNICIAN: + case NPC_DOOMFORGE_ARCANASMITH: + case NPC_RAGEREAVER_GOLEM: + case NPC_WRATH_HAMMER_CONSTRUCT: + if (creature->IsAlive() && creature->GetPositionZ() < -51.5f && creature->GetPositionZ() > -55.f) + ArgelmachAdds.push_back(creature->GetGUID()); + break; + case NPC_GOLEM_LORD_ARGELMACH: + ArgelmachGUID = creature->GetGUID(); + break; + case NPC_IRONHAND_GUARDIAN: + IronhandGUID[IronhandCounter] = creature->GetGUID(); + IronhandCounter++; + break; + case NPC_ARENA_SPECTATOR: + ArenaSpectators.push_back(creature->GetGUID()); + if (encounter[TYPE_RING_OF_LAW] == DONE) // added for crashes { - if (Creature* argelmach = instance->GetCreature(ArgelmachGUID)) + creature->SetFaction(FACTION_NEUTRAL); + creature->SetReactState(REACT_DEFENSIVE); + } + break; + case NPC_SHADOWFORGE_PEASANT: + case NPC_SHADOWFORCE_CITIZEN: // both do the same + if (creature->GetDistance2d(CenterOfRingOfLaw.GetPositionX(), CenterOfRingOfLaw.GetPositionY()) < (float)RADIUS_RING_OF_LAW) + ArenaSpectators.push_back(creature->GetGUID()); + if (encounter[TYPE_RING_OF_LAW] == DONE) // added for crashes + { + creature->SetFaction(FACTION_NEUTRAL); + creature->SetReactState(REACT_DEFENSIVE); + } + break; + case NPC_SHADOWFORGE_SENATOR: + // keep track of Senators that are not too far from emperor. Can't really use emperor as creature due to him possibly not being spawned. + // some senators spawn at ring of law + if (creature->GetDistance2d(EmperorSpawnPos.GetPositionX(), EmperorSpawnPos.GetPositionY()) < (float)DISTANCE_EMPEROR_ROOM) + EmperorSenatorsVector.push_back(creature->GetGUID()); + break; + default: + break; + } + } + + void OnGameObjectCreate(GameObject* go) override + { + switch (go->GetEntry()) + { + case GO_ARENA1: + GoArena1GUID = go->GetGUID(); + break; + case GO_ARENA2: + GoArena2GUID = go->GetGUID(); + break; + case GO_ARENA3: + GoArena3GUID = go->GetGUID(); + break; + case GO_ARENA4: + GoArena4GUID = go->GetGUID(); + break; + case GO_SHADOW_LOCK: + GoShadowLockGUID = go->GetGUID(); + break; + case GO_SHADOW_MECHANISM: + GoShadowMechGUID = go->GetGUID(); + break; + case GO_SHADOW_GIANT_DOOR: + GoShadowGiantGUID = go->GetGUID(); + break; + case GO_SHADOW_DUMMY: + GoShadowDummyGUID = go->GetGUID(); + break; + case GO_BAR_KEG_SHOT: + GoBarKegGUID = go->GetGUID(); + break; + case GO_BAR_KEG_TRAP: + GoBarKegTrapGUID = go->GetGUID(); + break; + case GO_BAR_DOOR: + GoBarDoorGUID = go->GetGUID(); + break; + case GO_TOMB_ENTER: + GoTombEnterGUID = go->GetGUID(); + break; + case GO_TOMB_EXIT: + GoTombExitGUID = go->GetGUID(); + if (GhostKillCount >= 7) + HandleGameObject(ObjectGuid::Empty, true, go); + else + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_LYCEUM: + GoLyceumGUID = go->GetGUID(); + break; + case GO_SF_S: + GoSFSGUID = go->GetGUID(); + break; + case GO_SF_N: + GoSFNGUID = go->GetGUID(); + break; + case GO_GOLEM_ROOM_N: + GoGolemNGUID = go->GetGUID(); + break; + case GO_GOLEM_ROOM_S: + GoGolemSGUID = go->GetGUID(); + break; + case GO_THRONE_ROOM: + GoThroneGUID = go->GetGUID(); + break; + case GO_CHEST_SEVEN: + GoChestGUID = go->GetGUID(); + break; + case GO_SPECTRAL_CHALICE: + GoSpectralChaliceGUID = go->GetGUID(); + break; + } + } + + void OnUnitDeath(Unit* unit) override + { + uint32 deadSenators = 0; + switch (unit->GetEntry()) + { + case NPC_WEAPON_TECHNICIAN: + case NPC_DOOMFORGE_ARCANASMITH: + case NPC_RAGEREAVER_GOLEM: + case NPC_WRATH_HAMMER_CONSTRUCT: + ArgelmachAdds.remove(unit->GetGUID()); + break; + case NPC_MAGMUS: + SetData(TYPE_IRON_HALL, DONE); + break; + case NPC_SHADOWFORGE_SENATOR: + deadSenators = 1; //hacky, but we cannot count the unit that just died through its state because OnUnitDeath() is called before the state is set. + for (const auto &senatorGUID: EmperorSenatorsVector) + if (Creature* senator = instance->GetCreature(senatorGUID)) + if (!senator->IsAlive() || senator->isDying()) + deadSenators++; + + if (Creature* emperor = instance->GetCreature(EmperorGUID)) + // send % of senators that died + emperor->AI()->SetData(0, (100 * deadSenators) / EmperorSenatorsVector.size()); + break; + case NPC_ANGERREL: + case NPC_DOPEREL: + case NPC_HATEREL: + case NPC_VILEREL: + case NPC_SEETHREL: + case NPC_GLOOMREL: + case NPC_DOOMREL: + GhostKillCount++; + if (GhostKillCount >= 7) + SetData(TYPE_TOMB_OF_SEVEN, DONE); + break; + default: + break; + } + } + + void SetData(uint32 type, uint32 data) override + { + LOG_DEBUG("scripts.ai", "Instance Blackrock Depths: SetData update (Type: {} Data {})", type, data); + + switch (type) + { + case TYPE_RING_OF_LAW: + encounter[0] = data; + switch (data) + { + case IN_PROGRESS: + TempSummonGrimstone = instance->SummonCreature(NPC_GRIMSTONE, GrimstonePositon); + break; + case FAIL: + if (TempSummonGrimstone) { - GuidList adds = ArgelmachAdds; - for (GuidList::const_iterator itr = adds.begin(); itr != adds.end();) + TempSummonGrimstone->RemoveFromWorld(); + TempSummonGrimstone = nullptr; + timeRingFail = GameTime::GetGameTime().count(); + } + SetData(TYPE_RING_OF_LAW, NOT_STARTED); + break; + case DONE: + for (auto const& itr : ArenaSpectators) + if (Creature* spectator = instance->GetCreature(itr)) { - if (Creature* argelmachAdd = instance->GetCreature(*itr)) + spectator->SetFaction(FACTION_NEUTRAL); + spectator->SetReactState(REACT_DEFENSIVE); + } + break; + default: + break; + } + break; + case TYPE_VAULT: + encounter[1] = data; + break; + case TYPE_BAR: + if (data == SPECIAL) + ++BarAleCount; + else + encounter[2] = data; + break; + case TYPE_TOMB_OF_SEVEN: + encounter[3] = data; + switch (data) + { + case IN_PROGRESS: + HandleGameObject(GoTombExitGUID, false); + HandleGameObject(GoTombEnterGUID, false); + break; + case DONE: + DoRespawnGameObject(GoChestGUID, DAY); + HandleGameObject(GoTombExitGUID, true); + HandleGameObject(GoTombEnterGUID, true); + break; + } + break; + case TYPE_LYCEUM: + encounter[4] = data; + if (data == DONE) + { + HandleGameObject(GetGuidData(DATA_GOLEM_DOOR_N), true); + HandleGameObject(GetGuidData(DATA_GOLEM_DOOR_S), true); + if (Creature* magmus = instance->GetCreature(MagmusGUID)) + magmus->AI()->Talk(0); + ReplaceMoiraIfSaved(); // Need to place the correct final boss, but we need her to be spawned first. + } + break; + case TYPE_IRON_HALL: + encounter[5] = data; + switch (data) + { + case NOT_STARTED: + case IN_PROGRESS: + for (int i = 0; i < 6; i++) + if (Creature* ironhand = instance->GetCreature(IronhandGUID[i])) + ironhand->AI()->SetData(0, data == IN_PROGRESS); + break; + case DONE: + HandleGameObject(GetGuidData(DATA_THRONE_DOOR), true); + break; + default: + break; + } + break; + case DATA_OPEN_COFFER_DOORS: + OpenedCoofers += 1; + if (OpenedCoofers == 12) + { + Position pos = {812.15f, -348.91f, -50.579f, 0.7f}; + if (TempSummon* summon = instance->SummonCreature(NPC_WATCHMAN_DOOMGRIP, pos)) + summon->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN); + } + break; + case DATA_GOLEM_LORD_ARGELMACH_INIT: + { + if (Creature* argelmach = instance->GetCreature(ArgelmachGUID)) + { + GuidList adds = ArgelmachAdds; + for (GuidList::const_iterator itr = adds.begin(); itr != adds.end();) + { + if (Creature* argelmachAdd = instance->GetCreature(*itr)) + { + if (argelmachAdd->GetEntry() == NPC_WRATH_HAMMER_CONSTRUCT) { - if (argelmachAdd->GetEntry() == NPC_WRATH_HAMMER_CONSTRUCT) + argelmachAdd->RemoveAurasDueToSpell(SPELL_STONED); + argelmachAdd->AI()->AttackStart(argelmach->GetVictim()); + itr = adds.erase(itr); + } + else if (argelmachAdd->GetEntry() == NPC_RAGEREAVER_GOLEM) + { + if (argelmachAdd->IsWithinDist2d(argelmach, 10.f)) { argelmachAdd->RemoveAurasDueToSpell(SPELL_STONED); argelmachAdd->AI()->AttackStart(argelmach->GetVictim()); itr = adds.erase(itr); } - else if (argelmachAdd->GetEntry() == NPC_RAGEREAVER_GOLEM) - { - if (argelmachAdd->IsWithinDist2d(argelmach, 10.f)) - { - argelmachAdd->RemoveAurasDueToSpell(SPELL_STONED); - argelmachAdd->AI()->AttackStart(argelmach->GetVictim()); - itr = adds.erase(itr); - } - else - ++itr; - } else - { ++itr; - } } else { ++itr; } } - - if (!adds.empty()) - { - argelmach->SetReactState(REACT_PASSIVE); - argelmach->SetTarget(); - argelmach->AI()->SetData(DATA_GOLEM_LORD_ARGELMACH_INIT, IN_PROGRESS); - } else { - argelmach->AI()->SetData(DATA_GOLEM_LORD_ARGELMACH_INIT, DONE); + ++itr; } } - break; - } - case DATA_GOLEM_LORD_ARGELMACH_ADDS: - { - if (Creature* argelmach = instance->GetCreature(ArgelmachGUID)) + + if (!adds.empty()) { - argelmach->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT); - argelmach->m_Events.AddEventAtOffset(new RestoreAttack(argelmach), 3s); - - for (ObjectGuid const& argelmachAddGUID : ArgelmachAdds) - { - if (Creature* argelmachAdd = instance->GetCreature(argelmachAddGUID)) - { - if (!argelmachAdd->IsInCombat()) - { - argelmachAdd->RemoveAurasDueToSpell(SPELL_STONED); - argelmachAdd->AI()->AttackStart(argelmach->GetVictim()); - } - } - } + argelmach->SetReactState(REACT_PASSIVE); + argelmach->SetTarget(); + argelmach->AI()->SetData(DATA_GOLEM_LORD_ARGELMACH_INIT, IN_PROGRESS); } - break; - } - default: - break; - } - - if (data == DONE || GhostKillCount >= 7) - { - OUT_SAVE_INST_DATA; - - std::ostringstream saveStream; - saveStream << encounter[0] << ' ' << encounter[1] << ' ' << encounter[2] << ' ' - << encounter[3] << ' ' << encounter[4] << ' ' << encounter[5] << ' ' << GhostKillCount; - - str_data = saveStream.str(); - - SaveToDB(); - OUT_SAVE_INST_DATA_COMPLETE; - } - } - - uint32 GetData(uint32 type) const override - { - switch (type) - { - case TYPE_RING_OF_LAW: - return encounter[0]; - case TYPE_VAULT: - return encounter[1]; - case TYPE_BAR: - if (encounter[2] == IN_PROGRESS && BarAleCount == 3) - return SPECIAL; else - return encounter[2]; - case TYPE_TOMB_OF_SEVEN: - return encounter[3]; - case TYPE_LYCEUM: - return encounter[4]; - case TYPE_IRON_HALL: - return encounter[5]; - case DATA_TIME_RING_FAIL: - return timeRingFail; - case DATA_ARENA_MOBS: - return arenaMobsToSpawn; - case DATA_ARENA_BOSS: - return arenaBossToSpawn; - } - return 0; - } - - ObjectGuid GetGuidData(uint32 data) const override - { - switch (data) - { - case DATA_EMPEROR: - return EmperorGUID; - case DATA_PHALANX: - return PhalanxGUID; - case DATA_MOIRA: - return MoiraGUID; - case DATA_COREN: - return CorenGUID; - case DATA_ARENA1: - return GoArena1GUID; - case DATA_ARENA2: - return GoArena2GUID; - case DATA_ARENA3: - return GoArena3GUID; - case DATA_ARENA4: - return GoArena4GUID; - case DATA_GO_BAR_KEG: - return GoBarKegGUID; - case DATA_GO_BAR_KEG_TRAP: - return GoBarKegTrapGUID; - case DATA_GO_BAR_DOOR: - return GoBarDoorGUID; - case DATA_SF_BRAZIER_N: - return GoSFNGUID; - case DATA_SF_BRAZIER_S: - return GoSFSGUID; - case DATA_THRONE_DOOR: - return GoThroneGUID; - case DATA_GOLEM_DOOR_N: - return GoGolemNGUID; - case DATA_GOLEM_DOOR_S: - return GoGolemSGUID; - case DATA_GO_CHALICE: - return GoSpectralChaliceGUID; - case DATA_MAGMUS: - { - return MagmusGUID; - } - } - - return ObjectGuid::Empty; - } - - std::string GetSaveData() override - { - return str_data; - } - - void Load(const char* in) override - { - if (!in) - { - OUT_LOAD_INST_DATA_FAIL; - return; - } - - OUT_LOAD_INST_DATA(in); - - std::istringstream loadStream(in); - loadStream >> encounter[0] >> encounter[1] >> encounter[2] >> encounter[3] - >> encounter[4] >> encounter[5] >> GhostKillCount; - - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (encounter[i] == IN_PROGRESS) - encounter[i] = NOT_STARTED; - if (GhostKillCount > 0 && GhostKillCount < 7) - GhostKillCount = 0;//reset tomb of seven event - if (GhostKillCount >= 7) - GhostKillCount = 7; - - OUT_LOAD_INST_DATA_COMPLETE; - } - - void TombOfSevenEvent() - { - if (GhostKillCount < 7 && TombBossGUIDs[TombEventCounter]) - { - if (Creature* boss = instance->GetCreature(TombBossGUIDs[TombEventCounter])) - { - ++TombEventCounter; - boss->SetFaction(FACTION_DARK_IRON_DWARVES); - boss->SetImmuneToPC(false); - - // find suitable target here. - Player* target = boss->SelectNearestPlayer(130); - if (target && boss->CanCreatureAttack(target, true)) { - boss->AI()->AttackStart(target); - boss->AI()->DoZoneInCombat(); - tombResetTimer = TIMER_TOMB_RESET; + argelmach->AI()->SetData(DATA_GOLEM_LORD_ARGELMACH_INIT, DONE); } } + break; } + case DATA_GOLEM_LORD_ARGELMACH_ADDS: + { + if (Creature* argelmach = instance->GetCreature(ArgelmachGUID)) + { + argelmach->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT); + argelmach->m_Events.AddEventAtOffset(new RestoreAttack(argelmach), 3s); + + for (ObjectGuid const& argelmachAddGUID : ArgelmachAdds) + if (Creature* argelmachAdd = instance->GetCreature(argelmachAddGUID)) + if (!argelmachAdd->IsInCombat()) + { + argelmachAdd->RemoveAurasDueToSpell(SPELL_STONED); + argelmachAdd->AI()->AttackStart(argelmach->GetVictim()); + } + } + break; + } + default: + break; } - void TombOfSevenReset() + if (data == DONE || GhostKillCount >= 7) { - HandleGameObject(GoTombExitGUID, false);// close exit door - HandleGameObject(GoTombEnterGUID, true);// open entrance door - for (uint8 i = 0; i < 7; ++i) - { - if (Creature* boss = instance->GetCreature(TombBossGUIDs[i])) - { - if (!boss->IsAlive()) - { - //do not call EnterEvadeMode(), it will create infinit loops - boss->Respawn(); - boss->RemoveAllAuras(); - boss->GetThreatMgr().ClearAllThreat(); - boss->CombatStop(true); - boss->LoadCreaturesAddon(true); - boss->GetMotionMaster()->MoveTargetedHome(); - boss->SetLootRecipient(nullptr); - } - boss->SetFaction(FACTION_FRIENDLY); - boss->SetImmuneToPC(true); // think this is useless - if (i == 6) // doomrel needs explicit reset - { - boss->AI()->Reset(); - } - } - } + OUT_SAVE_INST_DATA; - GhostKillCount = 0; - TombEventCounter = 0; - TombTimer = TIMER_TOMB_START; - SetData(TYPE_TOMB_OF_SEVEN, NOT_STARTED); + std::ostringstream saveStream; + saveStream << encounter[0] << ' ' << encounter[1] << ' ' << encounter[2] << ' ' + << encounter[3] << ' ' << encounter[4] << ' ' << encounter[5] << ' ' << GhostKillCount; + + str_data = saveStream.str(); + + SaveToDB(); + OUT_SAVE_INST_DATA_COMPLETE; } + } - bool CheckTombReset(uint32 diff) + uint32 GetData(uint32 type) const override + { + switch (type) { - bool anyBossAlive = false; // status of the bosses up until the current one - for (uint8 i = 0; i < TombEventCounter; i++) - { - Creature* boss = instance->GetCreature(TombBossGUIDs[i]); - if (boss) - { - anyBossAlive |= boss->IsAlive(); - if (boss->IsAlive() && boss->IsInCombat()) - { - tombResetTimer = TIMER_TOMB_RESET; - return false; // any boss in combat means we shouldn't reset. - } - } - } - if (!anyBossAlive) // no boss alive, put reset timer back up - { - tombResetTimer = TIMER_TOMB_RESET; - } - tombResetTimer -= diff; - return tombResetTimer < diff; - } - - void Update(uint32 diff) override - { - if ((GetData(TYPE_TOMB_OF_SEVEN) == IN_PROGRESS) && GhostKillCount < 7) - { - if (TombTimer <= diff) - { - TombTimer = TIMER_TOMBOFTHESEVEN; - TombOfSevenEvent(); - } + case TYPE_RING_OF_LAW: + return encounter[0]; + case TYPE_VAULT: + return encounter[1]; + case TYPE_BAR: + if (encounter[2] == IN_PROGRESS && BarAleCount == 3) + return SPECIAL; else - { - TombTimer -= diff; - } + return encounter[2]; + case TYPE_TOMB_OF_SEVEN: + return encounter[3]; + case TYPE_LYCEUM: + return encounter[4]; + case TYPE_IRON_HALL: + return encounter[5]; + case DATA_TIME_RING_FAIL: + return timeRingFail; + case DATA_ARENA_MOBS: + return arenaMobsToSpawn; + case DATA_ARENA_BOSS: + return arenaBossToSpawn; + } + return 0; + } - if (CheckTombReset(diff)) + ObjectGuid GetGuidData(uint32 data) const override + { + switch (data) + { + case DATA_EMPEROR: + return EmperorGUID; + case DATA_PHALANX: + return PhalanxGUID; + case DATA_MOIRA: + return MoiraGUID; + case DATA_COREN: + return CorenGUID; + case DATA_ARENA1: + return GoArena1GUID; + case DATA_ARENA2: + return GoArena2GUID; + case DATA_ARENA3: + return GoArena3GUID; + case DATA_ARENA4: + return GoArena4GUID; + case DATA_GO_BAR_KEG: + return GoBarKegGUID; + case DATA_GO_BAR_KEG_TRAP: + return GoBarKegTrapGUID; + case DATA_GO_BAR_DOOR: + return GoBarDoorGUID; + case DATA_SF_BRAZIER_N: + return GoSFNGUID; + case DATA_SF_BRAZIER_S: + return GoSFSGUID; + case DATA_THRONE_DOOR: + return GoThroneGUID; + case DATA_GOLEM_DOOR_N: + return GoGolemNGUID; + case DATA_GOLEM_DOOR_S: + return GoGolemSGUID; + case DATA_GO_CHALICE: + return GoSpectralChaliceGUID; + case DATA_MAGMUS: + { + return MagmusGUID; + } + } + + return ObjectGuid::Empty; + } + + std::string GetSaveData() override + { + return str_data; + } + + void Load(const char* in) override + { + if (!in) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(in); + + std::istringstream loadStream(in); + loadStream >> encounter[0] >> encounter[1] >> encounter[2] >> encounter[3] + >> encounter[4] >> encounter[5] >> GhostKillCount; + + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + if (encounter[i] == IN_PROGRESS) + encounter[i] = NOT_STARTED; + if (GhostKillCount > 0 && GhostKillCount < 7) + GhostKillCount = 0;//reset tomb of seven event + if (GhostKillCount >= 7) + GhostKillCount = 7; + + OUT_LOAD_INST_DATA_COMPLETE; + } + + void TombOfSevenEvent() + { + if (GhostKillCount < 7 && TombBossGUIDs[TombEventCounter]) + { + if (Creature* boss = instance->GetCreature(TombBossGUIDs[TombEventCounter])) + { + ++TombEventCounter; + boss->SetFaction(FACTION_DARK_IRON_DWARVES); + boss->SetImmuneToPC(false); + + // find suitable target here. + Player* target = boss->SelectNearestPlayer(130); + if (target && boss->CanCreatureAttack(target, true)) { - TombOfSevenReset(); + boss->AI()->AttackStart(target); + boss->AI()->DoZoneInCombat(); + tombResetTimer = TIMER_TOMB_RESET; } } } - }; + } + + void TombOfSevenReset() + { + HandleGameObject(GoTombExitGUID, false);// close exit door + HandleGameObject(GoTombEnterGUID, true);// open entrance door + for (uint8 i = 0; i < 7; ++i) + if (Creature* boss = instance->GetCreature(TombBossGUIDs[i])) + { + if (!boss->IsAlive()) + { + //do not call EnterEvadeMode(), it will create infinit loops + boss->Respawn(); + boss->RemoveAllAuras(); + boss->GetThreatMgr().ClearAllThreat(); + boss->CombatStop(true); + boss->LoadCreaturesAddon(true); + boss->GetMotionMaster()->MoveTargetedHome(); + boss->SetLootRecipient(nullptr); + } + boss->SetFaction(FACTION_FRIENDLY); + boss->SetImmuneToPC(true); // think this is useless + if (i == 6) // doomrel needs explicit reset + boss->AI()->Reset(); + } + + GhostKillCount = 0; + TombEventCounter = 0; + TombTimer = TIMER_TOMB_START; + SetData(TYPE_TOMB_OF_SEVEN, NOT_STARTED); + } + + bool CheckTombReset(uint32 diff) + { + bool anyBossAlive = false; // status of the bosses up until the current one + for (uint8 i = 0; i < TombEventCounter; i++) + { + Creature* boss = instance->GetCreature(TombBossGUIDs[i]); + if (boss) + { + anyBossAlive |= boss->IsAlive(); + if (boss->IsAlive() && boss->IsInCombat()) + { + tombResetTimer = TIMER_TOMB_RESET; + return false; // any boss in combat means we shouldn't reset. + } + } + } + if (!anyBossAlive) // no boss alive, put reset timer back up + tombResetTimer = TIMER_TOMB_RESET; + tombResetTimer -= diff; + return tombResetTimer < diff; + } + + void Update(uint32 diff) override + { + if ((GetData(TYPE_TOMB_OF_SEVEN) == IN_PROGRESS) && GhostKillCount < 7) + { + if (TombTimer <= diff) + { + TombTimer = TIMER_TOMBOFTHESEVEN; + TombOfSevenEvent(); + } + else + { + TombTimer -= diff; + } + + if (CheckTombReset(diff)) + TombOfSevenReset(); + } + } }; void AddSC_instance_blackrock_depths() { - new instance_blackrock_depths(); + RegisterInstanceScript(instance_blackrock_depths, MAP_BLACKROCK_DEPTHS); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h index 8b33f180b..4e8f43dad 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h @@ -155,5 +155,6 @@ inline AI* GetBlackrockSpireAI(T* obj) } #define RegisterBlackrockSpireCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetBlackrockSpireAI) +#define RegisterBlackrockSpireGameObjectAI(ai_name) RegisterGameObjectAIWithFactory(ai_name, GetBlackrockSpireAI) #endif diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp index d979336cb..a4f436277 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp @@ -40,98 +40,85 @@ enum Events EVENT_CHECK_CONFLAGRATION_TARGET }; -class boss_drakkisath : public CreatureScript +struct boss_drakkisath : public BossAI { -public: - boss_drakkisath() : CreatureScript("boss_drakkisath") { } - - struct boss_drakkisathAI : public BossAI + boss_drakkisath(Creature* creature) : BossAI(creature, DATA_GENERAL_DRAKKISATH) { - boss_drakkisathAI(Creature* creature) : BossAI(creature, DATA_GENERAL_DRAKKISATH) - { - _conflagrateThreat = 0.0f; - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_FLAMESTRIKE, 6s); - events.ScheduleEvent(EVENT_CLEAVE, 8s); - events.ScheduleEvent(EVENT_CONFLAGRATION, 15s); - events.ScheduleEvent(EVENT_THUNDERCLAP, 17s); - events.ScheduleEvent(EVENT_PIERCE_ARMOR, 5s); - events.ScheduleEvent(EVENT_RAGE, 1s); - } - - 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_FLAMESTRIKE: - DoCastAOE(SPELL_FLAMESTRIKE); - events.ScheduleEvent(EVENT_FLAMESTRIKE, 10s); - break; - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, 8s); - break; - case EVENT_CONFLAGRATION: - DoCastVictim(SPELL_CONFLAGRATION); - - if (Unit* target = me->GetVictim()) - { - _conflagrateTarget = me->GetVictim()->GetGUID(); - _conflagrateThreat = me->GetThreatMgr().GetThreat(me->GetVictim()); - me->GetThreatMgr().ModifyThreatByPercent(target, -100); - } - events.ScheduleEvent(EVENT_CONFLAGRATION, 18s, 25s); - events.ScheduleEvent(EVENT_CHECK_CONFLAGRATION_TARGET, 10s); - break; - case EVENT_THUNDERCLAP: - DoCastVictim(SPELL_THUNDERCLAP); - events.ScheduleEvent(EVENT_THUNDERCLAP, 20s); - break; - case EVENT_PIERCE_ARMOR: - DoCastVictim(SPELL_PIERCE_ARMOR); - events.ScheduleEvent(EVENT_PIERCE_ARMOR, 40s); - break; - case EVENT_RAGE: - DoCastSelf(SPELL_RAGE); - events.ScheduleEvent(EVENT_RAGE, 35s); - break; - case EVENT_CHECK_CONFLAGRATION_TARGET: - if (Unit* target = ObjectAccessor::GetUnit(*me, _conflagrateTarget)) - { - me->GetThreatMgr().AddThreat(target, _conflagrateThreat); - } - break; - } - } - DoMeleeAttackIfReady(); - } - - private: - float _conflagrateThreat; - ObjectGuid _conflagrateTarget; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + _conflagrateThreat = 0.0f; } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(EVENT_FLAMESTRIKE, 6s); + events.ScheduleEvent(EVENT_CLEAVE, 8s); + events.ScheduleEvent(EVENT_CONFLAGRATION, 15s); + events.ScheduleEvent(EVENT_THUNDERCLAP, 17s); + events.ScheduleEvent(EVENT_PIERCE_ARMOR, 5s); + events.ScheduleEvent(EVENT_RAGE, 1s); + } + + 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_FLAMESTRIKE: + DoCastAOE(SPELL_FLAMESTRIKE); + events.ScheduleEvent(EVENT_FLAMESTRIKE, 10s); + break; + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, 8s); + break; + case EVENT_CONFLAGRATION: + DoCastVictim(SPELL_CONFLAGRATION); + + if (Unit* target = me->GetVictim()) + { + _conflagrateTarget = me->GetVictim()->GetGUID(); + _conflagrateThreat = me->GetThreatMgr().GetThreat(me->GetVictim()); + me->GetThreatMgr().ModifyThreatByPercent(target, -100); + } + events.ScheduleEvent(EVENT_CONFLAGRATION, 18s, 25s); + events.ScheduleEvent(EVENT_CHECK_CONFLAGRATION_TARGET, 10s); + break; + case EVENT_THUNDERCLAP: + DoCastVictim(SPELL_THUNDERCLAP); + events.ScheduleEvent(EVENT_THUNDERCLAP, 20s); + break; + case EVENT_PIERCE_ARMOR: + DoCastVictim(SPELL_PIERCE_ARMOR); + events.ScheduleEvent(EVENT_PIERCE_ARMOR, 40s); + break; + case EVENT_RAGE: + DoCastSelf(SPELL_RAGE); + events.ScheduleEvent(EVENT_RAGE, 35s); + break; + case EVENT_CHECK_CONFLAGRATION_TARGET: + if (Unit* target = ObjectAccessor::GetUnit(*me, _conflagrateTarget)) + me->GetThreatMgr().AddThreat(target, _conflagrateThreat); + break; + } + } + DoMeleeAttackIfReady(); + } + +private: + float _conflagrateThreat; + ObjectGuid _conflagrateTarget; }; void AddSC_boss_drakkisath() { - new boss_drakkisath(); + RegisterBlackrockSpireCreatureAI(boss_drakkisath); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp index f04431c5c..072ff592d 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp @@ -51,125 +51,114 @@ enum Events EVENT_SUMMONED_2 = 6 }; -class boss_gyth : public CreatureScript +struct boss_gyth : public BossAI { -public: - boss_gyth() : CreatureScript("boss_gyth") { } + boss_gyth(Creature* creature) : BossAI(creature, DATA_GYTH) { } - struct boss_gythAI : public BossAI + void Reset() override { - boss_gythAI(Creature* creature) : BossAI(creature, DATA_GYTH) { } - - void Reset() override + if (instance->GetBossState(DATA_GYTH) == IN_PROGRESS) { - if (instance->GetBossState(DATA_GYTH) == IN_PROGRESS) - { - instance->SetBossState(DATA_GYTH, NOT_STARTED); - summons.DespawnAll(); - me->DespawnOrUnsummon(); - } - - SetInvincibility(true); // Don't let boss die before summoning Rend. - - ScheduleHealthCheckEvent(25, [&] { - DoCastAOE(SPELL_SUMMON_REND, true); - me->RemoveAura(SPELL_REND_MOUNTS); - SetInvincibility(false); - }); + instance->SetBossState(DATA_GYTH, NOT_STARTED); + summons.DespawnAll(); + me->DespawnOrUnsummon(); } - void JustEngagedWith(Unit* /*who*/) override + SetInvincibility(true); // Don't let boss die before summoning Rend. + + ScheduleHealthCheckEvent(25, [&] { + DoCastAOE(SPELL_SUMMON_REND, true); + me->RemoveAura(SPELL_REND_MOUNTS); + SetInvincibility(false); + }); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + + events.ScheduleEvent(EVENT_CORROSIVE_ACID, 8s, 16s); + events.ScheduleEvent(EVENT_FREEZE, 8s, 16s); + events.ScheduleEvent(EVENT_FLAME_BREATH, 8s, 16s); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 12s, 18s); + } + + void EnterEvadeMode(EvadeReason why) override + { + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); + BossAI::EnterEvadeMode(why); + } + + void IsSummonedBy(WorldObject* /*summoner*/) override + { + events.ScheduleEvent(EVENT_SUMMONED_1, 1s); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + summon->AI()->AttackStart(me->SelectVictim()); + } + + // Prevent clearing summon list, otherwise Rend despawns if the drake is killed first. + void JustDied(Unit* /*killer*/) override + { + instance->SetBossState(DATA_GYTH, DONE); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) { - _JustEngagedWith(); - - events.ScheduleEvent(EVENT_CORROSIVE_ACID, 8s, 16s); - events.ScheduleEvent(EVENT_FREEZE, 8s, 16s); - events.ScheduleEvent(EVENT_FLAME_BREATH, 8s, 16s); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 12s, 18s); - } - - void EnterEvadeMode(EvadeReason why) override - { - instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); - BossAI::EnterEvadeMode(why); - } - - void IsSummonedBy(WorldObject* /*summoner*/) override - { - events.ScheduleEvent(EVENT_SUMMONED_1, 1s); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - summon->AI()->AttackStart(me->SelectVictim()); - } - - // Prevent clearing summon list, otherwise Rend despawns if the drake is killed first. - void JustDied(Unit* /*killer*/) override - { - instance->SetBossState(DATA_GYTH, DONE); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SUMMONED_1: - me->AddAura(SPELL_REND_MOUNTS, me); - if (GameObject* portcullis = me->FindNearestGameObject(GO_DR_PORTCULLIS, 40.0f)) - portcullis->UseDoorOrButton(); - events.ScheduleEvent(EVENT_SUMMONED_2, 2s); - break; - case EVENT_SUMMONED_2: - me->GetMotionMaster()->MoveWaypoint(GYTH_PATH_1, false); - break; - default: - break; - } - } - return; - } - events.Update(diff); while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { - case EVENT_CORROSIVE_ACID: - DoCast(me, SPELL_CORROSIVE_ACID); - events.ScheduleEvent(EVENT_CORROSIVE_ACID, 10s, 16s); + case EVENT_SUMMONED_1: + me->AddAura(SPELL_REND_MOUNTS, me); + if (GameObject* portcullis = me->FindNearestGameObject(GO_DR_PORTCULLIS, 40.0f)) + portcullis->UseDoorOrButton(); + events.ScheduleEvent(EVENT_SUMMONED_2, 2s); break; - case EVENT_FREEZE: - DoCast(me, SPELL_FREEZE); - events.ScheduleEvent(EVENT_FREEZE, 10s, 16s); - break; - case EVENT_FLAME_BREATH: - DoCast(me, SPELL_FLAMEBREATH); - events.ScheduleEvent(EVENT_FLAME_BREATH, 10s, 16s); - break; - case EVENT_KNOCK_AWAY: - DoCastVictim(SPELL_KNOCK_AWAY); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 14s, 20s); + case EVENT_SUMMONED_2: + me->GetMotionMaster()->MoveWaypoint(GYTH_PATH_1, false); break; default: break; } } - DoMeleeAttackIfReady(); + return; } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_CORROSIVE_ACID: + DoCast(me, SPELL_CORROSIVE_ACID); + events.ScheduleEvent(EVENT_CORROSIVE_ACID, 10s, 16s); + break; + case EVENT_FREEZE: + DoCast(me, SPELL_FREEZE); + events.ScheduleEvent(EVENT_FREEZE, 10s, 16s); + break; + case EVENT_FLAME_BREATH: + DoCast(me, SPELL_FLAMEBREATH); + events.ScheduleEvent(EVENT_FLAME_BREATH, 10s, 16s); + break; + case EVENT_KNOCK_AWAY: + DoCastVictim(SPELL_KNOCK_AWAY); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 14s, 20s); + break; + default: + break; + } + } + DoMeleeAttackIfReady(); } }; @@ -231,6 +220,6 @@ class spell_gyth_chromatic_protection : public AuraScript void AddSC_boss_gyth() { - new boss_gyth(); + RegisterBlackrockSpireCreatureAI(boss_gyth); RegisterSpellScript(spell_gyth_chromatic_protection); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_halycon.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_halycon.cpp index bd2ffde13..93f1c2054 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_halycon.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_halycon.cpp @@ -38,70 +38,59 @@ enum Events const Position SummonLocation = { -167.9561f, -411.7844f, 76.23057f, 1.53589f }; -class boss_halycon : public CreatureScript +struct boss_halycon : public BossAI { -public: - boss_halycon() : CreatureScript("boss_halycon") { } + boss_halycon(Creature* creature) : BossAI(creature, DATA_HALYCON) { } - struct boss_halyconAI : public BossAI + void Reset() override { - boss_halyconAI(Creature* creature) : BossAI(creature, DATA_HALYCON) { } + _Reset(); + } - void Reset() override + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(EVENT_REND, 17s, 20s); + events.ScheduleEvent(EVENT_THRASH, 10s, 12s); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + me->SummonCreature(NPC_GIZRUL_THE_SLAVENER, SummonLocation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 300000); + Talk(EMOTE_DEATH); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_REND, 17s, 20s); - events.ScheduleEvent(EVENT_THRASH, 10s, 12s); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - me->SummonCreature(NPC_GIZRUL_THE_SLAVENER, SummonLocation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 300000); - Talk(EMOTE_DEATH); - } - - 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) { - switch (eventId) - { - case EVENT_REND: - DoCastVictim(SPELL_REND); - events.ScheduleEvent(EVENT_REND, 8s, 10s); - break; - case EVENT_THRASH: - DoCast(me, SPELL_THRASH); - break; - default: - break; - } + case EVENT_REND: + DoCastVictim(SPELL_REND); + events.ScheduleEvent(EVENT_REND, 8s, 10s); + break; + case EVENT_THRASH: + DoCast(me, SPELL_THRASH); + break; + default: + break; } - DoMeleeAttackIfReady(); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + DoMeleeAttackIfReady(); } }; void AddSC_boss_halycon() { - new boss_halycon(); + RegisterBlackrockSpireCreatureAI(boss_halycon); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_highlord_omokk.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_highlord_omokk.cpp index b7e03f34f..5de41fcf1 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_highlord_omokk.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_highlord_omokk.cpp @@ -31,69 +31,58 @@ enum Events EVENT_KNOCK_AWAY = 2 }; -class boss_highlord_omokk : public CreatureScript +struct boss_highlord_omokk : public BossAI { -public: - boss_highlord_omokk() : CreatureScript("boss_highlord_omokk") { } + boss_highlord_omokk(Creature* creature) : BossAI(creature, DATA_HIGHLORD_OMOKK) { } - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackrockSpireAI(creature); + _Reset(); } - struct boss_highlordomokkAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_highlordomokkAI(Creature* creature) : BossAI(creature, DATA_HIGHLORD_OMOKK) { } + _JustEngagedWith(); + events.ScheduleEvent(EVENT_FRENZY, 20s); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 18s); + } - void Reset() override + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_FRENZY, 20s); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 18s); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - } - - 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) { - switch (eventId) - { - case EVENT_FRENZY: - DoCastVictim(SPELL_FRENZY); - events.ScheduleEvent(EVENT_FRENZY, 60s); - break; - case EVENT_KNOCK_AWAY: - DoCastVictim(SPELL_KNOCK_AWAY); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 12s); - break; - default: - break; - } + case EVENT_FRENZY: + DoCastVictim(SPELL_FRENZY); + events.ScheduleEvent(EVENT_FRENZY, 60s); + break; + case EVENT_KNOCK_AWAY: + DoCastVictim(SPELL_KNOCK_AWAY); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 12s); + break; + default: + break; } - DoMeleeAttackIfReady(); } - }; + DoMeleeAttackIfReady(); + } }; void AddSC_boss_highlordomokk() { - new boss_highlord_omokk(); + RegisterBlackrockSpireCreatureAI(boss_highlord_omokk); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_lord_valthalak.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_lord_valthalak.cpp index 0eeab9e2a..6b0f31217 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_lord_valthalak.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_lord_valthalak.cpp @@ -49,145 +49,130 @@ enum EventPhase EVENT_PHASE_FIGHT = 2 }; -class boss_lord_valthalak : public CreatureScript +struct boss_lord_valthalak : public BossAI { -public: - boss_lord_valthalak() : CreatureScript("boss_lord_valthalak") { } + boss_lord_valthalak(Creature* creature) : BossAI(creature, DATA_LORD_VALTHALAK) { } - struct boss_lord_valthalakAI : public BossAI + void Reset() override { - boss_lord_valthalakAI(Creature* creature) : BossAI(creature, DATA_LORD_VALTHALAK) { } + BossAI::Reset(); - void Reset() override - { - BossAI::Reset(); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - - frenzy40 = false; - frenzy15 = false; - } - - void IsSummonedBy(WorldObject* /*summoner*/) override - { - StartTalking(TALK_SUMMON, 8s); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - - Talk(TALK_AGGRO); - - events.SetPhase(EVENT_PHASE_FIGHT); - events.ScheduleEvent(EVENT_SUMMON_SPECTRAL_ASSASSIN, 6s, 8s, 0, EVENT_PHASE_FIGHT); - events.ScheduleEvent(EVENT_SHADOW_WRATH, 9s, 18s, 0, EVENT_PHASE_FIGHT); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - - instance->SetData(DATA_LORD_VALTHALAK, DONE); - } - - void StartTalking(uint32 talkGroupId, Milliseconds timer) - { - me->SetReactState(REACT_PASSIVE); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->AttackStop(); - - Talk(talkGroupId); - - events.SetPhase(EVENT_PHASE_TALK); - events.ScheduleEvent(EVENT_FIGHT, timer, 0, EVENT_PHASE_TALK); - } - - void StartFighting() - { - events.SetPhase(EVENT_PHASE_FIGHT); - - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - - DoZoneInCombat(); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override - { - if (!frenzy40 && me->HealthBelowPctDamaged(40, damage)) - { - frenzy40 = true; - DoCast(me, SPELL_FRENZY); - - events.CancelEvent(EVENT_SUMMON_SPECTRAL_ASSASSIN); - - StartTalking(TALK_40_HP, 5s); - } - - if (!frenzy15 && me->HealthBelowPctDamaged(15, damage)) - { - frenzy15 = true; - - events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 12s, 19s, 0, EVENT_PHASE_FIGHT); - - StartTalking(TALK_15_HP, 5s); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() && !events.IsInPhase(EVENT_PHASE_TALK)) - { - return; - } - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_FIGHT: - StartFighting(); - break; - case EVENT_SUMMON_SPECTRAL_ASSASSIN: - DoCast(me, SPELL_SUMMON_SPECTRAL_ASSASSIN); - events.ScheduleEvent(EVENT_SUMMON_SPECTRAL_ASSASSIN, 30s, 35s, 0, EVENT_PHASE_FIGHT); - break; - case EVENT_SHADOW_BOLT_VOLLEY: - DoCastVictim(SPELL_SHADOW_BOLT_VOLLEY); - events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 4s, 6s, 0, EVENT_PHASE_FIGHT); - break; - case EVENT_SHADOW_WRATH: - DoCastVictim(SPELL_SHADOW_WRATH); - events.ScheduleEvent(EVENT_SHADOW_WRATH, 19s, 24s, 0, EVENT_PHASE_FIGHT); - break; - default: - break; - } - } - - DoMeleeAttackIfReady(); - } - private: - bool frenzy40; - bool frenzy15; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + _frenzy40 = false; + _frenzy15 = false; } + + void IsSummonedBy(WorldObject* /*summoner*/) override + { + StartTalking(TALK_SUMMON, 8s); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + + Talk(TALK_AGGRO); + + events.SetPhase(EVENT_PHASE_FIGHT); + events.ScheduleEvent(EVENT_SUMMON_SPECTRAL_ASSASSIN, 6s, 8s, 0, EVENT_PHASE_FIGHT); + events.ScheduleEvent(EVENT_SHADOW_WRATH, 9s, 18s, 0, EVENT_PHASE_FIGHT); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + + instance->SetData(DATA_LORD_VALTHALAK, DONE); + } + + void StartTalking(uint32 talkGroupId, Milliseconds timer) + { + me->SetReactState(REACT_PASSIVE); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->AttackStop(); + + Talk(talkGroupId); + + events.SetPhase(EVENT_PHASE_TALK); + events.ScheduleEvent(EVENT_FIGHT, timer, 0, EVENT_PHASE_TALK); + } + + void StartFighting() + { + events.SetPhase(EVENT_PHASE_FIGHT); + + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + + DoZoneInCombat(); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override + { + if (!_frenzy40 && me->HealthBelowPctDamaged(40, damage)) + { + _frenzy40 = true; + DoCast(me, SPELL_FRENZY); + + events.CancelEvent(EVENT_SUMMON_SPECTRAL_ASSASSIN); + + StartTalking(TALK_40_HP, 5s); + } + + if (!_frenzy15 && me->HealthBelowPctDamaged(15, damage)) + { + _frenzy15 = true; + + events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 12s, 19s, 0, EVENT_PHASE_FIGHT); + + StartTalking(TALK_15_HP, 5s); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(EVENT_PHASE_TALK)) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_FIGHT: + StartFighting(); + break; + case EVENT_SUMMON_SPECTRAL_ASSASSIN: + DoCast(me, SPELL_SUMMON_SPECTRAL_ASSASSIN); + events.ScheduleEvent(EVENT_SUMMON_SPECTRAL_ASSASSIN, 30s, 35s, 0, EVENT_PHASE_FIGHT); + break; + case EVENT_SHADOW_BOLT_VOLLEY: + DoCastVictim(SPELL_SHADOW_BOLT_VOLLEY); + events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 4s, 6s, 0, EVENT_PHASE_FIGHT); + break; + case EVENT_SHADOW_WRATH: + DoCastVictim(SPELL_SHADOW_WRATH); + events.ScheduleEvent(EVENT_SHADOW_WRATH, 19s, 24s, 0, EVENT_PHASE_FIGHT); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } +private: + bool _frenzy40; + bool _frenzy15; }; void AddSC_boss_lord_valthalak() { - new boss_lord_valthalak(); + RegisterBlackrockSpireCreatureAI(boss_lord_valthalak); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp index 002047010..c772ed975 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp @@ -96,13 +96,9 @@ struct boss_mor_grayhoof : public BossAI case PHASE_HUMAN: spell = Acore::Containers::SelectRandomContainerElement(humanSpells); if (spell == SPELL_REJUVENATION || spell == SPELL_HEALING_TOUCH) - { DoCastSelf(spell); - } else - { DoCastAOE(spell); - } break; case PHASE_BEAR: spell = Acore::Containers::SelectRandomContainerElement(bearSpells); @@ -155,9 +151,7 @@ struct boss_mor_grayhoof : public BossAI { CastRandomSpell(PHASE_BEAR); if (context.GetRepeatCounter() <= 3) - { context.Repeat(); - } }); } else if (_phase == PHASE_BEAR && me->HealthBelowPct(50.f)) @@ -170,9 +164,7 @@ struct boss_mor_grayhoof : public BossAI { CastRandomSpell(PHASE_CAT); if (context.GetRepeatCounter() <= 3) - { context.Repeat(); - } }); } else if (_phase == PHASE_CAT && me->HealthBelowPct(25.f)) @@ -203,9 +195,7 @@ struct boss_mor_grayhoof : public BossAI void UpdateAI(uint32 diff) override { if (!UpdateVictim()) - { return; - } _scheduler.Update(diff, [this] { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_overlord_wyrmthalak.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_overlord_wyrmthalak.cpp index 4f2b2511e..74a8227c7 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_overlord_wyrmthalak.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_overlord_wyrmthalak.cpp @@ -46,93 +46,83 @@ constexpr uint32 CALL_HELP = 0; const Position SummonLocation1 = {-49.43f, -455.82f, 77.82f, 4.61f}; const Position SummonLocation2 = {-58.48f, -456.29f, 77.82f, 4.613f}; -class boss_overlord_wyrmthalak : public CreatureScript +struct boss_overlord_wyrmthalak : public BossAI { -public: - boss_overlord_wyrmthalak() : CreatureScript("boss_overlord_wyrmthalak") { } + boss_overlord_wyrmthalak(Creature* creature) : BossAI(creature, DATA_OVERLORD_WYRMTHALAK) { } - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackrockSpireAI(creature); + _Reset(); + _summoned = false; } - struct boss_overlordwyrmthalakAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_overlordwyrmthalakAI(Creature* creature) : BossAI(creature, DATA_OVERLORD_WYRMTHALAK) { } + _JustEngagedWith(); + events.ScheduleEvent(EVENT_BLAST_WAVE, 20s); + events.ScheduleEvent(EVENT_SHOUT, 2s); + events.ScheduleEvent(EVENT_CLEAVE, 6s); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 12s); + } - bool Summoned; + void JustDied(Unit* /*killer*/) override + { + instance->SetBossState(DATA_OVERLORD_WYRMTHALAK, DONE); + } - void Reset() override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (!_summoned && HealthBelowPct(51)) { - _Reset(); - Summoned = false; - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_BLAST_WAVE, 20s); - events.ScheduleEvent(EVENT_SHOUT, 2s); - events.ScheduleEvent(EVENT_CLEAVE, 6s); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 12s); - } - - void JustDied(Unit* /*killer*/) override - { - instance->SetBossState(DATA_OVERLORD_WYRMTHALAK, DONE); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (!Summoned && HealthBelowPct(51)) + Talk(CALL_HELP); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) { - Talk(CALL_HELP); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - { - if (Creature* warlord = me->SummonCreature(NPC_SPIRESTONE_WARLORD, SummonLocation1, TEMPSUMMON_TIMED_DESPAWN, 300 * IN_MILLISECONDS)) - warlord->AI()->AttackStart(target); - if (Creature* berserker = me->SummonCreature(NPC_SMOLDERTHORN_BERSERKER, SummonLocation2, TEMPSUMMON_TIMED_DESPAWN, 300 * IN_MILLISECONDS)) - berserker->AI()->AttackStart(target); - Summoned = true; - } + if (Creature* warlord = me->SummonCreature(NPC_SPIRESTONE_WARLORD, SummonLocation1, TEMPSUMMON_TIMED_DESPAWN, 300 * IN_MILLISECONDS)) + warlord->AI()->AttackStart(target); + if (Creature* berserker = me->SummonCreature(NPC_SMOLDERTHORN_BERSERKER, SummonLocation2, TEMPSUMMON_TIMED_DESPAWN, 300 * IN_MILLISECONDS)) + berserker->AI()->AttackStart(target); + _summoned = true; } - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_BLAST_WAVE: - DoCastVictim(SPELL_BLASTWAVE); - events.ScheduleEvent(EVENT_BLAST_WAVE, 20s); - break; - case EVENT_SHOUT: - DoCastVictim(SPELL_SHOUT); - events.ScheduleEvent(EVENT_SHOUT, 10s); - break; - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, 7s); - break; - case EVENT_KNOCK_AWAY: - DoCastVictim(SPELL_KNOCKAWAY); - events.ScheduleEvent(EVENT_KNOCK_AWAY, 14s); - break; - } - } - DoMeleeAttackIfReady(); } - }; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BLAST_WAVE: + DoCastVictim(SPELL_BLASTWAVE); + events.ScheduleEvent(EVENT_BLAST_WAVE, 20s); + break; + case EVENT_SHOUT: + DoCastVictim(SPELL_SHOUT); + events.ScheduleEvent(EVENT_SHOUT, 10s); + break; + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, 7s); + break; + case EVENT_KNOCK_AWAY: + DoCastVictim(SPELL_KNOCKAWAY); + events.ScheduleEvent(EVENT_KNOCK_AWAY, 14s); + break; + } + } + DoMeleeAttackIfReady(); + } + +private: + bool _summoned; }; void AddSC_boss_overlordwyrmthalak() { - new boss_overlord_wyrmthalak(); + RegisterBlackrockSpireCreatureAI(boss_overlord_wyrmthalak); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_pyroguard_emberseer.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_pyroguard_emberseer.cpp index 55c171291..753dfcb99 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_pyroguard_emberseer.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_pyroguard_emberseer.cpp @@ -68,241 +68,226 @@ enum Events EVENT_ENTER_COMBAT = 10 }; -class boss_pyroguard_emberseer : public CreatureScript +struct boss_pyroguard_emberseer : public BossAI { -public: - boss_pyroguard_emberseer() : CreatureScript("boss_pyroguard_emberseer") { } + boss_pyroguard_emberseer(Creature* creature) : BossAI(creature, DATA_PYROGAURD_EMBERSEER) { } - struct boss_pyroguard_emberseerAI : public BossAI + void Reset() override { - boss_pyroguard_emberseerAI(Creature* creature) : BossAI(creature, DATA_PYROGAURD_EMBERSEER) { } + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(true); + events.Reset(); + // Apply auras on spawn and reset + // DoCast(me, SPELL_FIRE_SHIELD_TRIGGER); // Need to find this in old DBC if possible + me->RemoveAura(SPELL_EMBERSEER_FULL_STRENGTH); + me->RemoveAura(SPELL_EMBERSEER_GROWING); + me->RemoveAura(SPELL_EMBERSEER_GROWING_TRIGGER); + events.ScheduleEvent(EVENT_RESPAWN, 5s); + // Hack for missing trigger spell + events.ScheduleEvent(EVENT_FIRE_SHIELD, 3s); - void Reset() override + // Open doors on reset + if (instance->GetBossState(DATA_PYROGAURD_EMBERSEER) == IN_PROGRESS) + OpenDoors(false); // Opens 2 entrance doors + } + + void SetData(uint32 /*type*/, uint32 data) override + { + switch (data) { - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToPC(true); - events.Reset(); - // Apply auras on spawn and reset - // DoCast(me, SPELL_FIRE_SHIELD_TRIGGER); // Need to find this in old DBC if possible - me->RemoveAura(SPELL_EMBERSEER_FULL_STRENGTH); - me->RemoveAura(SPELL_EMBERSEER_GROWING); - me->RemoveAura(SPELL_EMBERSEER_GROWING_TRIGGER); - events.ScheduleEvent(EVENT_RESPAWN, 5s); - // Hack for missing trigger spell - events.ScheduleEvent(EVENT_FIRE_SHIELD, 3s); - - // Open doors on reset - if (instance->GetBossState(DATA_PYROGAURD_EMBERSEER) == IN_PROGRESS) - OpenDoors(false); // Opens 2 entrance doors + case 1: + events.ScheduleEvent(EVENT_PRE_FIGHT_1, 2s); + instance->SetBossState(DATA_PYROGAURD_EMBERSEER, IN_PROGRESS); + break; + case 2: + // Close these two doors on Blackhand Incarcerators aggro + if (GameObject* door1 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_IN))) + if (door1->GetGoState() == GO_STATE_ACTIVE) + door1->SetGoState(GO_STATE_READY); + if (GameObject* door2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_DOORS))) + if (door2->GetGoState() == GO_STATE_ACTIVE) + door2->SetGoState(GO_STATE_READY); + break; + case 3: + Reset(); + break; + default: + break; } + } - void SetData(uint32 /*type*/, uint32 data) override + void JustEngagedWith(Unit* /*who*/) override + { + // ### TODO Check combat timing ### + events.ScheduleEvent(EVENT_FIRENOVA, 6s); + events.ScheduleEvent(EVENT_FLAMEBUFFET, 3s); + events.ScheduleEvent(EVENT_PYROBLAST, 14s); + } + + void JustDied(Unit* /*killer*/) override + { + // Activate all the runes + UpdateRunes(GO_STATE_READY); + // Opens all 3 doors + OpenDoors(true); + // Complete encounter + instance->SetBossState(DATA_PYROGAURD_EMBERSEER, DONE); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_ENCAGE_EMBERSEER) + if (!me->GetAuraCount(SPELL_ENCAGED_EMBERSEER)) + me->CastSpell(me, SPELL_ENCAGED_EMBERSEER); + + if (spell->Id == SPELL_EMBERSEER_GROWING_TRIGGER) { - switch (data) + if (me->GetAuraCount(SPELL_EMBERSEER_GROWING_TRIGGER) == 10) + Talk(EMOTE_TEN_STACK); + + if (me->GetAuraCount(SPELL_EMBERSEER_GROWING_TRIGGER) == 20) { - case 1: - events.ScheduleEvent(EVENT_PRE_FIGHT_1, 2s); - instance->SetBossState(DATA_PYROGAURD_EMBERSEER, IN_PROGRESS); - break; - case 2: - // Close these two doors on Blackhand Incarcerators aggro - if (GameObject* door1 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_IN))) - if (door1->GetGoState() == GO_STATE_ACTIVE) - door1->SetGoState(GO_STATE_READY); - if (GameObject* door2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_DOORS))) - if (door2->GetGoState() == GO_STATE_ACTIVE) - door2->SetGoState(GO_STATE_READY); - break; - case 3: - Reset(); - break; - default: - break; + events.CancelEvent(EVENT_FIRE_SHIELD); // temporarily cancel fire shield to keep it from interrupting combat start + + // Schedule out the pre-combat scene + events.ScheduleEvent(EVENT_PRE_ENTER_COMBAT_1, 0s); + events.ScheduleEvent(EVENT_PRE_ENTER_COMBAT_2, 2s); + events.ScheduleEvent(EVENT_ENTER_COMBAT, 5s); } } + } - void JustEngagedWith(Unit* /*who*/) override + void OpenDoors(bool Boss_Killed) + { + // These two doors reopen on reset or boss kill + if (GameObject* door1 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_IN))) + door1->SetGoState(GO_STATE_ACTIVE); + if (GameObject* door2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_DOORS))) + door2->SetGoState(GO_STATE_ACTIVE); + + // This door opens on boss kill + if (Boss_Killed) + if (GameObject* door3 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_OUT))) + door3->SetGoState(GO_STATE_ACTIVE); + } + + void UpdateRunes(GOState state) + { + // update all runes + if (GameObject* rune1 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_1))) + rune1->SetGoState(state); + if (GameObject* rune2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_2))) + rune2->SetGoState(state); + if (GameObject* rune3 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_3))) + rune3->SetGoState(state); + if (GameObject* rune4 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_4))) + rune4->SetGoState(state); + if (GameObject* rune5 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_5))) + rune5->SetGoState(state); + if (GameObject* rune6 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_6))) + rune6->SetGoState(state); + if (GameObject* rune7 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_7))) + rune7->SetGoState(state); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) { - // ### TODO Check combat timing ### - events.ScheduleEvent(EVENT_FIRENOVA, 6s); - events.ScheduleEvent(EVENT_FLAMEBUFFET, 3s); - events.ScheduleEvent(EVENT_PYROBLAST, 14s); - } - - void JustDied(Unit* /*killer*/) override - { - // Activate all the runes - UpdateRunes(GO_STATE_READY); - // Opens all 3 doors - OpenDoors(true); - // Complete encounter - instance->SetBossState(DATA_PYROGAURD_EMBERSEER, DONE); - } - - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - if (spell->Id == SPELL_ENCAGE_EMBERSEER) - { - if (!me->GetAuraCount(SPELL_ENCAGED_EMBERSEER)) - me->CastSpell(me, SPELL_ENCAGED_EMBERSEER); - } - - if (spell->Id == SPELL_EMBERSEER_GROWING_TRIGGER) - { - if (me->GetAuraCount(SPELL_EMBERSEER_GROWING_TRIGGER) == 10) - Talk(EMOTE_TEN_STACK); - - if (me->GetAuraCount(SPELL_EMBERSEER_GROWING_TRIGGER) == 20) - { - events.CancelEvent(EVENT_FIRE_SHIELD); // temporarily cancel fire shield to keep it from interrupting combat start - - // Schedule out the pre-combat scene - events.ScheduleEvent(EVENT_PRE_ENTER_COMBAT_1, 0s); - events.ScheduleEvent(EVENT_PRE_ENTER_COMBAT_2, 2s); - events.ScheduleEvent(EVENT_ENTER_COMBAT, 5s); - } - } - } - - void OpenDoors(bool Boss_Killed) - { - // These two doors reopen on reset or boss kill - if (GameObject* door1 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_IN))) - door1->SetGoState(GO_STATE_ACTIVE); - if (GameObject* door2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_DOORS))) - door2->SetGoState(GO_STATE_ACTIVE); - - // This door opens on boss kill - if (Boss_Killed) - if (GameObject* door3 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_OUT))) - door3->SetGoState(GO_STATE_ACTIVE); - } - - void UpdateRunes(GOState state) - { - // update all runes - if (GameObject* rune1 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_1))) - rune1->SetGoState(state); - if (GameObject* rune2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_2))) - rune2->SetGoState(state); - if (GameObject* rune3 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_3))) - rune3->SetGoState(state); - if (GameObject* rune4 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_4))) - rune4->SetGoState(state); - if (GameObject* rune5 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_5))) - rune5->SetGoState(state); - if (GameObject* rune6 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_6))) - rune6->SetGoState(state); - if (GameObject* rune7 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_EMBERSEER_RUNE_7))) - rune7->SetGoState(state); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_RESPAWN: - { - // Respawn all Blackhand Incarcerators - std::list creatureList; - GetCreatureListWithEntryInGrid(creatureList, me, NPC_BLACKHAND_INCARCERATOR, 35.0f); - for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) - if (Creature* creature = *itr) - { - if (!creature->IsAlive()) - creature->Respawn(); - - creature->AI()->SetData(1, 2); - } - me->AddAura(SPELL_ENCAGED_EMBERSEER, me); - instance->SetBossState(DATA_PYROGAURD_EMBERSEER, NOT_STARTED); - break; - } - case EVENT_PRE_FIGHT_1: - { - // Set data on all Blackhand Incarcerators - std::list creatureList; - GetCreatureListWithEntryInGrid(creatureList, me, NPC_BLACKHAND_INCARCERATOR, 35.0f); - for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) - { - if (Creature* creature = *itr) - creature->AI()->SetData(1, 1); - } - events.ScheduleEvent(EVENT_PRE_FIGHT_2, 2s); - break; - } - case EVENT_PRE_FIGHT_2: - me->CastSpell(me, SPELL_FREEZE_ANIM); - me->CastSpell(me, SPELL_EMBERSEER_GROWING); - Talk(EMOTE_ONE_STACK); - break; - case EVENT_FIRE_SHIELD: - // #### Spell isn't doing any damage ??? #### - DoCast(me, SPELL_FIRE_SHIELD); - events.ScheduleEvent(EVENT_FIRE_SHIELD, 3s); - break; - case EVENT_PRE_ENTER_COMBAT_1: - me->RemoveAura(SPELL_ENCAGED_EMBERSEER); - me->RemoveAura(SPELL_FREEZE_ANIM); - me->CastSpell(me, SPELL_EMBERSEER_FULL_STRENGTH); - Talk(EMOTE_FREE_OF_BONDS); - break; - case EVENT_PRE_ENTER_COMBAT_2: - Talk(YELL_FREE_OF_BONDS); - break; - case EVENT_ENTER_COMBAT: - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToPC(false); - DoZoneInCombat(); - // re-enable fire shield - events.ScheduleEvent(EVENT_FIRE_SHIELD, 0s); - break; - default: - break; - } - } - return; - } - events.Update(diff); while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { + case EVENT_RESPAWN: + { + // Respawn all Blackhand Incarcerators + std::list creatureList; + GetCreatureListWithEntryInGrid(creatureList, me, NPC_BLACKHAND_INCARCERATOR, 35.0f); + for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) + if (Creature* creature = *itr) + { + if (!creature->IsAlive()) + creature->Respawn(); + + creature->AI()->SetData(1, 2); + } + me->AddAura(SPELL_ENCAGED_EMBERSEER, me); + instance->SetBossState(DATA_PYROGAURD_EMBERSEER, NOT_STARTED); + break; + } + case EVENT_PRE_FIGHT_1: + { + // Set data on all Blackhand Incarcerators + std::list creatureList; + GetCreatureListWithEntryInGrid(creatureList, me, NPC_BLACKHAND_INCARCERATOR, 35.0f); + for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) + if (Creature* creature = *itr) + creature->AI()->SetData(1, 1); + events.ScheduleEvent(EVENT_PRE_FIGHT_2, 2s); + break; + } + case EVENT_PRE_FIGHT_2: + me->CastSpell(me, SPELL_FREEZE_ANIM); + me->CastSpell(me, SPELL_EMBERSEER_GROWING); + Talk(EMOTE_ONE_STACK); + break; case EVENT_FIRE_SHIELD: + // #### Spell isn't doing any damage ??? #### DoCast(me, SPELL_FIRE_SHIELD); events.ScheduleEvent(EVENT_FIRE_SHIELD, 3s); break; - case EVENT_FIRENOVA: - DoCast(me, SPELL_FIRENOVA); - events.ScheduleEvent(EVENT_FIRENOVA, 6s); + case EVENT_PRE_ENTER_COMBAT_1: + me->RemoveAura(SPELL_ENCAGED_EMBERSEER); + me->RemoveAura(SPELL_FREEZE_ANIM); + me->CastSpell(me, SPELL_EMBERSEER_FULL_STRENGTH); + Talk(EMOTE_FREE_OF_BONDS); break; - case EVENT_FLAMEBUFFET: - DoCast(me, SPELL_FLAMEBUFFET); - events.ScheduleEvent(EVENT_FLAMEBUFFET, 14s); + case EVENT_PRE_ENTER_COMBAT_2: + Talk(YELL_FREE_OF_BONDS); break; - case EVENT_PYROBLAST: - DoCastRandomTarget(SPELL_PYROBLAST, 0, 100.0f); - events.ScheduleEvent(EVENT_PYROBLAST, 15s); + case EVENT_ENTER_COMBAT: + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); + DoZoneInCombat(); + // re-enable fire shield + events.ScheduleEvent(EVENT_FIRE_SHIELD, 0s); break; default: break; } } - DoMeleeAttackIfReady(); + return; } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_FIRE_SHIELD: + DoCast(me, SPELL_FIRE_SHIELD); + events.ScheduleEvent(EVENT_FIRE_SHIELD, 3s); + break; + case EVENT_FIRENOVA: + DoCast(me, SPELL_FIRENOVA); + events.ScheduleEvent(EVENT_FIRENOVA, 6s); + break; + case EVENT_FLAMEBUFFET: + DoCast(me, SPELL_FLAMEBUFFET); + events.ScheduleEvent(EVENT_FLAMEBUFFET, 14s); + break; + case EVENT_PYROBLAST: + DoCastRandomTarget(SPELL_PYROBLAST, 0, 100.0f); + events.ScheduleEvent(EVENT_PYROBLAST, 15s); + break; + default: + break; + } + } + DoMeleeAttackIfReady(); } }; @@ -321,127 +306,114 @@ enum IncarceratorData EMOTE_FLEE = 0 }; -class npc_blackhand_incarcerator : public CreatureScript +struct npc_blackhand_incarcerator : public ScriptedAI { -public: - npc_blackhand_incarcerator() : CreatureScript("npc_blackhand_incarcerator") { } - - struct npc_blackhand_incarceratorAI : public ScriptedAI + npc_blackhand_incarcerator(Creature* creature) : ScriptedAI(creature) { - npc_blackhand_incarceratorAI(Creature* creature) : ScriptedAI(creature) + _fleedForAssistance = false; + } + + void Reset() override + { + me->SetImmuneToAll(true); + if (Creature* Emberseer = me->FindNearestCreature(NPC_PYROGAURD_EMBERSEER, 30.0f, true)) + Emberseer->AI()->SetData(1, 3); + + _fleedForAssistance = false; + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override + { + if (!_fleedForAssistance && me->HealthBelowPctDamaged(30, damage)) { - _fleedForAssistance = false; + _fleedForAssistance = true; + me->DoFleeToGetAssistance(); + Talk(EMOTE_FLEE); + } + } + + void SetData(uint32 data, uint32 value) override + { + if (data == 1 && value == 1) + { + me->SetImmuneToAll(false); + me->InterruptSpell(CURRENT_CHANNELED_SPELL); + DoZoneInCombat(); + _events.CancelEvent(EVENT_ENCAGED_EMBERSEER); } - void Reset() override + if (data == 1 && value == 2) + _events.ScheduleEvent(EVENT_ENCAGED_EMBERSEER, 1s); + } + + void JustEngagedWith(Unit* /*who*/) override + { + // Used to close doors + if (Creature* Emberseer = me->FindNearestCreature(NPC_PYROGAURD_EMBERSEER, 30.0f, true)) + Emberseer->AI()->SetData(1, 2); + + // Had to do this because CallForHelp will ignore any npcs without LOS + std::list creatureList; + GetCreatureListWithEntryInGrid(creatureList, me, NPC_BLACKHAND_INCARCERATOR, 60.0f); + for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) + if (Creature* creature = *itr) + creature->SetInCombatWithZone(); // AI()->AttackStart(me->GetVictim()); + + _events.ScheduleEvent(EVENT_STRIKE, 8s, 16s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) { - me->SetImmuneToAll(true); - if (Creature* Emberseer = me->FindNearestCreature(NPC_PYROGAURD_EMBERSEER, 30.0f, true)) - Emberseer->AI()->SetData(1, 3); - - _fleedForAssistance = false; - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override - { - if (!_fleedForAssistance && me->HealthBelowPctDamaged(30, damage)) - { - _fleedForAssistance = true; - me->DoFleeToGetAssistance(); - Talk(EMOTE_FLEE); - } - } - - void SetData(uint32 data, uint32 value) override - { - if (data == 1 && value == 1) - { - me->SetImmuneToAll(false); - me->InterruptSpell(CURRENT_CHANNELED_SPELL); - DoZoneInCombat(); - _events.CancelEvent(EVENT_ENCAGED_EMBERSEER); - } - - if (data == 1 && value == 2) - _events.ScheduleEvent(EVENT_ENCAGED_EMBERSEER, 1s); - } - - void JustEngagedWith(Unit* /*who*/) override - { - // Used to close doors - if (Creature* Emberseer = me->FindNearestCreature(NPC_PYROGAURD_EMBERSEER, 30.0f, true)) - Emberseer->AI()->SetData(1, 2); - - // Had to do this because CallForHelp will ignore any npcs without LOS - std::list creatureList; - GetCreatureListWithEntryInGrid(creatureList, me, NPC_BLACKHAND_INCARCERATOR, 60.0f); - for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) - { - if (Creature* creature = *itr) - creature->SetInCombatWithZone(); // AI()->AttackStart(me->GetVictim()); - } - - _events.ScheduleEvent(EVENT_STRIKE, 8s, 16s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - { - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_ENCAGED_EMBERSEER: - { - if (me->GetPositionX() == me->GetHomePosition().GetPositionX()) - if (!me->HasAura(SPELL_ENCAGE_EMBERSEER)) - if (Creature* Emberseer = me->FindNearestCreature(NPC_PYROGAURD_EMBERSEER, 30.0f, true)) - DoCast(Emberseer, SPELL_ENCAGE_EMBERSEER); - break; - } - } - } - return; - } - _events.Update(diff); while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { - case EVENT_STRIKE: - DoCastVictim(SPELL_STRIKE, true); - _events.ScheduleEvent(EVENT_STRIKE, 14s, 23s); - break; - case EVENT_ENCAGE: - DoCast(SelectTarget(SelectTargetMethod::Random, 0, 100, true), EVENT_ENCAGE, true); - _events.ScheduleEvent(EVENT_ENCAGE, 6s, 12s); - break; - default: - break; + case EVENT_ENCAGED_EMBERSEER: + { + if (me->GetPositionX() == me->GetHomePosition().GetPositionX()) + if (!me->HasAura(SPELL_ENCAGE_EMBERSEER)) + if (Creature* Emberseer = me->FindNearestCreature(NPC_PYROGAURD_EMBERSEER, 30.0f, true)) + DoCast(Emberseer, SPELL_ENCAGE_EMBERSEER); + break; + } } } - - DoMeleeAttackIfReady(); + return; } - private: - EventMap _events; - bool _fleedForAssistance; - }; + _events.Update(diff); - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_STRIKE: + DoCastVictim(SPELL_STRIKE, true); + _events.ScheduleEvent(EVENT_STRIKE, 14s, 23s); + break; + case EVENT_ENCAGE: + DoCast(SelectTarget(SelectTargetMethod::Random, 0, 100, true), EVENT_ENCAGE, true); + _events.ScheduleEvent(EVENT_ENCAGE, 6s, 12s); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); } + +private: + EventMap _events; + bool _fleedForAssistance; }; void AddSC_boss_pyroguard_emberseer() { - new boss_pyroguard_emberseer(); - new npc_blackhand_incarcerator(); + RegisterBlackrockSpireCreatureAI(boss_pyroguard_emberseer); + RegisterBlackrockSpireCreatureAI(npc_blackhand_incarcerator); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_quartermaster_zigris.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_quartermaster_zigris.cpp index 21145b294..1a1c8f0c6 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_quartermaster_zigris.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_quartermaster_zigris.cpp @@ -87,16 +87,12 @@ struct boss_quartermaster_zigris : public BossAI void UpdateAI(uint32 diff) override { if (!UpdateVictim()) - { return; - } events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } while (uint32 eventId = events.ExecuteEvent()) { @@ -135,9 +131,7 @@ struct boss_quartermaster_zigris : public BossAI } if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } } DoMeleeAttackIfReady(); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp index 5e6438422..6a26c7bf2 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp @@ -99,346 +99,327 @@ enum Events EVENT_MORTAL_STRIKE = 32, }; -class boss_rend_blackhand : public CreatureScript +struct boss_rend_blackhand : public BossAI { -public: - boss_rend_blackhand() : CreatureScript("boss_rend_blackhand") { } + boss_rend_blackhand(Creature* creature) : BossAI(creature, DATA_WARCHIEF_REND_BLACKHAND) { } - struct boss_rend_blackhandAI : public BossAI + void Reset() override { - boss_rend_blackhandAI(Creature* creature) : BossAI(creature, DATA_WARCHIEF_REND_BLACKHAND) { } + _Reset(); - void Reset() override + if (instance->GetBossState(DATA_GYTH) == IN_PROGRESS) { - _Reset(); - - if (instance->GetBossState(DATA_GYTH) == IN_PROGRESS) - { - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PREPARATION); - me->SetImmuneToAll(false); - return; - } - - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PREPARATION); - me->SetImmuneToAll(true); - gythEvent = false; - victorGUID.Clear(); - waveDoorGUID.Clear(); - _currentWave = 0; - - summons.DespawnAll(); - - if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 5.0f, true)) - victor->Respawn(true); - - if (GameObject* exitDoor = me->GetMap()->GetGameObject(instance->GetGuidData(GO_GYTH_ENTRY_DOOR))) - exitDoor->SetGoState(GO_STATE_ACTIVE); - - instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, NOT_STARTED); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PREPARATION); + me->SetImmuneToAll(false); + return; } - void SummonedCreatureDies(Creature* /*creature*/, Unit* /*killer*/) override + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PREPARATION); + me->SetImmuneToAll(true); + _gythEvent = false; + _victorGUID.Clear(); + _waveDoorGUID.Clear(); + _currentWave = 0; + + summons.DespawnAll(); + + if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 5.0f, true)) + victor->Respawn(true); + + if (GameObject* exitDoor = me->GetMap()->GetGameObject(instance->GetGuidData(GO_GYTH_ENTRY_DOOR))) + exitDoor->SetGoState(GO_STATE_ACTIVE); + + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, NOT_STARTED); + } + + void SummonedCreatureDies(Creature* /*creature*/, Unit* /*killer*/) override + { + if (!summons.IsAnyCreatureAlive()) + events.ScheduleEvent(EVENT_WAVES_TEXT_1 + _currentWave, 10s); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + + if (summon->GetEntry() == NPC_GYTH) { - if (!summons.IsAnyCreatureAlive()) - { - events.ScheduleEvent(EVENT_WAVES_TEXT_1 + _currentWave, 10s); - } - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - - if (summon->GetEntry() == NPC_GYTH) - { - me->DespawnOrUnsummon(); - return; - } - - summon->AI()->DoZoneInCombat(nullptr, 100.0f); - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_WHIRLWIND, 13s, 15s); - events.ScheduleEvent(EVENT_CLEAVE, 15s, 17s); - events.ScheduleEvent(EVENT_MORTAL_STRIKE, 17s, 19s); - } - - void EnterEvadeMode(EvadeReason why) override - { - instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); - instance->SetBossState(DATA_GYTH, FAIL); - BossAI::EnterEvadeMode(why); me->DespawnOrUnsummon(); + return; } - void IsSummonedBy(WorldObject* /*summoner*/) override + summon->AI()->DoZoneInCombat(nullptr, 100.0f); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(EVENT_WHIRLWIND, 13s, 15s); + events.ScheduleEvent(EVENT_CLEAVE, 15s, 17s); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, 17s, 19s); + } + + void EnterEvadeMode(EvadeReason why) override + { + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); + instance->SetBossState(DATA_GYTH, FAIL); + BossAI::EnterEvadeMode(why); + me->DespawnOrUnsummon(); + } + + void IsSummonedBy(WorldObject* /*summoner*/) override + { + Talk(EMOTE_BLACKHAND_DISMOUNT); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 75.0f, true)) + victor->AI()->SetData(1, 2); + + if (GameObject* exitDoor = me->GetMap()->GetGameObject(instance->GetGuidData(GO_GYTH_ENTRY_DOOR))) + exitDoor->SetGoState(GO_STATE_ACTIVE); + + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, DONE); + } + + void SummonedCreatureDespawn(Creature* creature) override + { + if (creature->IsAlive() && !summons.IsAnyCreatureInCombat()) + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); + + BossAI::SummonedCreatureDespawn(creature); + } + + void SetData(uint32 type, uint32 data) override + { + if (type == AREATRIGGER && data == AREATRIGGER_BLACKROCK_STADIUM) { - Talk(EMOTE_BLACKHAND_DISMOUNT); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 75.0f, true)) - victor->AI()->SetData(1, 2); - - if (GameObject* exitDoor = me->GetMap()->GetGameObject(instance->GetGuidData(GO_GYTH_ENTRY_DOOR))) - exitDoor->SetGoState(GO_STATE_ACTIVE); - - instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, DONE); - } - - void SummonedCreatureDespawn(Creature* creature) override - { - if (creature->IsAlive() && !summons.IsAnyCreatureInCombat()) + if (!_gythEvent) { - instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); - } + _gythEvent = true; - BossAI::SummonedCreatureDespawn(creature); - } - - void SetData(uint32 type, uint32 data) override - { - if (type == AREATRIGGER && data == AREATRIGGER_BLACKROCK_STADIUM) - { - if (!gythEvent) + if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 5.0f)) { - gythEvent = true; + if (!victor->IsAlive()) + victor->Respawn(true); - if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 5.0f)) - { - if (!victor->IsAlive()) - { - victor->Respawn(true); - } - - victorGUID = victor->GetGUID(); - } - - if (GameObject* portcullis = me->FindNearestGameObject(GO_DR_PORTCULLIS, 50.0f)) - waveDoorGUID = portcullis->GetGUID(); - - events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); - events.ScheduleEvent(EVENT_START_1, 1s); + _victorGUID = victor->GetGUID(); } + + if (GameObject* portcullis = me->FindNearestGameObject(GO_DR_PORTCULLIS, 50.0f)) + _waveDoorGUID = portcullis->GetGUID(); + + events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); + events.ScheduleEvent(EVENT_START_1, 1s); } } + } - void MovementInform(uint32 type, uint32 id) override + void MovementInform(uint32 type, uint32 id) override + { + if (type == WAYPOINT_MOTION_TYPE) { - if (type == WAYPOINT_MOTION_TYPE) + switch (id) { - switch (id) - { - case 6: - events.ScheduleEvent(EVENT_TELEPORT_1, 2s); - break; - } + case 6: + events.ScheduleEvent(EVENT_TELEPORT_1, 2s); + break; } } + } - void UpdateAI(uint32 diff) override + void UpdateAI(uint32 diff) override + { + if (_gythEvent) { - if (gythEvent) - { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_START_1: - instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, IN_PROGRESS); - instance->SetData(DATA_VAELASTRASZ, NOT_STARTED); - - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_0); - - if (GameObject* door2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_GYTH_ENTRY_DOOR))) - door2->SetGoState(GO_STATE_READY); - - events.ScheduleEvent(EVENT_START_2, 4s); - break; - case EVENT_START_2: - events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->HandleEmoteCommand(EMOTE_ONESHOT_POINT); - events.ScheduleEvent(EVENT_START_3, 4s); - break; - case EVENT_START_3: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_1); - events.ScheduleEvent(EVENT_SPAWN_WAVE, 2s); - events.ScheduleEvent(EVENT_TURN_TO_REND, 4s); - break; - case EVENT_TURN_TO_REND: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - { - victor->SetFacingToObject(me); - victor->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - } - break; - case EVENT_TURN_TO_PLAYER: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - if (Unit* player = victor->SelectNearestPlayer(60.0f)) - victor->SetFacingToObject(player); - break; - case EVENT_TURN_TO_FACING_1: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->SetFacingTo(1.518436f); - break; - case EVENT_TURN_TO_FACING_2: - me->SetFacingTo(1.658063f); - break; - case EVENT_TURN_TO_FACING_3: - me->SetFacingTo(1.500983f); - break; - case EVENT_WAVES_EMOTE_1: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); - break; - case EVENT_WAVES_EMOTE_2: - me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); - break; - case EVENT_WAVES_TEXT_1: - events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_2); - me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); - events.ScheduleEvent(EVENT_WAVES_EMOTE_1, 5s); - events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); - break; - case EVENT_WAVES_TEXT_2: - events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_3); - events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); - events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); - break; - case EVENT_WAVES_TEXT_3: - events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_4); - events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); - events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); - break; - case EVENT_WAVES_TEXT_4: - Talk(SAY_BLACKHAND_1); - events.ScheduleEvent(EVENT_WAVES_EMOTE_2, 4s); - events.ScheduleEvent(EVENT_TURN_TO_FACING_3, 8s); - events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); - break; - case EVENT_WAVES_TEXT_5: - events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_5); - events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); - events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); - break; - case EVENT_WAVES_COMPLETE_TEXT_1: - events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_6); - events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); - events.ScheduleEvent(EVENT_WAVES_COMPLETE_TEXT_2, 13s); - break; - case EVENT_WAVES_COMPLETE_TEXT_2: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_7); - Talk(SAY_BLACKHAND_2); - events.ScheduleEvent(EVENT_PATH_REND, 1s); - events.ScheduleEvent(EVENT_WAVES_COMPLETE_TEXT_3, 4s); - break; - case EVENT_WAVES_COMPLETE_TEXT_3: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->AI()->Talk(SAY_NEFARIUS_8); - events.ScheduleEvent(EVENT_PATH_NEFARIUS, 1s); - events.ScheduleEvent(EVENT_PATH_REND, 1s); - break; - case EVENT_PATH_NEFARIUS: - if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) - victor->GetMotionMaster()->MoveWaypoint(NEFARIUS_PATH_1, true); - break; - case EVENT_PATH_REND: - me->GetMotionMaster()->MoveWaypoint(REND_PATH_1, false); - break; - case EVENT_TELEPORT_1: - me->NearTeleportTo(194.2993f, -474.0814f, 121.4505f, -0.01225555f); - events.ScheduleEvent(EVENT_TELEPORT_2, 13s); - break; - case EVENT_TELEPORT_2: - me->NearTeleportTo(216.485f, -434.93f, 110.888f, -0.01225555f); - me->SummonCreature(NPC_GYTH, 211.762f, -397.5885f, 111.1817f, 4.747295f); - break; - case EVENT_SPAWN_WAVE: - SummonWave(); - break; - default: - break; - } - } - } - - if (!UpdateVictim()) - return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { - case EVENT_WHIRLWIND: - DoCast(SPELL_WHIRLWIND); - events.ScheduleEvent(EVENT_WHIRLWIND, 13s, 18s); + case EVENT_START_1: + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, IN_PROGRESS); + instance->SetData(DATA_VAELASTRASZ, NOT_STARTED); + + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_0); + + if (GameObject* door2 = me->GetMap()->GetGameObject(instance->GetGuidData(GO_GYTH_ENTRY_DOOR))) + door2->SetGoState(GO_STATE_READY); + + events.ScheduleEvent(EVENT_START_2, 4s); break; - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, 10s, 14s); + case EVENT_START_2: + events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->HandleEmoteCommand(EMOTE_ONESHOT_POINT); + events.ScheduleEvent(EVENT_START_3, 4s); break; - case EVENT_MORTAL_STRIKE: - DoCastVictim(SPELL_MORTAL_STRIKE); - events.ScheduleEvent(EVENT_MORTAL_STRIKE, 14s, 18s); + case EVENT_START_3: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_1); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 2s); + events.ScheduleEvent(EVENT_TURN_TO_REND, 4s); + break; + case EVENT_TURN_TO_REND: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + { + victor->SetFacingToObject(me); + victor->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + } + break; + case EVENT_TURN_TO_PLAYER: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + if (Unit* player = victor->SelectNearestPlayer(60.0f)) + victor->SetFacingToObject(player); + break; + case EVENT_TURN_TO_FACING_1: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->SetFacingTo(1.518436f); + break; + case EVENT_TURN_TO_FACING_2: + me->SetFacingTo(1.658063f); + break; + case EVENT_TURN_TO_FACING_3: + me->SetFacingTo(1.500983f); + break; + case EVENT_WAVES_EMOTE_1: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); + break; + case EVENT_WAVES_EMOTE_2: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + break; + case EVENT_WAVES_TEXT_1: + events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_2); + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); + events.ScheduleEvent(EVENT_WAVES_EMOTE_1, 5s); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); + break; + case EVENT_WAVES_TEXT_2: + events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_3); + events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); + break; + case EVENT_WAVES_TEXT_3: + events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_4); + events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); + break; + case EVENT_WAVES_TEXT_4: + Talk(SAY_BLACKHAND_1); + events.ScheduleEvent(EVENT_WAVES_EMOTE_2, 4s); + events.ScheduleEvent(EVENT_TURN_TO_FACING_3, 8s); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); + break; + case EVENT_WAVES_TEXT_5: + events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_5); + events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3s); + break; + case EVENT_WAVES_COMPLETE_TEXT_1: + events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0ms); + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_6); + events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4s); + events.ScheduleEvent(EVENT_WAVES_COMPLETE_TEXT_2, 13s); + break; + case EVENT_WAVES_COMPLETE_TEXT_2: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_7); + Talk(SAY_BLACKHAND_2); + events.ScheduleEvent(EVENT_PATH_REND, 1s); + events.ScheduleEvent(EVENT_WAVES_COMPLETE_TEXT_3, 4s); + break; + case EVENT_WAVES_COMPLETE_TEXT_3: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->AI()->Talk(SAY_NEFARIUS_8); + events.ScheduleEvent(EVENT_PATH_NEFARIUS, 1s); + events.ScheduleEvent(EVENT_PATH_REND, 1s); + break; + case EVENT_PATH_NEFARIUS: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + victor->GetMotionMaster()->MoveWaypoint(NEFARIUS_PATH_1, true); + break; + case EVENT_PATH_REND: + me->GetMotionMaster()->MoveWaypoint(REND_PATH_1, false); + break; + case EVENT_TELEPORT_1: + me->NearTeleportTo(194.2993f, -474.0814f, 121.4505f, -0.01225555f); + events.ScheduleEvent(EVENT_TELEPORT_2, 13s); + break; + case EVENT_TELEPORT_2: + me->NearTeleportTo(216.485f, -434.93f, 110.888f, -0.01225555f); + me->SummonCreature(NPC_GYTH, 211.762f, -397.5885f, 111.1817f, 4.747295f); + break; + case EVENT_SPAWN_WAVE: + SummonWave(); + break; + default: break; } } - DoMeleeAttackIfReady(); } - void SummonWave() + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - me->SummonCreatureGroup(_currentWave); - - if (GameObject* waveDoor = me->GetMap()->GetGameObject(waveDoorGUID)) + switch (eventId) { - waveDoor->UseDoorOrButton(); + case EVENT_WHIRLWIND: + DoCast(SPELL_WHIRLWIND); + events.ScheduleEvent(EVENT_WHIRLWIND, 13s, 18s); + break; + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, 10s, 14s); + break; + case EVENT_MORTAL_STRIKE: + DoCastVictim(SPELL_MORTAL_STRIKE); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, 14s, 18s); + break; } - - ++_currentWave; } - - private: - bool gythEvent; - uint8 _currentWave; - ObjectGuid victorGUID; - ObjectGuid waveDoorGUID; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + DoMeleeAttackIfReady(); } + + void SummonWave() + { + me->SummonCreatureGroup(_currentWave); + + if (GameObject* waveDoor = me->GetMap()->GetGameObject(_waveDoorGUID)) + waveDoor->UseDoorOrButton(); + + ++_currentWave; + } + +private: + bool _gythEvent; + uint8 _currentWave; + ObjectGuid _victorGUID; + ObjectGuid _waveDoorGUID; }; void AddSC_boss_rend_blackhand() { - new boss_rend_blackhand(); + RegisterBlackrockSpireCreatureAI(boss_rend_blackhand); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_solakar_flamewreath.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_solakar_flamewreath.cpp index d96758ee7..4964a8ff3 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_solakar_flamewreath.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_solakar_flamewreath.cpp @@ -38,211 +38,178 @@ enum Says SAY_SUMMON = 0, }; -class npc_rookery_hatcher : public CreatureScript +struct npc_rookery_hatcher : public CreatureAI { -public: - npc_rookery_hatcher() : CreatureScript("npc_rookery_hatcher") {} + npc_rookery_hatcher(Creature* creature) : CreatureAI(creature) {} - CreatureAI* GetAI(Creature* creature) const override + EventMap events; + GameObject* targetEgg; + Position targetPosition; + + void InitializeAI() override { - return GetBlackrockSpireAI(creature); + CreatureAI::InitializeAI(); + DoZoneInCombat(nullptr, 100.0f); + targetEgg = nullptr; } - struct npc_rookery_hatcherAI : public CreatureAI + void JustEngagedWith(Unit* /*who*/) override { - npc_rookery_hatcherAI(Creature* creature) : CreatureAI(creature) {} + events.ScheduleEvent(SPELL_HATCH_EGG, 1s); + } - EventMap events; + void UpdateAI(uint32 diff) override + { std::list nearbyEggs; - GameObject* targetEgg; - Position targetPosition; + float tempDist = 20; + float minDist = 25; + std::list nearbyWhelps; - void InitializeAI() override + if (!UpdateVictim()) + return; + + GetCreatureListWithEntryInGrid(nearbyWhelps, me, NPC_ROOKERY_WHELP, RANGE_WHELP_CALL_HELP); + for (auto const& whelp : nearbyWhelps) { - CreatureAI::InitializeAI(); - DoZoneInCombat(nullptr, 100.0f); - nearbyEggs.clear(); - targetEgg = nullptr; - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(SPELL_HATCH_EGG, 1s); - } - - void UpdateAI(uint32 diff) override - { - std::list nearbyEggs; - float tempDist = 20; - float minDist = 25; - std::list nearbyWhelps; - - if (!UpdateVictim()) + if (!whelp->IsInCombat()) { - return; + whelp->SetInCombatWith(me->GetVictim()); + whelp->AI()->AttackStart(me->GetVictim()); } - - GetCreatureListWithEntryInGrid(nearbyWhelps, me, NPC_ROOKERY_WHELP, RANGE_WHELP_CALL_HELP); - for (auto const& whelp : nearbyWhelps) - { - if (!whelp->IsInCombat()) - { - whelp->SetInCombatWith(me->GetVictim()); - whelp->AI()->AttackStart(me->GetVictim()); - } - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case SPELL_HATCH_EGG: - if (!targetEgg) // no target, try to find one - { - minDist = 50; - tempDist = 50; - me->GetGameObjectListWithEntryInGrid(nearbyEggs, GO_ROOKERY_EGG, 40); - for (auto const& egg : nearbyEggs) - { - if (egg->isSpawned() && egg->getLootState() == GO_READY) - { - tempDist = me->GetDistance2d(egg); - if (tempDist < minDist) - { - minDist = tempDist; - targetEgg = egg; - } - } - } - } - - if (targetEgg) //have a target, go to it and cast it - { - me->GetMotionMaster()->MovePoint(0, targetEgg->GetPosition()); - } - break; - default: - break; - } - } - - // cast takes 1.5second, during which we don't have a target - if (targetEgg && targetEgg->getLootState() == GO_READY && me->GetDistance2d(targetEgg) < RANGE_SPELL_HATCH_EGG) - { - me->StopMovingOnCurrentPos(); - me->SetFacingToObject(targetEgg); - targetPosition = me->GetPosition(); - DoCast(SPELL_HATCH_EGG); - targetEgg = nullptr; - events.ScheduleEvent(SPELL_HATCH_EGG, 6s, 8s); - } - else if (!me->HasUnitState(UNIT_STATE_CASTING) && !targetEgg) - { - if (Unit* victim = me->GetVictim()) - { - AttackStart(victim); - } - - if (me->GetDistance2d(me->GetVictim()) > me->GetMeleeReach()) - { - me->GetMotionMaster()->MovePoint(0, me->GetVictim()->GetPosition()); // a bit hacky, but needed to start moving once we've summoned an egg - } - } - DoMeleeAttackIfReady(); - } - }; -}; - -class boss_solakar_flamewreath : public CreatureScript -{ -public: - boss_solakar_flamewreath() : CreatureScript("boss_solakar_flamewreath") { } - - struct boss_solakar_flamewreathAI : public BossAI - { - boss_solakar_flamewreathAI(Creature* creature) : BossAI(creature, DATA_SOLAKAR_FLAMEWREATH) {} - - uint32 resetTimer; - - void Reset() override - { - _Reset(); - resetTimer = 10000; } - void InitializeAI() override - { - BossAI::InitializeAI(); - Talk(SAY_SUMMON); - DoZoneInCombat(nullptr, 100.0f); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + events.Update(diff); - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_WAR_STOMP, 17s, 20s); - resetTimer = 0; - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - instance->SetData(DATA_SOLAKAR_FLAMEWREATH, DONE); - } - - void ExecuteEvent(uint32 eventId) override + while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { - case SPELL_WAR_STOMP: - DoCastVictim(SPELL_WAR_STOMP); - events.ScheduleEvent(SPELL_WAR_STOMP, 17s, 20s); - break; + case SPELL_HATCH_EGG: + if (!targetEgg) // no target, try to find one + { + minDist = 50; + tempDist = 50; + me->GetGameObjectListWithEntryInGrid(nearbyEggs, GO_ROOKERY_EGG, 40); + for (auto const& egg : nearbyEggs) + { + if (egg->isSpawned() && egg->getLootState() == GO_READY) + { + tempDist = me->GetDistance2d(egg); + if (tempDist < minDist) + { + minDist = tempDist; + targetEgg = egg; + } + } + } + } + if (targetEgg) //have a target, go to it and cast it + me->GetMotionMaster()->MovePoint(0, targetEgg->GetPosition()); + break; default: break; } } - void UpdateAI(uint32 diff) override + // cast takes 1.5second, during which we don't have a target + if (targetEgg && targetEgg->getLootState() == GO_READY && me->GetDistance2d(targetEgg) < RANGE_SPELL_HATCH_EGG) { - if (!UpdateVictim()) - { - resetTimer -= diff; - if (resetTimer < diff) - { - instance->SetData(DATA_SOLAKAR_FLAMEWREATH, FAIL); - } - return; - } - resetTimer = 10000; - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - while (uint32 eventId = events.ExecuteEvent()) - { - ExecuteEvent(eventId); - } - - DoMeleeAttackIfReady(); + me->StopMovingOnCurrentPos(); + me->SetFacingToObject(targetEgg); + targetPosition = me->GetPosition(); + DoCast(SPELL_HATCH_EGG); + targetEgg = nullptr; + events.ScheduleEvent(SPELL_HATCH_EGG, 6s, 8s); } - }; + else if (!me->HasUnitState(UNIT_STATE_CASTING) && !targetEgg) + { + if (Unit* victim = me->GetVictim()) + AttackStart(victim); - CreatureAI* GetAI(Creature* creature) const override + if (me->GetDistance2d(me->GetVictim()) > me->GetMeleeReach()) + me->GetMotionMaster()->MovePoint(0, me->GetVictim()->GetPosition()); // a bit hacky, but needed to start moving once we've summoned an egg + } + DoMeleeAttackIfReady(); + } +}; + +struct boss_solakar_flamewreath : public BossAI +{ + boss_solakar_flamewreath(Creature* creature) : BossAI(creature, DATA_SOLAKAR_FLAMEWREATH) {} + + uint32 resetTimer; + + void Reset() override { - return GetBlackrockSpireAI(creature); + _Reset(); + resetTimer = 10000; + } + + void InitializeAI() override + { + BossAI::InitializeAI(); + Talk(SAY_SUMMON); + DoZoneInCombat(nullptr, 100.0f); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(SPELL_WAR_STOMP, 17s, 20s); + resetTimer = 0; + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + instance->SetData(DATA_SOLAKAR_FLAMEWREATH, DONE); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case SPELL_WAR_STOMP: + DoCastVictim(SPELL_WAR_STOMP); + events.ScheduleEvent(SPELL_WAR_STOMP, 17s, 20s); + break; + + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + if (resetTimer <= diff) + { + instance->SetData(DATA_SOLAKAR_FLAMEWREATH, FAIL); + return; + } + resetTimer -= diff; + return; + } + resetTimer = 10000; + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + while (uint32 eventId = events.ExecuteEvent()) + { + ExecuteEvent(eventId); + } + + DoMeleeAttackIfReady(); } }; void AddSC_boss_solakar_flamewreath() { - new boss_solakar_flamewreath(); - new npc_rookery_hatcher(); + RegisterBlackrockSpireCreatureAI(boss_solakar_flamewreath); + RegisterBlackrockSpireCreatureAI(npc_rookery_hatcher); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp index 366fb17b7..fcb9e2f69 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp @@ -90,185 +90,152 @@ private: // Used to make Hodir disengage whenever he leaves his room constexpr static float FirewalPositionY = -505.f; -class boss_the_beast : public CreatureScript +struct boss_the_beast : public BossAI { -public: - boss_the_beast() : CreatureScript("boss_the_beast") { } + boss_the_beast(Creature* creature) : BossAI(creature, DATA_THE_BEAST), _beastReached(false), _orcYelled(false) {} - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackrockSpireAI(creature); + _Reset(); + + if (_beastReached) + me->GetMotionMaster()->MoveWaypoint(BEAST_MOVEMENT_ID, true); } - struct boss_thebeastAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_thebeastAI(Creature* creature) : BossAI(creature, DATA_THE_BEAST), _beastReached(false), _orcYelled(false) {} + _JustEngagedWith(); + events.ScheduleEvent(EVENT_FLAME_BREAK, 12s); + events.ScheduleEvent(EVENT_IMMOLATE, 3s); + events.ScheduleEvent(EVENT_TERRIFYING_ROAR, 23s); + events.ScheduleEvent(EVENT_BERSERKER_CHARGE, 2s); + events.ScheduleEvent(EVENT_FIREBALL, 8s, 21s); + events.ScheduleEvent(EVENT_FIREBLAST, 5s, 8s); + } - void Reset() override + void SetData(uint32 type, uint32 /*data*/) override + { + switch (type) { - _Reset(); - - if (_beastReached) + case DATA_BEAST_ROOM: { - me->GetMotionMaster()->MoveWaypoint(BEAST_MOVEMENT_ID, true); - } - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_FLAME_BREAK, 12s); - events.ScheduleEvent(EVENT_IMMOLATE, 3s); - events.ScheduleEvent(EVENT_TERRIFYING_ROAR, 23s); - events.ScheduleEvent(EVENT_BERSERKER_CHARGE, 2s); - events.ScheduleEvent(EVENT_FIREBALL, 8s, 21s); - events.ScheduleEvent(EVENT_FIREBLAST, 5s, 8s); - } - - void SetData(uint32 type, uint32 /*data*/) override - { - switch (type) - { - case DATA_BEAST_ROOM: + if (!_orcYelled) { - if (!_orcYelled) + if (_nearbyOrcsGUIDs.empty()) + FindNearbyOrcs(); + + //! vector still empty, creatures are missing + if (_nearbyOrcsGUIDs.empty()) + return; + + _orcYelled = true; + + bool yelled = false; + for (ObjectGuid guid : _nearbyOrcsGUIDs) { - if (_nearbyOrcsGUIDs.empty()) + if (Creature* orc = ObjectAccessor::GetCreature(*me, guid)) { - FindNearbyOrcs(); - } - - //! vector still empty, creatures are missing - if (_nearbyOrcsGUIDs.empty()) - { - return; - } - - _orcYelled = true; - - bool yelled = false; - for (ObjectGuid guid : _nearbyOrcsGUIDs) - { - if (Creature* orc = ObjectAccessor::GetCreature(*me, guid)) + if (!yelled) { - if (!yelled) - { - yelled = true; - orc->AI()->Talk(SAY_BLACKHAND_DOOMED); - } - - orc->m_Events.AddEventAtOffset(new OrcMoveEvent(orc), 3s); - orc->m_Events.AddEventAtOffset(new OrcDeathEvent(orc), 9s); + yelled = true; + orc->AI()->Talk(SAY_BLACKHAND_DOOMED); } + + orc->m_Events.AddEventAtOffset(new OrcMoveEvent(orc), 3s); + orc->m_Events.AddEventAtOffset(new OrcDeathEvent(orc), 9s); } } - break; } - case DATA_BEAST_REACHED: + break; + } + case DATA_BEAST_REACHED: + { + if (!_beastReached) { - if (!_beastReached) - { - _beastReached = true; - me->GetMotionMaster()->MoveWaypoint(BEAST_MOVEMENT_ID, true); + _beastReached = true; + me->GetMotionMaster()->MoveWaypoint(BEAST_MOVEMENT_ID, true); - // There is a chance player logged in between areatriggers (realm crash or restart) - // executing part of script which happens when player enters boss room - // otherwise we will see weird behaviour when someone steps on the previous areatrigger (dead mob yelling/moving) - SetData(DATA_BEAST_ROOM, DATA_BEAST_ROOM); - } - break; + // There is a chance player logged in between areatriggers (realm crash or restart) + // executing part of script which happens when player enters boss room + // otherwise we will see weird behaviour when someone steps on the previous areatrigger (dead mob yelling/moving) + SetData(DATA_BEAST_ROOM, DATA_BEAST_ROOM); } + break; } } + } - void UpdateAI(uint32 diff) override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (me->GetPositionY() > FirewalPositionY) { - if (!UpdateVictim()) - { - return; - } + EnterEvadeMode(); + return; + } - if (me->GetPositionY() > FirewalPositionY) - { - EnterEvadeMode(); - return; - } + events.Update(diff); - events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_FLAME_BREAK: + DoCastVictim(SPELL_FLAMEBREAK); + events.ScheduleEvent(EVENT_FLAME_BREAK, 10s); + break; + case EVENT_IMMOLATE: + DoCastRandomTarget(SPELL_IMMOLATE, 0, 100.0f); + events.ScheduleEvent(EVENT_IMMOLATE, 8s); + break; + case EVENT_TERRIFYING_ROAR: + DoCastVictim(SPELL_TERRIFYINGROAR); + events.ScheduleEvent(EVENT_TERRIFYING_ROAR, 20s); + break; + case EVENT_BERSERKER_CHARGE: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 38.f, true)) + DoCast(target, SPELL_BERSERKER_CHARGE); + events.ScheduleEvent(EVENT_BERSERKER_CHARGE, 15s, 23s); + break; + case EVENT_FIREBALL: + DoCastVictim(SPELL_FIREBALL); + events.ScheduleEvent(EVENT_FIREBALL, 8s, 21s); + if (events.GetTimeUntilEvent(EVENT_FIREBLAST) < 3s) + events.RescheduleEvent(EVENT_FIREBLAST, 3s); + break; + case EVENT_FIREBLAST: + DoCastVictim(SPELL_FIREBLAST); + events.ScheduleEvent(EVENT_FIREBLAST, 5s, 8s); + if (events.GetTimeUntilEvent(EVENT_FIREBALL) < 3s) + events.RescheduleEvent(EVENT_FIREBALL, 3s); + break; + } if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_FLAME_BREAK: - DoCastVictim(SPELL_FLAMEBREAK); - events.ScheduleEvent(EVENT_FLAME_BREAK, 10s); - break; - case EVENT_IMMOLATE: - DoCastRandomTarget(SPELL_IMMOLATE, 0, 100.0f); - events.ScheduleEvent(EVENT_IMMOLATE, 8s); - break; - case EVENT_TERRIFYING_ROAR: - DoCastVictim(SPELL_TERRIFYINGROAR); - events.ScheduleEvent(EVENT_TERRIFYING_ROAR, 20s); - break; - case EVENT_BERSERKER_CHARGE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 38.f, true)) - { - DoCast(target, SPELL_BERSERKER_CHARGE); - } - events.ScheduleEvent(EVENT_BERSERKER_CHARGE, 15s, 23s); - break; - case EVENT_FIREBALL: - DoCastVictim(SPELL_FIREBALL); - events.ScheduleEvent(EVENT_FIREBALL, 8s, 21s); - if (events.GetTimeUntilEvent(EVENT_FIREBLAST) < 3s) - { - events.RescheduleEvent(EVENT_FIREBLAST, 3s); - } - break; - case EVENT_FIREBLAST: - DoCastVictim(SPELL_FIREBLAST); - events.ScheduleEvent(EVENT_FIREBLAST, 5s, 8s); - if (events.GetTimeUntilEvent(EVENT_FIREBALL) < 3s) - { - events.RescheduleEvent(EVENT_FIREBALL, 3s); - } - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - } - - DoMeleeAttackIfReady(); } - void FindNearbyOrcs() - { - std::list temp; - me->GetCreatureListWithEntryInGrid(temp, NPC_BLACKHAND_ELITE, 50.0f); - for (Creature* creature : temp) - { - if (creature->IsAlive()) - { - _nearbyOrcsGUIDs.push_back(creature->GetGUID()); - } - } - } + DoMeleeAttackIfReady(); + } - private: - bool _beastReached; - bool _orcYelled; - GuidVector _nearbyOrcsGUIDs; - }; + void FindNearbyOrcs() + { + std::list temp; + me->GetCreatureListWithEntryInGrid(temp, NPC_BLACKHAND_ELITE, 50.0f); + for (Creature* creature : temp) + if (creature->IsAlive()) + _nearbyOrcsGUIDs.push_back(creature->GetGUID()); + } + +private: + bool _beastReached; + bool _orcYelled; + GuidVector _nearbyOrcsGUIDs; }; //! The beast room areatrigger, this one triggers boss pathing. (AT Id 2066) @@ -279,17 +246,10 @@ public: bool OnTrigger(Player* player, AreaTrigger const* /*at*/) override { - if (player->IsGameMaster()) - { - return false; - } - if (InstanceScript* instance = player->GetInstanceScript()) { if (Creature* beast = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THE_BEAST))) - { beast->AI()->SetData(DATA_BEAST_REACHED, DATA_BEAST_REACHED); - } return true; } @@ -305,17 +265,10 @@ public: bool OnTrigger(Player* player, AreaTrigger const* /*at*/) override { - if (player->IsGameMaster()) - { - return false; - } - if (InstanceScript* instance = player->GetInstanceScript()) { if (Creature* beast = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THE_BEAST))) - { beast->AI()->SetData(DATA_BEAST_ROOM, DATA_BEAST_ROOM); - } return true; } @@ -326,7 +279,7 @@ public: void AddSC_boss_thebeast() { - new boss_the_beast(); + RegisterBlackrockSpireCreatureAI(boss_the_beast); new at_trigger_the_beast_movement(); new at_the_beast_room(); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_urok_doomhowl.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_urok_doomhowl.cpp index bdebc9ff9..ff9b0f2a0 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_urok_doomhowl.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_urok_doomhowl.cpp @@ -40,82 +40,67 @@ enum Events EVENT_INTIMIDATING_ROAR = 3 }; -class boss_urok_doomhowl : public CreatureScript +struct boss_urok_doomhowl : public BossAI { -public: - boss_urok_doomhowl() : CreatureScript("boss_urok_doomhowl") { } + boss_urok_doomhowl(Creature* creature) : BossAI(creature, DATA_UROK_DOOMHOWL) {} - struct boss_urok_doomhowlAI : public BossAI + void InitializeAI() override { - boss_urok_doomhowlAI(Creature* creature) : BossAI(creature, DATA_UROK_DOOMHOWL) {} + me->CastSpell(me, SPELL_UROK_SPAWN, true); + BossAI::InitializeAI(); + Talk(SAY_SUMMON); + DoZoneInCombat(nullptr, 100.0f); - void InitializeAI() override - { - me->CastSpell(me, SPELL_UROK_SPAWN, true); - BossAI::InitializeAI(); - Talk(SAY_SUMMON); - DoZoneInCombat(nullptr, 100.0f); + if (GameObject* challenge = instance->instance->GetGameObject(instance->GetGuidData(GO_UROK_CHALLENGE))) + challenge->Delete(); - if (GameObject* challenge = instance->instance->GetGameObject(instance->GetGuidData(GO_UROK_CHALLENGE))) - { - challenge->Delete(); - } + if (GameObject* pile = instance->instance->GetGameObject(instance->GetGuidData(GO_UROK_PILE))) + pile->DespawnOrUnsummon(0ms, Seconds(MONTH)); + } - if (GameObject* pile = instance->instance->GetGameObject(instance->GetGuidData(GO_UROK_PILE))) - { - pile->DespawnOrUnsummon(0ms, Seconds(MONTH)); - } - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(SPELL_REND, 17s, 20s); - events.ScheduleEvent(SPELL_STRIKE, 10s, 12s); - events.ScheduleEvent(SPELL_INTIMIDATING_ROAR, 25s, 30s); - } - - 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 SPELL_REND: - DoCastVictim(SPELL_REND); - events.ScheduleEvent(SPELL_REND, 8s, 10s); - break; - case SPELL_STRIKE: - DoCastVictim(SPELL_STRIKE); - events.ScheduleEvent(SPELL_STRIKE, 8s, 10s); - break; - case SPELL_INTIMIDATING_ROAR: - DoCastVictim(SPELL_INTIMIDATING_ROAR); - events.ScheduleEvent(SPELL_INTIMIDATING_ROAR, 40s, 45s); - break; - default: - break; - } - } - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void JustEngagedWith(Unit* /*who*/) override { - return GetBlackrockSpireAI(creature); + _JustEngagedWith(); + events.ScheduleEvent(SPELL_REND, 17s, 20s); + events.ScheduleEvent(SPELL_STRIKE, 10s, 12s); + events.ScheduleEvent(SPELL_INTIMIDATING_ROAR, 25s, 30s); + } + + 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 SPELL_REND: + DoCastVictim(SPELL_REND); + events.ScheduleEvent(SPELL_REND, 8s, 10s); + break; + case SPELL_STRIKE: + DoCastVictim(SPELL_STRIKE); + events.ScheduleEvent(SPELL_STRIKE, 8s, 10s); + break; + case SPELL_INTIMIDATING_ROAR: + DoCastVictim(SPELL_INTIMIDATING_ROAR); + events.ScheduleEvent(SPELL_INTIMIDATING_ROAR, 40s, 45s); + break; + default: + break; + } + } + DoMeleeAttackIfReady(); } }; void AddSC_boss_urok_doomhowl() { - new boss_urok_doomhowl(); + RegisterBlackrockSpireCreatureAI(boss_urok_doomhowl); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_warmaster_voone.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_warmaster_voone.cpp index 3240cadd2..339944fd3 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_warmaster_voone.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_warmaster_voone.cpp @@ -55,86 +55,75 @@ enum EventGroups GROUP_WARMASTER }; -class boss_warmaster_voone : public CreatureScript +struct boss_warmaster_voone : public BossAI { -public: - boss_warmaster_voone() : CreatureScript("boss_warmaster_voone") { } + boss_warmaster_voone(Creature* creature) : BossAI(creature, DATA_WARMASTER_VOONE) { } - struct boss_warmastervooneAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_warmastervooneAI(Creature* creature) : BossAI(creature, DATA_WARMASTER_VOONE) { } + _JustEngagedWith(); - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); + events.SetPhase(PHASE_BRAWLER); + events.ScheduleEvent(EVENT_THRASH, 3s, GROUP_BRAWLER, PHASE_BRAWLER); + events.ScheduleEvent(EVENT_THROW_AXE, 1s, GROUP_BRAWLER, PHASE_BRAWLER); + } - events.SetPhase(PHASE_BRAWLER); - events.ScheduleEvent(EVENT_THRASH, 3s, GROUP_BRAWLER, PHASE_BRAWLER); - events.ScheduleEvent(EVENT_THROW_AXE, 1s, GROUP_BRAWLER, PHASE_BRAWLER); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override - { - if (me->HealthBelowPctDamaged(65, damage) && events.IsInPhase(PHASE_BRAWLER)) - { - events.SetPhase(PHASE_THRASHER); - events.CancelEventGroup(GROUP_BRAWLER); - events.ScheduleEvent(EVENT_CLEAVE, 14s, GROUP_THRASHER, PHASE_THRASHER); - events.ScheduleEvent(EVENT_MORTAL_STRIKE, 12s, GROUP_THRASHER, PHASE_THRASHER); - } - else if (me->HealthBelowPctDamaged(40, damage) && events.IsInPhase(PHASE_THRASHER)) - { - events.SetPhase(PHASE_WARMASTER); - events.CancelEventGroup(GROUP_THRASHER); - events.ScheduleEvent(EVENT_SNAP_KICK, 8s, GROUP_WARMASTER, PHASE_WARMASTER); - events.ScheduleEvent(EVENT_UPPERCUT, 20s, GROUP_WARMASTER, PHASE_WARMASTER); - events.ScheduleEvent(EVENT_PUMMEL, 32s, GROUP_WARMASTER, PHASE_WARMASTER); - } - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_SNAP_KICK: - DoCastVictim(SPELL_SNAPKICK); - events.Repeat(6s); - break; - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.Repeat(12s); - break; - case EVENT_UPPERCUT: - DoCastVictim(SPELL_UPPERCUT); - events.Repeat(14s); - break; - case EVENT_MORTAL_STRIKE: - DoCastVictim(SPELL_MORTALSTRIKE); - events.Repeat(10s); - break; - case EVENT_PUMMEL: - DoCastVictim(SPELL_PUMMEL); - events.Repeat(16s); - break; - case EVENT_THROW_AXE: - DoCastRandomTarget(SPELL_THROWAXE); - events.Repeat(8s); - break; - case EVENT_THRASH: - DoCastSelf(SPELL_THRASH); - events.Repeat(10s); - break; - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override { - return GetBlackrockSpireAI(creature); + if (me->HealthBelowPctDamaged(65, damage) && events.IsInPhase(PHASE_BRAWLER)) + { + events.SetPhase(PHASE_THRASHER); + events.CancelEventGroup(GROUP_BRAWLER); + events.ScheduleEvent(EVENT_CLEAVE, 14s, GROUP_THRASHER, PHASE_THRASHER); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, 12s, GROUP_THRASHER, PHASE_THRASHER); + } + else if (me->HealthBelowPctDamaged(40, damage) && events.IsInPhase(PHASE_THRASHER)) + { + events.SetPhase(PHASE_WARMASTER); + events.CancelEventGroup(GROUP_THRASHER); + events.ScheduleEvent(EVENT_SNAP_KICK, 8s, GROUP_WARMASTER, PHASE_WARMASTER); + events.ScheduleEvent(EVENT_UPPERCUT, 20s, GROUP_WARMASTER, PHASE_WARMASTER); + events.ScheduleEvent(EVENT_PUMMEL, 32s, GROUP_WARMASTER, PHASE_WARMASTER); + } + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_SNAP_KICK: + DoCastVictim(SPELL_SNAPKICK); + events.Repeat(6s); + break; + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.Repeat(12s); + break; + case EVENT_UPPERCUT: + DoCastVictim(SPELL_UPPERCUT); + events.Repeat(14s); + break; + case EVENT_MORTAL_STRIKE: + DoCastVictim(SPELL_MORTALSTRIKE); + events.Repeat(10s); + break; + case EVENT_PUMMEL: + DoCastVictim(SPELL_PUMMEL); + events.Repeat(16s); + break; + case EVENT_THROW_AXE: + DoCastRandomTarget(SPELL_THROWAXE); + events.Repeat(8s); + break; + case EVENT_THRASH: + DoCastSelf(SPELL_THRASH); + events.Repeat(10s); + break; + } } }; void AddSC_boss_warmastervoone() { - new boss_warmaster_voone(); + RegisterBlackrockSpireCreatureAI(boss_warmaster_voone); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp index dc7cf3d41..62beba867 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp @@ -19,6 +19,7 @@ #include "Cell.h" #include "CellImpl.h" #include "CreatureScript.h" +#include "GameObjectAI.h" #include "GameObjectScript.h" #include "GridNotifiers.h" #include "InstanceMapScript.h" @@ -71,506 +72,534 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM, } // END }; -class instance_blackrock_spire : public InstanceMapScript +struct instance_blackrock_spire : public InstanceScript { -public: - instance_blackrock_spire() : InstanceMapScript(BRSScriptName, MAP_BLACKROCK_SPIRE) { } + uint32 CurrentSolakarWave = 0; + uint32 SolakarState = NOT_STARTED; // there should be a global instance encounter state, where is it? + GuidVector SolakarSummons; + uint32 VaelastraszState = NOT_STARTED; - struct instance_blackrock_spireMapScript : public InstanceScript + instance_blackrock_spire(InstanceMap* map) : InstanceScript(map) { - uint32 CurrentSolakarWave = 0; - uint32 SolakarState = NOT_STARTED; // there should be a global instance encounter state, where is it? - GuidVector SolakarSummons; - uint32 VaelastraszState = NOT_STARTED; + SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadMinionData(minionData); + LoadDoorData(doorData); + CurrentSolakarWave = 0; + SolakarState = NOT_STARTED; + SolakarSummons.clear(); + VaelastraszState = NOT_STARTED; + } - instance_blackrock_spireMapScript(InstanceMap* map) : InstanceScript(map) + void CreatureLooted(Creature* creature, LootType loot) override + { + switch (creature->GetEntry()) { - SetHeaders(DataHeader); - SetBossNumber(EncounterCount); - LoadMinionData(minionData); - LoadDoorData(doorData); - CurrentSolakarWave = 0; - SolakarState = NOT_STARTED; - SolakarSummons.clear(); - VaelastraszState = NOT_STARTED; + case NPC_THE_BEAST: + if (loot == LOOT_SKINNING) + creature->CastSpell(creature, SPELL_FINKLE_IS_EINHORN, true); + break; + } + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_UROK_MAGUS: + [[fallthrough]]; + case NPC_UROK_ENFORCER: + UrokMobs.push_back(creature->GetGUID()); + break; + case NPC_HIGHLORD_OMOKK: + HighlordOmokk = creature->GetGUID(); + break; + case NPC_SHADOW_HUNTER_VOSHGAJIN: + ShadowHunterVoshgajin = creature->GetGUID(); + break; + case NPC_WARMASTER_VOONE: + WarMasterVoone = creature->GetGUID(); + break; + case NPC_MOTHER_SMOLDERWEB: + MotherSmolderweb = creature->GetGUID(); + break; + case NPC_UROK_DOOMHOWL: + UrokDoomhowl = creature->GetGUID(); + break; + case NPC_QUARTERMASTER_ZIGRIS: + QuartermasterZigris = creature->GetGUID(); + break; + case NPC_GIZRUL_THE_SLAVENER: + GizrultheSlavener = creature->GetGUID(); + break; + case NPC_HALYCON: + Halycon = creature->GetGUID(); + break; + case NPC_OVERLORD_WYRMTHALAK: + OverlordWyrmthalak = creature->GetGUID(); + break; + case NPC_PYROGAURD_EMBERSEER: + PyroguardEmberseer = creature->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + creature->DisappearAndDie(); + break; + case NPC_WARCHIEF_REND_BLACKHAND: + if (GetBossState(DATA_GYTH) != IN_PROGRESS) + WarchiefRendBlackhand = creature->GetGUID(); + + if (GetBossState(DATA_GYTH) == DONE) + creature->DisappearAndDie(); + break; + case NPC_GYTH: + Gyth = creature->GetGUID(); + break; + case NPC_THE_BEAST: + TheBeast = creature->GetGUID(); + break; + case NPC_GENERAL_DRAKKISATH: + GeneralDrakkisath = creature->GetGUID(); + break; + case NPC_LORD_VICTOR_NEFARIUS: + LordVictorNefarius = creature->GetGUID(); + if (GetBossState(DATA_GYTH) == DONE) + creature->DisappearAndDie(); + break; + case NPC_FINKLE_EINHORN: + creature->AI()->Talk(SAY_FINKLE_GANG); + break; + case NPC_CHROMATIC_ELITE_GUARD: + AddMinion(creature); + break; + } + } + + void OnGameObjectCreate(GameObject* go) override + { + switch (go->GetEntry()) + { + case GO_EMBERSEER_IN: + go_emberseerin = go->GetGUID(); + HandleGameObject(ObjectGuid::Empty, GetBossState(DATA_DRAGONSPIRE_ROOM) == DONE, go); + break; + case GO_DOORS: + go_doors = go->GetGUID(); + if (GetBossState(DATA_DRAGONSPIRE_ROOM) == DONE) + HandleGameObject(ObjectGuid::Empty, true, go); + break; + case GO_EMBERSEER_OUT: + go_emberseerout = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, true, go); + break; + case GO_HALL_RUNE_1: + go_roomrunes[0] = go->GetGUID(); + if (GetBossState(DATA_HALL_RUNE_1) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_HALL_RUNE_2: + go_roomrunes[1] = go->GetGUID(); + if (GetBossState(DATA_HALL_RUNE_2) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_HALL_RUNE_3: + go_roomrunes[2] = go->GetGUID(); + if (GetBossState(DATA_HALL_RUNE_3) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_HALL_RUNE_4: + go_roomrunes[3] = go->GetGUID(); + if (GetBossState(DATA_HALL_RUNE_4) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_HALL_RUNE_5: + go_roomrunes[4] = go->GetGUID(); + if (GetBossState(DATA_HALL_RUNE_5) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_HALL_RUNE_6: + go_roomrunes[5] = go->GetGUID(); + if (GetBossState(DATA_HALL_RUNE_6) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_HALL_RUNE_7: + go_roomrunes[6] = go->GetGUID(); + if (GetBossState(DATA_HALL_RUNE_7) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_EMBERSEER_RUNE_1: + go_emberseerrunes[0] = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_EMBERSEER_RUNE_2: + go_emberseerrunes[1] = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_EMBERSEER_RUNE_3: + go_emberseerrunes[2] = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_EMBERSEER_RUNE_4: + go_emberseerrunes[3] = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_EMBERSEER_RUNE_5: + go_emberseerrunes[4] = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_EMBERSEER_RUNE_6: + go_emberseerrunes[5] = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_EMBERSEER_RUNE_7: + go_emberseerrunes[6] = go->GetGUID(); + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) + HandleGameObject(ObjectGuid::Empty, false, go); + break; + case GO_PORTCULLIS_ACTIVE: + go_portcullis_active = go->GetGUID(); + if (GetBossState(DATA_GYTH) == DONE) + HandleGameObject(ObjectGuid::Empty, true, go); + break; + case GO_UROK_PILE: + go_urokPile = go->GetGUID(); + break; + case GO_UROK_CIRCLE: + go_urokOgreCirles.push_back(go->GetGUID()); + break; + case GO_UROK_CHALLENGE: + go_urokChallenge = go->GetGUID(); + break; + default: + break; } - void CreatureLooted(Creature* creature, LootType loot) override + InstanceScript::OnGameObjectCreate(go); + } + + bool SetBossState(uint32 type, EncounterState state) override + { + if (!InstanceScript::SetBossState(type, state)) + return false; + + switch (type) { - switch (creature->GetEntry()) - { - case NPC_THE_BEAST: - if (loot == LOOT_SKINNING) + case DATA_WARCHIEF_REND_BLACKHAND: + if (state == FAIL) + { + if (Creature* rend = instance->GetCreature(WarchiefRendBlackhand)) + rend->Respawn(true); + + if (Creature* nefarius = instance->GetCreature(LordVictorNefarius)) + nefarius->AI()->Talk(SAY_NEFARIUS_REND_WIPE); + } + break; + default: + break; + } + + return true; + } + + void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override + { + switch (eventId) + { + case EVENT_PYROGUARD_EMBERSEER: + if (GetBossState(DATA_PYROGAURD_EMBERSEER) == NOT_STARTED) + if (Creature* Emberseer = instance->GetCreature(PyroguardEmberseer)) + Emberseer->AI()->SetData(1, 1); + break; + case EVENT_UROK_DOOMHOWL: + if (GetBossState(DATA_UROK_DOOMHOWL) == NOT_STARTED) + { + SetBossState(DATA_UROK_DOOMHOWL, IN_PROGRESS); + if (GameObject* pile = instance->GetGameObject(go_urokPile)) + pile->SetLootState(GO_JUST_DEACTIVATED); + } + break; + default: + break; + } + } + + void SetData(uint32 type, uint32 data) override + { + switch (type) + { + case AREATRIGGER: + if (data == AREATRIGGER_DRAGONSPIRE_HALL) + { + if (GetBossState(DATA_DRAGONSPIRE_ROOM) != DONE) + Events.ScheduleEvent(EVENT_DRAGONSPIRE_ROOM_STORE, 1s); + } + break; + case DATA_SOLAKAR_FLAMEWREATH: + switch (data) + { + case IN_PROGRESS: + if (SolakarState == NOT_STARTED) + Events.ScheduleEvent(EVENT_SOLAKAR_WAVE, 500ms); + break; + case FAIL: + for (ObjectGuid const& guid : SolakarSummons) + if (Creature* creature = instance->GetCreature(guid)) + creature->DespawnOrUnsummon(); + SolakarSummons.clear(); + CurrentSolakarWave = 0; + SetData(DATA_SOLAKAR_FLAMEWREATH, NOT_STARTED); + break; + case DONE: + break; + } + SolakarState = data; + break; + case DATA_VAELASTRASZ: + VaelastraszState = data; + break; + case DATA_UROK_DOOMHOWL: + if (data == FAIL) + { + if (GetBossState(DATA_UROK_DOOMHOWL) != NOT_STARTED) { - creature->CastSpell(creature, SPELL_FINKLE_IS_EINHORN, true); - } - break; - } - } - - void OnCreatureCreate(Creature* creature) override - { - switch (creature->GetEntry()) - { - case NPC_UROK_MAGUS: - [[fallthrough]]; - case NPC_UROK_ENFORCER: - UrokMobs.push_back(creature->GetGUID()); - break; - case NPC_HIGHLORD_OMOKK: - HighlordOmokk = creature->GetGUID(); - break; - case NPC_SHADOW_HUNTER_VOSHGAJIN: - ShadowHunterVoshgajin = creature->GetGUID(); - break; - case NPC_WARMASTER_VOONE: - WarMasterVoone = creature->GetGUID(); - break; - case NPC_MOTHER_SMOLDERWEB: - MotherSmolderweb = creature->GetGUID(); - break; - case NPC_UROK_DOOMHOWL: - UrokDoomhowl = creature->GetGUID(); - break; - case NPC_QUARTERMASTER_ZIGRIS: - QuartermasterZigris = creature->GetGUID(); - break; - case NPC_GIZRUL_THE_SLAVENER: - GizrultheSlavener = creature->GetGUID(); - break; - case NPC_HALYCON: - Halycon = creature->GetGUID(); - break; - case NPC_OVERLORD_WYRMTHALAK: - OverlordWyrmthalak = creature->GetGUID(); - break; - case NPC_PYROGAURD_EMBERSEER: - PyroguardEmberseer = creature->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - creature->DisappearAndDie(); - break; - case NPC_WARCHIEF_REND_BLACKHAND: - if (GetBossState(DATA_GYTH) != IN_PROGRESS) - { - WarchiefRendBlackhand = creature->GetGUID(); - } - - if (GetBossState(DATA_GYTH) == DONE) - creature->DisappearAndDie(); - break; - case NPC_GYTH: - Gyth = creature->GetGUID(); - break; - case NPC_THE_BEAST: - TheBeast = creature->GetGUID(); - break; - case NPC_GENERAL_DRAKKISATH: - GeneralDrakkisath = creature->GetGUID(); - break; - case NPC_LORD_VICTOR_NEFARIUS: - LordVictorNefarius = creature->GetGUID(); - if (GetBossState(DATA_GYTH) == DONE) - creature->DisappearAndDie(); - break; - case NPC_FINKLE_EINHORN: - creature->AI()->Talk(SAY_FINKLE_GANG); - break; - case NPC_CHROMATIC_ELITE_GUARD: - AddMinion(creature); - break; - } - } - - void OnGameObjectCreate(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_EMBERSEER_IN: - go_emberseerin = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, GetBossState(DATA_DRAGONSPIRE_ROOM) == DONE, go); - break; - case GO_DOORS: - go_doors = go->GetGUID(); - if (GetBossState(DATA_DRAGONSPIRE_ROOM) == DONE) - HandleGameObject(ObjectGuid::Empty, true, go); - break; - case GO_EMBERSEER_OUT: - go_emberseerout = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, true, go); - break; - case GO_HALL_RUNE_1: - go_roomrunes[0] = go->GetGUID(); - if (GetBossState(DATA_HALL_RUNE_1) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_HALL_RUNE_2: - go_roomrunes[1] = go->GetGUID(); - if (GetBossState(DATA_HALL_RUNE_2) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_HALL_RUNE_3: - go_roomrunes[2] = go->GetGUID(); - if (GetBossState(DATA_HALL_RUNE_3) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_HALL_RUNE_4: - go_roomrunes[3] = go->GetGUID(); - if (GetBossState(DATA_HALL_RUNE_4) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_HALL_RUNE_5: - go_roomrunes[4] = go->GetGUID(); - if (GetBossState(DATA_HALL_RUNE_5) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_HALL_RUNE_6: - go_roomrunes[5] = go->GetGUID(); - if (GetBossState(DATA_HALL_RUNE_6) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_HALL_RUNE_7: - go_roomrunes[6] = go->GetGUID(); - if (GetBossState(DATA_HALL_RUNE_7) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_EMBERSEER_RUNE_1: - go_emberseerrunes[0] = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_EMBERSEER_RUNE_2: - go_emberseerrunes[1] = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_EMBERSEER_RUNE_3: - go_emberseerrunes[2] = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_EMBERSEER_RUNE_4: - go_emberseerrunes[3] = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_EMBERSEER_RUNE_5: - go_emberseerrunes[4] = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_EMBERSEER_RUNE_6: - go_emberseerrunes[5] = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_EMBERSEER_RUNE_7: - go_emberseerrunes[6] = go->GetGUID(); - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == DONE) - HandleGameObject(ObjectGuid::Empty, false, go); - break; - case GO_PORTCULLIS_ACTIVE: - go_portcullis_active = go->GetGUID(); - if (GetBossState(DATA_GYTH) == DONE) - HandleGameObject(ObjectGuid::Empty, true, go); - break; - case GO_UROK_PILE: - go_urokPile = go->GetGUID(); - break; - case GO_UROK_CIRCLE: - go_urokOgreCirles.push_back(go->GetGUID()); - break; - case GO_UROK_CHALLENGE: - go_urokChallenge = go->GetGUID(); - break; - default: - break; - } - - InstanceScript::OnGameObjectCreate(go); - } - - bool SetBossState(uint32 type, EncounterState state) override - { - if (!InstanceScript::SetBossState(type, state)) - return false; - - switch (type) - { - case DATA_WARCHIEF_REND_BLACKHAND: - if (state == FAIL) - { - if (Creature* rend = instance->GetCreature(WarchiefRendBlackhand)) + SetBossState(DATA_UROK_DOOMHOWL, NOT_STARTED); + if (GameObject* challenge = instance->GetGameObject(go_urokChallenge)) + challenge->Delete(); + if (GameObject* pile = instance->GetGameObject(go_urokPile)) { - rend->Respawn(true); - } - - if (Creature* nefarius = instance->GetCreature(LordVictorNefarius)) - { - nefarius->AI()->Talk(SAY_NEFARIUS_REND_WIPE); + pile->SetLootState(GO_READY); + pile->Respawn(); } + for (auto const& circleGUID : go_urokOgreCirles) + if (GameObject* circle = instance->GetGameObject(circleGUID)) + circle->Delete(); + for (auto const& mobGUID : UrokMobs) + if (Creature* mob = instance->GetCreature(mobGUID)) + mob->DespawnOrUnsummon(); } - break; - default: - break; - } + } + break; + default: + break; + } + } - return true; + uint32 GetData(uint32 type) const override + { + if (type == DATA_SOLAKAR_FLAMEWREATH) + return SolakarState; + else if (type == DATA_VAELASTRASZ) + return VaelastraszState; + else + return InstanceScript::GetData(type); + } + + void SummonSolakarWave(uint8 number) + { + if (number < MAX_WAVE_COUNT) + { + if (Creature* summon = instance->SummonCreature(NPC_ROOKERY_GUARDIAN, SolakarPosLeft)) + SolakarSummons.push_back(summon->GetGUID()); + + if (Creature* summon = instance->SummonCreature(NPC_ROOKERY_HATCHER, SolakarPosRight)) + SolakarSummons.push_back(summon->GetGUID()); + + if (number == 0) + if (Creature* FirstHatcher = instance->GetCreature(SolakarSummons.back())) // works because we spawned a hatcher second + FirstHatcher->AI()->Talk(SAY_SOLAKAR_FIRST_HATCHER); + } + else if (number == MAX_WAVE_COUNT) + { + if (Creature* summon = instance->SummonCreature(NPC_SOLAKAR, SolakarPosBoss)) + SolakarSummons.push_back(summon->GetGUID()); + } + } + + ObjectGuid GetGuidData(uint32 type) const override + { + switch (type) + { + case DATA_HIGHLORD_OMOKK: + return HighlordOmokk; + case DATA_SHADOW_HUNTER_VOSHGAJIN: + return ShadowHunterVoshgajin; + case DATA_WARMASTER_VOONE: + return WarMasterVoone; + case DATA_MOTHER_SMOLDERWEB: + return MotherSmolderweb; + case DATA_UROK_DOOMHOWL: + return UrokDoomhowl; + case DATA_QUARTERMASTER_ZIGRIS: + return QuartermasterZigris; + case DATA_GIZRUL_THE_SLAVENER: + return GizrultheSlavener; + case DATA_HALYCON: + return Halycon; + case DATA_OVERLORD_WYRMTHALAK: + return OverlordWyrmthalak; + case DATA_PYROGAURD_EMBERSEER: + return PyroguardEmberseer; + case DATA_WARCHIEF_REND_BLACKHAND: + return WarchiefRendBlackhand; + case DATA_GYTH: + return Gyth; + case DATA_THE_BEAST: + return TheBeast; + case DATA_GENERAL_DRAKKISATH: + return GeneralDrakkisath; + case GO_EMBERSEER_IN: + return go_emberseerin; + case GO_DOORS: + return go_doors; + case GO_EMBERSEER_OUT: + return go_emberseerout; + case GO_HALL_RUNE_1: + return go_roomrunes[0]; + case GO_HALL_RUNE_2: + return go_roomrunes[1]; + case GO_HALL_RUNE_3: + return go_roomrunes[2]; + case GO_HALL_RUNE_4: + return go_roomrunes[3]; + case GO_HALL_RUNE_5: + return go_roomrunes[4]; + case GO_HALL_RUNE_6: + return go_roomrunes[5]; + case GO_HALL_RUNE_7: + return go_roomrunes[6]; + case GO_EMBERSEER_RUNE_1: + return go_emberseerrunes[0]; + case GO_EMBERSEER_RUNE_2: + return go_emberseerrunes[1]; + case GO_EMBERSEER_RUNE_3: + return go_emberseerrunes[2]; + case GO_EMBERSEER_RUNE_4: + return go_emberseerrunes[3]; + case GO_EMBERSEER_RUNE_5: + return go_emberseerrunes[4]; + case GO_EMBERSEER_RUNE_6: + return go_emberseerrunes[5]; + case GO_EMBERSEER_RUNE_7: + return go_emberseerrunes[6]; + case GO_PORTCULLIS_ACTIVE: + return go_portcullis_active; + case GO_UROK_PILE: + return go_urokPile; + case GO_UROK_CHALLENGE: + return go_urokChallenge; + default: + break; } - void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override + return ObjectGuid::Empty; + } + + void Update(uint32 diff) override + { + Events.Update(diff); + + while (uint32 eventId = Events.ExecuteEvent()) { switch (eventId) { - case EVENT_PYROGUARD_EMBERSEER: - if (GetBossState(DATA_PYROGAURD_EMBERSEER) == NOT_STARTED) - { - if (Creature* Emberseer = instance->GetCreature(PyroguardEmberseer)) - Emberseer->AI()->SetData(1, 1); - } + case EVENT_DRAGONSPIRE_ROOM_STORE: + Dragonspireroomstore(); + Events.ScheduleEvent(EVENT_DRAGONSPIRE_ROOM_CHECK, 3s); break; - case EVENT_UROK_DOOMHOWL: - if (GetBossState(DATA_UROK_DOOMHOWL) == NOT_STARTED) - { - SetBossState(DATA_UROK_DOOMHOWL, IN_PROGRESS); - if (GameObject* pile = instance->GetGameObject(go_urokPile)) - { - pile->SetLootState(GO_JUST_DEACTIVATED); - } - } - break; - default: - break; - } - } - - void SetData(uint32 type, uint32 data) override - { - switch (type) - { - case AREATRIGGER: - if (data == AREATRIGGER_DRAGONSPIRE_HALL) - { - if (GetBossState(DATA_DRAGONSPIRE_ROOM) != DONE) - Events.ScheduleEvent(EVENT_DRAGONSPIRE_ROOM_STORE, 1s); - } - break; - case DATA_SOLAKAR_FLAMEWREATH: - switch (data) - { - case IN_PROGRESS: - if (SolakarState == NOT_STARTED) - { - Events.ScheduleEvent(EVENT_SOLAKAR_WAVE, 500ms); - } - break; - case FAIL: - for (ObjectGuid const& guid : SolakarSummons) - { - if (Creature* creature = instance->GetCreature(guid)) - { - creature->DespawnOrUnsummon(); - } - } - SolakarSummons.clear(); - CurrentSolakarWave = 0; - SetData(DATA_SOLAKAR_FLAMEWREATH, NOT_STARTED); - break; - case DONE: - break; - } - SolakarState = data; - break; - case DATA_VAELASTRASZ: - VaelastraszState = data; - break; - case DATA_UROK_DOOMHOWL: - if (data == FAIL) - { - if (GetBossState(DATA_UROK_DOOMHOWL) != NOT_STARTED) - { - SetBossState(DATA_UROK_DOOMHOWL, NOT_STARTED); - if (GameObject* challenge = instance->GetGameObject(go_urokChallenge)) - { - challenge->Delete(); - } - if (GameObject* pile = instance->GetGameObject(go_urokPile)) - { - pile->SetLootState(GO_READY); - pile->Respawn(); - } - for (auto const& circleGUID : go_urokOgreCirles) - { - if (GameObject* circle = instance->GetGameObject(circleGUID)) - { - circle->Delete(); - } - } - for (auto const& mobGUID : UrokMobs) - { - if (Creature* mob = instance->GetCreature(mobGUID)) - { - mob->DespawnOrUnsummon(); - } - } - } - } - break; - default: - break; - } - } - - uint32 GetData(uint32 type) const override - { - if (type == DATA_SOLAKAR_FLAMEWREATH) - { - return SolakarState; - } - else if (type == DATA_VAELASTRASZ) - { - return VaelastraszState; - } - else - { - return InstanceScript::GetData(type); - } - } - - void SummonSolakarWave(uint8 number) - { - if (number < MAX_WAVE_COUNT) - { - if (Creature* summon = instance->SummonCreature(NPC_ROOKERY_GUARDIAN, SolakarPosLeft)) - { - SolakarSummons.push_back(summon->GetGUID()); - } - - if (Creature* summon = instance->SummonCreature(NPC_ROOKERY_HATCHER, SolakarPosRight)) - { - SolakarSummons.push_back(summon->GetGUID()); - } - - if (number == 0) - { - if (Creature* FirstHatcher = instance->GetCreature(SolakarSummons.back())) // works because we spawned a hatcher second - { - FirstHatcher->AI()->Talk(SAY_SOLAKAR_FIRST_HATCHER); - } - } - } - else if (number == MAX_WAVE_COUNT) - { - if (Creature* summon = instance->SummonCreature(NPC_SOLAKAR, SolakarPosBoss)) - { - SolakarSummons.push_back(summon->GetGUID()); - } - } - } - - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case DATA_HIGHLORD_OMOKK: - return HighlordOmokk; - case DATA_SHADOW_HUNTER_VOSHGAJIN: - return ShadowHunterVoshgajin; - case DATA_WARMASTER_VOONE: - return WarMasterVoone; - case DATA_MOTHER_SMOLDERWEB: - return MotherSmolderweb; - case DATA_UROK_DOOMHOWL: - return UrokDoomhowl; - case DATA_QUARTERMASTER_ZIGRIS: - return QuartermasterZigris; - case DATA_GIZRUL_THE_SLAVENER: - return GizrultheSlavener; - case DATA_HALYCON: - return Halycon; - case DATA_OVERLORD_WYRMTHALAK: - return OverlordWyrmthalak; - case DATA_PYROGAURD_EMBERSEER: - return PyroguardEmberseer; - case DATA_WARCHIEF_REND_BLACKHAND: - return WarchiefRendBlackhand; - case DATA_GYTH: - return Gyth; - case DATA_THE_BEAST: - return TheBeast; - case DATA_GENERAL_DRAKKISATH: - return GeneralDrakkisath; - case GO_EMBERSEER_IN: - return go_emberseerin; - case GO_DOORS: - return go_doors; - case GO_EMBERSEER_OUT: - return go_emberseerout; - case GO_HALL_RUNE_1: - return go_roomrunes[0]; - case GO_HALL_RUNE_2: - return go_roomrunes[1]; - case GO_HALL_RUNE_3: - return go_roomrunes[2]; - case GO_HALL_RUNE_4: - return go_roomrunes[3]; - case GO_HALL_RUNE_5: - return go_roomrunes[4]; - case GO_HALL_RUNE_6: - return go_roomrunes[5]; - case GO_HALL_RUNE_7: - return go_roomrunes[6]; - case GO_EMBERSEER_RUNE_1: - return go_emberseerrunes[0]; - case GO_EMBERSEER_RUNE_2: - return go_emberseerrunes[1]; - case GO_EMBERSEER_RUNE_3: - return go_emberseerrunes[2]; - case GO_EMBERSEER_RUNE_4: - return go_emberseerrunes[3]; - case GO_EMBERSEER_RUNE_5: - return go_emberseerrunes[4]; - case GO_EMBERSEER_RUNE_6: - return go_emberseerrunes[5]; - case GO_EMBERSEER_RUNE_7: - return go_emberseerrunes[6]; - case GO_PORTCULLIS_ACTIVE: - return go_portcullis_active; - case GO_UROK_PILE: - return go_urokPile; - case GO_UROK_CHALLENGE: - return go_urokChallenge; - default: - break; - } - - return ObjectGuid::Empty; - } - - void Update(uint32 diff) override - { - Events.Update(diff); - - while (uint32 eventId = Events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_DRAGONSPIRE_ROOM_STORE: - Dragonspireroomstore(); + case EVENT_DRAGONSPIRE_ROOM_CHECK: + Dragonspireroomcheck(); + if ((GetBossState(DATA_DRAGONSPIRE_ROOM) != DONE)) Events.ScheduleEvent(EVENT_DRAGONSPIRE_ROOM_CHECK, 3s); + break; + case EVENT_SOLAKAR_WAVE: + SummonSolakarWave(CurrentSolakarWave); + if (CurrentSolakarWave < MAX_WAVE_COUNT) + { + Events.ScheduleEvent(EVENT_SOLAKAR_WAVE, TIMER_SOLAKAR_WAVE); + CurrentSolakarWave++; + } + break; + default: + break; + } + } + } + + void Dragonspireroomstore() + { + + for (uint8 i = 0; i < 7; ++i) + { + // Refresh the creature list + runecreaturelist[i].clear(); + + if (GameObject* rune = instance->GetGameObject(go_roomrunes[i])) + { + for (uint8 j = 0; j < 3; ++j) + { + std::list creatureList; + GetCreatureListWithEntryInGrid(creatureList, rune, DragonspireMobs[j], 15.0f); + for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) + if (Creature* creature = *itr) + runecreaturelist[i].push_back(creature->GetGUID()); + } + } + } + } + + void Dragonspireroomcheck() + { + Creature* mob = nullptr; + GameObject* rune = nullptr; + + for (uint8 i = 0; i < 7; ++i) + { + bool _mobAlive = false; + rune = instance->GetGameObject(go_roomrunes[i]); + if (!rune) + continue; + + if (rune->GetGoState() == GO_STATE_ACTIVE) + { + for (ObjectGuid const& guid : runecreaturelist[i]) + { + mob = instance->GetCreature(guid); + if (mob && mob->IsAlive()) + _mobAlive = true; + } + } + + if (!_mobAlive && rune->GetGoState() == GO_STATE_ACTIVE) + { + HandleGameObject(ObjectGuid::Empty, false, rune); + + switch (rune->GetEntry()) + { + case GO_HALL_RUNE_1: + SetBossState(DATA_HALL_RUNE_1, DONE); break; - case EVENT_DRAGONSPIRE_ROOM_CHECK: - Dragonspireroomcheck(); - if ((GetBossState(DATA_DRAGONSPIRE_ROOM) != DONE)) - Events.ScheduleEvent(EVENT_DRAGONSPIRE_ROOM_CHECK, 3s); + case GO_HALL_RUNE_2: + SetBossState(DATA_HALL_RUNE_2, DONE); break; - case EVENT_SOLAKAR_WAVE: - SummonSolakarWave(CurrentSolakarWave); - if (CurrentSolakarWave < MAX_WAVE_COUNT) - { - Events.ScheduleEvent(EVENT_SOLAKAR_WAVE, TIMER_SOLAKAR_WAVE); - CurrentSolakarWave++; - } + case GO_HALL_RUNE_3: + SetBossState(DATA_HALL_RUNE_3, DONE); + break; + case GO_HALL_RUNE_4: + SetBossState(DATA_HALL_RUNE_4, DONE); + break; + case GO_HALL_RUNE_5: + SetBossState(DATA_HALL_RUNE_5, DONE); + break; + case GO_HALL_RUNE_6: + SetBossState(DATA_HALL_RUNE_6, DONE); + break; + case GO_HALL_RUNE_7: + SetBossState(DATA_HALL_RUNE_7, DONE); break; default: break; @@ -578,136 +607,49 @@ public: } } - void Dragonspireroomstore() + if (GetBossState(DATA_HALL_RUNE_1) == DONE && GetBossState(DATA_HALL_RUNE_2) == DONE && GetBossState(DATA_HALL_RUNE_3) == DONE && + GetBossState(DATA_HALL_RUNE_4) == DONE && GetBossState(DATA_HALL_RUNE_5) == DONE && GetBossState(DATA_HALL_RUNE_6) == DONE && + GetBossState(DATA_HALL_RUNE_7) == DONE) { - - for (uint8 i = 0; i < 7; ++i) - { - // Refresh the creature list - runecreaturelist[i].clear(); - - if (GameObject* rune = instance->GetGameObject(go_roomrunes[i])) - { - for (uint8 j = 0; j < 3; ++j) - { - std::list creatureList; - GetCreatureListWithEntryInGrid(creatureList, rune, DragonspireMobs[j], 15.0f); - for (std::list::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) - { - if (Creature* creature = *itr) - { - runecreaturelist[i].push_back(creature->GetGUID()); - } - } - } - } - } + SetBossState(DATA_DRAGONSPIRE_ROOM, DONE); + if (GameObject* door1 = instance->GetGameObject(go_emberseerin)) + HandleGameObject(ObjectGuid::Empty, true, door1); + if (GameObject* door2 = instance->GetGameObject(go_doors)) + HandleGameObject(ObjectGuid::Empty, true, door2); + if (GameObject* door3 = instance->GetGameObject(go_emberseerin)) + HandleGameObject(ObjectGuid::Empty, true, door3); } - - void Dragonspireroomcheck() - { - Creature* mob = nullptr; - GameObject* rune = nullptr; - - for (uint8 i = 0; i < 7; ++i) - { - bool _mobAlive = false; - rune = instance->GetGameObject(go_roomrunes[i]); - if (!rune) - continue; - - if (rune->GetGoState() == GO_STATE_ACTIVE) - { - for (ObjectGuid const& guid : runecreaturelist[i]) - { - mob = instance->GetCreature(guid); - if (mob && mob->IsAlive()) - _mobAlive = true; - } - } - - if (!_mobAlive && rune->GetGoState() == GO_STATE_ACTIVE) - { - HandleGameObject(ObjectGuid::Empty, false, rune); - - switch (rune->GetEntry()) - { - case GO_HALL_RUNE_1: - SetBossState(DATA_HALL_RUNE_1, DONE); - break; - case GO_HALL_RUNE_2: - SetBossState(DATA_HALL_RUNE_2, DONE); - break; - case GO_HALL_RUNE_3: - SetBossState(DATA_HALL_RUNE_3, DONE); - break; - case GO_HALL_RUNE_4: - SetBossState(DATA_HALL_RUNE_4, DONE); - break; - case GO_HALL_RUNE_5: - SetBossState(DATA_HALL_RUNE_5, DONE); - break; - case GO_HALL_RUNE_6: - SetBossState(DATA_HALL_RUNE_6, DONE); - break; - case GO_HALL_RUNE_7: - SetBossState(DATA_HALL_RUNE_7, DONE); - break; - default: - break; - } - } - } - - if (GetBossState(DATA_HALL_RUNE_1) == DONE && GetBossState(DATA_HALL_RUNE_2) == DONE && GetBossState(DATA_HALL_RUNE_3) == DONE && - GetBossState(DATA_HALL_RUNE_4) == DONE && GetBossState(DATA_HALL_RUNE_5) == DONE && GetBossState(DATA_HALL_RUNE_6) == DONE && - GetBossState(DATA_HALL_RUNE_7) == DONE) - { - SetBossState(DATA_DRAGONSPIRE_ROOM, DONE); - if (GameObject* door1 = instance->GetGameObject(go_emberseerin)) - HandleGameObject(ObjectGuid::Empty, true, door1); - if (GameObject* door2 = instance->GetGameObject(go_doors)) - HandleGameObject(ObjectGuid::Empty, true, door2); - if (GameObject* door3 = instance->GetGameObject(go_emberseerin)) - HandleGameObject(ObjectGuid::Empty, true, door3); - } - } - - protected: - EventMap Events; - ObjectGuid HighlordOmokk; - ObjectGuid ShadowHunterVoshgajin; - ObjectGuid WarMasterVoone; - ObjectGuid MotherSmolderweb; - ObjectGuid UrokDoomhowl; - ObjectGuid QuartermasterZigris; - ObjectGuid GizrultheSlavener; - ObjectGuid Halycon; - ObjectGuid OverlordWyrmthalak; - ObjectGuid PyroguardEmberseer; - ObjectGuid WarchiefRendBlackhand; - ObjectGuid Gyth; - ObjectGuid LordVictorNefarius; - ObjectGuid TheBeast; - ObjectGuid GeneralDrakkisath; - ObjectGuid go_emberseerin; - ObjectGuid go_doors; - ObjectGuid go_emberseerout; - ObjectGuid go_blackrockaltar; - ObjectGuid go_roomrunes[7]; - ObjectGuid go_emberseerrunes[7]; - GuidVector runecreaturelist[7]; - ObjectGuid go_portcullis_active; - ObjectGuid go_urokPile; - ObjectGuid go_urokChallenge; - std::vector go_urokOgreCirles; - std::vector UrokMobs; - }; - - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_blackrock_spireMapScript(map); } + +protected: + EventMap Events; + ObjectGuid HighlordOmokk; + ObjectGuid ShadowHunterVoshgajin; + ObjectGuid WarMasterVoone; + ObjectGuid MotherSmolderweb; + ObjectGuid UrokDoomhowl; + ObjectGuid QuartermasterZigris; + ObjectGuid GizrultheSlavener; + ObjectGuid Halycon; + ObjectGuid OverlordWyrmthalak; + ObjectGuid PyroguardEmberseer; + ObjectGuid WarchiefRendBlackhand; + ObjectGuid Gyth; + ObjectGuid LordVictorNefarius; + ObjectGuid TheBeast; + ObjectGuid GeneralDrakkisath; + ObjectGuid go_emberseerin; + ObjectGuid go_doors; + ObjectGuid go_emberseerout; + ObjectGuid go_blackrockaltar; + ObjectGuid go_roomrunes[7]; + ObjectGuid go_emberseerrunes[7]; + GuidVector runecreaturelist[7]; + ObjectGuid go_portcullis_active; + ObjectGuid go_urokPile; + ObjectGuid go_urokChallenge; + std::vector go_urokOgreCirles; + std::vector UrokMobs; }; /*##### @@ -769,25 +711,20 @@ public: } }; -class go_father_flame : public GameObjectScript +struct go_father_flame : public GameObjectAI { -public: - go_father_flame() : GameObjectScript("go_father_flame") {} + go_father_flame(GameObject* go) : GameObjectAI(go) {} - void OnLootStateChanged(GameObject* go, uint32 state, Unit* /*unit*/) override + void OnStateChanged(uint32 state, Unit* /*unit*/) override { - if (InstanceScript* instance = go->GetInstanceScript()) - { + if (InstanceScript* instance = me->GetInstanceScript()) if (state == GO_ACTIVATED) { if (instance->GetData(DATA_SOLAKAR_FLAMEWREATH) == IN_PROGRESS || instance->GetData(DATA_SOLAKAR_FLAMEWREATH) == DONE) - { return; - } instance->SetData(DATA_SOLAKAR_FLAMEWREATH, IN_PROGRESS); } - } } }; @@ -861,7 +798,6 @@ class spell_blackrock_spire_call_of_vaelastrasz : public SpellScript void OnEffect(SpellEffIndex /*effIndex*/) { if (Unit* caster = GetCaster()) - { if (InstanceScript* instance = caster->GetInstanceScript()) { instance->SetData(DATA_VAELASTRASZ, IN_PROGRESS); @@ -871,7 +807,6 @@ class spell_blackrock_spire_call_of_vaelastrasz : public SpellScript // despawn is called by the CreatureAI caster->SummonCreature(NPC_VAELASTRASZ_THE_RED, spawnPosition, TEMPSUMMON_TIMED_DESPAWN, 60 * IN_MILLISECONDS); } - } } void Register() override @@ -929,170 +864,153 @@ enum Events EVENT_FLAME_BREATH, }; -class npc_vaelastrasz_the_red : public CreatureScript +struct npc_vaelastrasz_the_red : public CreatureAI { -public: - npc_vaelastrasz_the_red() : CreatureScript("npc_vaelastrasz_the_red") { } + npc_vaelastrasz_the_red(Creature* creature) : CreatureAI(creature) { } - struct npc_vaelastrasz_the_redAI : public CreatureAI + void IsSummonedBy(WorldObject* summoner) override { - npc_vaelastrasz_the_redAI(Creature* creature) : CreatureAI(creature) { } + if (!summoner) + return; + _combatEnabled = false; + me->CastSpell(me, SPELL_VAELAN_SPAWNS, false); + me->SetFacingToObject(summoner); + Talk(SAY_RED_SUMMONED); + if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 100.0f)) + _victorGUID = victor->GetGUID(); + events.ScheduleEvent(EVENT_RED_1_TALK_BEFORE_TRANSFORM, 3s); + } - void IsSummonedBy(WorldObject* summoner) override + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - if (!summoner) + switch (eventId) { - return; - } - _combatEnabled = false; - me->CastSpell(me, SPELL_VAELAN_SPAWNS, false); - me->SetFacingToObject(summoner); - Talk(SAY_RED_SUMMONED); - if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 100.0f)) - { - _victorGUID = victor->GetGUID(); - } - events.ScheduleEvent(EVENT_RED_1_TALK_BEFORE_TRANSFORM, 3s); - } - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_RED_1_TALK_BEFORE_TRANSFORM: - Talk(SAY_RED_BEFORE_TRANSFORM); - events.ScheduleEvent(EVENT_RED_2_TRANSFORM, 2s); - break; - case EVENT_RED_2_TRANSFORM: - me->CastSpell(me, SPELL_TOUCH_OF_VAELASTRASZ, false); - me->SetEntry(NPC_VAELASTRASZ_UBRS); - me->SetDisplayId(MODEL_VAELASTRASZ_UBRS); - events.ScheduleEvent(EVENT_VAEL_TALK_SUMMON, 1s); - events.ScheduleEvent(EVENT_VAEL_1_START_COMBAT, 5s); - break; - case EVENT_VAEL_TALK_SUMMON: - Talk(SAY_VAEL_SUMMONED); - break; - case EVENT_VAEL_1_START_COMBAT: - _combatEnabled = true; - me->SetImmuneToNPC(false); - if (Creature* gyth = me->FindNearestCreature(NPC_GYTH, 100.0f, true)) + case EVENT_RED_1_TALK_BEFORE_TRANSFORM: + Talk(SAY_RED_BEFORE_TRANSFORM); + events.ScheduleEvent(EVENT_RED_2_TRANSFORM, 2s); + break; + case EVENT_RED_2_TRANSFORM: + me->CastSpell(me, SPELL_TOUCH_OF_VAELASTRASZ, false); + me->SetEntry(NPC_VAELASTRASZ_UBRS); + me->SetDisplayId(MODEL_VAELASTRASZ_UBRS); + events.ScheduleEvent(EVENT_VAEL_TALK_SUMMON, 1s); + events.ScheduleEvent(EVENT_VAEL_1_START_COMBAT, 5s); + break; + case EVENT_VAEL_TALK_SUMMON: + Talk(SAY_VAEL_SUMMONED); + break; + case EVENT_VAEL_1_START_COMBAT: + _combatEnabled = true; + me->SetImmuneToNPC(false); + if (Creature* gyth = me->FindNearestCreature(NPC_GYTH, 100.0f, true)) + { + me->AddThreat(gyth, 1000000.f); + me->AI()->AttackStart(gyth); + } + if (Creature* rend = me->FindNearestCreature(NPC_WARCHIEF_REND_BLACKHAND, 100.0f, true)) + { + if (!rend->IsImmuneToNPC() && rend->isTargetableForAttack()) { - me->AddThreat(gyth, 1000000.f); - me->AI()->AttackStart(gyth); - } - if (Creature* rend = me->FindNearestCreature(NPC_WARCHIEF_REND_BLACKHAND, 100.0f, true)) - { - if (!rend->IsImmuneToNPC() && rend->isTargetableForAttack()) + me->AddThreat(rend, 100000.f); + if (!me->FindNearestCreature(NPC_GYTH, 100.0f, true)) { - me->AddThreat(rend, 100000.f); - if (!me->FindNearestCreature(NPC_GYTH, 100.0f, true)) - { - me->AI()->AttackStart(rend); - } + me->AI()->AttackStart(rend); } } - _events2.ScheduleEvent(EVENT_FLAME_BREATH, 5s); - events.ScheduleEvent(EVENT_NEFARIUS_TALK_1, 500ms); - break; - case EVENT_NEFARIUS_TALK_1: - if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) - { - victor->GetMotionMaster()->Clear(); // stop pacing - victor->GetMotionMaster()->MoveIdle(); - victor->StopMovingOnCurrentPos(); - victor->SetFacingToObject(me); - victor->AI()->Talk(SAY_NEFARIUS_15); - } - events.ScheduleEvent(EVENT_NEFARIUS_TALK_2, 6s); - break; - case EVENT_NEFARIUS_TALK_2: - if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) - { - victor->SetFacingToObject(me); - victor->AI()->Talk(SAY_NEFARIUS_16); - } - events.ScheduleEvent(EVENT_NEFARIUS_TALK_3, 5s); - break; - case EVENT_NEFARIUS_TALK_3: - if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) - { - victor->SetFacingToObject(me); - victor->AI()->Talk(SAY_NEFARIUS_17); - } - events.ScheduleEvent(EVENT_NEFARIUS_CORRUPTION, 5s); - break; - case EVENT_NEFARIUS_CORRUPTION: - _combatEnabled = false; - me->AttackStop(); - me->RemoveAllAuras(); - me->StopMovingOnCurrentPos(); - me->SetFaction(FACTION_FRIENDLY); - if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) - { - victor->SetFacingToObject(me); - victor->CastSpell(me, SPELL_NEFARIUS_CORRUPTION, TRIGGERED_CAST_DIRECTLY); - } - events.ScheduleEvent(EVENT_VAEL_2_TRANSFORM, 1s); - break; - case EVENT_VAEL_2_TRANSFORM: - Talk(SAY_VAEL_STOP_COMBAT); - me->SetDisplayId(MODEL_VAELASTRASZ_THE_RED); - events.ScheduleEvent(EVENT_VAEL_3_DESPAWN, 500ms); - break; - case EVENT_VAEL_3_DESPAWN: - DoCast(me, SPELL_VAELASTRASZ_SPAWN); - me->DespawnOrUnsummon(1500ms); - break; - default: - break; - } - } - - if (!_combatEnabled || !UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - _events2.Update(diff); - - switch (_events2.ExecuteEvent()) - { - case EVENT_FLAME_BREATH: - me->CastSpell(me, SPELL_FLAMEBREATH, false); + } + _events2.ScheduleEvent(EVENT_FLAME_BREATH, 5s); + events.ScheduleEvent(EVENT_NEFARIUS_TALK_1, 500ms); + break; + case EVENT_NEFARIUS_TALK_1: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + { + victor->GetMotionMaster()->Clear(); // stop pacing + victor->GetMotionMaster()->MoveIdle(); + victor->StopMovingOnCurrentPos(); + victor->SetFacingToObject(me); + victor->AI()->Talk(SAY_NEFARIUS_15); + } + events.ScheduleEvent(EVENT_NEFARIUS_TALK_2, 6s); + break; + case EVENT_NEFARIUS_TALK_2: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + { + victor->SetFacingToObject(me); + victor->AI()->Talk(SAY_NEFARIUS_16); + } + events.ScheduleEvent(EVENT_NEFARIUS_TALK_3, 5s); + break; + case EVENT_NEFARIUS_TALK_3: + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + { + victor->SetFacingToObject(me); + victor->AI()->Talk(SAY_NEFARIUS_17); + } + events.ScheduleEvent(EVENT_NEFARIUS_CORRUPTION, 5s); + break; + case EVENT_NEFARIUS_CORRUPTION: + _combatEnabled = false; + me->AttackStop(); + me->RemoveAllAuras(); + me->StopMovingOnCurrentPos(); + me->SetFaction(FACTION_FRIENDLY); + if (Creature* victor = ObjectAccessor::GetCreature(*me, _victorGUID)) + { + victor->SetFacingToObject(me); + victor->CastSpell(me, SPELL_NEFARIUS_CORRUPTION, TRIGGERED_CAST_DIRECTLY); + } + events.ScheduleEvent(EVENT_VAEL_2_TRANSFORM, 1s); + break; + case EVENT_VAEL_2_TRANSFORM: + Talk(SAY_VAEL_STOP_COMBAT); + me->SetDisplayId(MODEL_VAELASTRASZ_THE_RED); + events.ScheduleEvent(EVENT_VAEL_3_DESPAWN, 500ms); + break; + case EVENT_VAEL_3_DESPAWN: + DoCast(me, SPELL_VAELASTRASZ_SPAWN); + me->DespawnOrUnsummon(1500ms); break; default: break; } - - DoMeleeAttackIfReady(); - return; } - private: - ObjectGuid _victorGUID; - bool _combatEnabled; - EventMap _events2; - }; + if (!_combatEnabled || !UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) + return; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockSpireAI(creature); + _events2.Update(diff); + + switch (_events2.ExecuteEvent()) + { + case EVENT_FLAME_BREATH: + me->CastSpell(me, SPELL_FLAMEBREATH, false); + break; + default: + break; + } + + DoMeleeAttackIfReady(); + return; } + +private: + ObjectGuid _victorGUID; + bool _combatEnabled; + EventMap _events2; }; void AddSC_instance_blackrock_spire() { - new instance_blackrock_spire(); + RegisterInstanceScript(instance_blackrock_spire, MAP_BLACKROCK_SPIRE); new at_dragonspire_hall(); new at_blackrock_stadium(); - new go_father_flame(); + RegisterBlackrockSpireGameObjectAI(go_father_flame); new near_scarshield_infiltrator(); new at_scarshield_infiltrator(); RegisterSpellScript(spell_blackrock_spire_call_of_vaelastrasz); - new npc_vaelastrasz_the_red(); + RegisterBlackrockSpireCreatureAI(npc_vaelastrasz_the_red); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h index f2a1aed32..b90747508 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/blackwing_lair.h @@ -127,5 +127,6 @@ inline AI* GetBlackwingLairAI(T* obj) } #define RegisterBlackwingLairCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetBlackwingLairAI) +#define RegisterBlackwingLairGameObjectAI(ai_name) RegisterGameObjectAIWithFactory(ai_name, GetBlackwingLairAI) #endif diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp index 55c5c7a94..467b2f96b 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_broodlord_lashlayer.cpp @@ -58,196 +58,157 @@ enum Actions ACTION_DISARMED = 1 }; -class boss_broodlord : public CreatureScript +struct boss_broodlord : public BossAI { -public: - boss_broodlord() : CreatureScript("boss_broodlord") { } + boss_broodlord(Creature* creature) : BossAI(creature, DATA_BROODLORD_LASHLAYER) { } - struct boss_broodlordAI : public BossAI + void JustEngagedWith(Unit* who) override { - boss_broodlordAI(Creature* creature) : BossAI(creature, DATA_BROODLORD_LASHLAYER) { } + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - Talk(SAY_AGGRO); - - events.ScheduleEvent(EVENT_CLEAVE, 8s); - events.ScheduleEvent(EVENT_BLASTWAVE, 12s); - events.ScheduleEvent(EVENT_MORTALSTRIKE, 20s); - events.ScheduleEvent(EVENT_KNOCKBACK, 30s); - events.ScheduleEvent(EVENT_CHECK, 1s); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - - std::list _goList; - GetGameObjectListWithEntryInGrid(_goList, me, GO_SUPPRESSION_DEVICE, 200.0f); - for (std::list::const_iterator itr = _goList.begin(); itr != _goList.end(); itr++) - { - ((*itr)->AI()->DoAction(ACTION_DEACTIVATE)); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, 7s); - break; - case EVENT_BLASTWAVE: - DoCastVictim(SPELL_BLASTWAVE); - events.ScheduleEvent(EVENT_BLASTWAVE, 20s, 35s); - break; - case EVENT_MORTALSTRIKE: - DoCastVictim(SPELL_MORTALSTRIKE); - events.ScheduleEvent(EVENT_MORTALSTRIKE, 25s, 35s); - break; - case EVENT_KNOCKBACK: - DoCastVictim(SPELL_KNOCKBACK); - if (DoGetThreat(me->GetVictim())) - DoModifyThreatByPercent(me->GetVictim(), -50); - events.ScheduleEvent(EVENT_KNOCKBACK, 15s, 30s); - break; - case EVENT_CHECK: - if (me->GetDistance(me->GetHomePosition()) > 150.0f) - { - Talk(SAY_LEASH); - EnterEvadeMode(); - } - events.ScheduleEvent(EVENT_CHECK, 1s); - break; - } - } - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); + events.ScheduleEvent(EVENT_CLEAVE, 8s); + events.ScheduleEvent(EVENT_BLASTWAVE, 12s); + events.ScheduleEvent(EVENT_MORTALSTRIKE, 20s); + events.ScheduleEvent(EVENT_KNOCKBACK, 30s); + events.ScheduleEvent(EVENT_CHECK, 1s); } -}; -class go_suppression_device : public GameObjectScript -{ - public: - go_suppression_device() : GameObjectScript("go_suppression_device") { } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); - void OnLootStateChanged(GameObject* go, uint32 state, Unit* /*unit*/) override + std::list _goList; + GetGameObjectListWithEntryInGrid(_goList, me, GO_SUPPRESSION_DEVICE, 200.0f); + for (std::list::const_iterator itr = _goList.begin(); itr != _goList.end(); itr++) + ((*itr)->AI()->DoAction(ACTION_DEACTIVATE)); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - switch (state) + switch (eventId) { - case GO_JUST_DEACTIVATED: // This case prevents the Gameobject despawn by Disarm Trap - go->SetLootState(GO_READY); - [[fallthrough]]; - case GO_ACTIVATED: - go->AI()->DoAction(ACTION_DISARMED); + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, 7s); + break; + case EVENT_BLASTWAVE: + DoCastVictim(SPELL_BLASTWAVE); + events.ScheduleEvent(EVENT_BLASTWAVE, 20s, 35s); + break; + case EVENT_MORTALSTRIKE: + DoCastVictim(SPELL_MORTALSTRIKE); + events.ScheduleEvent(EVENT_MORTALSTRIKE, 25s, 35s); + break; + case EVENT_KNOCKBACK: + DoCastVictim(SPELL_KNOCKBACK); + if (DoGetThreat(me->GetVictim())) + DoModifyThreatByPercent(me->GetVictim(), -50); + events.ScheduleEvent(EVENT_KNOCKBACK, 15s, 30s); + break; + case EVENT_CHECK: + if (me->GetDistance(me->GetHomePosition()) > 150.0f) + { + Talk(SAY_LEASH); + EnterEvadeMode(); + } + events.ScheduleEvent(EVENT_CHECK, 1s); break; } } - struct go_suppression_deviceAI : public GameObjectAI + DoMeleeAttackIfReady(); + } +}; + +struct go_suppression_device : public GameObjectAI +{ + go_suppression_device(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()), _active(true) { } + + void InitializeAI() override + { + if (_instance->GetBossState(DATA_BROODLORD_LASHLAYER) == DONE) { - go_suppression_deviceAI(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()), _active(true) { } - - void InitializeAI() override - { - if (_instance->GetBossState(DATA_BROODLORD_LASHLAYER) == DONE) - { - Deactivate(); - return; - } - - _events.ScheduleEvent(EVENT_SUPPRESSION_CAST, 5s); - } - - void UpdateAI(uint32 diff) override - { - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SUPPRESSION_CAST: - if (me->GetGoState() == GO_STATE_READY) - { - me->CastSpell(nullptr, SPELL_SUPPRESSION_AURA); - me->SendCustomAnim(0); - } - _events.ScheduleEvent(EVENT_SUPPRESSION_CAST, 5s); - break; - case EVENT_SUPPRESSION_RESET: - Activate(); - break; - } - } - } - - void DoAction(int32 action) override - { - if (action == ACTION_DEACTIVATE) - { - _events.CancelEvent(EVENT_SUPPRESSION_RESET); - } - else if (action == ACTION_DISARMED) - { - Deactivate(); - _events.CancelEvent(EVENT_SUPPRESSION_CAST); - - if (_instance->GetBossState(DATA_BROODLORD_LASHLAYER) != DONE) - { - _events.ScheduleEvent(EVENT_SUPPRESSION_RESET, 30s, 120s); - } - } - } - - void Activate() - { - if (_active) - return; - _active = true; - if (me->GetGoState() == GO_STATE_ACTIVE) - me->SetGoState(GO_STATE_READY); - me->SetLootState(GO_READY); - me->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - _events.ScheduleEvent(EVENT_SUPPRESSION_CAST, 5s); - me->Respawn(); - } - - void Deactivate() - { - if (!_active) - return; - _active = false; - me->SetGoState(GO_STATE_ACTIVE); - me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - _events.CancelEvent(EVENT_SUPPRESSION_CAST); - } - - private: - InstanceScript* _instance; - EventMap _events; - bool _active; - }; - - GameObjectAI* GetAI(GameObject* go) const override - { - return new go_suppression_deviceAI(go); + Deactivate(); + return; } + + _events.ScheduleEvent(EVENT_SUPPRESSION_CAST, 5s); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SUPPRESSION_CAST: + if (me->GetGoState() == GO_STATE_READY) + { + me->CastSpell(nullptr, SPELL_SUPPRESSION_AURA); + me->SendCustomAnim(0); + } + _events.ScheduleEvent(EVENT_SUPPRESSION_CAST, 5s); + break; + case EVENT_SUPPRESSION_RESET: + Activate(); + break; + } + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_DEACTIVATE) + { + _events.CancelEvent(EVENT_SUPPRESSION_RESET); + } + else if (action == ACTION_DISARMED) + { + Deactivate(); + _events.CancelEvent(EVENT_SUPPRESSION_CAST); + + if (_instance->GetBossState(DATA_BROODLORD_LASHLAYER) != DONE) + _events.ScheduleEvent(EVENT_SUPPRESSION_RESET, 30s, 120s); + } + } + + void Activate() + { + if (_active) + return; + _active = true; + if (me->GetGoState() == GO_STATE_ACTIVE) + me->SetGoState(GO_STATE_READY); + me->SetLootState(GO_READY); + me->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + _events.ScheduleEvent(EVENT_SUPPRESSION_CAST, 5s); + me->Respawn(); + } + + void Deactivate() + { + if (!_active) + return; + _active = false; + me->SetGoState(GO_STATE_ACTIVE); + me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + _events.CancelEvent(EVENT_SUPPRESSION_CAST); + } + +private: + InstanceScript* _instance; + EventMap _events; + bool _active; }; class spell_suppression_aura : public SpellScript @@ -271,7 +232,7 @@ class spell_suppression_aura : public SpellScript void AddSC_boss_broodlord() { - new boss_broodlord(); - new go_suppression_device(); + RegisterBlackwingLairCreatureAI(boss_broodlord); + RegisterBlackwingLairGameObjectAI(go_suppression_device); RegisterSpellScript(spell_suppression_aura); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp index 64d23aa43..e645a2c22 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_chromaggus.cpp @@ -70,203 +70,171 @@ enum Misc Position const homePos = { -7491.1587f, -1069.718f, 476.59094, 476.59094f }; -class boss_chromaggus : public CreatureScript +struct boss_chromaggus : public BossAI { -public: - boss_chromaggus() : CreatureScript("boss_chromaggus") { } - - struct boss_chromaggusAI : public BossAI + boss_chromaggus(Creature* creature) : BossAI(creature, DATA_CHROMAGGUS) { - boss_chromaggusAI(Creature* creature) : BossAI(creature, DATA_CHROMAGGUS) + Initialize(); + + // Select the 2 breaths that we are going to use until despawned so we don't end up casting 2 of the same breath. + _breathSpells = { SPELL_INCINERATE, SPELL_TIMELAPSE, SPELL_CORROSIVEACID, SPELL_IGNITEFLESH, SPELL_FROSTBURN }; + + Acore::Containers::RandomResize(_breathSpells, 2); + + // Hack fix: This is here to prevent him from being pulled from the floor underneath, remove it once maps are fixed. + creature->SetImmuneToAll(true); + } + + void Initialize() + { + Enraged = false; + } + + void Reset() override + { + _Reset(); + + Initialize(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + + events.ScheduleEvent(EVENT_SHIMMER, 1s); + events.ScheduleEvent(EVENT_BREATH, 30s); + events.ScheduleEvent(EVENT_BREATH, 60s); + events.ScheduleEvent(EVENT_AFFLICTION, 10s); + events.ScheduleEvent(EVENT_FRENZY, 15s); + } + + bool CanAIAttack(Unit const* victim) const override + { + return !victim->HasAura(SPELL_TIMELAPSE); + } + + void SetGUID(ObjectGuid const& guid, int32 id) override + { + if (id == GUID_LEVER_USER) { - Initialize(); - - // Select the 2 breaths that we are going to use until despawned so we don't end up casting 2 of the same breath. - _breathSpells = { SPELL_INCINERATE, SPELL_TIMELAPSE, SPELL_CORROSIVEACID, SPELL_IGNITEFLESH, SPELL_FROSTBURN }; - - Acore::Containers::RandomResize(_breathSpells, 2); - + _playerGUID = guid; // Hack fix: This is here to prevent him from being pulled from the floor underneath, remove it once maps are fixed. - creature->SetImmuneToAll(true); + me->SetImmuneToAll(false); } + } - void Initialize() + void PathEndReached(uint32 /*pathId*/) override + { + if (Unit* player = ObjectAccessor::GetUnit(*me, _playerGUID)) + me->SetInCombatWith(player); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - Enraged = false; - } - - void Reset() override - { - _Reset(); - - Initialize(); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - - events.ScheduleEvent(EVENT_SHIMMER, 1s); - events.ScheduleEvent(EVENT_BREATH, 30s); - events.ScheduleEvent(EVENT_BREATH, 60s); - events.ScheduleEvent(EVENT_AFFLICTION, 10s); - events.ScheduleEvent(EVENT_FRENZY, 15s); - } - - bool CanAIAttack(Unit const* victim) const override - { - return !victim->HasAura(SPELL_TIMELAPSE); - } - - void SetGUID(ObjectGuid const& guid, int32 id) override - { - if (id == GUID_LEVER_USER) + switch (eventId) { - _playerGUID = guid; - // Hack fix: This is here to prevent him from being pulled from the floor underneath, remove it once maps are fixed. - me->SetImmuneToAll(false); + case EVENT_SHIMMER: + { + // Cast new random vulnerabilty on self + DoCast(me, SPELL_ELEMENTAL_SHIELD); + Talk(EMOTE_SHIMMER); + events.ScheduleEvent(EVENT_SHIMMER, 17s, 25s); + break; + } + case EVENT_BREATH: + DoCastVictim(_breathSpells.front()); + _breathSpells.reverse(); + events.ScheduleEvent(EVENT_BREATH, 60s); + break; + case EVENT_AFFLICTION: + { + uint32 afflictionSpellID = RAND(SPELL_BROODAF_BLUE, SPELL_BROODAF_BLACK, SPELL_BROODAF_RED, SPELL_BROODAF_BRONZE, SPELL_BROODAF_GREEN); + std::vector playerTargets; + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->GetSource()->ToPlayer()) + if (!player->IsGameMaster() && !player->IsSpectator() && player->IsAlive()) + playerTargets.push_back(player); + + if (playerTargets.size() > 12) + Acore::Containers::RandomResize(playerTargets, 12); + + for (Player* player : playerTargets) + { + DoCast(player, afflictionSpellID, true); + + if (player->HasAllAuras(SPELL_BROODAF_BLUE, SPELL_BROODAF_BLACK, SPELL_BROODAF_RED, SPELL_BROODAF_BRONZE, SPELL_BROODAF_GREEN)) + DoCast(player, SPELL_CHROMATIC_MUT_1); + } + } + events.ScheduleEvent(EVENT_AFFLICTION, 10s); + break; + case EVENT_FRENZY: + DoCast(me, SPELL_FRENZY); + events.ScheduleEvent(EVENT_FRENZY, 10s, 15s); + break; } - } - - void PathEndReached(uint32 /*pathId*/) override - { - if (Unit* player = ObjectAccessor::GetUnit(*me, _playerGUID)) - { - me->SetInCombatWith(player); - } - } - - 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_SHIMMER: - { - // Cast new random vulnerabilty on self - DoCast(me, SPELL_ELEMENTAL_SHIELD); - Talk(EMOTE_SHIMMER); - events.ScheduleEvent(EVENT_SHIMMER, 17s, 25s); - break; - } - case EVENT_BREATH: - DoCastVictim(_breathSpells.front()); - _breathSpells.reverse(); - events.ScheduleEvent(EVENT_BREATH, 60s); - break; - case EVENT_AFFLICTION: - { - uint32 afflictionSpellID = RAND(SPELL_BROODAF_BLUE, SPELL_BROODAF_BLACK, SPELL_BROODAF_RED, SPELL_BROODAF_BRONZE, SPELL_BROODAF_GREEN); - std::vector playerTargets; - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - { - if (Player* player = itr->GetSource()->ToPlayer()) - { - if (!player->IsGameMaster() && !player->IsSpectator() && player->IsAlive()) - { - playerTargets.push_back(player); - } - } - } - - if (playerTargets.size() > 12) - { - Acore::Containers::RandomResize(playerTargets, 12); - } - - for (Player* player : playerTargets) - { - DoCast(player, afflictionSpellID, true); - - if (player->HasAllAuras(SPELL_BROODAF_BLUE, SPELL_BROODAF_BLACK, SPELL_BROODAF_RED, SPELL_BROODAF_BRONZE, SPELL_BROODAF_GREEN)) - DoCast(player, SPELL_CHROMATIC_MUT_1); - } - } - events.ScheduleEvent(EVENT_AFFLICTION, 10s); - break; - case EVENT_FRENZY: - DoCast(me, SPELL_FRENZY); - events.ScheduleEvent(EVENT_FRENZY, 10s, 15s); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - // Enrage if not already enraged and below 20% - if (!Enraged && HealthBelowPct(20)) - { - DoCast(me, SPELL_ENRAGE); - Enraged = true; - } - - DoMeleeAttackIfReady(); } - private: - std::list _breathSpells; - bool Enraged; - ObjectGuid _playerGUID; - }; + // Enrage if not already enraged and below 20% + if (!Enraged && HealthBelowPct(20)) + { + DoCast(me, SPELL_ENRAGE); + Enraged = true; + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); + DoMeleeAttackIfReady(); } + +private: + std::list _breathSpells; + bool Enraged; + ObjectGuid _playerGUID; }; -class go_chromaggus_lever : public GameObjectScript +struct go_chromaggus_lever : public GameObjectAI { - public: - go_chromaggus_lever() : GameObjectScript("go_chromaggus_lever") { } + go_chromaggus_lever(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()) { } - struct go_chromaggus_leverAI : public GameObjectAI + bool GossipHello(Player* player, bool reportUse) override + { + if (reportUse) { - go_chromaggus_leverAI(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()) { } - - bool GossipHello(Player* player, bool reportUse) override + if (_instance->GetBossState(DATA_CHROMAGGUS) != DONE && _instance->GetBossState(DATA_CHROMAGGUS) != IN_PROGRESS) { - if (reportUse) + if (Creature* creature = _instance->GetCreature(DATA_CHROMAGGUS)) { - if (_instance->GetBossState(DATA_CHROMAGGUS) != DONE && _instance->GetBossState(DATA_CHROMAGGUS) != IN_PROGRESS) - { - if (Creature* creature = _instance->GetCreature(DATA_CHROMAGGUS)) - { - creature->SetHomePosition(homePos); - creature->GetMotionMaster()->MoveWaypoint(creature->GetEntry() * 10, false); - creature->AI()->SetGUID(player->GetGUID(), GUID_LEVER_USER); - } - - if (GameObject* go = _instance->GetGameObject(DATA_GO_CHROMAGGUS_DOOR)) - _instance->HandleGameObject(ObjectGuid::Empty, true, go); - } - - me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE | GO_FLAG_IN_USE); - me->SetGoState(GO_STATE_ACTIVE); + creature->SetHomePosition(homePos); + creature->GetMotionMaster()->MoveWaypoint(creature->GetEntry() * 10, false); + creature->AI()->SetGUID(player->GetGUID(), GUID_LEVER_USER); } - return true; + if (GameObject* go = _instance->GetGameObject(DATA_GO_CHROMAGGUS_DOOR)) + _instance->HandleGameObject(ObjectGuid::Empty, true, go); } - private: - InstanceScript* _instance; - }; - - GameObjectAI* GetAI(GameObject* go) const override - { - return GetBlackwingLairAI(go); + me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE | GO_FLAG_IN_USE); + me->SetGoState(GO_STATE_ACTIVE); } + + return true; + } + +private: + InstanceScript* _instance; }; enum ElementalShieldSpells @@ -306,9 +274,7 @@ class spell_gen_elemental_shield : public SpellScript if (Unit* caster = GetCaster()) { for (uint32 spell = SPELL_FIRE_ELEMENTAL_SHIELD; spell <= SPELL_ARCANE_ELEMENTAL_SHIELD; ++spell) - { caster->RemoveAurasDueToSpell(spell); - } caster->CastSpell(caster, SPELL_FIRE_ELEMENTAL_SHIELD + urand(0, 4), true); } @@ -341,9 +307,7 @@ class spell_gen_brood_power : public SpellScript if (Unit* caster = GetCaster()) { for (uint32 spell = SPELL_RED_BROOD_POWER; spell <= SPELL_GREEN_BROOD_POWER; ++spell) - { caster->RemoveAurasDueToSpell(spell); - } caster->CastSpell(caster, RAND(SPELL_RED_BROOD_POWER, SPELL_BLUE_BROOD_POWER, SPELL_BRONZE_BROOD_POWER, SPELL_BLACK_BROOD_POWER, SPELL_GREEN_BROOD_POWER), true); } @@ -357,8 +321,8 @@ class spell_gen_brood_power : public SpellScript void AddSC_boss_chromaggus() { - new boss_chromaggus(); - new go_chromaggus_lever(); + RegisterBlackwingLairCreatureAI(boss_chromaggus); + RegisterBlackwingLairGameObjectAI(go_chromaggus_lever); RegisterSpellScript(spell_gen_elemental_shield); RegisterSpellScript(spell_gen_brood_power); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp index 832642ef1..0edd68587 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_ebonroc.cpp @@ -33,82 +33,69 @@ enum Events EVENT_SHADOWOFEBONROC = 3 }; -class boss_ebonroc : public CreatureScript +struct boss_ebonroc : public BossAI { -public: - boss_ebonroc() : CreatureScript("boss_ebonroc") { } + boss_ebonroc(Creature* creature) : BossAI(creature, DATA_EBONROC) { } - struct boss_ebonrocAI : public BossAI + void MovementInform(uint32 type, uint32 id) override { - boss_ebonrocAI(Creature* creature) : BossAI(creature, DATA_EBONROC) { } + if (type != WAYPOINT_MOTION_TYPE || id != 13) + return; - void MovementInform(uint32 type, uint32 id) override + me->GetMotionMaster()->MoveRandom(10.f); + + me->m_Events.AddEventAtOffset([this]() { - if (type != WAYPOINT_MOTION_TYPE || id != 13) + me->GetMotionMaster()->Initialize(); + }, 15s); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + + events.ScheduleEvent(EVENT_SHADOWFLAME, 18s); + events.ScheduleEvent(EVENT_WINGBUFFET, 30s); + events.ScheduleEvent(EVENT_SHADOWOFEBONROC, 8s, 10s); + } + + 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) { - return; + case EVENT_SHADOWFLAME: + DoCastVictim(SPELL_SHADOWFLAME); + events.ScheduleEvent(EVENT_SHADOWFLAME, 15s, 25s); + break; + case EVENT_WINGBUFFET: + DoCastVictim(SPELL_WINGBUFFET); + events.ScheduleEvent(EVENT_WINGBUFFET, 30s); + break; + case EVENT_SHADOWOFEBONROC: + DoCastVictim(SPELL_SHADOWOFEBONROC); + events.ScheduleEvent(EVENT_SHADOWOFEBONROC, 8s, 10s); + break; } - me->GetMotionMaster()->MoveRandom(10.f); - - me->m_Events.AddEventAtOffset([this]() - { - me->GetMotionMaster()->Initialize(); - }, 15s); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - - events.ScheduleEvent(EVENT_SHADOWFLAME, 18s); - events.ScheduleEvent(EVENT_WINGBUFFET, 30s); - events.ScheduleEvent(EVENT_SHADOWOFEBONROC, 8s, 10s); - } - - 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_SHADOWFLAME: - DoCastVictim(SPELL_SHADOWFLAME); - events.ScheduleEvent(EVENT_SHADOWFLAME, 15s, 25s); - break; - case EVENT_WINGBUFFET: - DoCastVictim(SPELL_WINGBUFFET); - events.ScheduleEvent(EVENT_WINGBUFFET, 30s); - break; - case EVENT_SHADOWOFEBONROC: - DoCastVictim(SPELL_SHADOWOFEBONROC); - events.ScheduleEvent(EVENT_SHADOWOFEBONROC, 8s, 10s); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); + DoMeleeAttackIfReady(); } }; void AddSC_boss_ebonroc() { - new boss_ebonroc(); + RegisterBlackwingLairCreatureAI(boss_ebonroc); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp index 197b1d843..7d5f217fd 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_firemaw.cpp @@ -33,69 +33,58 @@ enum Events EVENT_FLAMEBUFFET = 3 }; -class boss_firemaw : public CreatureScript +struct boss_firemaw : public BossAI { -public: - boss_firemaw() : CreatureScript("boss_firemaw") { } + boss_firemaw(Creature* creature) : BossAI(creature, DATA_FIREMAW) { } - struct boss_firemawAI : public BossAI + void JustEngagedWith(Unit* who) override { - boss_firemawAI(Creature* creature) : BossAI(creature, DATA_FIREMAW) { } + BossAI::JustEngagedWith(who); - void JustEngagedWith(Unit* who) override + events.ScheduleEvent(EVENT_SHADOWFLAME, 18s); + events.ScheduleEvent(EVENT_WINGBUFFET, 30s); + events.ScheduleEvent(EVENT_FLAMEBUFFET, 5s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - BossAI::JustEngagedWith(who); - - events.ScheduleEvent(EVENT_SHADOWFLAME, 18s); - events.ScheduleEvent(EVENT_WINGBUFFET, 30s); - events.ScheduleEvent(EVENT_FLAMEBUFFET, 5s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); + switch (eventId) + { + case EVENT_SHADOWFLAME: + DoCastVictim(SPELL_SHADOWFLAME); + events.ScheduleEvent(EVENT_SHADOWFLAME, 15s, 25s); + break; + case EVENT_WINGBUFFET: + DoCastVictim(SPELL_WINGBUFFET); + if (DoGetThreat(me->GetVictim())) + DoModifyThreatByPercent(me->GetVictim(), -75); + events.ScheduleEvent(EVENT_WINGBUFFET, 30s); + break; + case EVENT_FLAMEBUFFET: + DoCastVictim(SPELL_FLAMEBUFFET); + events.ScheduleEvent(EVENT_FLAMEBUFFET, 5s); + break; + } if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SHADOWFLAME: - DoCastVictim(SPELL_SHADOWFLAME); - events.ScheduleEvent(EVENT_SHADOWFLAME, 15s, 25s); - break; - case EVENT_WINGBUFFET: - DoCastVictim(SPELL_WINGBUFFET); - if (DoGetThreat(me->GetVictim())) - DoModifyThreatByPercent(me->GetVictim(), -75); - events.ScheduleEvent(EVENT_WINGBUFFET, 30s); - break; - case EVENT_FLAMEBUFFET: - DoCastVictim(SPELL_FLAMEBUFFET); - events.ScheduleEvent(EVENT_FLAMEBUFFET, 5s); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); + DoMeleeAttackIfReady(); } }; void AddSC_boss_firemaw() { - new boss_firemaw(); + RegisterBlackwingLairCreatureAI(boss_firemaw); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp index ce1028fe7..4933a0b63 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_flamegor.cpp @@ -38,70 +38,59 @@ enum Events EVENT_FRENZY = 3 }; -class boss_flamegor : public CreatureScript +struct boss_flamegor : public BossAI { -public: - boss_flamegor() : CreatureScript("boss_flamegor") { } + boss_flamegor(Creature* creature) : BossAI(creature, DATA_FLAMEGOR) { } - struct boss_flamegorAI : public BossAI + void JustEngagedWith(Unit* who) override { - boss_flamegorAI(Creature* creature) : BossAI(creature, DATA_FLAMEGOR) { } + BossAI::JustEngagedWith(who); - void JustEngagedWith(Unit* who) override + events.ScheduleEvent(EVENT_SHADOWFLAME, 18s); + events.ScheduleEvent(EVENT_WINGBUFFET, 30s); + events.ScheduleEvent(EVENT_FRENZY, 10s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - BossAI::JustEngagedWith(who); - - events.ScheduleEvent(EVENT_SHADOWFLAME, 18s); - events.ScheduleEvent(EVENT_WINGBUFFET, 30s); - events.ScheduleEvent(EVENT_FRENZY, 10s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); + switch (eventId) + { + case EVENT_SHADOWFLAME: + DoCastVictim(SPELL_SHADOWFLAME); + events.ScheduleEvent(EVENT_SHADOWFLAME, 15s, 25s); + break; + case EVENT_WINGBUFFET: + DoCastVictim(SPELL_WINGBUFFET); + if (DoGetThreat(me->GetVictim())) + DoModifyThreatByPercent(me->GetVictim(), -75); + events.ScheduleEvent(EVENT_WINGBUFFET, 30s); + break; + case EVENT_FRENZY: + Talk(EMOTE_FRENZY); + DoCast(me, SPELL_FRENZY); + events.ScheduleEvent(EVENT_FRENZY, 8s, 10s); + break; + } if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SHADOWFLAME: - DoCastVictim(SPELL_SHADOWFLAME); - events.ScheduleEvent(EVENT_SHADOWFLAME, 15s, 25s); - break; - case EVENT_WINGBUFFET: - DoCastVictim(SPELL_WINGBUFFET); - if (DoGetThreat(me->GetVictim())) - DoModifyThreatByPercent(me->GetVictim(), -75); - events.ScheduleEvent(EVENT_WINGBUFFET, 30s); - break; - case EVENT_FRENZY: - Talk(EMOTE_FRENZY); - DoCast(me, SPELL_FRENZY); - events.ScheduleEvent(EVENT_FRENZY, 8s, 10s); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); + DoMeleeAttackIfReady(); } }; void AddSC_boss_flamegor() { - new boss_flamegor(); + RegisterBlackwingLairCreatureAI(boss_flamegor); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 55d3d818e..b3eec7834 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -214,14 +214,10 @@ struct ClassCallSelector : public Acore::unary_function bool operator()(Unit const* target) const { if (!_me || !target || !target->IsPlayer()) - { return false; - } if (target->getClass() != _targetClass) - { return false; - } return true; } @@ -231,304 +227,291 @@ private: uint8 _targetClass; }; -class boss_victor_nefarius : public CreatureScript +struct boss_victor_nefarius : public BossAI { -public: - boss_victor_nefarius() : CreatureScript("boss_victor_nefarius") { } - - struct boss_victor_nefariusAI : public BossAI + boss_victor_nefarius(Creature* creature) : BossAI(creature, DATA_NEFARIAN) { - boss_victor_nefariusAI(Creature* creature) : BossAI(creature, DATA_NEFARIAN) + Initialize(); + + _nefarianLeftTunnel = instance->GetData(DATA_NEFARIAN_LEFT_TUNNEL); + _nefarianRightTunnel = instance->GetData(DATA_NEFARIAN_RIGHT_TUNNEL); + + if (!_nefarianLeftTunnel || !_nefarianRightTunnel) { - Initialize(); + // Victor Nefarius weekly mechanic drakonid spawn + // Pick 2 drakonids and keep them for the whole save duration (the drakonids can't be repeated). + std::vector nefarianDrakonidSpawners = { NPC_BLACK_SPAWNER, NPC_BLUE_SPAWNER, NPC_BRONZE_SPAWNER, NPC_GREEN_SPAWNER, NPC_RED_SPAWNER }; + Acore::Containers::RandomResize(nefarianDrakonidSpawners, 2); - _nefarianLeftTunnel = instance->GetData(DATA_NEFARIAN_LEFT_TUNNEL); - _nefarianRightTunnel = instance->GetData(DATA_NEFARIAN_RIGHT_TUNNEL); + _nefarianRightTunnel = nefarianDrakonidSpawners[0]; + _nefarianLeftTunnel = nefarianDrakonidSpawners[1]; - if (!_nefarianLeftTunnel || !_nefarianRightTunnel) - { - // Victor Nefarius weekly mechanic drakonid spawn - // Pick 2 drakonids and keep them for the whole save duration (the drakonids can't be repeated). - std::vector nefarianDrakonidSpawners = { NPC_BLACK_SPAWNER, NPC_BLUE_SPAWNER, NPC_BRONZE_SPAWNER, NPC_GREEN_SPAWNER, NPC_RED_SPAWNER }; - Acore::Containers::RandomResize(nefarianDrakonidSpawners, 2); - - _nefarianRightTunnel = nefarianDrakonidSpawners[0]; - _nefarianLeftTunnel = nefarianDrakonidSpawners[1]; - - // save it to instance - instance->SetData(DATA_NEFARIAN_LEFT_TUNNEL, _nefarianLeftTunnel); - instance->SetData(DATA_NEFARIAN_RIGHT_TUNNEL, _nefarianRightTunnel); - } + // save it to instance + instance->SetData(DATA_NEFARIAN_LEFT_TUNNEL, _nefarianLeftTunnel); + instance->SetData(DATA_NEFARIAN_RIGHT_TUNNEL, _nefarianRightTunnel); } + } - void Initialize() + void Initialize() + { + _killedAdds = 0; + } + + void Reset() override + { + Initialize(); + + if (me->GetMapId() == MAP_BLACKWING_LAIR) { - KilledAdds = 0; - } - - void Reset() override - { - Initialize(); - - if (me->GetMapId() == MAP_BLACKWING_LAIR) + if (Creature* nefarian = me->FindNearestCreature(NPC_NEFARIAN, 1000.0f, true)) { - if (Creature* nefarian = me->FindNearestCreature(NPC_NEFARIAN, 1000.0f, true)) + // Nefarian is spawned and he didn't finish his intro path yet, despawn it manually. + if (nefarian->GetMotionMaster()->GetCurrentMovementGeneratorType() == MovementGeneratorType::WAYPOINT_MOTION_TYPE) { - // Nefarian is spawned and he didn't finish his intro path yet, despawn it manually. - if (nefarian->GetMotionMaster()->GetCurrentMovementGeneratorType() == MovementGeneratorType::WAYPOINT_MOTION_TYPE) - { - nefarian->DespawnOrUnsummon(); - } - std::list drakonidBones; - me->GetGameObjectListWithEntryInGrid(drakonidBones, GO_DRAKONID_BONES, DEFAULT_VISIBILITY_INSTANCE); - for (auto const& bones : drakonidBones) - { - bones->DespawnOrUnsummon(); - } + nefarian->DespawnOrUnsummon(); } - else + std::list drakonidBones; + me->GetGameObjectListWithEntryInGrid(drakonidBones, GO_DRAKONID_BONES, DEFAULT_VISIBILITY_INSTANCE); + for (auto const& bones : drakonidBones) { - _Reset(); - } - - me->SetVisible(true); - me->SetPhaseMask(1, true); - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->SetFaction(FACTION_FRIENDLY); - me->SetStandState(UNIT_STAND_STATE_SIT_HIGH_CHAIR); - me->RemoveAura(SPELL_NEFARIANS_BARRIER); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - } - } - - void JustReachedHome() override - { - Reset(); - } - - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() != NPC_NEFARIAN) - { - BossAI::JustSummoned(summon); - } - } - - void SummonedCreatureDies(Creature* summon, Unit* /*unit*/) override - { - if (summon->GetEntry() == NPC_NEFARIAN) - { - summons.DespawnEntry(_nefarianLeftTunnel); - summons.DespawnEntry(_nefarianRightTunnel); - me->KillSelf(); - } - } - - void DoAction(int32 action) override - { - if (action == ACTION_RESET) - { - me->RemoveAura(SPELL_ROOT_SELF); - summons.DespawnAll(); - } - - if (action == ACTION_ADD_KILLED) - { - KilledAdds++; - - if (KilledAdds == MAX_DRAKONID_KILLED) - { - if (Creature* nefarian = me->SummonCreature(NPC_NEFARIAN, NefarianSpawn)) - { - nefarian->setActive(true); - nefarian->SetCanFly(true); - nefarian->SetDisableGravity(true); - nefarian->GetMotionMaster()->MoveWaypoint(NEFARIAN_PATH, false); - } - - events.Reset(); - DoCastSelf(SPELL_ROOT_SELF, true); - me->SetVisible(false); - // Stop spawning adds - EntryCheckPredicate pred(_nefarianRightTunnel); - summons.DoAction(ACTION_SPAWNER_STOP, pred); - EntryCheckPredicate pred2(_nefarianLeftTunnel); - summons.DoAction(ACTION_SPAWNER_STOP, pred2); + bones->DespawnOrUnsummon(); } } - } - - void JustDied(Unit* /*killer*/) override - { - instance->SetBossState(DATA_NEFARIAN, DONE); - instance->SaveToDB(); - } - - void BeginEvent() - { - _JustEngagedWith(); - - Talk(SAY_GAMESBEGIN_2); - - DoCast(me, SPELL_NEFARIANS_BARRIER); - me->SetCombatMovement(false); - me->SetImmuneToPC(false); - AttackStart(SelectTarget(SelectTargetMethod::Random, 0, 200.f, true)); - events.ScheduleEvent(EVENT_SHADOWBLINK, 500ms); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 3s); - events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 13s, 15s); - events.ScheduleEvent(EVENT_FEAR, 10s, 20s); - events.ScheduleEvent(EVENT_SILENCE, 20s, 25s); - events.ScheduleEvent(EVENT_MIND_CONTROL, 30s, 35s); - events.ScheduleEvent(EVENT_SPAWN_ADDS, 10s); - } - - void SetData(uint32 type, uint32 data) override - { - if (type == 1 && data == 1) + else { - me->StopMoving(); - events.ScheduleEvent(EVENT_PATH_2, 9s); + _Reset(); } - if (type == 1 && data == 2) - events.ScheduleEvent(EVENT_SUCCESS_1, 5s); + me->SetVisible(true); + me->SetPhaseMask(1, true); + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->SetFaction(FACTION_FRIENDLY); + me->SetStandState(UNIT_STAND_STATE_SIT_HIGH_CHAIR); + me->RemoveAura(SPELL_NEFARIANS_BARRIER); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } + } + + void JustReachedHome() override + { + Reset(); + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() != NPC_NEFARIAN) + BossAI::JustSummoned(summon); + } + + void SummonedCreatureDies(Creature* summon, Unit* /*unit*/) override + { + if (summon->GetEntry() == NPC_NEFARIAN) + { + summons.DespawnEntry(_nefarianLeftTunnel); + summons.DespawnEntry(_nefarianRightTunnel); + me->KillSelf(); + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_RESET) + { + me->RemoveAura(SPELL_ROOT_SELF); + summons.DespawnAll(); } - void UpdateAI(uint32 diff) override + if (action == ACTION_ADD_KILLED) { - if (!UpdateVictim()) - { - events.Update(diff); + _killedAdds++; - while (uint32 eventId = events.ExecuteEvent()) + if (_killedAdds == MAX_DRAKONID_KILLED) + { + if (Creature* nefarian = me->SummonCreature(NPC_NEFARIAN, NefarianSpawn)) { - switch (eventId) - { - case EVENT_PATH_2: - me->GetMotionMaster()->MoveWaypoint(NEFARIUS_PATH_2, false); - events.ScheduleEvent(EVENT_CHAOS_1, 7s); - break; - case EVENT_CHAOS_1: - if (Creature* gyth = me->FindNearestCreature(NPC_GYTH, 75.0f, true)) - { - me->SetFacingToObject(gyth); - Talk(SAY_CHAOS_SPELL); - } - events.ScheduleEvent(EVENT_CHAOS_2, 2s); - break; - case EVENT_CHAOS_2: - DoCast(SPELL_CHROMATIC_CHAOS); - me->SetFacingTo(1.570796f); - break; - case EVENT_SUCCESS_1: - if (Unit* player = me->SelectNearestPlayer(60.0f)) - { - me->SetFacingToObject(player); - Talk(SAY_SUCCESS); - if (GameObject* portcullis1 = me->FindNearestGameObject(GO_PORTCULLIS_ACTIVE, 65.0f)) - portcullis1->SetGoState(GO_STATE_ACTIVE); - if (GameObject* portcullis2 = me->FindNearestGameObject(GO_PORTCULLIS_TOBOSSROOMS, 80.0f)) - portcullis2->SetGoState(GO_STATE_ACTIVE); - } - events.ScheduleEvent(EVENT_SUCCESS_2, 4s); - break; - case EVENT_SUCCESS_2: - DoCast(me, SPELL_VAELASTRASZZ_SPAWN); - me->DespawnOrUnsummon(1s); - break; - case EVENT_PATH_3: - me->GetMotionMaster()->MoveWaypoint(NEFARIUS_PATH_3, false); - break; - case EVENT_START_EVENT: - BeginEvent(); - break; - default: - break; - } + nefarian->setActive(true); + nefarian->SetCanFly(true); + nefarian->SetDisableGravity(true); + nefarian->GetMotionMaster()->MoveWaypoint(NEFARIAN_PATH, false); } + + events.Reset(); + DoCastSelf(SPELL_ROOT_SELF, true); + me->SetVisible(false); + // Stop spawning adds + EntryCheckPredicate pred(_nefarianRightTunnel); + summons.DoAction(ACTION_SPAWNER_STOP, pred); + EntryCheckPredicate pred2(_nefarianLeftTunnel); + summons.DoAction(ACTION_SPAWNER_STOP, pred2); + } + } + } + + void JustDied(Unit* /*killer*/) override + { + instance->SetBossState(DATA_NEFARIAN, DONE); + instance->SaveToDB(); + } + + void BeginEvent() + { + _JustEngagedWith(); + + Talk(SAY_GAMESBEGIN_2); + + DoCast(me, SPELL_NEFARIANS_BARRIER); + me->SetCombatMovement(false); + me->SetImmuneToPC(false); + AttackStart(SelectTarget(SelectTargetMethod::Random, 0, 200.f, true)); + events.ScheduleEvent(EVENT_SHADOWBLINK, 500ms); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 3s); + events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 13s, 15s); + events.ScheduleEvent(EVENT_FEAR, 10s, 20s); + events.ScheduleEvent(EVENT_SILENCE, 20s, 25s); + events.ScheduleEvent(EVENT_MIND_CONTROL, 30s, 35s); + events.ScheduleEvent(EVENT_SPAWN_ADDS, 10s); + } + + void SetData(uint32 type, uint32 data) override + { + if (type == 1 && data == 1) + { + me->StopMoving(); + events.ScheduleEvent(EVENT_PATH_2, 9s); + } + + if (type == 1 && data == 2) + events.ScheduleEvent(EVENT_SUCCESS_1, 5s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_PATH_2: + me->GetMotionMaster()->MoveWaypoint(NEFARIUS_PATH_2, false); + events.ScheduleEvent(EVENT_CHAOS_1, 7s); + break; + case EVENT_CHAOS_1: + if (Creature* gyth = me->FindNearestCreature(NPC_GYTH, 75.0f, true)) + { + me->SetFacingToObject(gyth); + Talk(SAY_CHAOS_SPELL); + } + events.ScheduleEvent(EVENT_CHAOS_2, 2s); + break; + case EVENT_CHAOS_2: + DoCast(SPELL_CHROMATIC_CHAOS); + me->SetFacingTo(1.570796f); + break; + case EVENT_SUCCESS_1: + if (Unit* player = me->SelectNearestPlayer(60.0f)) + { + me->SetFacingToObject(player); + Talk(SAY_SUCCESS); + if (GameObject* portcullis1 = me->FindNearestGameObject(GO_PORTCULLIS_ACTIVE, 65.0f)) + portcullis1->SetGoState(GO_STATE_ACTIVE); + if (GameObject* portcullis2 = me->FindNearestGameObject(GO_PORTCULLIS_TOBOSSROOMS, 80.0f)) + portcullis2->SetGoState(GO_STATE_ACTIVE); + } + events.ScheduleEvent(EVENT_SUCCESS_2, 4s); + break; + case EVENT_SUCCESS_2: + DoCast(me, SPELL_VAELASTRASZZ_SPAWN); + me->DespawnOrUnsummon(1s); + break; + case EVENT_PATH_3: + me->GetMotionMaster()->MoveWaypoint(NEFARIUS_PATH_3, false); + break; + case EVENT_START_EVENT: + BeginEvent(); + break; + default: + break; + } + } + return; + } + + // Only do this if we haven't spawned nefarian yet + if (UpdateVictim() && _killedAdds <= MAX_DRAKONID_KILLED) + { + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - } - // Only do this if we haven't spawned nefarian yet - if (UpdateVictim() && KilledAdds <= MAX_DRAKONID_KILLED) + while (uint32 eventId = events.ExecuteEvent()) { - events.Update(diff); + switch (eventId) + { + case EVENT_SHADOW_BOLT: + DoCastRandomTarget(SPELL_SHADOWBOLT, 0, 150.f); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 2s, 4s); + break; + case EVENT_SHADOW_BOLT_VOLLEY: + DoCastAOE(SPELL_SHADOWBOLT_VOLLEY); + events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 19s, 25s); + break; + case EVENT_FEAR: + DoCastRandomTarget(SPELL_FEAR, 0, 40.0f); + events.ScheduleEvent(EVENT_FEAR, 10s, 20s); + break; + case EVENT_SILENCE: + DoCastRandomTarget(SPELL_SILENCE, 0, 150.f); + events.ScheduleEvent(EVENT_SILENCE, 14s,23s); + break; + case EVENT_MIND_CONTROL: + DoCastRandomTarget(SPELL_SHADOW_COMMAND, 0, 40.0f); + events.ScheduleEvent(EVENT_MIND_CONTROL, 24s, 30s); + break; + case EVENT_SHADOWBLINK: + DoCastSelf(SPELL_SHADOWBLINK); + events.ScheduleEvent(EVENT_SHADOWBLINK, 30s, 40s); + break; + case EVENT_SPAWN_ADDS: + // Spawn the spawners. + me->SummonCreature(_nefarianLeftTunnel, spawnerPositions[0]); + me->SummonCreature(_nefarianRightTunnel, spawnerPositions[1]); + break; + } if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SHADOW_BOLT: - DoCastRandomTarget(SPELL_SHADOWBOLT, 0, 150.f); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 2s, 4s); - break; - case EVENT_SHADOW_BOLT_VOLLEY: - DoCastAOE(SPELL_SHADOWBOLT_VOLLEY); - events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 19s, 25s); - break; - case EVENT_FEAR: - DoCastRandomTarget(SPELL_FEAR, 0, 40.0f); - events.ScheduleEvent(EVENT_FEAR, 10s, 20s); - break; - case EVENT_SILENCE: - DoCastRandomTarget(SPELL_SILENCE, 0, 150.f); - events.ScheduleEvent(EVENT_SILENCE, 14s,23s); - break; - case EVENT_MIND_CONTROL: - DoCastRandomTarget(SPELL_SHADOW_COMMAND, 0, 40.0f); - events.ScheduleEvent(EVENT_MIND_CONTROL, 24s, 30s); - break; - case EVENT_SHADOWBLINK: - DoCastSelf(SPELL_SHADOWBLINK); - events.ScheduleEvent(EVENT_SHADOWBLINK, 30s, 40s); - break; - case EVENT_SPAWN_ADDS: - // Spawn the spawners. - me->SummonCreature(_nefarianLeftTunnel, spawnerPositions[0]); - me->SummonCreature(_nefarianRightTunnel, spawnerPositions[1]); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } } } - - void sGossipSelect(Player* player, uint32 sender, uint32 action) override - { - if (sender == GOSSIP_ID && action == GOSSIP_OPTION_ID) - { - // pussywizard: - InstanceScript* instance = player->GetInstanceScript(); - if (!instance || instance->GetBossState(DATA_NEFARIAN) == DONE) - return; - - CloseGossipMenuFor(player); - Talk(SAY_GAMESBEGIN_1); - events.ScheduleEvent(EVENT_START_EVENT, 4s); - me->SetFaction(FACTION_DRAGONFLIGHT_BLACK); - me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToPC(true); - } - } - - private: - uint32 KilledAdds; - uint32 _nefarianRightTunnel; - uint32 _nefarianLeftTunnel; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); } + + void sGossipSelect(Player* player, uint32 sender, uint32 action) override + { + if (sender == GOSSIP_ID && action == GOSSIP_OPTION_ID) + { + // pussywizard: + InstanceScript* instance = player->GetInstanceScript(); + if (!instance || instance->GetBossState(DATA_NEFARIAN) == DONE) + return; + + CloseGossipMenuFor(player); + Talk(SAY_GAMESBEGIN_1); + events.ScheduleEvent(EVENT_START_EVENT, 4s); + me->SetFaction(FACTION_DRAGONFLIGHT_BLACK); + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(true); + } + } + +private: + uint32 _killedAdds; + uint32 _nefarianRightTunnel; + uint32 _nefarianLeftTunnel; }; struct boss_nefarian : public BossAI @@ -545,12 +528,8 @@ struct boss_nefarian : public BossAI { _Reset(); if (Creature* victor = me->FindNearestCreature(NPC_VICTOR_NEFARIUS, 200.f, true)) - { if (victor->AI()) - { victor->AI()->DoAction(ACTION_RESET); - } - } me->DespawnOrUnsummon(); } @@ -578,9 +557,7 @@ struct boss_nefarian : public BossAI void KilledUnit(Unit* victim) override { if (rand32() % 5) - { return; - } Talk(SAY_SLAY, victim); } @@ -588,14 +565,10 @@ struct boss_nefarian : public BossAI void MovementInform(uint32 type, uint32 id) override { if (type != WAYPOINT_MOTION_TYPE) - { return; - } if (id == 4) - { Talk(SAY_INTRO); - } if (id == 6) { @@ -617,9 +590,7 @@ struct boss_nefarian : public BossAI me->SetReactState(REACT_AGGRESSIVE); DoZoneInCombat(); if (me->GetVictim()) - { AttackStart(me->GetVictim()); - } events.ScheduleEvent(EVENT_SHADOWFLAME, 12s); events.ScheduleEvent(EVENT_FEAR, 25s, 35s); @@ -633,16 +604,12 @@ struct boss_nefarian : public BossAI void UpdateAI(uint32 diff) override { if (!UpdateVictim()) - { return; - } events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } while (uint32 eventId = events.ExecuteEvent()) { @@ -671,16 +638,10 @@ struct boss_nefarian : public BossAI break; case EVENT_CLASSCALL: if (classesPresent.empty()) - { for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList()) - { if (Unit* victim = ref->GetVictim()) - { if (victim->IsPlayer()) classesPresent.insert(victim->getClass()); - } - } - } uint8 targetClass = Acore::Containers::SelectRandomContainerElement(classesPresent); @@ -731,9 +692,7 @@ struct boss_nefarian : public BossAI me->GetMap()->DoForAllPlayers([&](Player* p) { if (!p->IsGameMaster()) - { me->CastSpell(p, SPELL_DEATH_KNIGHT, true); - } }); break; default: @@ -745,9 +704,7 @@ struct boss_nefarian : public BossAI } if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } } DoMeleeAttackIfReady(); @@ -783,9 +740,7 @@ struct npc_corrupted_totem : public ScriptedAI { me->AddUnitState(UNIT_STATE_ROOT); if (!me->HasAura(SPELL_ROOT_SELF)) - { me->AddAura(SPELL_ROOT_SELF, me); - } me->AddAura(AURA_AVOIDANCE, me); scheduler.CancelAll(); @@ -809,9 +764,7 @@ struct npc_corrupted_totem : public ScriptedAI } if (!spellId) - { return; - } std::vector mobsEntries = { @@ -835,25 +788,19 @@ struct npc_corrupted_totem : public ScriptedAI tmpMobList.pop_front(); if (!curr->IsAlive()) - { continue; - } if (apply && me->IsAlive()) { if (me->IsWithinDistInMap(curr, 40.f)) { if (!curr->HasAura(spellId)) - { curr->AddAura(spellId, curr); - } } else { if (curr->HasAura(spellId)) - { curr->RemoveAurasDueToSpell(spellId); - } } } else @@ -899,9 +846,7 @@ struct npc_corrupted_totem : public ScriptedAI void JustDied(Unit* /*killer*/) override { if (me->GetEntry() != NPC_TOTEM_C_FIRE_NOVA) - { SetAura(false); - } scheduler.CancelAll(); } @@ -909,9 +854,7 @@ struct npc_corrupted_totem : public ScriptedAI void UpdateAI(uint32 diff) override { if (!UpdateVictim()) - { return; - } scheduler.Update(diff); } @@ -952,9 +895,7 @@ struct npc_drakonid_spawner : public ScriptedAI void SummonedCreatureDies(Creature* summon, Unit* /*unit*/) override { if (Creature* victor = ObjectAccessor::GetCreature(*me, _owner)) - { victor->AI()->DoAction(ACTION_NEFARIUS_ADD_KILLED); - } ObjectGuid summonGuid = summon->GetGUID(); @@ -1002,15 +943,11 @@ class spell_class_call_handler : public SpellScript { Player const* player = target->ToPlayer(); if (!player || player->IsClass(CLASS_DEATH_KNIGHT)) // ignore all death knights from whatever spell, for some reason the condition below is not working x.x - { return true; - } auto it = classCallSpells.find(spellInfo->Id); if (it != classCallSpells.end()) // should never happen but only to be sure. - { return target->ToPlayer()->getClass() != it->second; - } return false; }); @@ -1022,9 +959,7 @@ class spell_class_call_handler : public SpellScript Unit* caster = GetCaster(); Unit* target = GetHitUnit(); if (!caster || !target) - { return; - } Position tp = caster->GetFirstCollisionPosition(5.f, 0.f); target->NearTeleportTo(tp.GetPositionX(), tp.GetPositionY(), tp.GetPositionZ(), tp.GetOrientation()); @@ -1033,9 +968,7 @@ class spell_class_call_handler : public SpellScript void HandleOnHitWarlock() { if (Unit* target = GetHitUnit()) - { target->CastSpell(target, SPELL_SUMMON_INFERNALS, true); - } } void Register() override @@ -1065,9 +998,7 @@ class aura_class_call_wild_magic : public AuraScript void HandlePeriodic(AuraEffect const* /*aurEff*/) { if (!GetTarget()) - { return; - } GetTarget()->CastSpell(GetTarget(), SPELL_POLYMORPH, true); } @@ -1092,12 +1023,8 @@ class aura_class_call_siphon_blessing : public AuraScript PreventDefaultAction(); if (Unit* target = GetTarget()) - { if (Unit* nefarian = target->FindNearestCreature(NPC_NEFARIAN, 100.f)) - { target->CastSpell(nefarian, SPELL_BLESSING_PROTECTION, true); - } - } } void Register() override @@ -1154,9 +1081,7 @@ class spell_corrupted_totems : public SpellScript { PreventHitDefaultEffect(effIndex); if (!GetCaster()) - { return; - } std::list spellList = { SPELL_CORRUPTED_FIRE_NOVA_TOTEM, SPELL_CORRUPTED_HEALING_TOTEM, SPELL_CORRUPTED_STONESKIN_TOTEM, SPELL_CORRUPTED_WINDFURY_TOTEM }; uint32 spellId = Acore::Containers::SelectRandomContainerElement(spellList); @@ -1207,15 +1132,11 @@ class spell_shadowblink : public SpellScript { Unit* caster = GetCaster(); if (!caster || !caster->ToCreature() || !caster->ToCreature()->AI()) - { return; - } Unit* target = caster->ToCreature()->AI()->SelectTarget(SelectTargetMethod::Random, 0, 200.f, true); if (!target) - { return; - } for (auto& itr : spellPos) { @@ -1252,9 +1173,7 @@ class spell_spawn_drakonid : public SpellScript { Unit* caster = GetCaster(); if (!caster) - { return; - } caster->CastSpell(caster, spawnerSpells[caster->GetEntry()], true); } @@ -1267,7 +1186,7 @@ class spell_spawn_drakonid : public SpellScript void AddSC_boss_nefarian() { - new boss_victor_nefarius(); + RegisterBlackwingLairCreatureAI(boss_victor_nefarius); RegisterCreatureAI(boss_nefarian); RegisterCreatureAI(npc_corrupted_totem); RegisterCreatureAI(npc_drakonid_spawner); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp index c17fd24f2..40d801ae0 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp @@ -16,6 +16,8 @@ */ #include "CreatureScript.h" +#include "GameObject.h" +#include "GameObjectAI.h" #include "GameObjectScript.h" #include "Player.h" #include "ScriptedCreature.h" @@ -70,214 +72,190 @@ enum EVENTS EVENT_CONFLAGRATION = 4 }; -class boss_razorgore : public CreatureScript +struct boss_razorgore : public BossAI { -public: - boss_razorgore() : CreatureScript("boss_razorgore") { } + boss_razorgore(Creature* creature) : BossAI(creature, DATA_RAZORGORE_THE_UNTAMED) { } - struct boss_razorgoreAI : public BossAI + void Reset() override { - boss_razorgoreAI(Creature* creature) : BossAI(creature, DATA_RAZORGORE_THE_UNTAMED) { } - - void Reset() override - { - _Reset(); - _charmerGUID.Clear(); - secondPhase = false; - summons.DespawnAll(); - instance->SetData(DATA_EGG_EVENT, NOT_STARTED); - } - - void JustDied(Unit* /*killer*/) override - { - if (secondPhase) - { - _JustDied(); - } - else - { - // Respawn shorty in case of failure during phase 1. - me->SetCorpseRemoveTime(25); - me->SetRespawnTime(30); - me->SaveRespawnTime(); - - // Might not be required, safe measure. - me->SetLootRecipient(nullptr); - - instance->SetData(DATA_EGG_EVENT, FAIL); - } - } - - bool CanAIAttack(Unit const* target) const override - { - return !(target->IsCreature() && !secondPhase); - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - - events.ScheduleEvent(EVENT_CLEAVE, 15s); - events.ScheduleEvent(EVENT_STOMP, 35s); - events.ScheduleEvent(EVENT_FIREBALL, 7s); - events.ScheduleEvent(EVENT_CONFLAGRATION, 12s); - - instance->SetData(DATA_EGG_EVENT, IN_PROGRESS); - } - - void DoChangePhase() - { - secondPhase = true; - _charmerGUID.Clear(); - me->RemoveAllAuras(); - - DoCastSelf(SPELL_WARMING_FLAMES, true); - - if (Creature* troops = instance->GetCreature(DATA_NEFARIAN_TROOPS)) - { - troops->AI()->Talk(EMOTE_TROOPS_RETREAT); - } - - for (ObjectGuid const& guid : _summonGUIDS) - { - if (Creature* creature = ObjectAccessor::GetCreature(*me, guid)) - { - if (creature->IsAlive()) - { - creature->CombatStop(true); - creature->SetReactState(REACT_PASSIVE); - creature->GetMotionMaster()->MovePoint(0, Position(-7560.568848f, -1028.553345f, 408.491211f, 0.523858f)); - } - } - } - } - - void SetGUID(ObjectGuid const& guid, int32 /*id*/) override - { - _charmerGUID = guid; - } - - void OnCharmed(bool apply) override - { - if (apply) - { - if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmerGUID)) - { - charmer->CastSpell(charmer, SPELL_MIND_EXHAUSTION, true); - charmer->CastSpell(me, SPELL_MINDCONTROL_VISUAL, false); - } - } - else - { - if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmerGUID)) - { - charmer->RemoveAurasDueToSpell(SPELL_MINDCONTROL_VISUAL); - me->EngageWithTarget(charmer); - me->AddThreat(charmer, 100.0f); - me->AI()->AttackStart(charmer); - } - } - } - - void DoAction(int32 action) override - { - if (action == ACTION_PHASE_TWO) - { - DoChangePhase(); - } - - if (action == TALK_EGG_BROKEN_RAND) - { - Talk(urand(SAY_EGGS_BROKEN1, SAY_EGGS_BROKEN3)); - } - } - - void JustSummoned(Creature* summon) override - { - _summonGUIDS.push_back(summon->GetGUID()); - summon->SetOwnerGUID(me->GetGUID()); - summons.Summon(summon); - } - - void SummonMovementInform(Creature* summon, uint32 movementType, uint32 /*pathId*/) override - { - if (movementType == POINT_MOTION_TYPE) - { - summon->DespawnOrUnsummon(); - } - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (!secondPhase && damage >= me->GetHealth()) - { - Talk(SAY_DEATH); - DoCastAOE(SPELL_EXPLODE_ORB); - DoCastAOE(SPELL_EXPLOSION); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (!me->IsCharmed()) - { - events.Update(diff); - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, 7s, 10s); - break; - case EVENT_STOMP: - DoCastVictim(SPELL_WARSTOMP); - events.ScheduleEvent(EVENT_STOMP, 15s, 25s); - break; - case EVENT_FIREBALL: - DoCastVictim(SPELL_FIREBALLVOLLEY); - events.ScheduleEvent(EVENT_FIREBALL, 12s, 15s); - break; - case EVENT_CONFLAGRATION: - DoCastVictim(SPELL_CONFLAGRATION); - events.ScheduleEvent(EVENT_CONFLAGRATION, 30s); - break; - } - } - - DoMeleeAttackIfReady(); - } - - private: - bool secondPhase; - ObjectGuid _charmerGUID; - GuidVector _summonGUIDS; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); + _Reset(); + _charmerGUID.Clear(); + secondPhase = false; + summons.DespawnAll(); + instance->SetData(DATA_EGG_EVENT, NOT_STARTED); } + + void JustDied(Unit* /*killer*/) override + { + if (secondPhase) + { + _JustDied(); + } + else + { + // Respawn shorty in case of failure during phase 1. + me->SetCorpseRemoveTime(25); + me->SetRespawnTime(30); + me->SaveRespawnTime(); + + // Might not be required, safe measure. + me->SetLootRecipient(nullptr); + + instance->SetData(DATA_EGG_EVENT, FAIL); + } + } + + bool CanAIAttack(Unit const* target) const override + { + return !(target->IsCreature() && !secondPhase); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + + events.ScheduleEvent(EVENT_CLEAVE, 15s); + events.ScheduleEvent(EVENT_STOMP, 35s); + events.ScheduleEvent(EVENT_FIREBALL, 7s); + events.ScheduleEvent(EVENT_CONFLAGRATION, 12s); + + instance->SetData(DATA_EGG_EVENT, IN_PROGRESS); + } + + void DoChangePhase() + { + secondPhase = true; + _charmerGUID.Clear(); + me->RemoveAllAuras(); + + DoCastSelf(SPELL_WARMING_FLAMES, true); + + if (Creature* troops = instance->GetCreature(DATA_NEFARIAN_TROOPS)) + troops->AI()->Talk(EMOTE_TROOPS_RETREAT); + + for (ObjectGuid const& guid : _summonGUIDS) + { + if (Creature* creature = ObjectAccessor::GetCreature(*me, guid)) + if (creature->IsAlive()) + { + creature->CombatStop(true); + creature->SetReactState(REACT_PASSIVE); + creature->GetMotionMaster()->MovePoint(0, Position(-7560.568848f, -1028.553345f, 408.491211f, 0.523858f)); + } + } + } + + void SetGUID(ObjectGuid const& guid, int32 /*id*/) override + { + _charmerGUID = guid; + } + + void OnCharmed(bool apply) override + { + if (apply) + { + if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmerGUID)) + { + charmer->CastSpell(charmer, SPELL_MIND_EXHAUSTION, true); + charmer->CastSpell(me, SPELL_MINDCONTROL_VISUAL, false); + } + } + else + { + if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmerGUID)) + { + charmer->RemoveAurasDueToSpell(SPELL_MINDCONTROL_VISUAL); + me->EngageWithTarget(charmer); + me->AddThreat(charmer, 100.0f); + me->AI()->AttackStart(charmer); + } + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_PHASE_TWO) + DoChangePhase(); + + if (action == TALK_EGG_BROKEN_RAND) + Talk(urand(SAY_EGGS_BROKEN1, SAY_EGGS_BROKEN3)); + } + + void JustSummoned(Creature* summon) override + { + _summonGUIDS.push_back(summon->GetGUID()); + summon->SetOwnerGUID(me->GetGUID()); + summons.Summon(summon); + } + + void SummonMovementInform(Creature* summon, uint32 movementType, uint32 /*pathId*/) override + { + if (movementType == POINT_MOTION_TYPE) + summon->DespawnOrUnsummon(); + } + + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (!secondPhase && damage >= me->GetHealth()) + { + Talk(SAY_DEATH); + DoCastAOE(SPELL_EXPLODE_ORB); + DoCastAOE(SPELL_EXPLOSION); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (!me->IsCharmed()) + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, 7s, 10s); + break; + case EVENT_STOMP: + DoCastVictim(SPELL_WARSTOMP); + events.ScheduleEvent(EVENT_STOMP, 15s, 25s); + break; + case EVENT_FIREBALL: + DoCastVictim(SPELL_FIREBALLVOLLEY); + events.ScheduleEvent(EVENT_FIREBALL, 12s, 15s); + break; + case EVENT_CONFLAGRATION: + DoCastVictim(SPELL_CONFLAGRATION); + events.ScheduleEvent(EVENT_CONFLAGRATION, 30s); + break; + } + } + + DoMeleeAttackIfReady(); + } + +private: + bool secondPhase; + ObjectGuid _charmerGUID; + GuidVector _summonGUIDS; }; -class go_orb_of_domination : public GameObjectScript +struct go_orb_of_domination : public GameObjectAI { -public: - go_orb_of_domination() : GameObjectScript("go_orb_of_domination") { } + go_orb_of_domination(GameObject* go) : GameObjectAI(go) { } - bool OnGossipHello(Player* player, GameObject* go) override + bool GossipHello(Player* player, bool /*reportUse*/) override { - if (InstanceScript* instance = go->GetInstanceScript()) + if (InstanceScript* instance = me->GetInstanceScript()) if (instance->GetData(DATA_EGG_EVENT) != DONE && !player->HasAura(SPELL_MIND_EXHAUSTION) && !player->GetPet()) - if (Creature* razor = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_RAZORGORE_THE_UNTAMED))) + if (Creature* razor = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAZORGORE_THE_UNTAMED))) { razor->AI()->SetGUID(player->GetGUID()); razor->Attack(player, true); @@ -294,12 +272,9 @@ class spell_egg_event : public SpellScript void HandleOnHit() { if (InstanceScript* instance = GetCaster()->GetInstanceScript()) - { instance->SetData(DATA_EGG_EVENT, SPECIAL); - } if (Creature* razorgore = GetCaster()->ToCreature()) - { if (GameObject* egg = GetHitGObj()) { razorgore->AI()->DoAction(TALK_EGG_BROKEN_RAND); @@ -307,7 +282,6 @@ class spell_egg_event : public SpellScript egg->UseDoorOrButton(10000); egg->SetRespawnTime(WEEK); } - } } void Register() override @@ -318,7 +292,7 @@ class spell_egg_event : public SpellScript void AddSC_boss_razorgore() { - new boss_razorgore(); - new go_orb_of_domination(); + RegisterBlackwingLairCreatureAI(boss_razorgore); + RegisterBlackwingLairGameObjectAI(go_orb_of_domination); RegisterSpellScript(spell_egg_event); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp index 3cfbf13e4..1980d0ba9 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp @@ -72,227 +72,214 @@ enum Events EVENT_BURNING_ADRENALINE = 12, }; -class boss_vaelastrasz : public CreatureScript +struct boss_vaelastrasz : public BossAI { -public: - boss_vaelastrasz() : CreatureScript("boss_vaelastrasz") { } - - struct boss_vaelAI : public BossAI + boss_vaelastrasz(Creature* creature) : BossAI(creature, DATA_VAELASTRAZ_THE_CORRUPT) { - boss_vaelAI(Creature* creature) : BossAI(creature, DATA_VAELASTRAZ_THE_CORRUPT) + Initialize(); + } + + void Initialize() + { + PlayerGUID.Clear(); + HasYelled = false; + _introDone = false; + _burningAdrenalineCast = 0; + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); + me->SetFaction(FACTION_FRIENDLY); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } + + void Reset() override + { + _Reset(); + me->SetHealth(me->CountPctFromMaxHealth(30)); + + if (!_introDone) { + me->SetStandState(UNIT_STAND_STATE_DEAD); + me->SetReactState(REACT_PASSIVE); Initialize(); + _eventsIntro.Reset(); } - - void Initialize() + else { - PlayerGUID.Clear(); HasYelled = false; - _introDone = false; _burningAdrenalineCast = 0; - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); - me->SetFaction(FACTION_FRIENDLY); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); } + } - void Reset() override + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + + DoCastAOE(SPELL_ESSENCE_OF_THE_RED); + // now drop damage requirement to be able to take loot + me->ResetPlayerDamageReq(); + + events.ScheduleEvent(EVENT_CLEAVE, 10s); + events.ScheduleEvent(EVENT_FLAME_BREATH, 15s); + events.ScheduleEvent(EVENT_FIRE_NOVA, 5s); + events.ScheduleEvent(EVENT_TAIL_SWEEP, 11s); + events.ScheduleEvent(EVENT_BURNING_ADRENALINE, 15s); + } + + void BeginSpeech(Unit* target) + { + PlayerGUID = target->GetGUID(); + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + _eventsIntro.ScheduleEvent(EVENT_SPEECH_1, 1s); + } + + void KilledUnit(Unit* victim) override + { + if (rand32() % 5) + return; + + Talk(SAY_KILLTARGET, victim); + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + _eventsIntro.Update(diff); + + // Speech + if (!_introDone) { - _Reset(); - me->SetHealth(me->CountPctFromMaxHealth(30)); - - if (!_introDone) - { - me->SetStandState(UNIT_STAND_STATE_DEAD); - me->SetReactState(REACT_PASSIVE); - Initialize(); - _eventsIntro.Reset(); - } - else - { - HasYelled = false; - _burningAdrenalineCast = 0; - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - - DoCastAOE(SPELL_ESSENCE_OF_THE_RED); - // now drop damage requirement to be able to take loot - me->ResetPlayerDamageReq(); - - events.ScheduleEvent(EVENT_CLEAVE, 10s); - events.ScheduleEvent(EVENT_FLAME_BREATH, 15s); - events.ScheduleEvent(EVENT_FIRE_NOVA, 5s); - events.ScheduleEvent(EVENT_TAIL_SWEEP, 11s); - events.ScheduleEvent(EVENT_BURNING_ADRENALINE, 15s); - } - - void BeginSpeech(Unit* target) - { - PlayerGUID = target->GetGUID(); - me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - _eventsIntro.ScheduleEvent(EVENT_SPEECH_1, 1s); - } - - void KilledUnit(Unit* victim) override - { - if (rand32() % 5) - return; - - Talk(SAY_KILLTARGET, victim); - } - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - _eventsIntro.Update(diff); - - // Speech - if (!_introDone) - { - while (uint32 eventId = _eventsIntro.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SPEECH_1: - me->SetStandState(UNIT_STAND_STATE_STAND); - me->SummonCreature(NPC_VICTOR_NEFARIUS, aNefariusSpawnLoc[0], aNefariusSpawnLoc[1], aNefariusSpawnLoc[2], aNefariusSpawnLoc[3], TEMPSUMMON_TIMED_DESPAWN, 26000); - _eventsIntro.ScheduleEvent(EVENT_SPEECH_2, 1s); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - break; - case EVENT_SPEECH_2: - if (Creature* nefarius = me->GetMap()->GetCreature(m_nefariusGuid)) - { - nefarius->CastSpell(me, SPELL_NEFARIUS_CORRUPTION, TRIGGERED_CAST_DIRECTLY); - nefarius->Yell(SAY_NEFARIAN_VAEL_INTRO); - nefarius->SetStandState(UNIT_STAND_STATE_STAND); - } - _eventsIntro.ScheduleEvent(EVENT_SPEECH_3, 18s); - break; - case EVENT_SPEECH_3: - if (Creature* nefarius = me->GetMap()->GetCreature(m_nefariusGuid)) - nefarius->CastSpell(me, SPELL_RED_LIGHTNING, TRIGGERED_NONE); - _eventsIntro.ScheduleEvent(EVENT_SPEECH_4, 2s); - break; - case EVENT_SPEECH_4: - Talk(SAY_LINE1); - me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - _eventsIntro.ScheduleEvent(EVENT_SPEECH_5, 12s); - break; - case EVENT_SPEECH_5: - Talk(SAY_LINE2); - me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - _eventsIntro.ScheduleEvent(EVENT_SPEECH_6, 12s); - break; - case EVENT_SPEECH_6: - Talk(SAY_LINE3); - me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - _eventsIntro.ScheduleEvent(EVENT_SPEECH_7, 17s); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - break; - case EVENT_SPEECH_7: - me->SetFaction(FACTION_DRAGONFLIGHT_BLACK); - if (PlayerGUID && ObjectAccessor::GetUnit(*me, PlayerGUID)) - AttackStart(ObjectAccessor::GetUnit(*me, PlayerGUID)); - me->SetReactState(REACT_AGGRESSIVE); - _introDone = true; - break; - } - } - } - - if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _eventsIntro.ExecuteEvent()) { switch (eventId) { - case EVENT_CLEAVE: - events.ScheduleEvent(EVENT_CLEAVE, 15s); - DoCastVictim(SPELL_CLEAVE); + case EVENT_SPEECH_1: + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SummonCreature(NPC_VICTOR_NEFARIUS, aNefariusSpawnLoc[0], aNefariusSpawnLoc[1], aNefariusSpawnLoc[2], aNefariusSpawnLoc[3], TEMPSUMMON_TIMED_DESPAWN, 26000); + _eventsIntro.ScheduleEvent(EVENT_SPEECH_2, 1s); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); break; - case EVENT_FLAME_BREATH: - DoCastVictim(SPELL_FLAME_BREATH); - events.ScheduleEvent(EVENT_FLAME_BREATH, 8s, 14s); - break; - case EVENT_FIRE_NOVA: - DoCastVictim(SPELL_FIRE_NOVA); - events.ScheduleEvent(EVENT_FIRE_NOVA, 3s, 5s); - break; - case EVENT_TAIL_SWEEP: - DoCastAOE(SPELL_TAIL_SWEEP); - events.ScheduleEvent(EVENT_TAIL_SWEEP, 15s); - break; - case EVENT_BURNING_ADRENALINE: - { - if (_burningAdrenalineCast < 2) // It's better to use TaskScheduler for this, but zzz + case EVENT_SPEECH_2: + if (Creature* nefarius = me->GetMap()->GetCreature(m_nefariusGuid)) { - //selects a random target that isn't the current victim and is a mana user (selects mana users) but not pets - //it also ignores targets who have the aura. We don't want to place the debuff on the same target twice. - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [&](Unit* u) { return u && !u->IsPet() && u->getPowerType() == POWER_MANA && !u->HasAura(SPELL_BURNING_ADRENALINE) && u != me->GetVictim(); })) - { - me->CastSpell(target, SPELL_BURNING_ADRENALINE, true); - } - - _burningAdrenalineCast++; + nefarius->CastSpell(me, SPELL_NEFARIUS_CORRUPTION, TRIGGERED_CAST_DIRECTLY); + nefarius->Yell(SAY_NEFARIAN_VAEL_INTRO); + nefarius->SetStandState(UNIT_STAND_STATE_STAND); } - else - { - me->CastSpell(me->GetVictim(), SPELL_BURNING_ADRENALINE, true); - _burningAdrenalineCast = 0; - } - events.ScheduleEvent(EVENT_BURNING_ADRENALINE, 15s); + _eventsIntro.ScheduleEvent(EVENT_SPEECH_3, 18s); + break; + case EVENT_SPEECH_3: + if (Creature* nefarius = me->GetMap()->GetCreature(m_nefariusGuid)) + nefarius->CastSpell(me, SPELL_RED_LIGHTNING, TRIGGERED_NONE); + _eventsIntro.ScheduleEvent(EVENT_SPEECH_4, 2s); + break; + case EVENT_SPEECH_4: + Talk(SAY_LINE1); + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + _eventsIntro.ScheduleEvent(EVENT_SPEECH_5, 12s); + break; + case EVENT_SPEECH_5: + Talk(SAY_LINE2); + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + _eventsIntro.ScheduleEvent(EVENT_SPEECH_6, 12s); + break; + case EVENT_SPEECH_6: + Talk(SAY_LINE3); + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + _eventsIntro.ScheduleEvent(EVENT_SPEECH_7, 17s); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + break; + case EVENT_SPEECH_7: + me->SetFaction(FACTION_DRAGONFLIGHT_BLACK); + if (PlayerGUID && ObjectAccessor::GetUnit(*me, PlayerGUID)) + AttackStart(ObjectAccessor::GetUnit(*me, PlayerGUID)); + me->SetReactState(REACT_AGGRESSIVE); + _introDone = true; break; - } } } - - // Yell if hp lower than 15% - if (HealthBelowPct(15) && !HasYelled) - { - Talk(SAY_HALFLIFE); - HasYelled = true; - } - - DoMeleeAttackIfReady(); } - void JustSummoned(Creature* summoned) override + if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - if (summoned->GetEntry() == NPC_VICTOR_NEFARIUS) + switch (eventId) { - // Set not selectable, so players won't interact with it - summoned->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - m_nefariusGuid = summoned->GetGUID(); + case EVENT_CLEAVE: + events.ScheduleEvent(EVENT_CLEAVE, 15s); + DoCastVictim(SPELL_CLEAVE); + break; + case EVENT_FLAME_BREATH: + DoCastVictim(SPELL_FLAME_BREATH); + events.ScheduleEvent(EVENT_FLAME_BREATH, 8s, 14s); + break; + case EVENT_FIRE_NOVA: + DoCastVictim(SPELL_FIRE_NOVA); + events.ScheduleEvent(EVENT_FIRE_NOVA, 3s, 5s); + break; + case EVENT_TAIL_SWEEP: + DoCastAOE(SPELL_TAIL_SWEEP); + events.ScheduleEvent(EVENT_TAIL_SWEEP, 15s); + break; + case EVENT_BURNING_ADRENALINE: + { + if (_burningAdrenalineCast < 2) // It's better to use TaskScheduler for this, but zzz + { + //selects a random target that isn't the current victim and is a mana user (selects mana users) but not pets + //it also ignores targets who have the aura. We don't want to place the debuff on the same target twice. + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [&](Unit* u) { return u && !u->IsPet() && u->getPowerType() == POWER_MANA && !u->HasAura(SPELL_BURNING_ADRENALINE) && u != me->GetVictim(); })) + me->CastSpell(target, SPELL_BURNING_ADRENALINE, true); + + _burningAdrenalineCast++; + } + else + { + me->CastSpell(me->GetVictim(), SPELL_BURNING_ADRENALINE, true); + _burningAdrenalineCast = 0; + } + events.ScheduleEvent(EVENT_BURNING_ADRENALINE, 15s); + break; + } } } - void sGossipSelect(Player* player, uint32 sender, uint32 action) override + // Yell if hp lower than 15% + if (HealthBelowPct(15) && !HasYelled) { - if (sender == GOSSIP_ID && action == 0) - { - CloseGossipMenuFor(player); - BeginSpeech(player); - } + Talk(SAY_HALFLIFE); + HasYelled = true; } - private: - ObjectGuid PlayerGUID; - ObjectGuid m_nefariusGuid; - bool HasYelled; - bool _introDone; - EventMap _eventsIntro; - uint8 _burningAdrenalineCast; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackwingLairAI(creature); + DoMeleeAttackIfReady(); } + + void JustSummoned(Creature* summoned) override + { + if (summoned->GetEntry() == NPC_VICTOR_NEFARIUS) + { + // Set not selectable, so players won't interact with it + summoned->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + m_nefariusGuid = summoned->GetGUID(); + } + } + + void sGossipSelect(Player* player, uint32 sender, uint32 action) override + { + if (sender == GOSSIP_ID && action == 0) + { + CloseGossipMenuFor(player); + BeginSpeech(player); + } + } + +private: + ObjectGuid PlayerGUID; + ObjectGuid m_nefariusGuid; + bool HasYelled; + bool _introDone; + EventMap _eventsIntro; + uint8 _burningAdrenalineCast; }; // 18173 - Burning Adrenaline @@ -308,9 +295,7 @@ class spell_vael_burning_adrenaline : public AuraScript void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { if (!GetTarget()) - { return; - } // Do the explosion, then kill the target. GetTarget()->CastSpell(GetTarget(), SPELL_BURNING_ADRENALINE_EXPLOSION, true); @@ -325,6 +310,6 @@ class spell_vael_burning_adrenaline : public AuraScript void AddSC_boss_vaelastrasz() { - new boss_vaelastrasz(); + RegisterBlackwingLairCreatureAI(boss_vaelastrasz); RegisterSpellScript(spell_vael_burning_adrenaline); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp index 2dfb99fc4..a77af22d0 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp @@ -72,404 +72,355 @@ Position const SummonPosition[8] = uint32 const Entry[3] = { 12422, 12416, 12420 }; -class instance_blackwing_lair : public InstanceMapScript +struct instance_blackwing_lair : public InstanceScript { -public: - instance_blackwing_lair() : InstanceMapScript(BWLScriptName, MAP_BLACKWING_LAIR) { } - - struct instance_blackwing_lair_InstanceMapScript : public InstanceScript + instance_blackwing_lair(Map* map) : InstanceScript(map) { - instance_blackwing_lair_InstanceMapScript(Map* map) : InstanceScript(map) + SetHeaders(DataHeader); + SetBossNumber(EncounterCount); + LoadDoorData(doorData); + LoadObjectData(creatureData, objectData); + } + + void Initialize() override + { + // Razorgore + EggCount = 0; + EggEvent = 0; + NefarianLeftTunnel = 0; + NefarianRightTunnel = 0; + addsCount.fill(0); + } + + void OnCreatureCreate(Creature* creature) override + { + // This is required because the tempspawn at Vael overwrites his GUID. + if (creature->GetEntry() == NPC_VICTOR_NEFARIUS && creature->ToTempSummon()) + return; + + InstanceScript::OnCreatureCreate(creature); + + switch (creature->GetEntry()) { - SetHeaders(DataHeader); - SetBossNumber(EncounterCount); - LoadDoorData(doorData); - LoadObjectData(creatureData, objectData); + case NPC_RAZORGORE: + razorgoreGUID = creature->GetGUID(); + break; + case NPC_BLACKWING_DRAGON: + ++addsCount[0]; + if (Creature* razor = instance->GetCreature(razorgoreGUID)) + if (CreatureAI* razorAI = razor->AI()) + razorAI->JustSummoned(creature); + break; + case NPC_BLACKWING_LEGIONAIRE: + case NPC_BLACKWING_MAGE: + ++addsCount[1]; + if (Creature* razor = instance->GetCreature(razorgoreGUID)) + if (CreatureAI* razorAI = razor->AI()) + razorAI->JustSummoned(creature); + break; + case NPC_BLACKWING_GUARDSMAN: + guardList.push_back(creature->GetGUID()); + break; + case NPC_NEFARIAN: + nefarianGUID = creature->GetGUID(); + break; + case NPC_BLACK_DRAKONID: + case NPC_BLUE_DRAKONID: + case NPC_BRONZE_DRAKONID: + case NPC_CHROMATIC_DRAKONID: + case NPC_GREEN_DRAKONID: + case NPC_RED_DRAKONID: + if (Creature* nefarius = GetCreature(DATA_LORD_VICTOR_NEFARIUS)) + if (CreatureAI* nefariusAI = nefarius->AI()) + nefariusAI->JustSummoned(creature); + break; + default: + break; + } + } + + void OnGameObjectCreate(GameObject* go) override + { + InstanceScript::OnGameObjectCreate(go); + + switch (go->GetEntry()) + { + case GO_BLACK_DRAGON_EGG: + if (GetBossState(DATA_FIREMAW) == DONE) + go->SetPhaseMask(2, true); + else + EggList.push_back(go->GetGUID()); + break; + default: + break; + } + } + + void OnGameObjectRemove(GameObject* go) override + { + InstanceScript::OnGameObjectRemove(go); + + if (go->GetEntry() == GO_BLACK_DRAGON_EGG) + EggList.remove(go->GetGUID()); + } + + uint32 GetData(uint32 data) const override + { + switch (data) + { + case DATA_NEFARIAN_LEFT_TUNNEL: + return NefarianLeftTunnel; + case DATA_NEFARIAN_RIGHT_TUNNEL: + return NefarianRightTunnel; + case DATA_EGG_EVENT: + return EggEvent; + default: + break; } - void Initialize() override + return 0; + } + + bool CheckRequiredBosses(uint32 bossId, Player const* /* player */) const override + { + switch (bossId) { - // Razorgore - EggCount = 0; - EggEvent = 0; - NefarianLeftTunnel = 0; - NefarianRightTunnel = 0; - addsCount.fill(0); + case DATA_BROODLORD_LASHLAYER: + if (GetBossState(DATA_VAELASTRAZ_THE_CORRUPT) != DONE) + return false; + break; + default: + break; } - void OnCreatureCreate(Creature* creature) override + return true; + } + + bool SetBossState(uint32 type, EncounterState state) override + { + if (!InstanceScript::SetBossState(type, state)) + return false; + + switch (type) { - // This is required because the tempspawn at Vael overwrites his GUID. - if (creature->GetEntry() == NPC_VICTOR_NEFARIUS && creature->ToTempSummon()) - { - return; - } - - InstanceScript::OnCreatureCreate(creature); - - switch (creature->GetEntry()) - { - case NPC_RAZORGORE: - razorgoreGUID = creature->GetGUID(); - break; - case NPC_BLACKWING_DRAGON: - ++addsCount[0]; - if (Creature* razor = instance->GetCreature(razorgoreGUID)) + case DATA_RAZORGORE_THE_UNTAMED: + if (state == DONE) + { + for (ObjectGuid const& guid : EggList) { - if (CreatureAI* razorAI = razor->AI()) - { - razorAI->JustSummoned(creature); - } + // Eggs should be destroyed instead + /// @todo: after dynamic spawns + if (GameObject* egg = instance->GetGameObject(guid)) + egg->SetPhaseMask(2, true); } - break; - case NPC_BLACKWING_LEGIONAIRE: - case NPC_BLACKWING_MAGE: - ++addsCount[1]; - if (Creature* razor = instance->GetCreature(razorgoreGUID)) - { - if (CreatureAI* razorAI = razor->AI()) - { - razorAI->JustSummoned(creature); - } - } - break; - case NPC_BLACKWING_GUARDSMAN: - guardList.push_back(creature->GetGUID()); - break; - case NPC_NEFARIAN: - nefarianGUID = creature->GetGUID(); - break; - case NPC_BLACK_DRAKONID: - case NPC_BLUE_DRAKONID: - case NPC_BRONZE_DRAKONID: - case NPC_CHROMATIC_DRAKONID: - case NPC_GREEN_DRAKONID: - case NPC_RED_DRAKONID: - if (Creature* nefarius = GetCreature(DATA_LORD_VICTOR_NEFARIUS)) - { - if (CreatureAI* nefariusAI = nefarius->AI()) - { - nefariusAI->JustSummoned(creature); - } - } - break; - default: - break; - } + } + break; + case DATA_NEFARIAN: + switch (state) + { + case FAIL: + _events.ScheduleEvent(EVENT_RESPAWN_NEFARIUS, 15min); + [[fallthrough]]; + case NOT_STARTED: + if (Creature* nefarian = instance->GetCreature(nefarianGUID)) + nefarian->DespawnOrUnsummon(); + break; + default: + break; + } + break; } + return true; + } - void OnGameObjectCreate(GameObject* go) override - { - InstanceScript::OnGameObjectCreate(go); - - switch (go->GetEntry()) - { - case GO_BLACK_DRAGON_EGG: - if (GetBossState(DATA_FIREMAW) == DONE) - { - go->SetPhaseMask(2, true); - } - else - { - EggList.push_back(go->GetGUID()); - } - break; - default: - break; - } - } - - void OnGameObjectRemove(GameObject* go) override - { - InstanceScript::OnGameObjectRemove(go); - - if (go->GetEntry() == GO_BLACK_DRAGON_EGG) - EggList.remove(go->GetGUID()); - } - - uint32 GetData(uint32 data) const override + void SetData(uint32 type, uint32 data) override + { + if (type == DATA_EGG_EVENT) { switch (data) { - case DATA_NEFARIAN_LEFT_TUNNEL: - return NefarianLeftTunnel; - case DATA_NEFARIAN_RIGHT_TUNNEL: - return NefarianRightTunnel; - case DATA_EGG_EVENT: - return EggEvent; - default: + case DONE: + EggEvent = data; break; - } - - return 0; - } - - bool CheckRequiredBosses(uint32 bossId, Player const* /* player */) const override - { - switch (bossId) - { - case DATA_BROODLORD_LASHLAYER: - if (GetBossState(DATA_VAELASTRAZ_THE_CORRUPT) != DONE) - return false; + case FAIL: + _events.CancelEvent(EVENT_RAZOR_SPAWN); break; - default: + case IN_PROGRESS: + _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45s); + EggEvent = data; + EggCount = 0; + addsCount.fill(0); break; - } + case NOT_STARTED: + _events.CancelEvent(EVENT_RAZOR_SPAWN); + EggEvent = data; + EggCount = 0; + addsCount.fill(0); - return true; - } + for (ObjectGuid const& guid : EggList) + DoRespawnGameObject(guid, 0); - bool SetBossState(uint32 type, EncounterState state) override - { - if (!InstanceScript::SetBossState(type, state)) - return false; + DoRespawnCreature(DATA_GRETHOK); - switch (type) - { - case DATA_RAZORGORE_THE_UNTAMED: - if (state == DONE) + for (ObjectGuid const& guid : guardList) + DoRespawnCreature(guid); + + break; + case SPECIAL: + if (EggEvent == NOT_STARTED) + SetData(DATA_EGG_EVENT, IN_PROGRESS); + if (++EggCount >= EggList.size()) { - for (ObjectGuid const& guid : EggList) + if (Creature* razor = instance->GetCreature(razorgoreGUID)) { - // Eggs should be destroyed instead - /// @todo: after dynamic spawns - if (GameObject* egg = instance->GetGameObject(guid)) - { - egg->SetPhaseMask(2, true); - } + SetData(DATA_EGG_EVENT, DONE); + razor->RemoveAurasDueToSpell(19832); // MindControl + DoRemoveAurasDueToSpellOnPlayers(19832); } - } - break; - case DATA_NEFARIAN: - switch (state) - { - case FAIL: - _events.ScheduleEvent(EVENT_RESPAWN_NEFARIUS, 15min); - [[fallthrough]]; - case NOT_STARTED: - if (Creature* nefarian = instance->GetCreature(nefarianGUID)) - { - nefarian->DespawnOrUnsummon(); - } - break; - default: - break; - } - break; - } - return true; - } - - void SetData(uint32 type, uint32 data) override - { - if (type == DATA_EGG_EVENT) - { - switch (data) - { - case DONE: - EggEvent = data; - break; - case FAIL: + _events.ScheduleEvent(EVENT_RAZOR_PHASE_TWO, 1s); _events.CancelEvent(EVENT_RAZOR_SPAWN); - break; - case IN_PROGRESS: - _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45s); - EggEvent = data; - EggCount = 0; - addsCount.fill(0); - break; - case NOT_STARTED: - _events.CancelEvent(EVENT_RAZOR_SPAWN); - EggEvent = data; - EggCount = 0; - addsCount.fill(0); - - for (ObjectGuid const& guid : EggList) - { - DoRespawnGameObject(guid, 0); - } - - DoRespawnCreature(DATA_GRETHOK); - - for (ObjectGuid const& guid : guardList) - { - DoRespawnCreature(guid); - } - - break; - case SPECIAL: - if (EggEvent == NOT_STARTED) - SetData(DATA_EGG_EVENT, IN_PROGRESS); - if (++EggCount >= EggList.size()) - { - if (Creature* razor = instance->GetCreature(razorgoreGUID)) - { - SetData(DATA_EGG_EVENT, DONE); - razor->RemoveAurasDueToSpell(19832); // MindControl - DoRemoveAurasDueToSpellOnPlayers(19832); - } - _events.ScheduleEvent(EVENT_RAZOR_PHASE_TWO, 1s); - _events.CancelEvent(EVENT_RAZOR_SPAWN); - } - break; - } - } - - if (type == DATA_NEFARIAN_LEFT_TUNNEL) - { - NefarianLeftTunnel = data; - } - - if (type == DATA_NEFARIAN_RIGHT_TUNNEL) - { - NefarianRightTunnel = data; - } - } - - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case DATA_RAZORGORE_THE_UNTAMED: - return razorgoreGUID; - default: - break; - } - - return ObjectGuid::Empty; - } - - void OnUnitDeath(Unit* unit) override - { - switch (unit->GetEntry()) - { - case NPC_BLACKWING_DRAGON: - --addsCount[0]; - if (EggEvent != DONE && !_events.HasTimeUntilEvent(EVENT_RAZOR_SPAWN)) - { - _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 1s); } break; - case NPC_BLACKWING_LEGIONAIRE: - case NPC_BLACKWING_MAGE: - --addsCount[1]; - if (EggEvent != DONE && !_events.HasTimeUntilEvent(EVENT_RAZOR_SPAWN)) - { - _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 1s); - } - break; - default: - break; } } - void Update(uint32 diff) override + if (type == DATA_NEFARIAN_LEFT_TUNNEL) + NefarianLeftTunnel = data; + + if (type == DATA_NEFARIAN_RIGHT_TUNNEL) + NefarianRightTunnel = data; + } + + ObjectGuid GetGuidData(uint32 type) const override + { + switch (type) { - if (_events.Empty()) - return; + case DATA_RAZORGORE_THE_UNTAMED: + return razorgoreGUID; + default: + break; + } - _events.Update(diff); + return ObjectGuid::Empty; + } - while (uint32 eventId = _events.ExecuteEvent()) + void OnUnitDeath(Unit* unit) override + { + switch (unit->GetEntry()) + { + case NPC_BLACKWING_DRAGON: + --addsCount[0]; + if (EggEvent != DONE && !_events.HasTimeUntilEvent(EVENT_RAZOR_SPAWN)) + _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 1s); + break; + case NPC_BLACKWING_LEGIONAIRE: + case NPC_BLACKWING_MAGE: + --addsCount[1]; + if (EggEvent != DONE && !_events.HasTimeUntilEvent(EVENT_RAZOR_SPAWN)) + _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 1s); + break; + default: + break; + } + } + + void Update(uint32 diff) override + { + if (_events.Empty()) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_RAZOR_SPAWN: - if (EggEvent == IN_PROGRESS) + case EVENT_RAZOR_SPAWN: + if (EggEvent == IN_PROGRESS) + { + bool spawnMoreAdds = true; + for (uint8 i = urand(2, 5); i > 0; --i) { - bool spawnMoreAdds = true; - for (uint8 i = urand(2, 5); i > 0; --i) - { - uint32 mobEntry = Entry[urand(0, 2)]; - uint32 dragonkinsCount = addsCount[0]; - uint32 orcsCount = addsCount[1]; + uint32 mobEntry = Entry[urand(0, 2)]; + uint32 dragonkinsCount = addsCount[0]; + uint32 orcsCount = addsCount[1]; - // If more than 12 dragonkins... - if (dragonkinsCount >= 12) + // If more than 12 dragonkins... + if (dragonkinsCount >= 12) + { + //... and more than 40 orcs - stop spawning more adds. + if (orcsCount >= 40) { - //... and more than 40 orcs - stop spawning more adds. - if (orcsCount >= 40) - { - spawnMoreAdds = false; - break; - } - //... - stop spawning them. - else if (mobEntry == NPC_BLACKWING_DRAGON) - { - continue; - } + spawnMoreAdds = false; + break; } - // If more than 40 orcs - stop spawning them. - else if (orcsCount >= 40 && mobEntry != NPC_BLACKWING_DRAGON) + //... - stop spawning them. + else if (mobEntry == NPC_BLACKWING_DRAGON) { continue; } - - if (Creature* summon = instance->SummonCreature(mobEntry, SummonPosition[urand(0, 7)])) - { - summon->AI()->DoZoneInCombat(); - } } - - if (spawnMoreAdds) + // If more than 40 orcs - stop spawning them. + else if (orcsCount >= 40 && mobEntry != NPC_BLACKWING_DRAGON) { - _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 15s); + continue; } + + if (Creature* summon = instance->SummonCreature(mobEntry, SummonPosition[urand(0, 7)])) + summon->AI()->DoZoneInCombat(); } - break; - case EVENT_RAZOR_PHASE_TWO: - _events.CancelEvent(EVENT_RAZOR_SPAWN); - if (Creature* razor = instance->GetCreature(razorgoreGUID)) - razor->AI()->DoAction(ACTION_PHASE_TWO); - break; - case EVENT_RESPAWN_NEFARIUS: - if (Creature* nefarius = GetCreature(DATA_LORD_VICTOR_NEFARIUS)) - { - nefarius->SetPhaseMask(1, true); - nefarius->setActive(true); - nefarius->Respawn(); - nefarius->GetMotionMaster()->MoveTargetedHome(); - } - break; - } + + if (spawnMoreAdds) + _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 15s); + } + break; + case EVENT_RAZOR_PHASE_TWO: + _events.CancelEvent(EVENT_RAZOR_SPAWN); + if (Creature* razor = instance->GetCreature(razorgoreGUID)) + razor->AI()->DoAction(ACTION_PHASE_TWO); + break; + case EVENT_RESPAWN_NEFARIUS: + if (Creature* nefarius = GetCreature(DATA_LORD_VICTOR_NEFARIUS)) + { + nefarius->SetPhaseMask(1, true); + nefarius->setActive(true); + nefarius->Respawn(); + nefarius->GetMotionMaster()->MoveTargetedHome(); + } + break; } } - - void ReadSaveDataMore(std::istringstream& data) override - { - data >> NefarianLeftTunnel; - data >> NefarianRightTunnel; - } - - void WriteSaveDataMore(std::ostringstream& data) override - { - data << NefarianLeftTunnel << ' ' << NefarianRightTunnel; - } - - protected: - ObjectGuid razorgoreGUID; - ObjectGuid nefarianGUID; - ObjectGuid nefarianDoorGUID; - - // Razorgore - uint8 EggCount; - uint32 EggEvent; - GuidList EggList; - GuidList guardList; - std::array addsCount; - - // Nefarian - uint32 NefarianLeftTunnel; - uint32 NefarianRightTunnel; - - // Misc - EventMap _events; - }; - - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_blackwing_lair_InstanceMapScript(map); } + + void ReadSaveDataMore(std::istringstream& data) override + { + data >> NefarianLeftTunnel; + data >> NefarianRightTunnel; + } + + void WriteSaveDataMore(std::ostringstream& data) override + { + data << NefarianLeftTunnel << ' ' << NefarianRightTunnel; + } + +protected: + ObjectGuid razorgoreGUID; + ObjectGuid nefarianGUID; + ObjectGuid nefarianDoorGUID; + + // Razorgore + uint8 EggCount; + uint32 EggEvent; + GuidList EggList; + GuidList guardList; + std::array addsCount; + + // Nefarian + uint32 NefarianLeftTunnel; + uint32 NefarianRightTunnel; + + // Misc + EventMap _events; }; enum ShadowFlame @@ -527,7 +478,7 @@ public: void AddSC_instance_blackwing_lair() { - new instance_blackwing_lair(); + RegisterInstanceScript(instance_blackwing_lair, MAP_BLACKWING_LAIR); RegisterSpellScript(spell_bwl_shadowflame); new at_orb_of_command(); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp index 448ec46ff..96cfbed45 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp @@ -43,88 +43,73 @@ enum Events EVENT_LIVING_BOMB, }; -class boss_baron_geddon : public CreatureScript +struct boss_baron_geddon : public BossAI { -public: - boss_baron_geddon() : CreatureScript("boss_baron_geddon") { } - - struct boss_baron_geddonAI : public BossAI + boss_baron_geddon(Creature* creature) : BossAI(creature, DATA_GEDDON), + armageddonCasted(false) { - boss_baron_geddonAI(Creature* creature) : BossAI(creature, DATA_GEDDON), - armageddonCasted(false) - { - } - - void Reset() override - { - _Reset(); - armageddonCasted = false; - } - - void JustEngagedWith(Unit* /*attacker*/) override - { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_INFERNO, 13s, 15s); - events.ScheduleEvent(EVENT_IGNITE_MANA, 7s, 19s); - events.ScheduleEvent(EVENT_LIVING_BOMB, 11s, 16s); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override - { - // If boss is below 2% hp - cast Armageddon - if (!armageddonCasted && damage < me->GetHealth() && me->HealthBelowPctDamaged(2, damage)) - { - me->RemoveAurasDueToSpell(SPELL_INFERNO); - me->StopMoving(); - if (me->CastSpell(me, SPELL_ARMAGEDDON, TRIGGERED_FULL_MASK) == SPELL_CAST_OK) - { - Talk(EMOTE_SERVICE); - armageddonCasted = true; - } - } - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_INFERNO: - { - DoCastAOE(SPELL_INFERNO); - events.Repeat(21s, 26s); - break; - } - case EVENT_IGNITE_MANA: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_IGNITE_MANA)) - { - DoCast(target, SPELL_IGNITE_MANA); - } - - events.Repeat(27s, 32s); - break; - } - case EVENT_LIVING_BOMB: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) - { - DoCast(target, SPELL_LIVING_BOMB); - } - - events.Repeat(11s, 16s); - break; - } - } - } - - private: - bool armageddonCasted; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } + + void Reset() override + { + _Reset(); + armageddonCasted = false; + } + + void JustEngagedWith(Unit* /*attacker*/) override + { + _JustEngagedWith(); + events.ScheduleEvent(EVENT_INFERNO, 13s, 15s); + events.ScheduleEvent(EVENT_IGNITE_MANA, 7s, 19s); + events.ScheduleEvent(EVENT_LIVING_BOMB, 11s, 16s); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override + { + // If boss is below 2% hp - cast Armageddon + if (!armageddonCasted && damage < me->GetHealth() && me->HealthBelowPctDamaged(2, damage)) + { + me->RemoveAurasDueToSpell(SPELL_INFERNO); + me->StopMoving(); + if (me->CastSpell(me, SPELL_ARMAGEDDON, TRIGGERED_FULL_MASK) == SPELL_CAST_OK) + { + Talk(EMOTE_SERVICE); + armageddonCasted = true; + } + } + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_INFERNO: + { + DoCastAOE(SPELL_INFERNO); + events.Repeat(21s, 26s); + break; + } + case EVENT_IGNITE_MANA: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_IGNITE_MANA)) + DoCast(target, SPELL_IGNITE_MANA); + + events.Repeat(27s, 32s); + break; + } + case EVENT_LIVING_BOMB: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) + DoCast(target, SPELL_LIVING_BOMB); + + events.Repeat(11s, 16s); + break; + } + } + } + +private: + bool armageddonCasted; }; // 19695 Inferno @@ -149,9 +134,7 @@ class spell_geddon_inferno_aura : public AuraScript void HandleAfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { if (Creature* pCreatureTarget = GetTarget()->ToCreature()) - { pCreatureTarget->SetReactState(REACT_AGGRESSIVE); - } } void PeriodicTick(AuraEffect const* aurEff) @@ -209,9 +192,7 @@ class spell_geddon_armageddon_aura : public AuraScript void HandleAfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { if (Creature* pCreatureTarget = GetTarget()->ToCreature()) - { pCreatureTarget->SetReactState(REACT_AGGRESSIVE); - } } void Register() override @@ -223,7 +204,7 @@ class spell_geddon_armageddon_aura : public AuraScript void AddSC_boss_baron_geddon() { - new boss_baron_geddon(); + RegisterMoltenCoreCreatureAI(boss_baron_geddon); // Spells RegisterSpellScript(spell_geddon_inferno_aura); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_garr.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_garr.cpp index 15f3995b0..1e18daf55 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_garr.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_garr.cpp @@ -51,123 +51,93 @@ enum Events EVENT_MAGMA_SHACKLES, }; -class boss_garr : public CreatureScript +struct boss_garr : public BossAI { -public: - boss_garr() : CreatureScript("boss_garr") {} - - struct boss_garrAI : public BossAI + boss_garr(Creature* creature) : BossAI(creature, DATA_GARR), + massEruptionTimer(600000) // 10 mins { - boss_garrAI(Creature* creature) : BossAI(creature, DATA_GARR), - massEruptionTimer(600000) // 10 mins + } + + void Reset() override + { + _Reset(); + massEruptionTimer = 600000; + } + + void JustEngagedWith(Unit* /*attacker*/) override + { + _JustEngagedWith(); + DoCastSelf(SPELL_SEPARATION_ANXIETY, true); + events.ScheduleEvent(EVENT_ANTIMAGIC_PULSE, 15s); + events.ScheduleEvent(EVENT_MAGMA_SHACKLES, 10s); + massEruptionTimer = 600000; // 10 mins + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + // This should always process + if (massEruptionTimer <= diff) { + Talk(EMOTE_MASS_ERRUPTION, me); + DoCastAOE(SPELL_ERUPTION_TRIGGER, true); + massEruptionTimer = 20000; + } + else + { + massEruptionTimer -= diff; } - void Reset() override + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 const eventId = events.ExecuteEvent()) { - _Reset(); - massEruptionTimer = 600000; - } - - void JustEngagedWith(Unit* /*attacker*/) override - { - _JustEngagedWith(); - DoCastSelf(SPELL_SEPARATION_ANXIETY, true); - events.ScheduleEvent(EVENT_ANTIMAGIC_PULSE, 15s); - events.ScheduleEvent(EVENT_MAGMA_SHACKLES, 10s); - massEruptionTimer = 600000; // 10 mins - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) + switch (eventId) { - return; + case EVENT_ANTIMAGIC_PULSE: + { + DoCastSelf(SPELL_ANTIMAGIC_PULSE); + events.Repeat(20s); + break; + } + case EVENT_MAGMA_SHACKLES: + { + DoCastSelf(SPELL_MAGMA_SHACKLES); + events.Repeat(15s); + break; + } } - // This should always process - if (massEruptionTimer <= diff) - { - Talk(EMOTE_MASS_ERRUPTION, me); - DoCastAOE(SPELL_ERUPTION_TRIGGER, true); - massEruptionTimer = 20000; - } - else - { - massEruptionTimer -= diff; - } - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } - - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_ANTIMAGIC_PULSE: - { - DoCastSelf(SPELL_ANTIMAGIC_PULSE); - events.Repeat(20s); - break; - } - case EVENT_MAGMA_SHACKLES: - { - DoCastSelf(SPELL_MAGMA_SHACKLES); - events.Repeat(15s); - break; - } - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - } - - DoMeleeAttackIfReady(); } - private: - uint32 massEruptionTimer; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); + DoMeleeAttackIfReady(); } + +private: + uint32 massEruptionTimer; }; -class npc_garr_firesworn : public CreatureScript +struct npc_garr_firesworn : public ScriptedAI { -public: - npc_garr_firesworn() : CreatureScript("npc_garr_firesworn") {} + npc_garr_firesworn(Creature* creature) : ScriptedAI(creature) {} - struct npc_garr_fireswornAI : public ScriptedAI + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/ ) override { - npc_garr_fireswornAI(Creature* creature) : ScriptedAI(creature) {} - - void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/ ) override + if (damage >= me->GetHealth()) { - if (damage >= me->GetHealth()) - { - // Prevent double damage because Firesworn can kill himself with Massive Erruption - if (me != attacker) - { - DoCastSelf(SPELL_ERUPTION, true); - } + // Prevent double damage because Firesworn can kill himself with Massive Eruption + if (me != attacker) + DoCastSelf(SPELL_ERUPTION, true); - DoCastAOE(SPELL_ENRAGE_TRIGGER); - } + DoCastAOE(SPELL_ENRAGE_TRIGGER); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } }; @@ -210,9 +180,7 @@ class spell_garr_frenzy : public SpellScript void HandleHit(SpellEffIndex /*effIndex*/) { if (Unit* target = GetHitUnit()) - { target->CastSpell(target, SPELL_FRENZY); - } } void Register() override @@ -223,8 +191,8 @@ class spell_garr_frenzy : public SpellScript void AddSC_boss_garr() { - new boss_garr(); - new npc_garr_firesworn(); + RegisterMoltenCoreCreatureAI(boss_garr); + RegisterMoltenCoreCreatureAI(npc_garr_firesworn); // Spells RegisterSpellScript(spell_garr_separation_anxiety_aura); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_gehennas.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_gehennas.cpp index 53662ddca..26d4af08c 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_gehennas.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_gehennas.cpp @@ -34,74 +34,57 @@ enum Events EVENT_SHADOW_BOLT, }; -class boss_gehennas : public CreatureScript +struct boss_gehennas : public BossAI { -public: - boss_gehennas() : CreatureScript("boss_gehennas") { } + boss_gehennas(Creature* creature) : BossAI(creature, DATA_GEHENNAS) {} - struct boss_gehennasAI : public BossAI + void JustEngagedWith(Unit* /*attacker*/) override { - boss_gehennasAI(Creature* creature) : BossAI(creature, DATA_GEHENNAS) {} + _JustEngagedWith(); + events.ScheduleEvent(EVENT_GEHENNAS_CURSE, 6s, 9s); + events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 10s); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 3s, 5s); + } - void JustEngagedWith(Unit* /*attacker*/) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_GEHENNAS_CURSE, 6s, 9s); - events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 10s); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 3s, 5s); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) + case EVENT_GEHENNAS_CURSE: { - case EVENT_GEHENNAS_CURSE: + DoCastVictim(SPELL_GEHENNAS_CURSE); + events.Repeat(25s, 30s); + break; + } + case EVENT_RAIN_OF_FIRE: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) + DoCast(target, SPELL_RAIN_OF_FIRE, true); + events.Repeat(6s); + break; + } + case EVENT_SHADOW_BOLT: + { + if (urand(0, 1)) { - DoCastVictim(SPELL_GEHENNAS_CURSE); - events.Repeat(25s, 30s); - break; - } - case EVENT_RAIN_OF_FIRE: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) - { - DoCast(target, SPELL_RAIN_OF_FIRE, true); - } - events.Repeat(6s); - break; - } - case EVENT_SHADOW_BOLT: - { - if (urand(0, 1)) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false)) - { - DoCast(target, SPELL_SHADOW_BOLT_RANDOM); - } - else - { - DoCastVictim(SPELL_SHADOW_BOLT_VICTIM); - } - } + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false)) + DoCast(target, SPELL_SHADOW_BOLT_RANDOM); else - { DoCastVictim(SPELL_SHADOW_BOLT_VICTIM); - } - - events.Repeat(5s); - break; } + else + { + DoCastVictim(SPELL_SHADOW_BOLT_VICTIM); + } + + events.Repeat(5s); + break; } } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } }; void AddSC_boss_gehennas() { - new boss_gehennas(); + RegisterMoltenCoreCreatureAI(boss_gehennas); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp index d88c1e2b3..84d95bb1d 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp @@ -40,191 +40,157 @@ enum Spells SPELL_FULL_HEAL = 17683, }; -class boss_golemagg : public CreatureScript +struct boss_golemagg : public BossAI { -public: - boss_golemagg() : CreatureScript("boss_golemagg") { } + boss_golemagg(Creature* creature) : BossAI(creature, DATA_GOLEMAGG), + earthquakeTimer(0), + pyroblastTimer(0), + enraged(false) + {} - struct boss_golemaggAI : public BossAI + void Reset() override { - boss_golemaggAI(Creature* creature) : BossAI(creature, DATA_GOLEMAGG), - earthquakeTimer(0), - pyroblastTimer(0), - enraged(false) - {} + _Reset(); + earthquakeTimer = 0; + pyroblastTimer = urand(3000, 7000); + enraged = false; + DoCastSelf(SPELL_MAGMASPLASH); + DoCastSelf(SPELL_GOLEMAGG_TRUST_AURA); + DoCastSelf(SPELL_DOUBLE_ATTACK); + } - void Reset() override + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (!enraged && me->HealthBelowPctDamaged(10, damage)) { - _Reset(); - earthquakeTimer = 0; - pyroblastTimer = urand(3000, 7000); - enraged = false; - DoCastSelf(SPELL_MAGMASPLASH); - DoCastSelf(SPELL_GOLEMAGG_TRUST_AURA); - DoCastSelf(SPELL_DOUBLE_ATTACK); + DoCastSelf(SPELL_ATTRACK_RAGER, true); + DoCastAOE(SPELL_EARTHQUAKE, true); + earthquakeTimer = 5000; + enraged = true; } + } - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + // Should not get impact by cast state (cast should always happen) + if (earthquakeTimer) { - if (!enraged && me->HealthBelowPctDamaged(10, damage)) + if (earthquakeTimer <= diff) { - DoCastSelf(SPELL_ATTRACK_RAGER, true); - DoCastAOE(SPELL_EARTHQUAKE, true); + DoCastSelf(SPELL_EARTHQUAKE, true); earthquakeTimer = 5000; - enraged = true; - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - { - return; - } - - // Should not get impact by cast state (cast should always happen) - if (earthquakeTimer) - { - if (earthquakeTimer <= diff) - { - DoCastSelf(SPELL_EARTHQUAKE, true); - earthquakeTimer = 5000; - } - else - { - earthquakeTimer -= diff; - } - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - if (pyroblastTimer <= diff) - { - DoCastRandomTarget(SPELL_PYROBLAST); - - pyroblastTimer = 7000; } else { - pyroblastTimer -= diff; + earthquakeTimer -= diff; } - - DoMeleeAttackIfReady(); } - private: - uint32 earthquakeTimer; - uint32 pyroblastTimer; - bool enraged; - }; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); + if (pyroblastTimer <= diff) + { + DoCastRandomTarget(SPELL_PYROBLAST); + + pyroblastTimer = 7000; + } + else + { + pyroblastTimer -= diff; + } + + DoMeleeAttackIfReady(); } + +private: + uint32 earthquakeTimer; + uint32 pyroblastTimer; + bool enraged; }; -class npc_core_rager : public CreatureScript +struct npc_core_rager : public ScriptedAI { -public: - npc_core_rager() : CreatureScript("npc_core_rager") { } - - struct npc_core_ragerAI : public ScriptedAI + npc_core_rager(Creature* creature) : ScriptedAI(creature), + instance(creature->GetInstanceScript()), + mangleTimer(7000), + rangeCheckTimer(1000) { - npc_core_ragerAI(Creature* creature) : ScriptedAI(creature), - instance(creature->GetInstanceScript()), - mangleTimer(7000), - rangeCheckTimer(1000) - { - } - - void Reset() override - { - mangleTimer = 7000; // These times are probably wrong - rangeCheckTimer = 1000; - - if (instance->GetBossState(DATA_GOLEMAGG) == DONE) - { - DoCastSelf(SPELL_CORE_RAGER_QUIET_SUICIDE, true); - } - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override - { - // Just in case if something will go bad, let players to kill this creature - if (instance->GetBossState(DATA_GOLEMAGG) == DONE) - { - return; - } - - if (me->HealthBelowPctDamaged(50, damage)) - { - damage = 0; - DoCastSelf(SPELL_FULL_HEAL, true); - Talk(EMOTE_LOWHP); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - { - return; - } - - // Should have no impact from unit state - if (rangeCheckTimer <= diff) - { - Creature const* golemagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_GOLEMAGG)); - if (golemagg && me->GetDistance(golemagg) > 100.0f) - { - instance->DoAction(ACTION_RESET_GOLEMAGG_ENCOUNTER); - return; - } - - rangeCheckTimer = 1000; - } - else - { - rangeCheckTimer -= diff; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - // Mangle - if (mangleTimer <= diff) - { - DoCastVictim(SPELL_MANGLE); - mangleTimer = 10000; - } - else - { - mangleTimer -= diff; - } - - DoMeleeAttackIfReady(); - } - - private: - InstanceScript* instance; - uint32 mangleTimer; - uint32 rangeCheckTimer; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } + + void Reset() override + { + mangleTimer = 7000; // These times are probably wrong + rangeCheckTimer = 1000; + + if (instance->GetBossState(DATA_GOLEMAGG) == DONE) + DoCastSelf(SPELL_CORE_RAGER_QUIET_SUICIDE, true); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override + { + // Just in case if something will go bad, let players to kill this creature + if (instance->GetBossState(DATA_GOLEMAGG) == DONE) + return; + + if (me->HealthBelowPctDamaged(50, damage)) + { + damage = 0; + DoCastSelf(SPELL_FULL_HEAL, true); + Talk(EMOTE_LOWHP); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + // Should have no impact from unit state + if (rangeCheckTimer <= diff) + { + Creature const* golemagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_GOLEMAGG)); + if (golemagg && me->GetDistance(golemagg) > 100.0f) + { + instance->DoAction(ACTION_RESET_GOLEMAGG_ENCOUNTER); + return; + } + + rangeCheckTimer = 1000; + } + else + { + rangeCheckTimer -= diff; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + // Mangle + if (mangleTimer <= diff) + { + DoCastVictim(SPELL_MANGLE); + mangleTimer = 10000; + } + else + { + mangleTimer -= diff; + } + + DoMeleeAttackIfReady(); + } + +private: + InstanceScript* instance; + uint32 mangleTimer; + uint32 rangeCheckTimer; }; void AddSC_boss_golemagg() { - new boss_golemagg(); - new npc_core_rager(); + RegisterMoltenCoreCreatureAI(boss_golemagg); + RegisterMoltenCoreCreatureAI(npc_core_rager); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_lucifron.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_lucifron.cpp index dd38b6ca8..1f8f89b3f 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_lucifron.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_lucifron.cpp @@ -33,56 +33,45 @@ enum Events EVENT_SHADOW_SHOCK = 3, }; -class boss_lucifron : public CreatureScript +struct boss_lucifron : public BossAI { -public: - boss_lucifron() : CreatureScript("boss_lucifron") { } + boss_lucifron(Creature* creature) : BossAI(creature, DATA_LUCIFRON) {} - struct boss_lucifronAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_lucifronAI(Creature* creature) : BossAI(creature, DATA_LUCIFRON) {} + _JustEngagedWith(); + events.ScheduleEvent(EVENT_IMPENDING_DOOM, 6s, 11s); + events.ScheduleEvent(EVENT_LUCIFRON_CURSE, 11s, 14s); + events.ScheduleEvent(EVENT_SHADOW_SHOCK, 5s); + } - void JustEngagedWith(Unit* /*who*/) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_IMPENDING_DOOM, 6s, 11s); - events.ScheduleEvent(EVENT_LUCIFRON_CURSE, 11s, 14s); - events.ScheduleEvent(EVENT_SHADOW_SHOCK, 5s); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) + case EVENT_IMPENDING_DOOM: { - case EVENT_IMPENDING_DOOM: - { - DoCastVictim(SPELL_IMPENDING_DOOM); - events.Repeat(20s); - break; - } - case EVENT_LUCIFRON_CURSE: - { - DoCastVictim(SPELL_LUCIFRON_CURSE); - events.Repeat(20s); - break; - } - case EVENT_SHADOW_SHOCK: - { - DoCastVictim(SPELL_SHADOW_SHOCK); - events.Repeat(5s); - break; - } + DoCastVictim(SPELL_IMPENDING_DOOM); + events.Repeat(20s); + break; + } + case EVENT_LUCIFRON_CURSE: + { + DoCastVictim(SPELL_LUCIFRON_CURSE); + events.Repeat(20s); + break; + } + case EVENT_SHADOW_SHOCK: + { + DoCastVictim(SPELL_SHADOW_SHOCK); + events.Repeat(5s); + break; } } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } }; void AddSC_boss_lucifron() { - new boss_lucifron(); + RegisterMoltenCoreCreatureAI(boss_lucifron); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp index f0d7563a8..9d1eb17ae 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp @@ -47,73 +47,58 @@ enum Events constexpr float MELEE_TARGET_LOOKUP_DIST = 10.0f; -class boss_magmadar : public CreatureScript +struct boss_magmadar : public BossAI { -public: - boss_magmadar() : CreatureScript("boss_magmadar") {} + boss_magmadar(Creature* creature) : BossAI(creature, DATA_MAGMADAR) {} - struct boss_magmadarAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_magmadarAI(Creature* creature) : BossAI(creature, DATA_MAGMADAR) {} + _JustEngagedWith(); + events.ScheduleEvent(EVENT_FRENZY, 8500ms); + events.ScheduleEvent(EVENT_PANIC, 9500ms); + events.ScheduleEvent(EVENT_LAVA_BOMB, 12s); + events.ScheduleEvent(EVENT_LAVA_BOMB_RANGED, 15s); + } - void JustEngagedWith(Unit* /*who*/) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_FRENZY, 8500ms); - events.ScheduleEvent(EVENT_PANIC, 9500ms); - events.ScheduleEvent(EVENT_LAVA_BOMB, 12s); - events.ScheduleEvent(EVENT_LAVA_BOMB_RANGED, 15s); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) + case EVENT_FRENZY: { - case EVENT_FRENZY: - { - Talk(EMOTE_FRENZY); - DoCastSelf(SPELL_FRENZY); - events.Repeat(15s, 20s); - break; - } - case EVENT_PANIC: - { - DoCastVictim(SPELL_PANIC); - events.Repeat(31s, 38s); - break; - } - case EVENT_LAVA_BOMB: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, MELEE_TARGET_LOOKUP_DIST, true)) - { - DoCast(target, SPELL_LAVA_BOMB); - } + Talk(EMOTE_FRENZY); + DoCastSelf(SPELL_FRENZY); + events.Repeat(15s, 20s); + break; + } + case EVENT_PANIC: + { + DoCastVictim(SPELL_PANIC); + events.Repeat(31s, 38s); + break; + } + case EVENT_LAVA_BOMB: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, MELEE_TARGET_LOOKUP_DIST, true)) + DoCast(target, SPELL_LAVA_BOMB); - events.Repeat(12s, 15s); - break; - } - case EVENT_LAVA_BOMB_RANGED: + events.Repeat(12s, 15s); + break; + } + case EVENT_LAVA_BOMB_RANGED: + { + std::list targets; + SelectTargetList(targets, 1, SelectTargetMethod::Random, 1, [this](Unit* target) { - std::list targets; - SelectTargetList(targets, 1, SelectTargetMethod::Random, 1, [this](Unit* target) - { - return target && target->IsPlayer() && target->GetDistance(me) > MELEE_TARGET_LOOKUP_DIST && target->GetDistance(me) < 100.0f; - }); + return target && target->IsPlayer() && target->GetDistance(me) > MELEE_TARGET_LOOKUP_DIST && target->GetDistance(me) < 100.0f; + }); - if (!targets.empty()) - { - DoCast(targets.front() , SPELL_LAVA_BOMB_RANGED); - } - events.Repeat(12s, 15s); - break; - } + if (!targets.empty()) + DoCast(targets.front() , SPELL_LAVA_BOMB_RANGED); + events.Repeat(12s, 15s); + break; } } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } }; @@ -162,7 +147,7 @@ class spell_magmadar_lava_bomb : public SpellScript void AddSC_boss_magmadar() { - new boss_magmadar(); + RegisterMoltenCoreCreatureAI(boss_magmadar); // Spells RegisterSpellScript(spell_magmadar_lava_bomb); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp index ad4da94c6..7498b88fc 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp @@ -131,412 +131,389 @@ struct MajordomoAddData MajordomoAddData(ObjectGuid _guid, uint32 _creatureEntry, Position _spawnPos) : guid(_guid), creatureEntry(_creatureEntry), spawnPos(_spawnPos) { } }; -class boss_majordomo : public CreatureScript +struct boss_majordomo : public BossAI { -public: - boss_majordomo() : CreatureScript("boss_majordomo") {} + boss_majordomo(Creature* creature) : BossAI(creature, DATA_MAJORDOMO_EXECUTUS) {} - struct boss_majordomoAI : public BossAI + void JustDied(Unit* /*killer*/) override { - boss_majordomoAI(Creature* creature) : BossAI(creature, DATA_MAJORDOMO_EXECUTUS) {} - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - me->DespawnOrUnsummon(10s, 0s); - } - - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() == NPC_RAGNAROS) - { - summon->CastSpell(summon, SPELL_RAGNAROS_FADE); - summon->CastSpell(summon, SPELL_RAGNAROS_SUBMERGE_EFFECT, true); - summon->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - summon->SetImmuneToAll(true); - summon->SetReactState(REACT_PASSIVE); - } - } - - void InitializeAI() override - { - BossAI::InitializeAI(); - if (instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE) - { - events.SetPhase(PHASE_COMBAT); - - std::list p_summons; - me->SummonCreatureGroup(SUMMON_GROUP_ADDS, &p_summons); - if (!p_summons.empty()) - { - for (TempSummon const* summon : p_summons) - { - if (summon) - { - static_minionsGUIDS.insert(summon->GetGUID()); - majordomoSummonsData[summon->GetGUID().GetCounter()] = MajordomoAddData(summon->GetGUID(), summon->GetEntry(), summon->GetPosition()); - } - } - } - } - else - { - events.SetPhase(PHASE_NONE); - me->SetImmuneToAll(true); - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->SetFaction(FACTION_MAJORDOMO_FRIENDLY); - } - } - - void Reset() override - { - me->ResetLootMode(); - events.Reset(); - aliveMinionsGUIDS.clear(); - - if (instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE) - { - events.SetPhase(PHASE_COMBAT); - instance->SetBossState(DATA_MAJORDOMO_EXECUTUS, NOT_STARTED); - - for (auto const& summon : majordomoSummonsData) - { - if (ObjectAccessor::GetCreature(*me, summon.second.guid)) - { - continue; - } - - if (Creature* spawn = me->SummonCreature(summon.second.creatureEntry, summon.second.spawnPos)) - { - static_minionsGUIDS.erase(summon.second.guid); // Erase the guid from the previous, no longer existing, spawn. - static_minionsGUIDS.insert(spawn->GetGUID()); - majordomoSummonsData.erase(summon.second.guid.GetCounter()); - majordomoSummonsData[spawn->GetGUID().GetCounter()] = MajordomoAddData(spawn->GetGUID(), spawn->GetEntry(), spawn->GetPosition()); - } - } - - me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - } - else - { - static_minionsGUIDS.clear(); - majordomoSummonsData.clear(); - summons.DespawnAll(); - } - } - - bool CanAIAttack(Unit const* /*target*/) const override - { - return instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE; - } - - void KilledUnit(Unit* victim) override - { - if (roll_chance_i(25) && victim->IsPlayer()) - { - Talk(SAY_SLAY); - } - } - - void JustEngagedWith(Unit* /*attacker*/) override - { - if (!events.IsInPhase(PHASE_COMBAT)) - { - return; - } - - _JustEngagedWith(); - DoCastAOE(SPELL_SEPARATION_ANXIETY); - Talk(SAY_AGGRO); - DoCastSelf(SPELL_AEGIS_OF_RAGNAROS, true); - - events.ScheduleEvent(EVENT_SHIELD_REFLECTION, 30s, PHASE_COMBAT, PHASE_COMBAT); - events.ScheduleEvent(EVENT_TELEPORT_RANDOM, 25s, PHASE_COMBAT, PHASE_COMBAT); - events.ScheduleEvent(EVENT_TELEPORT_TARGET, 15s, PHASE_COMBAT, PHASE_COMBAT); - - aliveMinionsGUIDS.clear(); - aliveMinionsGUIDS = static_minionsGUIDS; - } - - void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override - { - aliveMinionsGUIDS.erase(summon->GetGUID()); - if (summon->GetEntry() == NPC_FLAMEWAKER_HEALER || summon->GetEntry() == NPC_FLAMEWAKER_ELITE) - { - uint32 const remainingAdds = std::count_if(aliveMinionsGUIDS.begin(), aliveMinionsGUIDS.end(), [](ObjectGuid const& summonGuid) - { - return summonGuid.GetEntry() == NPC_FLAMEWAKER_HEALER || summonGuid.GetEntry() == NPC_FLAMEWAKER_ELITE; - }); - - // Last remaining add - if (remainingAdds == 1) - { - Talk(SAY_LAST_ADD); - DoCastAOE(SPELL_CHAMPION); - } - // 50% of adds - else if (remainingAdds == 4) - { - DoCastAOE(SPELL_IMMUNE_POLY); - } - else if (!remainingAdds) - { - static_minionsGUIDS.clear(); - - instance->SetBossState(DATA_MAJORDOMO_EXECUTUS, DONE); - events.CancelEventGroup(PHASE_COMBAT); - me->GetMap()->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, me->GetEntry(), me); - me->SetImmuneToAll(true); - me->SetFaction(FACTION_MAJORDOMO_FRIENDLY); - EnterEvadeMode(); - Talk(SAY_DEFEAT); - return; - } - DoCastAOE(SPELL_ENCOURAGEMENT); - } - } - - void JustReachedHome() override - { - _JustReachedHome(); - if (instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) == DONE) - { - events.Reset(); - events.SetPhase(PHASE_DEFEAT_OUTRO); - events.ScheduleEvent(EVENT_DEFEAT_OUTRO_1, 7500ms, PHASE_DEFEAT_OUTRO, PHASE_DEFEAT_OUTRO); - } - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override - { - if (events.IsInPhase(PHASE_COMBAT) && me->GetHealth() <= damage) - { - damage = 0; - } - } - - void UpdateAI(uint32 diff) override - { - - switch (events.GetPhaseMask()) - { - case (1 << (PHASE_COMBAT - 1)): - { - if (!UpdateVictim()) - { - return; - } - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SHIELD_REFLECTION: - { - if (rand_chance() <= 50.f) - { - DoCastSelf(SPELL_MAGIC_REFLECTION); - } - else - { - DoCastSelf(SPELL_DAMAGE_REFLECTION); - } - events.Repeat(30s); - break; - } - case EVENT_TELEPORT_RANDOM: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false)) - { - DoCastSelf(SPELL_HATE_TO_ZERO, true); - DoCast(target, SPELL_TELEPORT_RANDOM); - } - - events.Repeat(30s); - break; - } - case EVENT_TELEPORT_TARGET: - { - DoCastSelf(SPELL_HATE_TO_ZERO, true); - DoCastAOE(SPELL_TELEPORT_TARGET); - events.Repeat(30s); - break; - } - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - } - - DoMeleeAttackIfReady(); - break; - } - case (1 << (PHASE_DEFEAT_OUTRO - 1)): - { - events.Update(diff); - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_DEFEAT_OUTRO_1: - { - Talk(SAY_DEFEAT_2); - events.ScheduleEvent(EVENT_DEFEAT_OUTRO_2, 8s, PHASE_DEFEAT_OUTRO, PHASE_DEFEAT_OUTRO); - break; - } - case EVENT_DEFEAT_OUTRO_2: - { - Talk(SAY_DEFEAT_3); - events.ScheduleEvent(EVENT_DEFEAT_OUTRO_3, 21500ms, PHASE_DEFEAT_OUTRO, PHASE_DEFEAT_OUTRO); - break; - } - case EVENT_DEFEAT_OUTRO_3: - { - DoCastSelf(SPELL_TELEPORT_SELF); - break; - } - } - } - break; - } - case (1 << (PHASE_RAGNAROS_SUMMONING - 1)): - { - events.Update(diff); - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_RAGNAROS_SUMMON_1: - { - if (GameObject* lavaSplash = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_LAVA_SPLASH))) - { - lavaSplash->SetRespawnTime(900); - lavaSplash->Refresh(); - } - if (GameObject* lavaSteam = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_LAVA_STEAM))) - { - lavaSteam->SetRespawnTime(900); - lavaSteam->Refresh(); - } - Talk(SAY_RAG_SUM_2); - // Next event will get triggered in MovementInform - me->SetWalk(true); - me->GetMotionMaster()->MovePoint(POINT_RAGNAROS_SUMMON, MajordomoMoveRagPos, FORCED_MOVEMENT_NONE, 0.f, true, false); - break; - } - case EVENT_RAGNAROS_SUMMON_2: - { - if (GameObject* lavaSteam = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_LAVA_STEAM))) - { - me->SetFacingToObject(lavaSteam); - } - - Talk(SAY_SUMMON_MAJ); - events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_3, 16700ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); - events.ScheduleEvent(EVENT_RAGNAROS_EMERGE, 15s, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); - break; - } - case EVENT_RAGNAROS_SUMMON_3: - { - if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) - { - ragnaros->AI()->Talk(SAY_ARRIVAL1_RAG); - } - events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_4, 11700ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); - break; - } - case EVENT_RAGNAROS_SUMMON_4: - { - Talk(SAY_ARRIVAL2_MAJ); - events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_5, 8700ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); - break; - } - case EVENT_RAGNAROS_SUMMON_5: - { - if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) - { - ragnaros->AI()->Talk(SAY_ARRIVAL3_RAG); - } - - events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_6, 16500ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); - break; - } - case EVENT_RAGNAROS_SUMMON_6: - { - if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) - { - ragnaros->CastSpell(me, SPELL_ELEMENTAL_FIRE, true); - ragnaros->AI()->DoAction(ACTION_FINISH_RAGNAROS_INTRO); - } - break; - } - // Additional events - case EVENT_RAGNAROS_EMERGE: - { - if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) - { - ragnaros->RemoveAurasDueToSpell(SPELL_RAGNAROS_FADE); - ragnaros->CastSpell(ragnaros, SPELL_RAGNA_EMERGE); - } - }break; - } - } - break; - } - } - } - - void MovementInform(uint32 type, uint32 pointId) override - { - if (type == POINT_MOTION_TYPE && pointId == POINT_RAGNAROS_SUMMON) - { - DoCastAOE(SPELL_SUMMON_RAGNAROS); - events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_2, 11500ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); - } - } - - void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override - { - if (events.IsInPhase(PHASE_DEFEAT_OUTRO) && spellInfo->Id == SPELL_TELEPORT_SELF) - { - me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->SetHomePosition(MajordomoRagnaros); - me->NearTeleportTo(MajordomoRagnaros.GetPositionX(), MajordomoRagnaros.GetPositionY(), MajordomoRagnaros.GetPositionZ(), MajordomoRagnaros.GetOrientation()); - events.SetPhase(PHASE_NONE); - } - } - - void DoAction(int32 action) override - { - if (action == ACTION_START_RAGNAROS_INTRO && !events.IsInPhase(PHASE_RAGNAROS_SUMMONING)) - { - events.SetPhase(PHASE_RAGNAROS_SUMMONING); - events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_1, 5s, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); - } - } - private: - GuidSet static_minionsGUIDS; // contained data should be changed on encounter completion - GuidSet aliveMinionsGUIDS; // used for calculations - std::unordered_map majordomoSummonsData; - }; - - bool OnGossipHello(Player* player, Creature* creature) override - { - AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - SendGossipMenuFor(player, TEXT_ID_SUMMON_1, creature->GetGUID()); - return true; + Talk(SAY_DEATH); + me->DespawnOrUnsummon(10s, 0s); } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_RAGNAROS) + { + summon->CastSpell(summon, SPELL_RAGNAROS_FADE); + summon->CastSpell(summon, SPELL_RAGNAROS_SUBMERGE_EFFECT, true); + summon->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + summon->SetImmuneToAll(true); + summon->SetReactState(REACT_PASSIVE); + } + } + + void InitializeAI() override + { + BossAI::InitializeAI(); + if (instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE) + { + events.SetPhase(PHASE_COMBAT); + + std::list p_summons; + me->SummonCreatureGroup(SUMMON_GROUP_ADDS, &p_summons); + if (!p_summons.empty()) + { + for (TempSummon const* summon : p_summons) + { + if (summon) + { + static_minionsGUIDS.insert(summon->GetGUID()); + majordomoSummonsData[summon->GetGUID().GetCounter()] = MajordomoAddData(summon->GetGUID(), summon->GetEntry(), summon->GetPosition()); + } + } + } + } + else + { + events.SetPhase(PHASE_NONE); + me->SetImmuneToAll(true); + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->SetFaction(FACTION_MAJORDOMO_FRIENDLY); + } + } + + void Reset() override + { + me->ResetLootMode(); + events.Reset(); + aliveMinionsGUIDS.clear(); + + if (instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE) + { + events.SetPhase(PHASE_COMBAT); + instance->SetBossState(DATA_MAJORDOMO_EXECUTUS, NOT_STARTED); + + for (auto const& summon : majordomoSummonsData) + { + if (ObjectAccessor::GetCreature(*me, summon.second.guid)) + continue; + + if (Creature* spawn = me->SummonCreature(summon.second.creatureEntry, summon.second.spawnPos)) + { + static_minionsGUIDS.erase(summon.second.guid); // Erase the guid from the previous, no longer existing, spawn. + static_minionsGUIDS.insert(spawn->GetGUID()); + majordomoSummonsData.erase(summon.second.guid.GetCounter()); + majordomoSummonsData[spawn->GetGUID().GetCounter()] = MajordomoAddData(spawn->GetGUID(), spawn->GetEntry(), spawn->GetPosition()); + } + } + + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + } + else + { + static_minionsGUIDS.clear(); + majordomoSummonsData.clear(); + summons.DespawnAll(); + } + } + + bool CanAIAttack(Unit const* /*target*/) const override + { + return instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE; + } + + void KilledUnit(Unit* victim) override + { + if (roll_chance_i(25) && victim->IsPlayer()) + Talk(SAY_SLAY); + } + + void JustEngagedWith(Unit* /*attacker*/) override + { + if (!events.IsInPhase(PHASE_COMBAT)) + return; + + _JustEngagedWith(); + DoCastAOE(SPELL_SEPARATION_ANXIETY); + Talk(SAY_AGGRO); + DoCastSelf(SPELL_AEGIS_OF_RAGNAROS, true); + + events.ScheduleEvent(EVENT_SHIELD_REFLECTION, 30s, PHASE_COMBAT, PHASE_COMBAT); + events.ScheduleEvent(EVENT_TELEPORT_RANDOM, 25s, PHASE_COMBAT, PHASE_COMBAT); + events.ScheduleEvent(EVENT_TELEPORT_TARGET, 15s, PHASE_COMBAT, PHASE_COMBAT); + + aliveMinionsGUIDS.clear(); + aliveMinionsGUIDS = static_minionsGUIDS; + } + + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + aliveMinionsGUIDS.erase(summon->GetGUID()); + if (summon->GetEntry() == NPC_FLAMEWAKER_HEALER || summon->GetEntry() == NPC_FLAMEWAKER_ELITE) + { + uint32 const remainingAdds = std::count_if(aliveMinionsGUIDS.begin(), aliveMinionsGUIDS.end(), [](ObjectGuid const& summonGuid) + { + return summonGuid.GetEntry() == NPC_FLAMEWAKER_HEALER || summonGuid.GetEntry() == NPC_FLAMEWAKER_ELITE; + }); + + // Last remaining add + if (remainingAdds == 1) + { + Talk(SAY_LAST_ADD); + DoCastAOE(SPELL_CHAMPION); + } + // 50% of adds + else if (remainingAdds == 4) + { + DoCastAOE(SPELL_IMMUNE_POLY); + } + else if (!remainingAdds) + { + static_minionsGUIDS.clear(); + + instance->SetBossState(DATA_MAJORDOMO_EXECUTUS, DONE); + events.CancelEventGroup(PHASE_COMBAT); + me->GetMap()->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, me->GetEntry(), me); + me->SetImmuneToAll(true); + me->SetFaction(FACTION_MAJORDOMO_FRIENDLY); + EnterEvadeMode(); + Talk(SAY_DEFEAT); + return; + } + DoCastAOE(SPELL_ENCOURAGEMENT); + } + } + + void JustReachedHome() override + { + _JustReachedHome(); + if (instance->GetBossState(DATA_MAJORDOMO_EXECUTUS) == DONE) + { + events.Reset(); + events.SetPhase(PHASE_DEFEAT_OUTRO); + events.ScheduleEvent(EVENT_DEFEAT_OUTRO_1, 7500ms, PHASE_DEFEAT_OUTRO, PHASE_DEFEAT_OUTRO); + } + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override + { + if (events.IsInPhase(PHASE_COMBAT) && me->GetHealth() <= damage) + { + damage = 0; + } + } + + void UpdateAI(uint32 diff) override + { + + switch (events.GetPhaseMask()) + { + case (1 << (PHASE_COMBAT - 1)): + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHIELD_REFLECTION: + { + if (rand_chance() <= 50.f) + { + DoCastSelf(SPELL_MAGIC_REFLECTION); + } + else + { + DoCastSelf(SPELL_DAMAGE_REFLECTION); + } + events.Repeat(30s); + break; + } + case EVENT_TELEPORT_RANDOM: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false)) + { + DoCastSelf(SPELL_HATE_TO_ZERO, true); + DoCast(target, SPELL_TELEPORT_RANDOM); + } + + events.Repeat(30s); + break; + } + case EVENT_TELEPORT_TARGET: + { + DoCastSelf(SPELL_HATE_TO_ZERO, true); + DoCastAOE(SPELL_TELEPORT_TARGET); + events.Repeat(30s); + break; + } + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + break; + } + case (1 << (PHASE_DEFEAT_OUTRO - 1)): + { + events.Update(diff); + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_DEFEAT_OUTRO_1: + { + Talk(SAY_DEFEAT_2); + events.ScheduleEvent(EVENT_DEFEAT_OUTRO_2, 8s, PHASE_DEFEAT_OUTRO, PHASE_DEFEAT_OUTRO); + break; + } + case EVENT_DEFEAT_OUTRO_2: + { + Talk(SAY_DEFEAT_3); + events.ScheduleEvent(EVENT_DEFEAT_OUTRO_3, 21500ms, PHASE_DEFEAT_OUTRO, PHASE_DEFEAT_OUTRO); + break; + } + case EVENT_DEFEAT_OUTRO_3: + { + DoCastSelf(SPELL_TELEPORT_SELF); + break; + } + } + } + break; + } + case (1 << (PHASE_RAGNAROS_SUMMONING - 1)): + { + events.Update(diff); + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_RAGNAROS_SUMMON_1: + { + if (GameObject* lavaSplash = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_LAVA_SPLASH))) + { + lavaSplash->SetRespawnTime(900); + lavaSplash->Refresh(); + } + if (GameObject* lavaSteam = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_LAVA_STEAM))) + { + lavaSteam->SetRespawnTime(900); + lavaSteam->Refresh(); + } + Talk(SAY_RAG_SUM_2); + // Next event will get triggered in MovementInform + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(POINT_RAGNAROS_SUMMON, MajordomoMoveRagPos, FORCED_MOVEMENT_NONE, 0.f, true, false); + break; + } + case EVENT_RAGNAROS_SUMMON_2: + { + if (GameObject* lavaSteam = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_LAVA_STEAM))) + { + me->SetFacingToObject(lavaSteam); + } + + Talk(SAY_SUMMON_MAJ); + events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_3, 16700ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); + events.ScheduleEvent(EVENT_RAGNAROS_EMERGE, 15s, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); + break; + } + case EVENT_RAGNAROS_SUMMON_3: + { + if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) + { + ragnaros->AI()->Talk(SAY_ARRIVAL1_RAG); + } + events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_4, 11700ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); + break; + } + case EVENT_RAGNAROS_SUMMON_4: + { + Talk(SAY_ARRIVAL2_MAJ); + events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_5, 8700ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); + break; + } + case EVENT_RAGNAROS_SUMMON_5: + { + if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) + { + ragnaros->AI()->Talk(SAY_ARRIVAL3_RAG); + } + + events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_6, 16500ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); + break; + } + case EVENT_RAGNAROS_SUMMON_6: + { + if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) + { + ragnaros->CastSpell(me, SPELL_ELEMENTAL_FIRE, true); + ragnaros->AI()->DoAction(ACTION_FINISH_RAGNAROS_INTRO); + } + break; + } + // Additional events + case EVENT_RAGNAROS_EMERGE: + { + if (Creature* ragnaros = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAGNAROS))) + { + ragnaros->RemoveAurasDueToSpell(SPELL_RAGNAROS_FADE); + ragnaros->CastSpell(ragnaros, SPELL_RAGNA_EMERGE); + } + }break; + } + } + break; + } + } + } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type == POINT_MOTION_TYPE && pointId == POINT_RAGNAROS_SUMMON) + { + DoCastAOE(SPELL_SUMMON_RAGNAROS); + events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_2, 11500ms, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); + } + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override + { + if (events.IsInPhase(PHASE_DEFEAT_OUTRO) && spellInfo->Id == SPELL_TELEPORT_SELF) + { + me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + me->SetHomePosition(MajordomoRagnaros); + me->NearTeleportTo(MajordomoRagnaros.GetPositionX(), MajordomoRagnaros.GetPositionY(), MajordomoRagnaros.GetPositionZ(), MajordomoRagnaros.GetOrientation()); + events.SetPhase(PHASE_NONE); + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_START_RAGNAROS_INTRO && !events.IsInPhase(PHASE_RAGNAROS_SUMMONING)) + { + events.SetPhase(PHASE_RAGNAROS_SUMMONING); + events.ScheduleEvent(EVENT_RAGNAROS_SUMMON_1, 5s, PHASE_RAGNAROS_SUMMONING, PHASE_RAGNAROS_SUMMONING); + } + } + + void sGossipHello(Player* player) override + { + AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + SendGossipMenuFor(player, TEXT_ID_SUMMON_1, me->GetGUID()); + } + + void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override { ClearGossipMenuFor(player); switch (action) @@ -544,40 +521,39 @@ public: case GOSSIP_ACTION_INFO_DEF: { AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, TEXT_ID_SUMMON_2, creature->GetGUID()); + SendGossipMenuFor(player, TEXT_ID_SUMMON_2, me->GetGUID()); break; } case GOSSIP_ACTION_INFO_DEF+1: { AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, TEXT_ID_SUMMON_2, creature->GetGUID()); + SendGossipMenuFor(player, TEXT_ID_SUMMON_2, me->GetGUID()); break; } case GOSSIP_ACTION_INFO_DEF+2: { AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_3, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - SendGossipMenuFor(player, TEXT_ID_SUMMON_3, creature->GetGUID()); + SendGossipMenuFor(player, TEXT_ID_SUMMON_3, me->GetGUID()); break; } case GOSSIP_ACTION_INFO_DEF+3: { CloseGossipMenuFor(player); - creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - creature->AI()->Talk(SAY_RAG_SUM_1, player); - creature->AI()->DoAction(ACTION_START_RAGNAROS_INTRO); + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + Talk(SAY_RAG_SUM_1, player); + DoAction(ACTION_START_RAGNAROS_INTRO); break; } default: CloseGossipMenuFor(player); break; } - return true; } - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); - } +private: + GuidSet static_minionsGUIDS; // contained data should be changed on encounter completion + GuidSet aliveMinionsGUIDS; // used for calculations + std::unordered_map majordomoSummonsData; }; // 20538 Hate to Zero (SERVERSIDE) @@ -593,12 +569,8 @@ class spell_hate_to_zero : public SpellScript void HandleHit(SpellEffIndex /*effIndex*/) { if (Unit* caster = GetCaster()) - { if (Creature* creatureCaster = caster->ToCreature()) - { creatureCaster->GetThreatMgr().ResetAllThreat(); - } - } } void Register() override @@ -641,9 +613,7 @@ class spell_summon_ragnaros : public SpellScript void HandleHit() { if (Unit* caster = GetCaster()) - { caster->SummonCreature(NPC_RAGNAROS, RagnarosSummonPos, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 2 * HOUR * IN_MILLISECONDS); - } } void Register() override @@ -654,7 +624,7 @@ class spell_summon_ragnaros : public SpellScript void AddSC_boss_majordomo() { - new boss_majordomo(); + RegisterMoltenCoreCreatureAI(boss_majordomo); // Spells RegisterSpellScript(spell_hate_to_zero); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp index 01145b41e..45ec1dccc 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp @@ -112,390 +112,353 @@ enum Misc constexpr float DEATH_ORIENTATION = 4.0f; -class boss_ragnaros : public CreatureScript +struct boss_ragnaros : public BossAI { -public: - boss_ragnaros() : CreatureScript("boss_ragnaros") {} - - struct boss_ragnarosAI : public BossAI + boss_ragnaros(Creature* creature) : BossAI(creature, DATA_RAGNAROS), + _isIntroDone(false), + _hasYelledMagmaBurst(false), + _processingMagmaBurst(false), + _hasSubmergedOnce(false), + _isKnockbackEmoteAllowed(true) { - boss_ragnarosAI(Creature* creature) : BossAI(creature, DATA_RAGNAROS), - _isIntroDone(false), - _hasYelledMagmaBurst(false), - _processingMagmaBurst(false), - _hasSubmergedOnce(false), - _isKnockbackEmoteAllowed(true) + } + + void Reset() override + { + _Reset(); + + // Never reset intro events! + if (_isIntroDone && !(extraEvents.GetPhaseMask() & (1 << (PHASE_INTRO - 1)))) { - } - - void Reset() override - { - _Reset(); - - // Never reset intro events! - if (_isIntroDone && !(extraEvents.GetPhaseMask() & (1 << (PHASE_INTRO - 1)))) - { - extraEvents.Reset(); - extraEvents.SetPhase(PHASE_EMERGED); - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToAll(false); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); - me->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE); - } - - _hasYelledMagmaBurst = false; - _processingMagmaBurst = false; - _hasSubmergedOnce = false; - _isKnockbackEmoteAllowed = true; - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); - me->SetControlled(true, UNIT_STATE_ROOT); - _lavaBurstGUIDS.clear(); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (events.IsInPhase(PHASE_SUBMERGED) && damage >= me->GetHealth()) - { - damage = 0; - } - } - - void DoAction(int32 action) override - { - if (action == ACTION_FINISH_RAGNAROS_INTRO) - { - extraEvents.SetPhase(PHASE_INTRO); - extraEvents.ScheduleEvent(EVENT_INTRO_SAY, 5s, 0, PHASE_INTRO); - } - } - - void JustSummoned(Creature* summon) override - { - BossAI::JustSummoned(summon); - if (summon->GetEntry() == NPC_FLAME_OF_RAGNAROS) - { - summon->CastSpell((Unit*)nullptr, SPELL_INTENSE_HEAT, true, nullptr, nullptr, me->GetGUID()); - } - else if (summon->GetEntry() == NPC_SON_OF_FLAME) - { - DoZoneInCombat(summon); - } - } - - void SetGUID(ObjectGuid const& guid, int32 index) override - { - if (index == GO_LAVA_BURST) - { - if (_lavaBurstGUIDS.empty()) - { - extraEvents.ScheduleEvent(EVENT_LAVA_BURST_TRIGGER, 1ms); - } - - _lavaBurstGUIDS.insert(guid); - } - } - - void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override - { - summons.Despawn(summon); - if (events.IsInPhase(PHASE_SUBMERGED) && !summons.HasEntry(NPC_SON_OF_FLAME)) - { - HandleEmerge(); - } - } - - void JustEngagedWith(Unit* /*who*/) override - { - _JustEngagedWith(); - events.SetPhase(PHASE_EMERGED); - ScheduleCombatEvents(); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); extraEvents.Reset(); - me->SetFacingTo(DEATH_ORIENTATION); + extraEvents.SetPhase(PHASE_EMERGED); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToAll(false); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); + me->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE); } - void KilledUnit(Unit* victim) override + _hasYelledMagmaBurst = false; + _processingMagmaBurst = false; + _hasSubmergedOnce = false; + _isKnockbackEmoteAllowed = true; + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); + me->SetControlled(true, UNIT_STATE_ROOT); + _lavaBurstGUIDS.clear(); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (events.IsInPhase(PHASE_SUBMERGED) && damage >= me->GetHealth()) { - if (roll_chance_i(25) && victim->IsPlayer()) + damage = 0; + } + } + + void DoAction(int32 action) override + { + if (action == ACTION_FINISH_RAGNAROS_INTRO) + { + extraEvents.SetPhase(PHASE_INTRO); + extraEvents.ScheduleEvent(EVENT_INTRO_SAY, 5s, 0, PHASE_INTRO); + } + } + + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); + if (summon->GetEntry() == NPC_FLAME_OF_RAGNAROS) + summon->CastSpell((Unit*)nullptr, SPELL_INTENSE_HEAT, true, nullptr, nullptr, me->GetGUID()); + else if (summon->GetEntry() == NPC_SON_OF_FLAME) + DoZoneInCombat(summon); + } + + void SetGUID(ObjectGuid const& guid, int32 index) override + { + if (index == GO_LAVA_BURST) + { + if (_lavaBurstGUIDS.empty()) + extraEvents.ScheduleEvent(EVENT_LAVA_BURST_TRIGGER, 1ms); + + _lavaBurstGUIDS.insert(guid); + } + } + + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + summons.Despawn(summon); + if (events.IsInPhase(PHASE_SUBMERGED) && !summons.HasEntry(NPC_SON_OF_FLAME)) + HandleEmerge(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + _JustEngagedWith(); + events.SetPhase(PHASE_EMERGED); + ScheduleCombatEvents(); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + extraEvents.Reset(); + me->SetFacingTo(DEATH_ORIENTATION); + } + + void KilledUnit(Unit* victim) override + { + if (roll_chance_i(25) && victim->IsPlayer()) + Talk(SAY_KILL); + } + + void AttackStart(Unit* target) override + { + if (target && me->Attack(target, true)) + DoStartNoMovement(target); + } + + void EnterEvadeMode(EvadeReason why) override + { + if (!me->GetThreatMgr().IsThreatListEmpty()) + { + if (!_processingMagmaBurst) { - Talk(SAY_KILL); + // Boss try to evade, but still got some targets on threat list - it means that none of these targets are in melee range - cast magma blast + _processingMagmaBurst = true; + events.ScheduleEvent(EVENT_MAGMA_BLAST, 4s, PHASE_EMERGED, PHASE_EMERGED); } } - - void AttackStart(Unit* target) override + else { - if (target && me->Attack(target, true)) - { - DoStartNoMovement(target); - } + BossAI::EnterEvadeMode(why); } + } - void EnterEvadeMode(EvadeReason why) override + bool CanAIAttack(Unit const* victim) const override + { + // Used for Magma Blast handling to force EnterEvadeMode if there are no melee targets + return me->IsWithinMeleeRange(victim); + } + + void UpdateAI(uint32 diff) override + { + if (!extraEvents.Empty()) { - if (!me->GetThreatMgr().IsThreatListEmpty()) - { - if (!_processingMagmaBurst) - { - // Boss try to evade, but still got some targets on threat list - it means that none of these targets are in melee range - cast magma blast - _processingMagmaBurst = true; - events.ScheduleEvent(EVENT_MAGMA_BLAST, 4s, PHASE_EMERGED, PHASE_EMERGED); - } - } - else - { - BossAI::EnterEvadeMode(why); - } - } + extraEvents.Update(diff); - bool CanAIAttack(Unit const* victim) const override - { - // Used for Magma Blast handling to force EnterEvadeMode if there are no melee targets - return me->IsWithinMeleeRange(victim); - } - - void UpdateAI(uint32 diff) override - { - if (!extraEvents.Empty()) - { - extraEvents.Update(diff); - - while (uint32 const eventId = extraEvents.ExecuteEvent()) - { - switch (eventId) - { - // Intro events - case EVENT_INTRO_SAY: - { - Talk(SAY_ARRIVAL5_RAG); - extraEvents.ScheduleEvent(EVENT_INTRO_MAKE_ATTACKABLE, 2500ms, 0, PHASE_INTRO); - break; - } - case EVENT_INTRO_MAKE_ATTACKABLE: - { - _isIntroDone = true; - extraEvents.SetPhase(PHASE_EMERGED); - me->RemoveAurasDueToSpell(SPELL_RAGNAROS_SUBMERGE_EFFECT); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetImmuneToAll(false); - me->SetReactState(REACT_AGGRESSIVE); - DoZoneInCombat(); - break; - } - // Submerge events - case EVENT_EMERGE: - { - HandleEmerge(); - break; - } - case EVENT_RESET_KNOCKBACK_EMOTE: - { - _isKnockbackEmoteAllowed = true; - break; - } - case EVENT_LAVA_BURST_TRIGGER: - { - if (!_lavaBurstGUIDS.empty()) - { - ObjectGuid lavaBurstGUID = Acore::Containers::SelectRandomContainerElement(_lavaBurstGUIDS); - - if (GameObject* go = ObjectAccessor::GetGameObject(*me, lavaBurstGUID)) - { - go->CastSpell(nullptr, SPELL_LAVA_BURST_TRAP); - go->SendCustomAnim(0); - } - - _lavaBurstGUIDS.erase(lavaBurstGUID); - extraEvents.Repeat(1s); - } - else - { - events.RescheduleEvent(EVENT_LAVA_BURST, 10s, PHASE_EMERGED, PHASE_EMERGED); - } - - break; - } - } - } - } - - if (!UpdateVictim()) - { - if (!_processingMagmaBurst) - { - return; - } - } - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - // Base combat events - (mainly emerge phase) - while (uint32 const eventId = events.ExecuteEvent()) + while (uint32 const eventId = extraEvents.ExecuteEvent()) { switch (eventId) { - case EVENT_WRATH_OF_RAGNAROS: + // Intro events + case EVENT_INTRO_SAY: { - DoCastVictim(SPELL_WRATH_OF_RAGNAROS); - - if (urand(0, 1)) - { - Talk(SAY_WRATH); - } - events.Repeat(25s); + Talk(SAY_ARRIVAL5_RAG); + extraEvents.ScheduleEvent(EVENT_INTRO_MAKE_ATTACKABLE, 2500ms, 0, PHASE_INTRO); break; } - case EVENT_HAND_OF_RAGNAROS: + case EVENT_INTRO_MAKE_ATTACKABLE: { - DoCastSelf(SPELL_HAND_OF_RAGNAROS); - if (_isKnockbackEmoteAllowed) + _isIntroDone = true; + extraEvents.SetPhase(PHASE_EMERGED); + me->RemoveAurasDueToSpell(SPELL_RAGNAROS_SUBMERGE_EFFECT); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetImmuneToAll(false); + me->SetReactState(REACT_AGGRESSIVE); + DoZoneInCombat(); + break; + } + // Submerge events + case EVENT_EMERGE: + { + HandleEmerge(); + break; + } + case EVENT_RESET_KNOCKBACK_EMOTE: + { + _isKnockbackEmoteAllowed = true; + break; + } + case EVENT_LAVA_BURST_TRIGGER: + { + if (!_lavaBurstGUIDS.empty()) { - Talk(SAY_KNOCKBACK); + ObjectGuid lavaBurstGUID = Acore::Containers::SelectRandomContainerElement(_lavaBurstGUIDS); + + if (GameObject* go = ObjectAccessor::GetGameObject(*me, lavaBurstGUID)) + { + go->CastSpell(nullptr, SPELL_LAVA_BURST_TRAP); + go->SendCustomAnim(0); + } + + _lavaBurstGUIDS.erase(lavaBurstGUID); + extraEvents.Repeat(1s); + } + else + { + events.RescheduleEvent(EVENT_LAVA_BURST, 10s, PHASE_EMERGED, PHASE_EMERGED); + } + + break; + } + } + } + } + + if (!UpdateVictim()) + { + if (!_processingMagmaBurst) + return; + } + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + // Base combat events - (mainly emerge phase) + while (uint32 const eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_WRATH_OF_RAGNAROS: + { + DoCastVictim(SPELL_WRATH_OF_RAGNAROS); + + if (urand(0, 1)) + Talk(SAY_WRATH); + events.Repeat(25s); + break; + } + case EVENT_HAND_OF_RAGNAROS: + { + DoCastSelf(SPELL_HAND_OF_RAGNAROS); + if (_isKnockbackEmoteAllowed) + { + Talk(SAY_KNOCKBACK); + _isKnockbackEmoteAllowed = false; + extraEvents.RescheduleEvent(EVENT_RESET_KNOCKBACK_EMOTE, 5s); + } + events.Repeat(20s); + break; + } + case EVENT_LAVA_BURST: + { + DoCastAOE(SPELL_LAVA_BURST); + break; + } + case EVENT_MAGMA_BLAST: + { + _processingMagmaBurst = false; + + if (!IsVictimWithinMeleeRange()) + { + DoCastRandomTarget(SPELL_MAGMA_BLAST); + + if (!_hasYelledMagmaBurst) + { + Talk(SAY_MAGMABURST); + _hasYelledMagmaBurst = true; + } + } + + break; + } + case EVENT_MIGHT_OF_RAGNAROS: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [](Unit const* target) + { + return target->IsPlayer() && target->getPowerType() == POWER_MANA; + })) + { + if (me->CastSpell(target, SPELL_MIGHT_OF_RAGNAROS) == SPELL_CAST_OK && _isKnockbackEmoteAllowed) + { + Talk(SAY_KNOCKBACK, me); _isKnockbackEmoteAllowed = false; extraEvents.RescheduleEvent(EVENT_RESET_KNOCKBACK_EMOTE, 5s); } - events.Repeat(20s); - break; - } - case EVENT_LAVA_BURST: - { - DoCastAOE(SPELL_LAVA_BURST); - break; - } - case EVENT_MAGMA_BLAST: - { - _processingMagmaBurst = false; - - if (!IsVictimWithinMeleeRange()) - { - DoCastRandomTarget(SPELL_MAGMA_BLAST); - - if (!_hasYelledMagmaBurst) - { - Talk(SAY_MAGMABURST); - _hasYelledMagmaBurst = true; - } - } - - break; - } - case EVENT_MIGHT_OF_RAGNAROS: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [](Unit const* target) - { - return target->IsPlayer() && target->getPowerType() == POWER_MANA; - })) - { - if (me->CastSpell(target, SPELL_MIGHT_OF_RAGNAROS) == SPELL_CAST_OK && _isKnockbackEmoteAllowed) - { - Talk(SAY_KNOCKBACK, me); - _isKnockbackEmoteAllowed = false; - extraEvents.RescheduleEvent(EVENT_RESET_KNOCKBACK_EMOTE, 5s); - } - } - events.Repeat(11s, 30s); - break; - } - case EVENT_SUBMERGE: - { - events.CancelEventGroup(PHASE_EMERGED); - events.SetPhase(PHASE_SUBMERGED); - extraEvents.SetPhase(PHASE_SUBMERGED); - me->SetReactState(REACT_PASSIVE); - me->InterruptNonMeleeSpells(false); - me->AttackStop(); - DoResetThreatList(); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SUBMERGED); - DoCastSelf(SPELL_RAGNA_SUBMERGE_VISUAL, true); - //me->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE); - - Talk(_hasSubmergedOnce ? SAY_REINFORCEMENTS2 : SAY_REINFORCEMENTS1); - - DoCastAOE(SPELL_SUMMON_SONS_FLAME); - - if (!_hasSubmergedOnce) - { - _hasSubmergedOnce = true; - } - - extraEvents.ScheduleEvent(EVENT_EMERGE, 90s, PHASE_SUBMERGED, PHASE_SUBMERGED); - break; } + events.Repeat(11s, 30s); + break; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) + case EVENT_SUBMERGE: { - return; + events.CancelEventGroup(PHASE_EMERGED); + events.SetPhase(PHASE_SUBMERGED); + extraEvents.SetPhase(PHASE_SUBMERGED); + me->SetReactState(REACT_PASSIVE); + me->InterruptNonMeleeSpells(false); + me->AttackStop(); + DoResetThreatList(); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SUBMERGED); + DoCastSelf(SPELL_RAGNA_SUBMERGE_VISUAL, true); + //me->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE); + + Talk(_hasSubmergedOnce ? SAY_REINFORCEMENTS2 : SAY_REINFORCEMENTS1); + + DoCastAOE(SPELL_SUMMON_SONS_FLAME); + + if (!_hasSubmergedOnce) + _hasSubmergedOnce = true; + + extraEvents.ScheduleEvent(EVENT_EMERGE, 90s, PHASE_SUBMERGED, PHASE_SUBMERGED); + break; } } - DoMeleeAttackIfReady(); - } - - private: - EventMap extraEvents; - bool _isIntroDone; - bool _hasYelledMagmaBurst; - bool _processingMagmaBurst; - bool _hasSubmergedOnce; - bool _isKnockbackEmoteAllowed; // Prevents possible text overlap - - GuidSet _lavaBurstGUIDS; - - void HandleEmerge() - { - if (events.IsInPhase(PHASE_EMERGED)) - { + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - } - - events.SetPhase(PHASE_EMERGED); - ScheduleCombatEvents(); - extraEvents.CancelEventGroup(PHASE_SUBMERGED); - extraEvents.SetPhase(PHASE_EMERGED); - - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); - me->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE); - - me->RemoveAurasDueToSpell(SPELL_RAGNA_SUBMERGE_VISUAL); - - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) - { - AttackStart(target); - } } - void ScheduleCombatEvents() - { - events.RescheduleEvent(EVENT_ERUPTION, 15s, PHASE_EMERGED, PHASE_EMERGED); - events.RescheduleEvent(EVENT_WRATH_OF_RAGNAROS, 30s, PHASE_EMERGED, PHASE_EMERGED); - events.RescheduleEvent(EVENT_HAND_OF_RAGNAROS, 25s, PHASE_EMERGED, PHASE_EMERGED); - events.RescheduleEvent(EVENT_LAVA_BURST, 10s, PHASE_EMERGED, PHASE_EMERGED); - events.RescheduleEvent(EVENT_SUBMERGE, 180s, PHASE_EMERGED, PHASE_EMERGED); - events.RescheduleEvent(EVENT_MIGHT_OF_RAGNAROS, 11s, PHASE_EMERGED, PHASE_EMERGED); - } + DoMeleeAttackIfReady(); + } - bool IsVictimWithinMeleeRange() const - { - return me->GetVictim() && me->IsWithinMeleeRange(me->GetVictim()); - } - }; +private: + EventMap extraEvents; + bool _isIntroDone; + bool _hasYelledMagmaBurst; + bool _processingMagmaBurst; + bool _hasSubmergedOnce; + bool _isKnockbackEmoteAllowed; // Prevents possible text overlap - CreatureAI* GetAI(Creature* creature) const override + GuidSet _lavaBurstGUIDS; + + void HandleEmerge() { - return GetMoltenCoreAI(creature); + if (events.IsInPhase(PHASE_EMERGED)) + return; + + events.SetPhase(PHASE_EMERGED); + ScheduleCombatEvents(); + extraEvents.CancelEventGroup(PHASE_SUBMERGED); + extraEvents.SetPhase(PHASE_EMERGED); + + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); + me->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE); + + me->RemoveAurasDueToSpell(SPELL_RAGNA_SUBMERGE_VISUAL); + + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) + AttackStart(target); + } + + void ScheduleCombatEvents() + { + events.RescheduleEvent(EVENT_ERUPTION, 15s, PHASE_EMERGED, PHASE_EMERGED); + events.RescheduleEvent(EVENT_WRATH_OF_RAGNAROS, 30s, PHASE_EMERGED, PHASE_EMERGED); + events.RescheduleEvent(EVENT_HAND_OF_RAGNAROS, 25s, PHASE_EMERGED, PHASE_EMERGED); + events.RescheduleEvent(EVENT_LAVA_BURST, 10s, PHASE_EMERGED, PHASE_EMERGED); + events.RescheduleEvent(EVENT_SUBMERGE, 180s, PHASE_EMERGED, PHASE_EMERGED); + events.RescheduleEvent(EVENT_MIGHT_OF_RAGNAROS, 11s, PHASE_EMERGED, PHASE_EMERGED); + } + + bool IsVictimWithinMeleeRange() const + { + return me->GetVictim() && me->IsWithinMeleeRange(me->GetVictim()); } }; @@ -547,9 +510,7 @@ class spell_ragnaros_summon_sons_of_flame : public SpellScript if (Unit* caster = GetCaster()) { for (uint32 spell : RagnarosSoFSpells) - { caster->CastSpell(caster, spell, true); - } } } @@ -561,7 +522,7 @@ class spell_ragnaros_summon_sons_of_flame : public SpellScript void AddSC_boss_ragnaros() { - new boss_ragnaros(); + RegisterMoltenCoreCreatureAI(boss_ragnaros); RegisterSpellScript(spell_ragnaros_lava_burst_randomizer); RegisterSpellScript(spell_ragnaros_summon_sons_of_flame); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp index 45c9f7ad1..dfac88db9 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp @@ -41,70 +41,57 @@ enum Events EVENT_SHAZZRAH_GATE, }; -class boss_shazzrah : public CreatureScript +struct boss_shazzrah : public BossAI { -public: - boss_shazzrah() : CreatureScript("boss_shazzrah") { } + boss_shazzrah(Creature* creature) : BossAI(creature, DATA_SHAZZRAH) {} - struct boss_shazzrahAI : public BossAI + void JustEngagedWith(Unit* /*target*/) override { - boss_shazzrahAI(Creature* creature) : BossAI(creature, DATA_SHAZZRAH) {} + _JustEngagedWith(); + events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 2s, 4s); + events.ScheduleEvent(EVENT_SHAZZRAH_CURSE, 7s,11s); + events.ScheduleEvent(EVENT_MAGIC_GROUNDING, 14s, 19s); + events.ScheduleEvent(EVENT_COUNTERSPELL, 9s, 10s); + events.ScheduleEvent(EVENT_SHAZZRAH_GATE, 30s); + } - void JustEngagedWith(Unit* /*target*/) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 2s, 4s); - events.ScheduleEvent(EVENT_SHAZZRAH_CURSE, 7s,11s); - events.ScheduleEvent(EVENT_MAGIC_GROUNDING, 14s, 19s); - events.ScheduleEvent(EVENT_COUNTERSPELL, 9s, 10s); - events.ScheduleEvent(EVENT_SHAZZRAH_GATE, 30s); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) + case EVENT_ARCANE_EXPLOSION: { - case EVENT_ARCANE_EXPLOSION: - { - DoCastVictim(SPELL_ARCANE_EXPLOSION); - events.Repeat(4s, 5s); - break; - } - case EVENT_SHAZZRAH_CURSE: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_SHAZZRAH_CURSE)) - { - DoCast(target, SPELL_SHAZZRAH_CURSE); - } - events.Repeat(23s, 26s); - break; - } - case EVENT_MAGIC_GROUNDING: - { - DoCastSelf(SPELL_MAGIC_GROUNDING); - events.Repeat(7s, 9s); - break; - } - case EVENT_COUNTERSPELL: - { - DoCastAOE(SPELL_COUNTERSPELL); - events.Repeat(15s, 18s); - break; - } - case EVENT_SHAZZRAH_GATE: - { - DoCastAOE(SPELL_SHAZZRAH_GATE_DUMMY); - events.RescheduleEvent(EVENT_ARCANE_EXPLOSION, 3s, 6s); - events.Repeat(45s); - break; - } + DoCastVictim(SPELL_ARCANE_EXPLOSION); + events.Repeat(4s, 5s); + break; + } + case EVENT_SHAZZRAH_CURSE: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_SHAZZRAH_CURSE)) + DoCast(target, SPELL_SHAZZRAH_CURSE); + events.Repeat(23s, 26s); + break; + } + case EVENT_MAGIC_GROUNDING: + { + DoCastSelf(SPELL_MAGIC_GROUNDING); + events.Repeat(7s, 9s); + break; + } + case EVENT_COUNTERSPELL: + { + DoCastAOE(SPELL_COUNTERSPELL); + events.Repeat(15s, 18s); + break; + } + case EVENT_SHAZZRAH_GATE: + { + DoCastAOE(SPELL_SHAZZRAH_GATE_DUMMY); + events.RescheduleEvent(EVENT_ARCANE_EXPLOSION, 3s, 6s); + events.Repeat(45s); + break; } } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } }; @@ -128,30 +115,22 @@ class spell_shazzrah_gate_dummy : public SpellScript Player const* plrTarget = target->ToPlayer(); // Should not target non player targets if (!plrTarget) - { return true; - } // Should skip current victim if (caster->GetVictim() == plrTarget) - { return true; - } // Should not target enemies within melee range if (plrTarget->IsWithinMeleeRange(caster)) - { return true; - } return false; }); } if (!targets.empty()) - { Acore::Containers::RandomResize(targets, 1); - } } void HandleScript(SpellEffIndex /*effIndex*/) @@ -182,7 +161,7 @@ class spell_shazzrah_gate_dummy : public SpellScript void AddSC_boss_shazzrah() { - new boss_shazzrah(); + RegisterMoltenCoreCreatureAI(boss_shazzrah); // Spells RegisterSpellScript(spell_shazzrah_gate_dummy); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_sulfuron_harbinger.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_sulfuron_harbinger.cpp index edc39e8cc..3eb80a537 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_sulfuron_harbinger.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_sulfuron_harbinger.cpp @@ -47,172 +47,138 @@ enum Events EVENT_IMMOLATE, }; -class boss_sulfuron : public CreatureScript +struct boss_sulfuron : public BossAI { -public: - boss_sulfuron() : CreatureScript("boss_sulfuron") {} + boss_sulfuron(Creature* creature) : BossAI(creature, DATA_SULFURON) {} - struct boss_sulfuronAI : public BossAI + void JustEngagedWith(Unit* /*who*/) override { - boss_sulfuronAI(Creature* creature) : BossAI(creature, DATA_SULFURON) {} + _JustEngagedWith(); + events.ScheduleEvent(EVENT_DEMORALIZING_SHOUT, 6s, 20s); + events.ScheduleEvent(EVENT_INSPIRE, 7s, 10s); + events.ScheduleEvent(EVENT_KNOCKDOWN, 6s); + events.ScheduleEvent(EVENT_FLAMESPEAR, 2s); + } - void JustEngagedWith(Unit* /*who*/) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - _JustEngagedWith(); - events.ScheduleEvent(EVENT_DEMORALIZING_SHOUT, 6s, 20s); - events.ScheduleEvent(EVENT_INSPIRE, 7s, 10s); - events.ScheduleEvent(EVENT_KNOCKDOWN, 6s); - events.ScheduleEvent(EVENT_FLAMESPEAR, 2s); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) + case EVENT_DEMORALIZING_SHOUT: { - case EVENT_DEMORALIZING_SHOUT: - { - DoCastVictim(SPELL_DEMORALIZING_SHOUT); - events.Repeat(12s, 18s); - break; - } - case EVENT_INSPIRE: - { - std::list healers = DoFindFriendlyMissingBuff(45.0f, SPELL_INSPIRE); - if (!healers.empty()) - { - DoCast(Acore::Containers::SelectRandomContainerElement(healers), SPELL_INSPIRE); - } + DoCastVictim(SPELL_DEMORALIZING_SHOUT); + events.Repeat(12s, 18s); + break; + } + case EVENT_INSPIRE: + { + std::list healers = DoFindFriendlyMissingBuff(45.0f, SPELL_INSPIRE); + if (!healers.empty()) + DoCast(Acore::Containers::SelectRandomContainerElement(healers), SPELL_INSPIRE); - DoCastSelf(SPELL_INSPIRE); - events.Repeat(13s, 20s); - break; - } - case EVENT_KNOCKDOWN: - { - DoCastVictim(SPELL_KNOCKDOWN); - events.Repeat(10s, 20s); - break; - } - case EVENT_FLAMESPEAR: - { - DoCastRandomTarget(SPELL_FLAMESPEAR); - events.Repeat(12s, 16s); - break; - } + DoCastSelf(SPELL_INSPIRE); + events.Repeat(13s, 20s); + break; + } + case EVENT_KNOCKDOWN: + { + DoCastVictim(SPELL_KNOCKDOWN); + events.Repeat(10s, 20s); + break; + } + case EVENT_FLAMESPEAR: + { + DoCastRandomTarget(SPELL_FLAMESPEAR); + events.Repeat(12s, 16s); + break; } } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } }; -class npc_flamewaker_priest : public CreatureScript +struct npc_flamewaker_priest : public ScriptedAI { -public: - npc_flamewaker_priest() : CreatureScript("npc_flamewaker_priest") {} + npc_flamewaker_priest(Creature* creature) : ScriptedAI(creature) {} - struct npc_flamewaker_priestAI : public ScriptedAI + void Reset() override { - npc_flamewaker_priestAI(Creature* creature) : ScriptedAI(creature) {} + events.Reset(); + } - void Reset() override - { - events.Reset(); - } + void JustDied(Unit* /*killer*/) override + { + events.Reset(); + } - void JustDied(Unit* /*killer*/) override - { - events.Reset(); - } + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_DARK_STRIKE, 4s, 7s); + events.ScheduleEvent(EVENT_DARK_MENDING, 15s, 30s); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 2s, 4s); + events.ScheduleEvent(EVENT_IMMOLATE, 3500ms, 6s); + } - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_DARK_STRIKE, 4s, 7s); - events.ScheduleEvent(EVENT_DARK_MENDING, 15s, 30s); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 2s, 4s); - events.ScheduleEvent(EVENT_IMMOLATE, 3500ms, 6s); - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void UpdateAI(uint32 diff) override + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 const eventId = events.ExecuteEvent()) { - if (!UpdateVictim()) + switch (eventId) { - return; + case EVENT_DARK_STRIKE: + { + DoCastVictim(SPELL_DARK_STRIKE); + events.Repeat(4s, 7s); + break; + } + case EVENT_DARK_MENDING: + { + if (Unit* target = DoSelectLowestHpFriendly(60.0f, 1)) + { + if (target->GetGUID() != me->GetGUID()) + { + DoCast(target, SPELL_DARK_MENDING); + } + } + events.Repeat(15s, 20s); + break; + } + case EVENT_SHADOW_WORD_PAIN: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_SHADOW_WORD_PAIN)) + DoCast(target, SPELL_SHADOW_WORD_PAIN); + events.Repeat(2500ms, 5s); + break; + } + case EVENT_IMMOLATE: + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_IMMOLATE)) + DoCast(target, SPELL_IMMOLATE); + events.Repeat(5s, 7s); + break; + } } - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - { return; - } - - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_DARK_STRIKE: - { - DoCastVictim(SPELL_DARK_STRIKE); - events.Repeat(4s, 7s); - break; - } - case EVENT_DARK_MENDING: - { - if (Unit* target = DoSelectLowestHpFriendly(60.0f, 1)) - { - if (target->GetGUID() != me->GetGUID()) - { - DoCast(target, SPELL_DARK_MENDING); - } - } - events.Repeat(15s, 20s); - break; - } - case EVENT_SHADOW_WORD_PAIN: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_SHADOW_WORD_PAIN)) - { - DoCast(target, SPELL_SHADOW_WORD_PAIN); - } - events.Repeat(2500ms, 5s); - break; - } - case EVENT_IMMOLATE: - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, true, -SPELL_IMMOLATE)) - { - DoCast(target, SPELL_IMMOLATE); - } - events.Repeat(5s, 7s); - break; - } - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - } - - DoMeleeAttackIfReady(); } - private: - EventMap events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); + DoMeleeAttackIfReady(); } + +private: + EventMap events; }; void AddSC_boss_sulfuron() { - new boss_sulfuron(); - new npc_flamewaker_priest(); + RegisterMoltenCoreCreatureAI(boss_sulfuron); + RegisterMoltenCoreCreatureAI(npc_flamewaker_priest); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp index e9163cb89..b51e49018 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp @@ -53,384 +53,331 @@ MCBossObject const linkedBossObjData[MAX_MC_LINKED_BOSS_OBJ]= constexpr uint8 SAY_SPAWN = 1; -class instance_molten_core : public InstanceMapScript +struct instance_molten_core : public InstanceScript { -public: - instance_molten_core() : InstanceMapScript(MCScriptName, MAP_MOLTEN_CORE) {} - - struct instance_molten_core_InstanceMapScript : public InstanceScript + instance_molten_core(Map* map) : InstanceScript(map) { - instance_molten_core_InstanceMapScript(Map* map) : InstanceScript(map) - { - SetHeaders(DataHeader); - SetBossNumber(MAX_ENCOUNTER); - LoadMinionData(minionData); - } - - void OnPlayerEnter(Player* /*player*/) override - { - if (CheckMajordomoExecutus()) - { - SummonMajordomoExecutus(); - } - } - - void OnCreatureCreate(Creature* creature) override - { - switch (creature->GetEntry()) - { - case NPC_GOLEMAGG_THE_INCINERATOR: - { - _golemaggGUID = creature->GetGUID(); - break; - } - case NPC_CORE_RAGER: - { - _golemaggMinionsGUIDS.insert(creature->GetGUID()); - break; - } - case NPC_MAJORDOMO_EXECUTUS: - { - _majordomoExecutusGUID = creature->GetGUID(); - break; - } - case NPC_GARR: - { - _garrGUID = creature->GetGUID(); - break; - } - case NPC_RAGNAROS: - { - _ragnarosGUID = creature->GetGUID(); - break; - } - case NPC_FIRESWORN: - case NPC_FLAMEWALKER: - case NPC_FLAMEWALKER_PROTECTOR: - case NPC_FLAMEWALKER_PRIEST: - case NPC_FLAMEWALKER_HEALER: - case NPC_FLAMEWALKER_ELITE: - { - AddMinion(creature); - break; - } - } - } - - void OnCreatureRemove(Creature* creature) override - { - switch (creature->GetEntry()) - { - case NPC_FIRESWORN: - { - RemoveMinion(creature); - break; - } - case NPC_FLAMEWALKER: - case NPC_FLAMEWALKER_PROTECTOR: - case NPC_FLAMEWALKER_PRIEST: - case NPC_FLAMEWALKER_HEALER: - case NPC_FLAMEWALKER_ELITE: - { - RemoveMinion(creature); - break; - } - } - } - - void OnGameObjectCreate(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_CACHE_OF_THE_FIRELORD: - { - _cacheOfTheFirelordGUID = go->GetGUID(); - break; - } - case GO_CIRCLE_GEDDON: - case GO_CIRCLE_GARR: - case GO_CIRCLE_GEHENNAS: - case GO_CIRCLE_GOLEMAGG: - case GO_CIRCLE_MAGMADAR: - case GO_CIRCLE_SHAZZRAH: - case GO_CIRCLE_SULFURON: - { - for (uint8 i = 0; i < MAX_MC_LINKED_BOSS_OBJ; ++i) - { - if (linkedBossObjData[i].circleId != go->GetEntry()) - { - continue; - } - - if (GetBossState(linkedBossObjData[i].bossId) == DONE) - { - go->DespawnOrUnsummon(0ms, Seconds(WEEK)); - } - else - { - _circlesGUIDs[linkedBossObjData[i].bossId] = go->GetGUID(); - } - } - - break; - } - case GO_RUNE_KRESS: - case GO_RUNE_MOHN: - case GO_RUNE_BLAZ: - case GO_RUNE_MAZJ: - case GO_RUNE_ZETH: - case GO_RUNE_THERI: - case GO_RUNE_KORO: - { - for (uint8 i = 0; i < MAX_MC_LINKED_BOSS_OBJ; ++i) - { - if (linkedBossObjData[i].runeId != go->GetEntry()) - { - continue; - } - - if (GetBossState(linkedBossObjData[i].bossId) == DONE) - { - go->UseDoorOrButton(WEEK * IN_MILLISECONDS); - } - else - { - _runesGUIDs[linkedBossObjData[i].bossId] = go->GetGUID(); - } - } - break; - } - case GO_LAVA_STEAM: - { - _lavaSteamGUID = go->GetGUID(); - break; - } - case GO_LAVA_SPLASH: - { - _lavaSplashGUID = go->GetGUID(); - break; - } - case GO_LAVA_BURST: - { - if (Creature* ragnaros = instance->GetCreature(_ragnarosGUID)) - { - ragnaros->AI()->SetGUID(go->GetGUID(), GO_LAVA_BURST); - } - break; - } - } - } - - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case DATA_GOLEMAGG: - return _golemaggGUID; - case DATA_MAJORDOMO_EXECUTUS: - return _majordomoExecutusGUID; - case DATA_GARR: - return _garrGUID; - case DATA_LAVA_STEAM: - return _lavaSteamGUID; - case DATA_LAVA_SPLASH: - return _lavaSplashGUID; - case DATA_RAGNAROS: - return _ragnarosGUID; - } - - return ObjectGuid::Empty; - } - - bool SetBossState(uint32 bossId, EncounterState state) override - { - if (!InstanceScript::SetBossState(bossId, state)) - { - return false; - } - - if (bossId == DATA_MAJORDOMO_EXECUTUS && state == DONE) - { - if (GameObject* cache = instance->GetGameObject(_cacheOfTheFirelordGUID)) - { - cache->SetRespawnTime(7 * DAY); - cache->SetLootRecipient(instance); - } - } - else if (bossId == DATA_GOLEMAGG) - { - switch (state) - { - case NOT_STARTED: - case FAIL: - { - if (!_golemaggMinionsGUIDS.empty()) - { - for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) - { - Creature* minion = instance->GetCreature(minionGuid); - if (minion && minion->isDead()) - { - minion->Respawn(); - } - } - } - break; - } - case IN_PROGRESS: - { - if (!_golemaggMinionsGUIDS.empty()) - { - for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) - { - if (Creature* minion = instance->GetCreature(minionGuid)) - { - minion->AI()->DoZoneInCombat(nullptr, 150.0f); - } - } - } - break; - } - case DONE: - { - if (!_golemaggMinionsGUIDS.empty()) - { - for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) - { - if (Creature* minion = instance->GetCreature(minionGuid)) - { - minion->CastSpell(minion, SPELL_CORE_RAGER_QUIET_SUICIDE, true); - } - } - _golemaggMinionsGUIDS.clear(); - } - break; - } - default: - break; - } - } - // Perform needed checks for Majordomu - if (bossId < DATA_MAJORDOMO_EXECUTUS && state == DONE) - { - if (GameObject* circle = instance->GetGameObject(_circlesGUIDs[bossId])) - { - circle->DespawnOrUnsummon(0ms, Seconds(WEEK)); - _circlesGUIDs[bossId].Clear(); - } - - if (GameObject* rune = instance->GetGameObject(_runesGUIDs[bossId])) - { - rune->UseDoorOrButton(WEEK * IN_MILLISECONDS); - _runesGUIDs[bossId].Clear(); - } - - if (CheckMajordomoExecutus()) - { - SummonMajordomoExecutus(); - } - } - - return true; - } - - void DoAction(int32 action) override - { - if (action == ACTION_RESET_GOLEMAGG_ENCOUNTER) - { - if (Creature* golemagg = instance->GetCreature(_golemaggGUID)) - { - golemagg->AI()->EnterEvadeMode(); - } - - if (!_golemaggMinionsGUIDS.empty()) - { - for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) - { - if (Creature* minion = instance->GetCreature(minionGuid)) - { - minion->AI()->EnterEvadeMode(); - } - } - } - } - } - - void SummonMajordomoExecutus() - { - if (instance->GetCreature(_majordomoExecutusGUID)) - { - return; - } - - if (GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE) - { - if (Creature* creature = instance->SummonCreature(NPC_MAJORDOMO_EXECUTUS, MajordomoSummonPos)) - { - creature->AI()->Talk(SAY_SPAWN); - } - } - else - { - instance->SummonCreature(NPC_MAJORDOMO_EXECUTUS, MajordomoRagnaros); - } - } - - bool CheckMajordomoExecutus() const - { - if (GetBossState(DATA_RAGNAROS) == DONE) - { - return false; - } - - for (uint8 i = 0; i < DATA_MAJORDOMO_EXECUTUS; ++i) - { - if (i == DATA_LUCIFRON) - { - continue; - } - - if (GetBossState(i) != DONE) - { - return false; - } - } - - // Prevent spawning if Ragnaros is present - if (instance->GetCreature(_ragnarosGUID)) - { - return false; - } - - return true; - } - - private: - std::unordered_map _circlesGUIDs; - std::unordered_map _runesGUIDs; - - // Golemagg encounter related - ObjectGuid _golemaggGUID; - GuidSet _golemaggMinionsGUIDS; - - // Ragnaros encounter related - ObjectGuid _ragnarosGUID; - ObjectGuid _lavaSteamGUID; - ObjectGuid _lavaSplashGUID; - - ObjectGuid _majordomoExecutusGUID; - ObjectGuid _cacheOfTheFirelordGUID; - ObjectGuid _garrGUID; - ObjectGuid _magmadarGUID; - }; - - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_molten_core_InstanceMapScript(map); + SetHeaders(DataHeader); + SetBossNumber(MAX_ENCOUNTER); + LoadMinionData(minionData); } + + void OnPlayerEnter(Player* /*player*/) override + { + if (CheckMajordomoExecutus()) + SummonMajordomoExecutus(); + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_GOLEMAGG_THE_INCINERATOR: + { + _golemaggGUID = creature->GetGUID(); + break; + } + case NPC_CORE_RAGER: + { + _golemaggMinionsGUIDS.insert(creature->GetGUID()); + break; + } + case NPC_MAJORDOMO_EXECUTUS: + { + _majordomoExecutusGUID = creature->GetGUID(); + break; + } + case NPC_GARR: + { + _garrGUID = creature->GetGUID(); + break; + } + case NPC_RAGNAROS: + { + _ragnarosGUID = creature->GetGUID(); + break; + } + case NPC_FIRESWORN: + case NPC_FLAMEWALKER: + case NPC_FLAMEWALKER_PROTECTOR: + case NPC_FLAMEWALKER_PRIEST: + case NPC_FLAMEWALKER_HEALER: + case NPC_FLAMEWALKER_ELITE: + { + AddMinion(creature); + break; + } + } + } + + void OnCreatureRemove(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_FIRESWORN: + { + RemoveMinion(creature); + break; + } + case NPC_FLAMEWALKER: + case NPC_FLAMEWALKER_PROTECTOR: + case NPC_FLAMEWALKER_PRIEST: + case NPC_FLAMEWALKER_HEALER: + case NPC_FLAMEWALKER_ELITE: + { + RemoveMinion(creature); + break; + } + } + } + + void OnGameObjectCreate(GameObject* go) override + { + switch (go->GetEntry()) + { + case GO_CACHE_OF_THE_FIRELORD: + { + _cacheOfTheFirelordGUID = go->GetGUID(); + break; + } + case GO_CIRCLE_GEDDON: + case GO_CIRCLE_GARR: + case GO_CIRCLE_GEHENNAS: + case GO_CIRCLE_GOLEMAGG: + case GO_CIRCLE_MAGMADAR: + case GO_CIRCLE_SHAZZRAH: + case GO_CIRCLE_SULFURON: + { + for (uint8 i = 0; i < MAX_MC_LINKED_BOSS_OBJ; ++i) + { + if (linkedBossObjData[i].circleId != go->GetEntry()) + continue; + + if (GetBossState(linkedBossObjData[i].bossId) == DONE) + go->DespawnOrUnsummon(0ms, Seconds(WEEK)); + else + _circlesGUIDs[linkedBossObjData[i].bossId] = go->GetGUID(); + } + + break; + } + case GO_RUNE_KRESS: + case GO_RUNE_MOHN: + case GO_RUNE_BLAZ: + case GO_RUNE_MAZJ: + case GO_RUNE_ZETH: + case GO_RUNE_THERI: + case GO_RUNE_KORO: + { + for (uint8 i = 0; i < MAX_MC_LINKED_BOSS_OBJ; ++i) + { + if (linkedBossObjData[i].runeId != go->GetEntry()) + continue; + + if (GetBossState(linkedBossObjData[i].bossId) == DONE) + go->UseDoorOrButton(WEEK * IN_MILLISECONDS); + else + _runesGUIDs[linkedBossObjData[i].bossId] = go->GetGUID(); + } + break; + } + case GO_LAVA_STEAM: + { + _lavaSteamGUID = go->GetGUID(); + break; + } + case GO_LAVA_SPLASH: + { + _lavaSplashGUID = go->GetGUID(); + break; + } + case GO_LAVA_BURST: + { + if (Creature* ragnaros = instance->GetCreature(_ragnarosGUID)) + ragnaros->AI()->SetGUID(go->GetGUID(), GO_LAVA_BURST); + break; + } + } + } + + ObjectGuid GetGuidData(uint32 type) const override + { + switch (type) + { + case DATA_GOLEMAGG: + return _golemaggGUID; + case DATA_MAJORDOMO_EXECUTUS: + return _majordomoExecutusGUID; + case DATA_GARR: + return _garrGUID; + case DATA_LAVA_STEAM: + return _lavaSteamGUID; + case DATA_LAVA_SPLASH: + return _lavaSplashGUID; + case DATA_RAGNAROS: + return _ragnarosGUID; + } + + return ObjectGuid::Empty; + } + + bool SetBossState(uint32 bossId, EncounterState state) override + { + if (!InstanceScript::SetBossState(bossId, state)) + return false; + + if (bossId == DATA_MAJORDOMO_EXECUTUS && state == DONE) + { + if (GameObject* cache = instance->GetGameObject(_cacheOfTheFirelordGUID)) + { + cache->SetRespawnTime(7 * DAY); + cache->SetLootRecipient(instance); + } + } + else if (bossId == DATA_GOLEMAGG) + { + switch (state) + { + case NOT_STARTED: + case FAIL: + { + if (!_golemaggMinionsGUIDS.empty()) + { + for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) + { + Creature* minion = instance->GetCreature(minionGuid); + if (minion && minion->isDead()) + minion->Respawn(); + } + } + break; + } + case IN_PROGRESS: + { + if (!_golemaggMinionsGUIDS.empty()) + { + for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) + { + if (Creature* minion = instance->GetCreature(minionGuid)) + minion->AI()->DoZoneInCombat(nullptr, 150.0f); + } + } + break; + } + case DONE: + { + if (!_golemaggMinionsGUIDS.empty()) + { + for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) + { + if (Creature* minion = instance->GetCreature(minionGuid)) + minion->CastSpell(minion, SPELL_CORE_RAGER_QUIET_SUICIDE, true); + } + _golemaggMinionsGUIDS.clear(); + } + break; + } + default: + break; + } + } + // Perform needed checks for Majordomu + if (bossId < DATA_MAJORDOMO_EXECUTUS && state == DONE) + { + if (GameObject* circle = instance->GetGameObject(_circlesGUIDs[bossId])) + { + circle->DespawnOrUnsummon(0ms, Seconds(WEEK)); + _circlesGUIDs[bossId].Clear(); + } + + if (GameObject* rune = instance->GetGameObject(_runesGUIDs[bossId])) + { + rune->UseDoorOrButton(WEEK * IN_MILLISECONDS); + _runesGUIDs[bossId].Clear(); + } + + if (CheckMajordomoExecutus()) + SummonMajordomoExecutus(); + } + + return true; + } + + void DoAction(int32 action) override + { + if (action == ACTION_RESET_GOLEMAGG_ENCOUNTER) + { + if (Creature* golemagg = instance->GetCreature(_golemaggGUID)) + golemagg->AI()->EnterEvadeMode(); + + if (!_golemaggMinionsGUIDS.empty()) + { + for (ObjectGuid const& minionGuid : _golemaggMinionsGUIDS) + { + if (Creature* minion = instance->GetCreature(minionGuid)) + minion->AI()->EnterEvadeMode(); + } + } + } + } + + void SummonMajordomoExecutus() + { + if (instance->GetCreature(_majordomoExecutusGUID)) + return; + + if (GetBossState(DATA_MAJORDOMO_EXECUTUS) != DONE) + { + if (Creature* creature = instance->SummonCreature(NPC_MAJORDOMO_EXECUTUS, MajordomoSummonPos)) + creature->AI()->Talk(SAY_SPAWN); + } + else + { + instance->SummonCreature(NPC_MAJORDOMO_EXECUTUS, MajordomoRagnaros); + } + } + + bool CheckMajordomoExecutus() const + { + if (GetBossState(DATA_RAGNAROS) == DONE) + return false; + + for (uint8 i = 0; i < DATA_MAJORDOMO_EXECUTUS; ++i) + { + if (i == DATA_LUCIFRON) + continue; + + if (GetBossState(i) != DONE) + return false; + } + + // Prevent spawning if Ragnaros is present + if (instance->GetCreature(_ragnarosGUID)) + return false; + + return true; + } + +private: + std::unordered_map _circlesGUIDs; + std::unordered_map _runesGUIDs; + + // Golemagg encounter related + ObjectGuid _golemaggGUID; + GuidSet _golemaggMinionsGUIDS; + + // Ragnaros encounter related + ObjectGuid _ragnarosGUID; + ObjectGuid _lavaSteamGUID; + ObjectGuid _lavaSplashGUID; + + ObjectGuid _majordomoExecutusGUID; + ObjectGuid _cacheOfTheFirelordGUID; + ObjectGuid _garrGUID; + ObjectGuid _magmadarGUID; }; void AddSC_instance_molten_core() { - new instance_molten_core(); + RegisterInstanceScript(instance_molten_core, MAP_MOLTEN_CORE); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.cpp index 117f06926..4b9548ecf 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.cpp @@ -46,75 +46,60 @@ enum Spells }; // Serrated Bites timer may be wrong -class npc_mc_core_hound : public CreatureScript +struct npc_mc_core_hound : public CreatureAI { -public: - npc_mc_core_hound() : CreatureScript("npc_mc_core_hound") {} - - struct npc_mc_core_houndAI : public CreatureAI + npc_mc_core_hound(Creature* creature) : + CreatureAI(creature), + instance(creature->GetInstanceScript()), + serratedBiteTimer(3000) { - npc_mc_core_houndAI(Creature* creature) : - CreatureAI(creature), - instance(creature->GetInstanceScript()), - serratedBiteTimer(3000) - { - } - - void Reset() override - { - serratedBiteTimer = 3000; - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override - { - // Prevent receiving any extra damage if Hound is playing dead - if (me->HasAura(SPELL_PLAY_DEAD)) - { - damage = 0; - return; - } - else if (me->GetHealth() <= damage) - { - damage = 0; - Talk(EMOTE_SMOLDERING); - DoCastSelf(SPELL_PLAY_DEAD); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - { - return; - } - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasAura(SPELL_PLAY_DEAD)) - { - return; - } - - if (serratedBiteTimer <= diff) - { - DoCastVictim(SPELL_SERRATED_BITE); - serratedBiteTimer = urand(5000, 6000); - } - else - { - serratedBiteTimer -= diff; - } - - DoMeleeAttackIfReady(); - } - - private: - InstanceScript* instance; - uint32 serratedBiteTimer; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetMoltenCoreAI(creature); } + + void Reset() override + { + serratedBiteTimer = 3000; + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + // Prevent receiving any extra damage if Hound is playing dead + if (me->HasAura(SPELL_PLAY_DEAD)) + { + damage = 0; + return; + } + else if (me->GetHealth() <= damage) + { + damage = 0; + Talk(EMOTE_SMOLDERING); + DoCastSelf(SPELL_PLAY_DEAD); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasAura(SPELL_PLAY_DEAD)) + return; + + if (serratedBiteTimer <= diff) + { + DoCastVictim(SPELL_SERRATED_BITE); + serratedBiteTimer = urand(5000, 6000); + } + else + { + serratedBiteTimer -= diff; + } + + DoMeleeAttackIfReady(); + } + +private: + InstanceScript* instance; + uint32 serratedBiteTimer; }; // 19822 Play Dead @@ -136,9 +121,7 @@ class spell_mc_play_dead_aura : public AuraScript { Creature* creatureTarget = GetTarget()->ToCreature(); if (!creatureTarget) - { return; - } creatureTarget->CastSpell(creatureTarget, SPELL_PLAY_DEAD_PACIFY, true); creatureTarget->SetDynamicFlag(UNIT_DYNFLAG_DEAD); @@ -154,9 +137,7 @@ class spell_mc_play_dead_aura : public AuraScript { Creature* creatureTarget = GetTarget()->ToCreature(); if (!creatureTarget) - { return; - } creatureTarget->RemoveAurasDueToSpell(SPELL_PLAY_DEAD_PACIFY); creatureTarget->RemoveDynamicFlag(UNIT_DYNFLAG_DEAD); @@ -166,9 +147,7 @@ class spell_mc_play_dead_aura : public AuraScript creatureTarget->SetReactState(REACT_AGGRESSIVE); if (!creatureTarget->IsInCombat()) - { return; - } bool shouldDie = true; std::list hounds; @@ -246,9 +225,7 @@ struct npc_lava_spawn : public ScriptedAI void UpdateAI(uint32 diff) override { if (!UpdateVictim()) - { return; - } _scheduler.Update(diff); @@ -262,7 +239,7 @@ private: void AddSC_molten_core() { // Creatures - new npc_mc_core_hound(); + RegisterMoltenCoreCreatureAI(npc_mc_core_hound); RegisterCreatureAI(npc_lava_spawn); // Spells diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h index 0dea07421..ca58eee1d 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h @@ -127,4 +127,7 @@ inline AI* GetMoltenCoreAI(T* obj) return GetInstanceAI(obj, MCScriptName); } +#define RegisterMoltenCoreCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetMoltenCoreAI) +#define RegisterMoltenCoreGameObjectAI(ai_name) RegisterGameObjectAIWithFactory(ai_name, GetMoltenCoreAI) + #endif diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp index e55b7dcdf..01afffe50 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp @@ -45,12 +45,7 @@ enum Spells struct boss_curator : public BossAI { boss_curator(Creature* creature) : BossAI(creature, DATA_CURATOR) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_maiden_of_virtue.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_maiden_of_virtue.cpp index 93d9c0d3a..e6ccebfef 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_maiden_of_virtue.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_maiden_of_virtue.cpp @@ -39,12 +39,7 @@ enum Spells struct boss_maiden_of_virtue : public BossAI { boss_maiden_of_virtue(Creature* creature) : BossAI(creature, DATA_MAIDEN) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void JustEngagedWith(Unit* who) override { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp index 6bb780a83..8b6256ad8 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp @@ -75,10 +75,6 @@ struct boss_moroes : public BossAI boss_moroes(Creature* creature) : BossAI(creature, DATA_MOROES) { _activeGuests = 0; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void InitializeAI() override diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp index 289b2d596..c1a6341bf 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp @@ -74,12 +74,7 @@ enum Events struct boss_tenris_mirkblood : public BossAI { boss_tenris_mirkblood(Creature* creature) : BossAI(creature, DATA_MIRKBLOOD) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_terestian_illhoof.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_terestian_illhoof.cpp index 13b37f3e0..15bffcc2c 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_terestian_illhoof.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_terestian_illhoof.cpp @@ -147,12 +147,7 @@ private: struct boss_terestian_illhoof : public BossAI { boss_terestian_illhoof(Creature* creature) : BossAI(creature, DATA_TERESTIAN) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp index 484a0ada7..910f95557 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp @@ -176,6 +176,7 @@ public: void JustEngagedWith(Unit* /*who*/) override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp index d25849cd0..9caf6524e 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp @@ -190,11 +190,10 @@ struct boss_felblood_kaelthas : public BossAI damage = me->GetHealth() - 1; if (me->isRegeneratingHealth()) { - me->CombatStop(); me->CastStop(); me->SetRegeneratingHealth(false); me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE); - me->SetImmuneToAll(true); + me->SetImmuneToAll(true, true); me->SetReactState(REACT_PASSIVE); LapseAction(ACTION_REMOVE_FLY); scheduler.CancelAll(); diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp index 3ab236d8c..333c6ffee 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp @@ -92,6 +92,7 @@ public: me->m_Events.KillAllEvents(false); me->SetUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); me->setActive(false); + SetEquipmentSlots(true); } void StartEvent() @@ -244,6 +245,8 @@ public: Talk(SAY_BREAKOUT2); DoCastSelf(SPELL_ANTI_MAGIC_ZONE); break; + default: + break; } } diff --git a/src/server/scripts/EasternKingdoms/ShadowfangKeep/boss_apothecary_hummel.cpp b/src/server/scripts/EasternKingdoms/ShadowfangKeep/boss_apothecary_hummel.cpp index 895fb283e..0efd989f1 100644 --- a/src/server/scripts/EasternKingdoms/ShadowfangKeep/boss_apothecary_hummel.cpp +++ b/src/server/scripts/EasternKingdoms/ShadowfangKeep/boss_apothecary_hummel.cpp @@ -87,12 +87,7 @@ public: struct boss_apothecary_hummelAI : public BossAI { boss_apothecary_hummelAI(Creature* creature) : BossAI(creature, DATA_APOTHECARY_HUMMEL), _deadCount(0), _isDead(false) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp index 8253bf810..f32644ea0 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp @@ -161,7 +161,6 @@ struct boss_kalecgos : public BossAI me->m_Events.AddEventAtOffset([&] { me->SetReactState(REACT_PASSIVE); - me->CombatStop(); me->RemoveAllAuras(); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); Talk(SAY_EVIL_ENRAGE); diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp index c74788d8f..63e45764c 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp @@ -126,10 +126,6 @@ struct npc_kiljaeden_controller : public NullCreatureAI npc_kiljaeden_controller(Creature* creature) : NullCreatureAI(creature), summons(me) { instance = creature->GetInstanceScript(); - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void ResetOrbs() @@ -233,11 +229,6 @@ struct boss_kiljaeden : public BossAI boss_kiljaeden(Creature* creature) : BossAI(creature, DATA_KILJAEDEN) { me->SetReactState(REACT_PASSIVE); - - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void InitializeAI() override diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp index d8fc79846..414d3467e 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp @@ -434,8 +434,7 @@ class spell_entropius_black_hole_effect : public SpellScript float targetX = baseX + maxDist * cos(angle); float targetY = baseY + maxDist * sin(angle); float hitX, hitY, hitZ; - if (VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos( - unit->GetMapId(), + if (unit->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos( baseX, baseY, z, targetX, targetY, z, hitX, hitY, hitZ, diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp index 5ae5e5059..b0407d303 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp @@ -292,12 +292,7 @@ enum SunbladeArchMageSpells struct npc_sunblade_arch_mage : public ScriptedAI { npc_sunblade_arch_mage(Creature* creature) : ScriptedAI(creature) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp index aa0653557..c5455dc4e 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp @@ -86,12 +86,7 @@ enum Actions struct boss_halazzi : public BossAI { boss_halazzi(Creature* creature) : BossAI(creature, DATA_HALAZZI) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp index 71811383b..e0a7967fc 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp @@ -233,12 +233,7 @@ static PlayerAbilityStruct PlayerAbility[13][3] = struct boss_hexlord_malacrass : public BossAI { boss_hexlord_malacrass(Creature* creature) : BossAI(creature, DATA_HEXLORD) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_janalai.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_janalai.cpp index d8c63ecb4..2ace89da2 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_janalai.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_janalai.cpp @@ -120,12 +120,7 @@ enum Misc struct boss_janalai : public BossAI { boss_janalai(Creature* creature) : BossAI(creature, DATA_JANALAI) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp index 7140a1ae8..1ddca06ed 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp @@ -115,11 +115,6 @@ struct boss_thekal : public BossAI ); context.Repeat(5s, 25s); }); - - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void JustDied(Unit* /*killer*/) override diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp index e706c472c..fa67c73e8 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp @@ -74,11 +74,6 @@ public: void InitializeAI() override { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - Reset(); } diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index 1101b999b..9555f853e 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -162,7 +162,7 @@ void AddSC_undercity(); void AddSC_western_plaguelands(); void AddSC_westfall(); //void AddSC_wetlands(); -void AddSC_burning_steppes(); +//void AddSC_burning_steppes(); // The name of this function should match: // void Add${NameOfDirectory}Scripts() @@ -314,5 +314,5 @@ void AddEasternKingdomsScripts() AddSC_western_plaguelands(); AddSC_westfall(); //AddSC_wetlands(); - AddSC_burning_steppes(); + //AddSC_burning_steppes(); } diff --git a/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp b/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp index 1979f0d7f..c4ea03272 100644 --- a/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp @@ -52,6 +52,7 @@ public: { npc_professor_phizzlethorpeAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp b/src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp deleted file mode 100644 index 308ac6608..000000000 --- a/src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of the AzerothCore 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 . - */ - -#include "CreatureScript.h" -#include "Player.h" -#include "ScriptedCreature.h" - -/*###### -## NPC 9836: Maredis Firestar -######*/ - -enum MaredisFirestar -{ - ITEM_LIBRAM_OF_RUMINATION = 11732, - ITEM_LIBRAM_OF_CONSTITUTION = 11733, - ITEM_LIBRAM_OF_TENACITY = 11734, - ITEM_LIBRAM_OF_RESILIENCE = 11736, - ITEM_LIBRAM_OF_VORACITY = 11737, - GOSSIP_LIBRAM_OF_RUMINATION = 2299, - GOSSIP_LIBRAM_OF_CONSTITUTION = 2300, - GOSSIP_LIBRAM_OF_TENACITY = 2301, - GOSSIP_LIBRAM_OF_RESILIENCE = 2302, - GOSSIP_LIBRAM_OF_VORACITY = 2303 -}; - -class npc_maredis_firestar : public CreatureScript -{ -public: - npc_maredis_firestar() : CreatureScript("npc_maredis_firestar") {} - - struct npc_maredis_firestarAI : public CreatureAI - { - npc_maredis_firestarAI(Creature* creature) : CreatureAI(creature) {} - - void sGossipHello(Player* player) override - { - // If player has 2 different librams on him he will only see top most one. - // Count is default 1. In bank is default false. - if (player->HasItemCount(ITEM_LIBRAM_OF_RUMINATION)) - { - player->PrepareGossipMenu(me, GOSSIP_LIBRAM_OF_RUMINATION); - player->SendPreparedGossip(me); - } - else if (player->HasItemCount(ITEM_LIBRAM_OF_CONSTITUTION)) - { - player->PrepareGossipMenu(me, GOSSIP_LIBRAM_OF_CONSTITUTION); - player->SendPreparedGossip(me); - } - else if (player->HasItemCount(ITEM_LIBRAM_OF_TENACITY)) - { - player->PrepareGossipMenu(me, GOSSIP_LIBRAM_OF_TENACITY); - player->SendPreparedGossip(me); - } - else if (player->HasItemCount(ITEM_LIBRAM_OF_RESILIENCE)) - { - player->PrepareGossipMenu(me, GOSSIP_LIBRAM_OF_RESILIENCE); - player->SendPreparedGossip(me); - } - else if (player->HasItemCount(ITEM_LIBRAM_OF_VORACITY)) - { - player->PrepareGossipMenu(me, GOSSIP_LIBRAM_OF_VORACITY); - player->SendPreparedGossip(me); - } - } - - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override - { - // All gossip menus only have one option. Conditions are handled in db. - player->PrepareQuestMenu(me->GetGUID()); - player->SendPreparedQuest(me->GetGUID()); - } - }; -}; - -void AddSC_burning_steppes() -{ - new npc_maredis_firestar(); -} diff --git a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp index 13d92bc96..365d7fe6b 100644 --- a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp @@ -45,6 +45,7 @@ struct npc_ranger_lilatha : public npc_escortAI { npc_ranger_lilatha(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp index 8d08ca387..08a111e59 100644 --- a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp @@ -137,6 +137,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp index e6c8fbeae..4d3072519 100644 --- a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp +++ b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp @@ -68,6 +68,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp index dcf642d0e..1147fdf04 100644 --- a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp +++ b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp @@ -55,6 +55,7 @@ public: { npc_deathstalker_erlandAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp b/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp index 2cfb08110..d06fd85d8 100644 --- a/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp +++ b/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp @@ -88,6 +88,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -318,6 +319,7 @@ public: uiPhase = 0; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index 747a452b3..eab33d378 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -1423,6 +1423,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -2820,6 +2821,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/EasternKingdoms/zone_westfall.cpp b/src/server/scripts/EasternKingdoms/zone_westfall.cpp index 7e2bbb70f..ab89105c8 100644 --- a/src/server/scripts/EasternKingdoms/zone_westfall.cpp +++ b/src/server/scripts/EasternKingdoms/zone_westfall.cpp @@ -81,6 +81,7 @@ public: textCounter = SAY_DS_DOWN_1; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp index 62bbb72f7..85749ea1c 100644 --- a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp @@ -55,6 +55,7 @@ public: IsFriendSummoned = false; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Events/winter_veil.cpp b/src/server/scripts/Events/winter_veil.cpp index 32c848550..ffb82280e 100644 --- a/src/server/scripts/Events/winter_veil.cpp +++ b/src/server/scripts/Events/winter_veil.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "CreatureScript.h" #include "GameEventMgr.h" #include "Player.h" #include "ScriptedCreature.h" diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp index 145495965..0ac933683 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp @@ -50,10 +50,6 @@ public: boss_anetheron(Creature* creature) : BossAI(creature, DATA_ANETHERON) { _recentlySpoken = false; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void JustEngagedWith(Unit * who) override diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp index f2f6c003d..9db55d36e 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp @@ -186,10 +186,6 @@ struct boss_archimonde : public BossAI { boss_archimonde(Creature* creature) : BossAI(creature, DATA_ARCHIMONDE) { - scheduler.SetValidator([&] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp index 63a1a3a6c..22024068e 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp @@ -47,10 +47,6 @@ public: boss_azgalor(Creature* creature) : BossAI(creature, DATA_AZGALOR) { _recentlySpoken = false; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void JustEngagedWith(Unit * who) override diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp index a5d03d912..889ccc178 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp @@ -48,12 +48,7 @@ struct boss_kazrogal : public BossAI { public: boss_kazrogal(Creature* creature) : BossAI(creature, DATA_KAZROGAL) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp index a2ce47ff3..2bdf44abf 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp @@ -50,10 +50,6 @@ public: boss_rage_winterchill(Creature* creature) : BossAI(creature, DATA_WINTERCHILL) { _recentlySpoken = false; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void JustEngagedWith(Unit* who) override diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal.cpp index 8bad06221..9b8743654 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal.cpp @@ -352,12 +352,7 @@ class spell_cannibalize_heal : public SpellScript struct npc_hyjal_ground_trash : public ScriptedAI { npc_hyjal_ground_trash(Creature* creature) : ScriptedAI(creature) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { @@ -574,12 +569,7 @@ struct npc_hyjal_ground_trash : public ScriptedAI struct npc_hyjal_gargoyle : public ScriptedAI { npc_hyjal_gargoyle(Creature* creature) : ScriptedAI(creature) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { @@ -648,12 +638,7 @@ struct npc_hyjal_gargoyle : public ScriptedAI struct npc_hyjal_frost_wyrm : public ScriptedAI { npc_hyjal_frost_wyrm(Creature* creature) : ScriptedAI(creature) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index 2df10021e..2ad76cf20 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -175,6 +175,18 @@ enum Misc ACTION_FORCE_CHANGE_LOCK = 2, POINT_CHRONOS = 1, + + // Stephanie Sindree crowd + NPC_STEPHANIE_SINDREE = 31019, + SAY_STEPHANIE_CROWD_1 = 4, + SAY_STEPHANIE_CROWD_2 = 5, + SAY_STEPHANIE_CROWD_3 = 6, + SAY_STEPHANIE_RESPONSE_1 = 0, + SAY_STEPHANIE_RESPONSE_2 = 1, + + // Brandon Eiredeck crowd + NPC_BRANDON_EIREDECK = 31023, + SAY_BRANDON_CROWD_AMBIENT = 3, }; enum Events @@ -515,6 +527,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 uiPointId) override { switch (uiPointId) @@ -1521,12 +1534,24 @@ public: pInstance = me->GetInstanceScript(); if (!pInstance || pInstance->GetData(DATA_ARTHAS_EVENT) < COS_PROGRESS_FINISHED_CITY_INTRO) allowTimer++; + + isStephanieCrowd = me->GetEntry() == NPC_CITY_MAN3 && me->GetDistance(2149.56f, 1339.46f, 132.531f) < 10.0f; + stephanieDialogueTimer = isStephanieCrowd ? urand(5000, 15000) : 0; + stephanieDialoguePhase = 0; + + isBrandonCrowd = me->GetDistance(2267.86f, 1144.93f, 138.403f) < 10.0f; + ambientTalkTimer = isBrandonCrowd ? urand(5000, 15000) : 0; } bool locked; uint32 changeTimer; InstanceScript* pInstance; uint32 allowTimer; + bool isStephanieCrowd; + uint32 stephanieDialogueTimer; + uint8 stephanieDialoguePhase; + bool isBrandonCrowd; + uint32 ambientTalkTimer; void Reset() override { @@ -1587,6 +1612,61 @@ public: { ScriptedAI::UpdateAI(diff); + if (isStephanieCrowd && stephanieDialogueTimer) + { + if (stephanieDialogueTimer <= diff) + { + Creature* stephanie = me->FindNearestCreature(NPC_STEPHANIE_SINDREE, 10.0f); + switch (stephanieDialoguePhase) + { + case 0: + Talk(SAY_STEPHANIE_CROWD_1); + stephanieDialogueTimer = 5000; + stephanieDialoguePhase = 1; + break; + case 1: + if (stephanie) + stephanie->AI()->Talk(SAY_STEPHANIE_RESPONSE_1); + stephanieDialogueTimer = 5000; + stephanieDialoguePhase = 2; + break; + case 2: + Talk(SAY_STEPHANIE_CROWD_2); + stephanieDialogueTimer = 5000; + stephanieDialoguePhase = 3; + break; + case 3: + if (stephanie) + stephanie->AI()->Talk(SAY_STEPHANIE_RESPONSE_2); + stephanieDialogueTimer = 8000; + stephanieDialoguePhase = 4; + break; + case 4: + Talk(SAY_STEPHANIE_CROWD_3); + stephanieDialogueTimer = urand(45000, 60000); + stephanieDialoguePhase = 0; + break; + } + } + else + stephanieDialogueTimer -= diff; + + return; + } + + if (isBrandonCrowd && ambientTalkTimer) + { + if (ambientTalkTimer <= diff) + { + Talk(SAY_BRANDON_CROWD_AMBIENT); + ambientTalkTimer = urand(15000, 25000); + } + else + ambientTalkTimer -= diff; + + return; + } + if (allowTimer) { allowTimer += diff; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/boss_captain_skarloc.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/boss_captain_skarloc.cpp index c75f00b1f..e3f9c5408 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/boss_captain_skarloc.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/boss_captain_skarloc.cpp @@ -53,12 +53,7 @@ const Position startPath[WAYPOINTS_COUNT] = struct boss_captain_skarloc : public BossAI { boss_captain_skarloc(Creature* creature) : BossAI(creature, DATA_CAPTAIN_SKARLOC), summons(me) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } SummonList summons; bool _spawnedAdds; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp index fc54b6215..0030f23bc 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp @@ -303,6 +303,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -1014,6 +1015,7 @@ public: Start(false); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (waypointId == 7) diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp index bcae88f33..8286e98e3 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp @@ -227,6 +227,7 @@ struct npc_general_andorov : public npc_escortAI events.ScheduleEvent(EVENT_STRIKE, 2s, 5s); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp index fc9fe8949..69adba859 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_skeram.cpp @@ -95,7 +95,7 @@ struct boss_skeram : public BossAI creature->CastSpell(creature, SPELL_BIRTH, true); creature->SetControlled(true, UNIT_STATE_ROOT); creature->SetReactState(REACT_PASSIVE); - creature->SetImmuneToAll(true); + creature->SetImmuneToAll(true, true); _copiesGUIDs.push_back(creature->GetGUID()); } @@ -211,7 +211,7 @@ struct boss_skeram : public BossAI _copiesGUIDs.clear(); DoCast(me, SPELL_SUMMON_IMAGES, true); me->SetReactState(REACT_PASSIVE); - me->SetImmuneToAll(true); + me->SetImmuneToAll(true, true); me->SetControlled(true, UNIT_STATE_ROOT); Talk(SAY_SPLIT); _hpct -= 25.0f; diff --git a/src/server/scripts/Kalimdor/boss_azuregos.cpp b/src/server/scripts/Kalimdor/boss_azuregos.cpp index e6b320789..99941d209 100644 --- a/src/server/scripts/Kalimdor/boss_azuregos.cpp +++ b/src/server/scripts/Kalimdor/boss_azuregos.cpp @@ -53,12 +53,7 @@ public: struct boss_azuregosAI : public ScriptedAI { boss_azuregosAI(Creature* creature) : ScriptedAI(creature) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Kalimdor/zone_ashenvale.cpp b/src/server/scripts/Kalimdor/zone_ashenvale.cpp index 5fee325ec..50b7dccef 100644 --- a/src/server/scripts/Kalimdor/zone_ashenvale.cpp +++ b/src/server/scripts/Kalimdor/zone_ashenvale.cpp @@ -126,6 +126,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp index 2316a2e33..9779fb315 100644 --- a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp @@ -246,6 +246,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_darkshore.cpp b/src/server/scripts/Kalimdor/zone_darkshore.cpp index c2a452f18..7b40eac34 100644 --- a/src/server/scripts/Kalimdor/zone_darkshore.cpp +++ b/src/server/scripts/Kalimdor/zone_darkshore.cpp @@ -330,6 +330,7 @@ public: // pSummoned->AI()->AttackStart(me); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp index aea34b3c7..649b4adea 100644 --- a/src/server/scripts/Kalimdor/zone_desolace.cpp +++ b/src/server/scripts/Kalimdor/zone_desolace.cpp @@ -257,6 +257,7 @@ public: me->SetFaction(faction); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { RelocateSummons(); diff --git a/src/server/scripts/Kalimdor/zone_felwood.cpp b/src/server/scripts/Kalimdor/zone_felwood.cpp index 93f33e72d..26d3cec1f 100644 --- a/src/server/scripts/Kalimdor/zone_felwood.cpp +++ b/src/server/scripts/Kalimdor/zone_felwood.cpp @@ -127,7 +127,7 @@ public: bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override { - if (player->IsGameMaster() || !player->IsAlive()) + if (!player->IsAlive()) return false; // Handle Call Ancients event start - The area trigger summons 3 ancients diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index d521c6693..4aee0c0ad 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -104,58 +104,6 @@ public: } }; -/*###### -## npc_great_bear_spirit -######*/ - -class npc_great_bear_spirit : public CreatureScript -{ -public: - npc_great_bear_spirit() : CreatureScript("npc_great_bear_spirit") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF: - AddGossipItemFor(player, 3881, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, 4721, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 1: - AddGossipItemFor(player, 3883, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, 4733, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - AddGossipItemFor(player, 3884, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - SendGossipMenuFor(player, 4734, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - SendGossipMenuFor(player, 4735, creature->GetGUID()); - if (player->GetQuestStatus(5929) == QUEST_STATUS_INCOMPLETE) - player->AreaExploredOrEventHappens(5929); - if (player->GetQuestStatus(5930) == QUEST_STATUS_INCOMPLETE) - player->AreaExploredOrEventHappens(5930); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - //ally or horde quest - if (player->GetQuestStatus(5929) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(5930) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, 3882, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - SendGossipMenuFor(player, 4719, creature->GetGUID()); - } - else - SendGossipMenuFor(player, 4718, creature->GetGUID()); - - return true; - } -}; - /*###### ## npc_silva_filnaveth ######*/ @@ -553,6 +501,7 @@ public: else if (EventOnWait) EventTimer -= diff; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { CurrWP = waypointId; @@ -713,7 +662,6 @@ public: void AddSC_moonglade() { new npc_bunthen_plainswind(); - new npc_great_bear_spirit(); new npc_silva_filnaveth(); new npc_clintar_spirit(); new npc_omen(); diff --git a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp index 36c6c56c2..a2f826416 100644 --- a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp +++ b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp @@ -19,7 +19,6 @@ #include "CreatureScript.h" #include "Player.h" #include "ScriptedCreature.h" -#include "ScriptedGossip.h" #include "TaskScheduler.h" #include "LFGMgr.h" @@ -142,8 +141,6 @@ enum ThrallWarchief : uint32 // What the Wind Carries (ID: 6566) QUEST_WHAT_THE_WIND_CARRIES = 6566, - GOSSIP_MENU_THRALL = 3664, - GOSSIP_RESPONSE_THRALL_FIRST = 5733, // Deathknight Starting Zone End QUEST_WARCHIEFS_BLESSING = 13189, @@ -157,45 +154,6 @@ class npc_thrall_warchief : public CreatureScript public: npc_thrall_warchief() : CreatureScript("npc_thrall_warchief") { } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - - uint32 DiscussionOrder = action - GOSSIP_ACTION_INFO_DEF; - - if (DiscussionOrder>= 1 && DiscussionOrder <= 6) - { - uint32 NextAction = GOSSIP_ACTION_INFO_DEF + DiscussionOrder + 1; - uint32 GossipResponse = GOSSIP_RESPONSE_THRALL_FIRST + DiscussionOrder - 1; - - AddGossipItemFor(player, GOSSIP_MENU_THRALL + DiscussionOrder, 0, GOSSIP_SENDER_MAIN, NextAction); - SendGossipMenuFor(player, GossipResponse, creature->GetGUID()); - } - else if (DiscussionOrder == 7) - { - CloseGossipMenuFor(player); - player->AreaExploredOrEventHappens(QUEST_WHAT_THE_WIND_CARRIES); - } - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - { - player->PrepareQuestMenu(creature->GetGUID()); - } - - if (player->GetQuestStatus(QUEST_WHAT_THE_WIND_CARRIES) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, GOSSIP_MENU_THRALL, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - } - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - return true; - } - bool OnQuestReward(Player* player, Creature* creature, Quest const* quest, uint32 /*item*/) override { switch (quest->GetQuestId()) @@ -232,6 +190,12 @@ public: ShockTimer = 8000; } + void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override + { + if (menuId == 3670) + player->AreaExploredOrEventHappens(QUEST_WHAT_THE_WIND_CARRIES); + } + void JustEngagedWith(Unit* /*who*/) override { } void JustDied(Unit* /*killer*/) override diff --git a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp index 3392248da..0d4bf1937 100644 --- a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp +++ b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp @@ -19,61 +19,6 @@ #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" - -/*###### -## npc_braug_dimspirit -######*/ - -#define GOSSIP_HBD1 "Ysera" -#define GOSSIP_HBD2 "Neltharion" -#define GOSSIP_HBD3 "Nozdormu" -#define GOSSIP_HBD4 "Alexstrasza" -#define GOSSIP_HBD5 "Malygos" - -class npc_braug_dimspirit : public CreatureScript -{ -public: - npc_braug_dimspirit() : CreatureScript("npc_braug_dimspirit") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - CloseGossipMenuFor(player); - creature->CastSpell(player, 6766, false); - } - if (action == GOSSIP_ACTION_INFO_DEF + 2) - { - CloseGossipMenuFor(player); - player->AreaExploredOrEventHappens(6627); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(6627) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HBD5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - SendGossipMenuFor(player, 5820, creature->GetGUID()); - } - else - SendGossipMenuFor(player, 5819, creature->GetGUID()); - - return true; - } -}; - /*###### ## npc_kaya_flathoof ######*/ @@ -102,6 +47,7 @@ public: { npc_kaya_flathoofAI(Creature* creature) : npc_escortAI(creature) {} + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -161,6 +107,5 @@ public: void AddSC_stonetalon_mountains() { - new npc_braug_dimspirit(); new npc_kaya_flathoof(); } diff --git a/src/server/scripts/Kalimdor/zone_tanaris.cpp b/src/server/scripts/Kalimdor/zone_tanaris.cpp index 602d1ea43..cd5d007b3 100644 --- a/src/server/scripts/Kalimdor/zone_tanaris.cpp +++ b/src/server/scripts/Kalimdor/zone_tanaris.cpp @@ -20,7 +20,6 @@ #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" #include "ScriptedFollowerAI.h" -#include "ScriptedGossip.h" /*###### ## npc_aquementas @@ -168,6 +167,7 @@ public: { npc_custodian_of_timeAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) @@ -293,95 +293,6 @@ public: return false; } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - player->CastSpell(player, 34891, true); //(Flight through Caverns) - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - { - player->PrepareQuestMenu(creature->GetGUID()); - } - - if (player->GetQuestStatus(10279) == QUEST_STATUS_INCOMPLETE || player->GetQuestRewardStatus(10279)) - { - AddGossipItemFor(player, 8072, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, 9978, creature->GetGUID()); - } - else - { - SendGossipMenuFor(player, 9977, creature->GetGUID()); - } - - return true; - } -}; - -/*###### -## npc_stone_watcher_of_norgannon -######*/ - -class npc_stone_watcher_of_norgannon : public CreatureScript -{ -public: - npc_stone_watcher_of_norgannon() : CreatureScript("npc_stone_watcher_of_norgannon") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF: - AddGossipItemFor(player, 57001, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, 1675, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+1: - AddGossipItemFor(player, 57002, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, 1676, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+2: - AddGossipItemFor(player, 57003, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - SendGossipMenuFor(player, 1677, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+3: - AddGossipItemFor(player, 57004, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); - SendGossipMenuFor(player, 1678, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+4: - AddGossipItemFor(player, 57005, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - SendGossipMenuFor(player, 1679, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+5: - CloseGossipMenuFor(player); - player->AreaExploredOrEventHappens(2954); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - { - player->PrepareQuestMenu(creature->GetGUID()); - } - - if (player->GetQuestStatus(2954) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, 57000, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - } - - SendGossipMenuFor(player, 1674, creature->GetGUID()); - - return true; - } }; /*#### @@ -551,6 +462,5 @@ void AddSC_tanaris() new npc_aquementas(); new npc_custodian_of_time(); new npc_steward_of_time(); - new npc_stone_watcher_of_norgannon(); new npc_tooga(); } diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index fa4eabc52..8fe236fe5 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -74,6 +74,7 @@ public: void Reset() override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -494,6 +495,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp index ac747b38b..9c60689c5 100644 --- a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp +++ b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp @@ -85,6 +85,7 @@ public: void Reset() override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -170,6 +171,7 @@ public: void Reset() override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp b/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp index 885ddac1f..ff4845c7e 100644 --- a/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp +++ b/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp @@ -69,6 +69,7 @@ public: uint32 DemoralizingShoutTimer; + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (Player* player = GetPlayerForEscort()) diff --git a/src/server/scripts/Kalimdor/zone_winterspring.cpp b/src/server/scripts/Kalimdor/zone_winterspring.cpp index 2130ce14e..cf9930b45 100644 --- a/src/server/scripts/Kalimdor/zone_winterspring.cpp +++ b/src/server/scripts/Kalimdor/zone_winterspring.cpp @@ -20,40 +20,6 @@ #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" - -/*###### -## npc_rivern_frostwind -######*/ - -class npc_rivern_frostwind : public CreatureScript -{ -public: - npc_rivern_frostwind() : CreatureScript("npc_rivern_frostwind") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (creature->IsVendor() && player->GetReputationRank(589) == REP_EXALTED) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - enum Says { // Escort texts @@ -383,6 +349,7 @@ public: StartNextDialogueText(SAY_PRIESTESS_ALTAR_3); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 pointId) override { switch (pointId) @@ -607,7 +574,6 @@ public: void AddSC_winterspring() { - new npc_rivern_frostwind(); new npc_ranshalla(); new go_elune_fire(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp index 2e0eac307..be33fad5a 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp @@ -60,11 +60,6 @@ public: _initTalk = false; _canTalk = true; _minionInCombat = false; - - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp index 89adac195..041d68360 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp @@ -172,12 +172,7 @@ private: struct boss_taldaram : public BossAI { boss_taldaram(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM), vanishDamage(0) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void InitializeAI() override { diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp index 962007d02..d85f19f79 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp @@ -635,7 +635,7 @@ private: { Creature* tsunami = me->SummonCreature(NPC_FLAME_TSUNAMI, 3211.0f, FlameTsunamiLeftOffsets[i], 57.083332f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 13500); - if (((i - 2) % 3 == 0) && tsunami) // If center of wave + if (((i - 1) % 3 == 0) && tsunami) // If center of wave tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_VISUAL, true); } @@ -648,7 +648,7 @@ private: { Creature* tsunami = me->SummonCreature(NPC_FLAME_TSUNAMI, 3286.0f, FlameTsunamiRightOffsets[i], 57.083332f, 3.14f, TEMPSUMMON_TIMED_DESPAWN, 13500); - if (((i - 2) % 3 == 0) && tsunami) // If center of wave + if (((i - 1) % 3 == 0) && tsunami) // If center of wave tsunami->CastSpell(tsunami, SPELL_FLAME_TSUNAMI_VISUAL, true); } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp index fb6b32d03..faf314e6f 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp @@ -233,6 +233,7 @@ public: zarithrian->AI()->JustSummoned(me); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (waypointId == MAX_PATH_FLAMECALLER_WAYPOINTS) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp index 8d1bc1864..4f8d0fcea 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp @@ -519,6 +519,7 @@ public: bCheck = false; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 uiPoint) override { if (uiPoint == 1) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp index 99d69be84..6b3ac0d9e 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp @@ -340,6 +340,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 i) override { if (i == 12) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp index 374f1d2b5..d1cd70bd0 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp @@ -592,6 +592,7 @@ public: void EnterEvadeMode(EvadeReason /*why*/) override {} + using CreatureAI::WaypointReached; void WaypointReached(uint32 i) override { if (!pInstance) diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp index ef62d41ad..9866eb162 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp @@ -32,7 +32,7 @@ enum Yells SAY_CORRUPT_SOUL = 4, }; -enum eSpells +enum Spells { SPELL_SOULSTORM_CHANNEL_OOC = 69008, @@ -51,7 +51,7 @@ enum eSpells SPELL_SOULSTORM = 68872, }; -enum eEvents +enum Events { EVENT_SPELL_SHADOW_BOLT = 1, EVENT_SPELL_FEAR, @@ -60,207 +60,171 @@ enum eEvents EVENT_START_SOULSTORM, }; -class boss_bronjahm : public CreatureScript +struct boss_bronjahm : public BossAI { -public: - boss_bronjahm() : CreatureScript("boss_bronjahm") { } + boss_bronjahm(Creature* creature) : BossAI(creature, DATA_BRONJAHM) { } - struct boss_bronjahmAI : public ScriptedAI + void JustReachedHome() override { - boss_bronjahmAI(Creature* creature) : ScriptedAI(creature), summons(me) - { - pInstance = creature->GetInstanceScript(); - } + BossAI::JustReachedHome(); + DoCastSelf(SPELL_SOULSTORM_CHANNEL_OOC, true); + } - InstanceScript* pInstance; - EventMap events; - SummonList summons; - - void JustReachedHome() override - { - me->CastSpell(me, SPELL_SOULSTORM_CHANNEL_OOC, true); - } - - void Reset() override - { - me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); - me->CastSpell(me, SPELL_SOULSTORM_CHANNEL_OOC, true); - events.Reset(); - summons.DespawnAll(); - if (pInstance) - pInstance->SetData(DATA_BRONJAHM, NOT_STARTED); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - me->RemoveAurasDueToSpell(SPELL_SOULSTORM_CHANNEL_OOC); - - DoZoneInCombat(); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_SHADOW_BOLT, 2s); - events.RescheduleEvent(EVENT_SPELL_MAGICS_BANE, 5s, 10s); - events.RescheduleEvent(EVENT_SPELL_CORRUPT_SOUL, 14s, 20s); - - if (pInstance) - pInstance->SetData(DATA_BRONJAHM, IN_PROGRESS); - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (!me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE) && me->HealthBelowPctDamaged(35, damage)) - { - me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - me->CastSpell(me, SPELL_TELEPORT, false); - events.CancelEvent(EVENT_SPELL_CORRUPT_SOUL); - events.DelayEvents(6s); - events.RescheduleEvent(EVENT_SPELL_FEAR, 8s, 14s); - } - } - - void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override - { - if (spell->Id == SPELL_TELEPORT) - { - me->CastSpell(me, SPELL_TELEPORT_VISUAL, true); - events.RescheduleEvent(EVENT_START_SOULSTORM, 1ms); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - if (me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) - if (me->isAttackReady()) - me->SetFacingToObject(me->GetVictim()); - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_SHADOW_BOLT: - if (!me->IsWithinMeleeRange(me->GetVictim())) - me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false); - events.Repeat(2s); - break; - case EVENT_SPELL_FEAR: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true)) - me->CastCustomSpell(SPELL_FEAR, SPELLVALUE_MAX_TARGETS, 1, target, false); - events.Repeat(8s, 12s); - break; - case EVENT_SPELL_MAGICS_BANE: - me->CastSpell(me->GetVictim(), SPELL_MAGICS_BANE, false); - events.Repeat(10s, 15s); - break; - case EVENT_SPELL_CORRUPT_SOUL: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - { - Talk(SAY_CORRUPT_SOUL); - me->CastSpell(target, SPELL_CORRUPT_SOUL, false); - } - events.Repeat(20s, 25s); - break; - case EVENT_START_SOULSTORM: - Talk(SAY_SOUL_STORM); - me->CastSpell(me, SPELL_SOULSTORM, false); - me->CastSpell(me, SPELL_TELEPORT_VISUAL, true); - me->CastSpell(me, SPELL_SOULSTORM_VISUAL, true); - - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(DATA_BRONJAHM, DONE); - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - Talk(SAY_SLAY); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - summon->SetReactState(REACT_PASSIVE); - } - - void EnterEvadeMode(EvadeReason why) override - { - me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); - ScriptedAI::EnterEvadeMode(why); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetForgeOfSoulsAI(creature); + BossAI::Reset(); + me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); + DoCastSelf(SPELL_SOULSTORM_CHANNEL_OOC, true); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + me->RemoveAurasDueToSpell(SPELL_SOULSTORM_CHANNEL_OOC); + + events.RescheduleEvent(EVENT_SPELL_SHADOW_BOLT, 2s); + events.RescheduleEvent(EVENT_SPELL_MAGICS_BANE, 5s, 10s); + events.RescheduleEvent(EVENT_SPELL_CORRUPT_SOUL, 14s, 20s); + } + + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (!me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE) && me->HealthBelowPctDamaged(35, damage)) + { + me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveIdle(); + DoCastSelf(SPELL_TELEPORT); + events.CancelEvent(EVENT_SPELL_CORRUPT_SOUL); + events.DelayEvents(6s); + events.RescheduleEvent(EVENT_SPELL_FEAR, 8s, 14s); + } + } + + void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TELEPORT) + { + DoCastSelf(SPELL_TELEPORT_VISUAL, true); + events.RescheduleEvent(EVENT_START_SOULSTORM, 1ms); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) + if (me->isAttackReady()) + me->SetFacingToObject(me->GetVictim()); + + switch (events.ExecuteEvent()) + { + case EVENT_SPELL_SHADOW_BOLT: + if (!me->IsWithinMeleeRange(me->GetVictim())) + DoCastVictim(SPELL_SHADOW_BOLT); + events.Repeat(2s); + break; + case EVENT_SPELL_FEAR: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true)) + me->CastCustomSpell(SPELL_FEAR, SPELLVALUE_MAX_TARGETS, 1, target); + events.Repeat(8s, 12s); + break; + case EVENT_SPELL_MAGICS_BANE: + DoCastVictim(SPELL_MAGICS_BANE); + events.Repeat(10s, 15s); + break; + case EVENT_SPELL_CORRUPT_SOUL: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) + { + Talk(SAY_CORRUPT_SOUL); + DoCast(target, SPELL_CORRUPT_SOUL); + } + events.Repeat(20s, 25s); + break; + case EVENT_START_SOULSTORM: + Talk(SAY_SOUL_STORM); + DoCastSelf(SPELL_SOULSTORM); + DoCastSelf(SPELL_TELEPORT_VISUAL, true); + DoCastSelf(SPELL_SOULSTORM_VISUAL, true); + break; + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + Talk(SAY_SLAY); + } + + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); + summon->SetReactState(REACT_PASSIVE); + } + + void EnterEvadeMode(EvadeReason why) override + { + me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); + BossAI::EnterEvadeMode(why); } }; -class npc_fos_corrupted_soul_fragment : public CreatureScript +struct npc_fos_corrupted_soul_fragment : public NullCreatureAI { -public: - npc_fos_corrupted_soul_fragment() : CreatureScript("npc_fos_corrupted_soul_fragment") { } - - struct npc_fos_corrupted_soul_fragmentAI : public NullCreatureAI + npc_fos_corrupted_soul_fragment(Creature* creature) : NullCreatureAI(creature) { - npc_fos_corrupted_soul_fragmentAI(Creature* creature) : NullCreatureAI(creature) - { - pInstance = me->GetInstanceScript(); - } + Instance = me->GetInstanceScript(); + } - uint32 timer; - InstanceScript* pInstance; + uint32 Timer = 0; + InstanceScript* Instance = nullptr; - void Reset() override - { - timer = 0; - } - - void UpdateAI(uint32 diff) override - { - if (pInstance) - if (Creature* b = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_BRONJAHM))) - { - if (me->GetExactDist2d(b) <= 2.0f) - { - me->GetMotionMaster()->MoveIdle(); - me->CastSpell(b, SPELL_CONSUME_SOUL, true); - me->DespawnOrUnsummon(1ms); - return; - } - - if (timer <= diff) - { - if (!me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) - me->GetMotionMaster()->MovePoint(0, *b); - timer = 1000; - } - else - timer -= diff; - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetForgeOfSoulsAI(creature); + Timer = 0; + } + + void UpdateAI(uint32 diff) override + { + if (!Instance) + return; + + Creature* bronjahm = Instance->GetCreature(DATA_BRONJAHM); + if (!bronjahm) + return; + + if (me->GetExactDist2d(bronjahm) <= 2.0f) + { + me->GetMotionMaster()->MoveIdle(); + me->CastSpell(bronjahm, SPELL_CONSUME_SOUL, true); + me->DespawnOrUnsummon(1ms); + return; + } + + if (Timer <= diff) + { + if (!me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) + me->GetMotionMaster()->MovePoint(0, *bronjahm); + Timer = 1000; + } + else + Timer -= diff; } }; @@ -275,7 +239,7 @@ class spell_bronjahm_magic_bane : public SpellScript if (Unit* caster = GetCaster()) { - const int32 maxDamage = caster->GetMap()->GetSpawnMode() == 1 ? 15000 : 10000; + int32 const maxDamage = caster->GetMap()->GetSpawnMode() == 1 ? 15000 : 10000; int32 newDamage = GetHitDamage(); newDamage += GetHitUnit()->GetMaxPower(POWER_MANA) / 2; newDamage = std::min(maxDamage, newDamage); @@ -365,9 +329,8 @@ class spell_bronjahm_soulstorm_targeting : public SpellScript void AddSC_boss_bronjahm() { - new boss_bronjahm(); - new npc_fos_corrupted_soul_fragment(); - + RegisterForgeOfSoulsCreatureAI(boss_bronjahm); + RegisterForgeOfSoulsCreatureAI(npc_fos_corrupted_soul_fragment); RegisterSpellScript(spell_bronjahm_magic_bane); RegisterSpellScript(spell_bronjahm_soulstorm_channel_ooc_aura); RegisterSpellScript(spell_bronjahm_soulstorm_visual_aura); diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp index 102b87ed6..fec69d495 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp @@ -24,7 +24,7 @@ #include "SpellScriptLoader.h" #include "forge_of_souls.h" -enum eTexts +enum Texts { SAY_FACE_AGGRO = 0, SAY_FACE_ANGER_SLAY = 1, @@ -38,7 +38,7 @@ enum eTexts SAY_FACE_WAILING_SOUL = 9, }; -enum eSpells +enum Spells { SPELL_PHANTOM_BLAST = 68982, SPELL_PHANTOM_BLAST_H = 70322, @@ -57,7 +57,7 @@ enum eSpells SPELL_WAILING_SOULS_DMG_H = 70324, // 100yd, 104.0 }; -enum eEvents +enum Events { EVENT_SPELL_PHANTOM_BLAST = 1, EVENT_SPELL_MIRRORED_SOUL, @@ -66,245 +66,214 @@ enum eEvents EVENT_SPELL_WAILING_SOULS, }; -enum eDisplayIds +enum ModelIds { - DISPLAY_ANGER = 30148, - DISPLAY_SORROW = 30149, - DISPLAY_DESIRE = 30150, + MODEL_ANGER = 30148, + MODEL_SORROW = 30149, + MODEL_DESIRE = 30150, }; -enum eMisc +enum Misc { NPC_CRUCIBLE_OF_SOULS = 37094, + NPC_UNLEASHED_SOUL = 36595, QUEST_TEMPERING_THE_BLADE_A = 24476, QUEST_TEMPERING_THE_BLADE_H = 24560, }; -class boss_devourer_of_souls : public CreatureScript +struct boss_devourer_of_souls : public BossAI { -public: - boss_devourer_of_souls() : CreatureScript("boss_devourer_of_souls") { } + boss_devourer_of_souls(Creature* creature) : BossAI(creature, DATA_DEVOURER) { } - struct boss_devourer_of_soulsAI : public ScriptedAI + bool AchievementCompleted = true; + + void Reset() override { - boss_devourer_of_soulsAI(Creature* creature) : ScriptedAI(creature), summons(me) - { - pInstance = creature->GetInstanceScript(); - } + BossAI::Reset(); + AchievementCompleted = true; + me->SetControlled(false, UNIT_STATE_ROOT); + me->DisableRotate(false); + me->SetReactState(REACT_AGGRESSIVE); + } - InstanceScript* pInstance; - EventMap events; - SummonList summons; - bool bAchiev; - - void Reset() override - { - bAchiev = true; - me->SetControlled(false, UNIT_STATE_ROOT); - me->DisableRotate(false); - me->SetReactState(REACT_AGGRESSIVE); - events.Reset(); - summons.DespawnAll(); - if (pInstance) - pInstance->SetData(DATA_DEVOURER, NOT_STARTED); - } - - uint32 GetData(uint32 id) const override - { - if (id == 1) - return bAchiev; - - return 0; - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_FACE_AGGRO); - DoZoneInCombat(); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_PHANTOM_BLAST, 5s); - events.RescheduleEvent(EVENT_SPELL_MIRRORED_SOUL, 9s); - events.RescheduleEvent(EVENT_SPELL_WELL_OF_SOULS, 6s, 8s); - events.RescheduleEvent(EVENT_SPELL_UNLEASHED_SOULS, 18s, 20s); - events.RescheduleEvent(EVENT_SPELL_WAILING_SOULS, 65s); - - if (pInstance) - pInstance->SetData(DATA_DEVOURER, IN_PROGRESS); - - // Suport for Quest Tempering the Blade - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) - { - Player* player = itr->GetSource(); - if ((player->GetTeamId() == TEAM_ALLIANCE && player->GetQuestStatus(QUEST_TEMPERING_THE_BLADE_A) == QUEST_STATUS_INCOMPLETE) || - (player->GetTeamId() == TEAM_HORDE && player->GetQuestStatus(QUEST_TEMPERING_THE_BLADE_H) == QUEST_STATUS_INCOMPLETE)) - { - if (!me->FindNearestCreature(NPC_CRUCIBLE_OF_SOULS, 100.0f)) - me->SummonCreature(NPC_CRUCIBLE_OF_SOULS, 5672.29f, 2520.69f, 713.44f, 0.96f); - } - } - } - - void SpellHitTarget(Unit* target, SpellInfo const* spell) override - { - if (spell->Id == SPELL_PHANTOM_BLAST_H) - bAchiev = false; - else if (spell->Id == SPELL_WAILING_SOULS_TARGETING) - { - me->SetOrientation(me->GetAngle(target)); - me->SetControlled(true, UNIT_STATE_ROOT); - me->DisableRotate(true); - me->SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty); - me->SetReactState(REACT_PASSIVE); - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveIdle(); - me->StopMovingOnCurrentPos(); - - me->SetFacingToObject(target); - me->SendMovementFlagUpdate(); - me->CastSpell(me, SPELL_WAILING_SOULS, false); - } - } - - bool CanAIAttack(Unit const* target) const override { return target->GetPositionZ() > 706.5f; } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (Spell* s = me->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) - if (s->m_spellInfo->Id == SPELL_MIRRORED_SOUL) - { - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_PHANTOM_BLAST: - me->CastSpell(me->GetVictim(), SPELL_PHANTOM_BLAST, false); - events.Repeat(5s); - break; - default: - events.Repeat(1s); - break; - } - - if (!me->GetCurrentSpell(CURRENT_GENERIC_SPELL)) - { - me->ClearUnitState(UNIT_STATE_CASTING); - DoMeleeAttackIfReady(); - me->AddUnitState(UNIT_STATE_CASTING); - } - - return; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_PHANTOM_BLAST: - me->CastSpell(me->GetVictim(), SPELL_PHANTOM_BLAST, false); - events.Repeat(5s); - break; - case EVENT_SPELL_MIRRORED_SOUL: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) - { - me->CastSpell(target, SPELL_MIRRORED_SOUL, false); - me->setAttackTimer(BASE_ATTACK, 2500); - Talk(EMOTE_MIRRORED_SOUL); - } - events.Repeat(20s, 30s); - break; - case EVENT_SPELL_WELL_OF_SOULS: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true)) - me->CastSpell(target, SPELL_WELL_OF_SOULS, false); - events.Repeat(25s, 30s); - events.DelayEventsToMax(4s, 0); - break; - case EVENT_SPELL_UNLEASHED_SOULS: - me->CastSpell(me, SPELL_UNLEASHED_SOULS, false); - Talk(SAY_FACE_UNLEASH_SOUL); - Talk(EMOTE_UNLEASH_SOUL); - events.Repeat(30s, 40s); - events.DelayEventsToMax(5s, 0); - me->setAttackTimer(BASE_ATTACK, 5500); - break; - case EVENT_SPELL_WAILING_SOULS: - Talk(SAY_FACE_WAILING_SOUL); - Talk(EMOTE_WAILING_SOUL); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) - me->CastCustomSpell(SPELL_WAILING_SOULS_TARGETING, SPELLVALUE_MAX_TARGETS, 1, target, false); - events.Repeat(80s); - events.DelayEventsToMax(20s, 0); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_FACE_DEATH); - summons.DespawnAll(); - if (pInstance) - pInstance->SetData(DATA_DEVOURER, DONE); - } - - void KilledUnit(Unit* victim) override - { - if (!victim->IsPlayer()) - return; - - int32 textId = 0; - switch (me->GetDisplayId()) - { - case DISPLAY_ANGER: - textId = SAY_FACE_ANGER_SLAY; - break; - case DISPLAY_SORROW: - textId = SAY_FACE_SORROW_SLAY; - break; - case DISPLAY_DESIRE: - textId = SAY_FACE_DESIRE_SLAY; - break; - default: - break; - } - - if (textId) - Talk(textId); - } - - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() != NPC_CRUCIBLE_OF_SOULS) - summons.Summon(summon); - - if (summon->GetEntry() == 36595) - if (Player* plr = summon->SelectNearestPlayer(100.0f)) - { - summon->AddThreat(plr, 100000.0f); - summon->AI()->AttackStart(plr); - } - } - - void EnterEvadeMode(EvadeReason why) override - { - me->SetControlled(false, UNIT_STATE_ROOT); - me->DisableRotate(false); - ScriptedAI::EnterEvadeMode(why); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + uint32 GetData(uint32 id) const override { - return GetForgeOfSoulsAI(creature); + if (id == 1) + return AchievementCompleted; + + return 0; + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_FACE_AGGRO); + events.RescheduleEvent(EVENT_SPELL_PHANTOM_BLAST, 5s); + events.RescheduleEvent(EVENT_SPELL_MIRRORED_SOUL, 9s); + events.RescheduleEvent(EVENT_SPELL_WELL_OF_SOULS, 6s, 8s); + events.RescheduleEvent(EVENT_SPELL_UNLEASHED_SOULS, 18s, 20s); + events.RescheduleEvent(EVENT_SPELL_WAILING_SOULS, 65s); + + // Support for Quest Tempering the Blade + me->GetMap()->DoForAllPlayers([this](Player* player) + { + if (me->FindNearestCreature(NPC_CRUCIBLE_OF_SOULS, 100.0f)) + return; + + uint32 questId = player->GetTeamId() == TEAM_ALLIANCE ? QUEST_TEMPERING_THE_BLADE_A : QUEST_TEMPERING_THE_BLADE_H; + if (player->GetQuestStatus(questId) == QUEST_STATUS_INCOMPLETE) + me->SummonCreature(NPC_CRUCIBLE_OF_SOULS, 5672.29f, 2520.69f, 713.44f, 0.96f); + }); + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (spell->Id == SPELL_PHANTOM_BLAST_H) + AchievementCompleted = false; + else if (spell->Id == SPELL_WAILING_SOULS_TARGETING) + { + me->SetOrientation(me->GetAngle(target)); + me->SetControlled(true, UNIT_STATE_ROOT); + me->DisableRotate(true); + me->SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveIdle(); + me->StopMovingOnCurrentPos(); + + me->SetFacingToObject(target); + me->SendMovementFlagUpdate(); + DoCastSelf(SPELL_WAILING_SOULS); + } + } + + bool CanAIAttack(Unit const* target) const override { return target->GetPositionZ() > 706.5f; } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (Spell* s = me->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + if (s->m_spellInfo->Id == SPELL_MIRRORED_SOUL) + { + switch (events.ExecuteEvent()) + { + case EVENT_SPELL_PHANTOM_BLAST: + DoCastVictim(SPELL_PHANTOM_BLAST); + events.Repeat(5s); + break; + default: + events.Repeat(1s); + break; + } + + if (!me->GetCurrentSpell(CURRENT_GENERIC_SPELL)) + { + me->ClearUnitState(UNIT_STATE_CASTING); + DoMeleeAttackIfReady(); + me->AddUnitState(UNIT_STATE_CASTING); + } + + return; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_SPELL_PHANTOM_BLAST: + DoCastVictim(SPELL_PHANTOM_BLAST); + events.Repeat(5s); + break; + case EVENT_SPELL_MIRRORED_SOUL: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) + { + DoCast(target, SPELL_MIRRORED_SOUL); + me->setAttackTimer(BASE_ATTACK, 2500); + Talk(EMOTE_MIRRORED_SOUL); + } + events.Repeat(20s, 30s); + break; + case EVENT_SPELL_WELL_OF_SOULS: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true)) + DoCast(target, SPELL_WELL_OF_SOULS); + events.Repeat(25s, 30s); + events.DelayEventsToMax(4s, 0); + break; + case EVENT_SPELL_UNLEASHED_SOULS: + DoCastSelf(SPELL_UNLEASHED_SOULS); + Talk(SAY_FACE_UNLEASH_SOUL); + Talk(EMOTE_UNLEASH_SOUL); + events.Repeat(30s, 40s); + events.DelayEventsToMax(5s, 0); + me->setAttackTimer(BASE_ATTACK, 5500); + break; + case EVENT_SPELL_WAILING_SOULS: + Talk(SAY_FACE_WAILING_SOUL); + Talk(EMOTE_WAILING_SOUL); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) + me->CastCustomSpell(SPELL_WAILING_SOULS_TARGETING, SPELLVALUE_MAX_TARGETS, 1, target); + events.Repeat(80s); + events.DelayEventsToMax(20s, 0); + break; + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_FACE_DEATH); + } + + void KilledUnit(Unit* victim) override + { + if (!victim->IsPlayer()) + return; + + uint8 textId = 0; + switch (me->GetDisplayId()) + { + case MODEL_ANGER: + textId = SAY_FACE_ANGER_SLAY; + break; + case MODEL_SORROW: + textId = SAY_FACE_SORROW_SLAY; + break; + case MODEL_DESIRE: + textId = SAY_FACE_DESIRE_SLAY; + break; + default: + break; + } + + if (textId) + Talk(textId); + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() != NPC_CRUCIBLE_OF_SOULS) + BossAI::JustSummoned(summon); + + if (summon->GetEntry() == NPC_UNLEASHED_SOUL) + if (Player* player = summon->SelectNearestPlayer(100.0f)) + { + summon->AddThreat(player, 100000.0f); + summon->AI()->AttackStart(player); + } + } + + void EnterEvadeMode(EvadeReason why) override + { + me->SetControlled(false, UNIT_STATE_ROOT); + me->DisableRotate(false); + BossAI::EnterEvadeMode(why); } }; @@ -317,7 +286,7 @@ class spell_wailing_souls_periodic_aura : public AuraScript return ValidateSpellInfo({ SPELL_WAILING_SOULS_DMG_N }); } - int8 dir; + int8 dir = 0; bool Load() override { @@ -328,31 +297,30 @@ class spell_wailing_souls_periodic_aura : public AuraScript void HandlePeriodicTick(AuraEffect const* aurEff) { PreventDefaultAction(); - if (Unit* t = GetTarget()) + if (Unit* target = GetTarget()) { if (aurEff->GetTickNumber() < 30) { - // spinning, casting, etc. float diff = (2 * M_PI) / (4 * 30); - float new_o = t->GetOrientation() + diff * dir; - if (new_o >= 2 * M_PI) - new_o -= 2 * M_PI; - else if (new_o < 0) - new_o += 2 * M_PI; - t->UpdateOrientation(new_o); - t->SetFacingTo(new_o); - t->CastSpell(t, SPELL_WAILING_SOULS_DMG_N, true); + float newOrientation = target->GetOrientation() + diff * dir; + if (newOrientation >= 2 * M_PI) + newOrientation -= 2 * M_PI; + else if (newOrientation < 0) + newOrientation += 2 * M_PI; + target->UpdateOrientation(newOrientation); + target->SetFacingTo(newOrientation); + target->CastSpell(target, SPELL_WAILING_SOULS_DMG_N, true); } else if (aurEff->GetTickNumber() == 33) { - t->SetControlled(false, UNIT_STATE_ROOT); - t->DisableRotate(false); - if (t->IsCreature()) - t->ToCreature()->SetReactState(REACT_AGGRESSIVE); - if (t->GetVictim()) + target->SetControlled(false, UNIT_STATE_ROOT); + target->DisableRotate(false); + if (target->IsCreature()) + target->ToCreature()->SetReactState(REACT_AGGRESSIVE); + if (target->GetVictim()) { - t->SetGuidValue(UNIT_FIELD_TARGET, t->GetVictim()->GetGUID()); - t->GetMotionMaster()->MoveChase(t->GetVictim()); + target->SetGuidValue(UNIT_FIELD_TARGET, target->GetVictim()->GetGUID()); + target->GetMotionMaster()->MoveChase(target->GetVictim()); } } else if (aurEff->GetTickNumber() >= 34) @@ -368,6 +336,6 @@ class spell_wailing_souls_periodic_aura : public AuraScript void AddSC_boss_devourer_of_souls() { - new boss_devourer_of_souls(); + RegisterForgeOfSoulsCreatureAI(boss_devourer_of_souls); RegisterSpellScript(spell_wailing_souls_periodic_aura); } diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h index e8ec81c40..117b04719 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/forge_of_souls.h @@ -30,6 +30,11 @@ enum Data DATA_BRONJAHM, DATA_DEVOURER, MAX_ENCOUNTER, + + DATA_LEADER_FIRST, + DATA_LEADER_SECOND, + DATA_GUARD_FIRST, + DATA_GUARD_SECOND, }; enum Creatures @@ -99,4 +104,6 @@ inline AI* GetForgeOfSoulsAI(T* obj) return GetInstanceAI(obj, ForgeOfSoulsScriptName); } +#define RegisterForgeOfSoulsCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetForgeOfSoulsAI) + #endif diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp index 4646d90ae..e77fcda11 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp @@ -21,12 +21,29 @@ #include "forge_of_souls.h" #include "Group.h" +enum Misc +{ + NPC_CORRUPTED_SOUL_FRAGMENT = 36535, + + ACHIEV_CRITERIA_SOUL_POWER = 12752, + ACHIEV_CRITERIA_THREE_FACED = 12976, + + SOUL_POWER_FRAGMENT_COUNT = 4, +}; + BossBoundaryData const boundaries = { { DATA_BRONJAHM, new CircleBoundary(Position(5297.3f, 2506.45f), 100.96) }, { DATA_DEVOURER, new ParallelogramBoundary(Position(5663.56f, 2570.53f), Position(5724.39f, 2520.45f), Position(5570.36f, 2461.42f)) } }; +static ObjectData const creatureData[] = +{ + { NPC_BRONJAHM, DATA_BRONJAHM }, + { NPC_DEVOURER, DATA_DEVOURER }, + { 0, 0 } +}; + class instance_forge_of_souls : public InstanceMapScript { public: @@ -42,38 +59,22 @@ public: instance_forge_of_souls_InstanceScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + SetBossNumber(MAX_ENCOUNTER); + LoadObjectData(creatureData, nullptr); LoadBossBoundaries(boundaries); } - uint32 m_auiEncounter[MAX_ENCOUNTER]; - std::string str_data; - ObjectGuid NPC_BronjahmGUID; - ObjectGuid NPC_DevourerGUID; - - ObjectGuid NPC_LeaderFirstGUID; - ObjectGuid NPC_LeaderSecondGUID; - ObjectGuid NPC_GuardFirstGUID; - ObjectGuid NPC_GuardSecondGUID; - - void Initialize() override - { - memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); - } - - bool IsEncounterInProgress() const override - { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) return true; - - return false; - } + ObjectGuid LeaderFirstGUID; + ObjectGuid LeaderSecondGUID; + ObjectGuid GuardFirstGUID; + ObjectGuid GuardSecondGUID; void OnPlayerEnter(Player* player) override { InstanceScript::OnPlayerEnter(player); // this will happen only after crash and loading the instance from db - if (m_auiEncounter[0] == DONE && m_auiEncounter[1] == DONE && (!NPC_LeaderSecondGUID || !instance->GetCreature(NPC_LeaderSecondGUID))) + if (AllBossesDone() && (!LeaderSecondGUID || !instance->GetCreature(LeaderSecondGUID))) { Position pos = {5658.15f, 2502.564f, 708.83f, 0.885207f}; instance->SummonCreature(NPC_SYLVANAS_PART2, pos); @@ -82,44 +83,40 @@ public: void OnCreatureCreate(Creature* creature) override { + InstanceScript::OnCreatureCreate(creature); + switch (creature->GetEntry()) { - case NPC_BRONJAHM: - NPC_BronjahmGUID = creature->GetGUID(); - break; - case NPC_DEVOURER: - NPC_DevourerGUID = creature->GetGUID(); - break; case NPC_SYLVANAS_PART1: if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_JAINA_PART1); - NPC_LeaderFirstGUID = creature->GetGUID(); + LeaderFirstGUID = creature->GetGUID(); - if (m_auiEncounter[0] == DONE && m_auiEncounter[1] == DONE) + if (AllBossesDone()) creature->SetVisible(false); break; case NPC_SYLVANAS_PART2: if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_JAINA_PART2); - NPC_LeaderSecondGUID = creature->GetGUID(); + LeaderSecondGUID = creature->GetGUID(); break; case NPC_LORALEN: if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_ELANDRA); - if (!NPC_GuardFirstGUID) + if (!GuardFirstGUID) { - NPC_GuardFirstGUID = creature->GetGUID(); - if (m_auiEncounter[0] == DONE && m_auiEncounter[1] == DONE) + GuardFirstGUID = creature->GetGUID(); + if (AllBossesDone()) creature->SetVisible(false); } break; case NPC_KALIRA: if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_KORELN); - if (!NPC_GuardSecondGUID) + if (!GuardSecondGUID) { - NPC_GuardSecondGUID = creature->GetGUID(); - if (m_auiEncounter[0] == DONE && m_auiEncounter[1] == DONE) + GuardSecondGUID = creature->GetGUID(); + if (AllBossesDone()) creature->SetVisible(false); } break; @@ -128,9 +125,9 @@ public: void HandleOutro() { - if (!NPC_LeaderSecondGUID || !instance->GetCreature(NPC_LeaderSecondGUID)) + if (!LeaderSecondGUID || !instance->GetCreature(LeaderSecondGUID)) if (Creature* leader = instance->SummonCreature(NPC_SYLVANAS_PART2, outroSpawnPoint)) - if (Creature* boss = instance->GetCreature(NPC_DevourerGUID)) + if (Creature* boss = GetCreature(DATA_DEVOURER)) { float angle = boss->GetAngle(leader); leader->GetMotionMaster()->MovePoint(1, boss->GetPositionX() + 10.0f * cos(angle), boss->GetPositionY() + 10.0f * std::sin(angle), boss->GetPositionZ()); @@ -144,69 +141,37 @@ public: } } - void SetData(uint32 type, uint32 data) override + bool SetBossState(uint32 type, EncounterState state) override { - switch (type) - { - case DATA_BRONJAHM: - m_auiEncounter[type] = data; - break; - case DATA_DEVOURER: - m_auiEncounter[type] = data; - if (m_auiEncounter[0] == DONE && m_auiEncounter[1] == DONE) - HandleOutro(); - break; - } + if (!InstanceScript::SetBossState(type, state)) + return false; - if (data == DONE) - SaveToDB(); - } + if (state == DONE && AllBossesDone()) + HandleOutro(); - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case DATA_BRONJAHM: - return NPC_BronjahmGUID; - } - - return ObjectGuid::Empty; + return true; } bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override { switch (criteria_id) { - case 12752: // Soul Power - if (Creature* c = instance->GetCreature(NPC_BronjahmGUID)) + case ACHIEV_CRITERIA_SOUL_POWER: + if (Creature* bronjahm = GetCreature(DATA_BRONJAHM)) { - std::list L; - uint8 count = 0; - c->GetCreaturesWithEntryInRange(L, 200.0f, 36535); // find all Corrupted Soul Fragment (36535) - for( std::list::const_iterator itr = L.begin(); itr != L.end(); ++itr ) - if ((*itr)->IsAlive()) - ++count; - return (count >= 4); + std::list fragments; + bronjahm->GetCreaturesWithEntryInRange(fragments, 200.0f, NPC_CORRUPTED_SOUL_FRAGMENT); + return std::count_if(fragments.begin(), fragments.end(), + [](Creature const* fragment) { return fragment->IsAlive(); }) >= SOUL_POWER_FRAGMENT_COUNT; } break; - case 12976: - if (Creature* c = instance->GetCreature(NPC_DevourerGUID)) - return (bool)c->AI()->GetData(1); + case ACHIEV_CRITERIA_THREE_FACED: + if (Creature* devourer = GetCreature(DATA_DEVOURER)) + return devourer->AI()->GetData(1) != 0; break; } return false; } - - void ReadSaveDataMore(std::istringstream& data) override - { - data >> m_auiEncounter[0]; - data >> m_auiEncounter[1]; - } - - void WriteSaveDataMore(std::ostringstream& data) override - { - data << m_auiEncounter[0] << ' ' << m_auiEncounter[1]; - } }; }; diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp index 12772dced..699581697 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp @@ -16,173 +16,151 @@ */ #include "CreatureScript.h" -#include "halls_of_reflection.h" #include "ScriptedCreature.h" #include "SpellMgr.h" +#include "halls_of_reflection.h" enum Yells { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_IMPENDING_DESPAIR = 3, - SAY_DEFILING_HORROR = 4, + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_IMPENDING_DESPAIR = 3, + SAY_DEFILING_HORROR = 4, }; enum Spells { - SPELL_QUIVERING_STRIKE = 72422, - SPELL_IMPENDING_DESPAIR = 72426, - SPELL_DEFILING_HORROR = 72435, + SPELL_QUIVERING_STRIKE = 72422, + SPELL_IMPENDING_DESPAIR = 72426, + SPELL_DEFILING_HORROR = 72435, }; enum Events { - EVENT_NONE, - EVENT_QUIVERING_STRIKE, + EVENT_QUIVERING_STRIKE = 1, EVENT_IMPENDING_DESPAIR, EVENT_DEFILING_HORROR, EVENT_UNROOT, }; -const uint32 hopelessnessId[3] = { 72395, 72396, 72397 }; +constexpr uint32 hopelessnessId[3] = { 72395, 72396, 72397 }; -class boss_falric : public CreatureScript +struct boss_falric : public BossAI { -public: - boss_falric() : CreatureScript("boss_falric") { } + boss_falric(Creature* creature) : BossAI(creature, DATA_FALRIC) { } - struct boss_falricAI : public ScriptedAI + void Reset() override { - boss_falricAI(Creature* creature) : ScriptedAI(creature) - { - pInstance = creature->GetInstanceScript(); - } - - InstanceScript* pInstance; - EventMap events; - uint8 uiHopelessnessCount; - uint16 startFightTimer; - - void Reset() override - { - startFightTimer = 0; - uiHopelessnessCount = 0; - me->SetImmuneToAll(true); - me->SetControlled(false, UNIT_STATE_ROOT); - events.Reset(); - if (pInstance) - pInstance->SetData(DATA_FALRIC, NOT_STARTED); - } - - void JustEngagedWith(Unit* /*who*/) override - { - me->SetImmuneToAll(false); - - events.ScheduleEvent(EVENT_QUIVERING_STRIKE, 5s); - events.ScheduleEvent(EVENT_IMPENDING_DESPAIR, 11s); - events.ScheduleEvent(EVENT_DEFILING_HORROR, 20s); - } - - void DoAction(int32 a) override - { - if (a == 1) - { - Talk(SAY_AGGRO); - startFightTimer = 8000; - } - } - - void UpdateAI(uint32 diff) override - { - if (startFightTimer) - { - if (startFightTimer <= diff) - { - startFightTimer = 0; - me->SetInCombatWithZone(); - } - else - startFightTimer -= diff; - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_QUIVERING_STRIKE: - me->CastSpell(me->GetVictim(), SPELL_QUIVERING_STRIKE, false); - events.ScheduleEvent(EVENT_QUIVERING_STRIKE, 5s); - break; - case EVENT_IMPENDING_DESPAIR: - if (Unit* target = SelectTargetFromPlayerList(45.0f, 0, true)) - { - Talk(SAY_IMPENDING_DESPAIR); - me->CastSpell(target, SPELL_IMPENDING_DESPAIR, false); - } - events.ScheduleEvent(EVENT_IMPENDING_DESPAIR, 12s); - break; - case EVENT_DEFILING_HORROR: - Talk(SAY_DEFILING_HORROR); - me->CastSpell((Unit*)nullptr, SPELL_DEFILING_HORROR, false); - me->SetControlled(true, UNIT_STATE_ROOT); - events.DelayEventsToMax(5s, 0); - events.ScheduleEvent(EVENT_UNROOT, 4s); - events.ScheduleEvent(EVENT_DEFILING_HORROR, 20s); - break; - case EVENT_UNROOT: - me->SetControlled(false, UNIT_STATE_ROOT); - break; - } - - if ((uiHopelessnessCount == 0 && HealthBelowPct(67)) || (uiHopelessnessCount == 1 && HealthBelowPct(34)) || (uiHopelessnessCount == 2 && HealthBelowPct(11))) - { - if (uiHopelessnessCount) - me->RemoveOwnedAura(sSpellMgr->GetSpellIdForDifficulty(hopelessnessId[uiHopelessnessCount - 1], me)); - - me->CastSpell((Unit*)nullptr, hopelessnessId[uiHopelessnessCount], true); - ++uiHopelessnessCount; - } - - if (!me->HasUnitState(UNIT_STATE_ROOT)) - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(DATA_FALRIC, DONE); - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - Talk(SAY_SLAY); - } - - void EnterEvadeMode(EvadeReason why) override - { - me->SetControlled(false, UNIT_STATE_ROOT); - ScriptedAI::EnterEvadeMode(why); - if (startFightTimer) - Reset(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); + BossAI::Reset(); + _hopelessnessCount = 0; + _startingFight = false; + me->SetImmuneToAll(true); + me->SetControlled(false, UNIT_STATE_ROOT); } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + me->SetImmuneToAll(false); + + events.ScheduleEvent(EVENT_QUIVERING_STRIKE, 5s); + events.ScheduleEvent(EVENT_IMPENDING_DESPAIR, 11s); + events.ScheduleEvent(EVENT_DEFILING_HORROR, 20s); + } + + void DoAction(int32 action) override + { + if (action == 1) + { + Talk(SAY_AGGRO); + _startingFight = true; + me->m_Events.AddEventAtOffset([this]() + { + _startingFight = false; + me->SetInCombatWithZone(); + }, 8s); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_QUIVERING_STRIKE: + DoCastVictim(SPELL_QUIVERING_STRIKE); + events.Repeat(5s); + break; + case EVENT_IMPENDING_DESPAIR: + if (Unit* target = SelectTargetFromPlayerList(45.0f, 0, true)) + { + Talk(SAY_IMPENDING_DESPAIR); + DoCast(target, SPELL_IMPENDING_DESPAIR); + } + events.Repeat(12s); + break; + case EVENT_DEFILING_HORROR: + Talk(SAY_DEFILING_HORROR); + DoCastAOE(SPELL_DEFILING_HORROR); + me->SetControlled(true, UNIT_STATE_ROOT); + events.DelayEventsToMax(5s, 0); + events.ScheduleEvent(EVENT_UNROOT, 4s); + events.Repeat(20s); + break; + case EVENT_UNROOT: + me->SetControlled(false, UNIT_STATE_ROOT); + break; + } + + if ((_hopelessnessCount == 0 && HealthBelowPct(67)) + || (_hopelessnessCount == 1 && HealthBelowPct(34)) + || (_hopelessnessCount == 2 && HealthBelowPct(11))) + { + if (_hopelessnessCount) + me->RemoveOwnedAura(sSpellMgr->GetSpellIdForDifficulty(hopelessnessId[_hopelessnessCount - 1], me)); + + DoCastAOE(hopelessnessId[_hopelessnessCount], true); + ++_hopelessnessCount; + } + + if (!me->HasUnitState(UNIT_STATE_ROOT)) + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + Talk(SAY_SLAY); + } + + void EnterEvadeMode(EvadeReason why) override + { + me->SetControlled(false, UNIT_STATE_ROOT); + BossAI::EnterEvadeMode(why); + if (_startingFight) + Reset(); + } + +private: + uint8 _hopelessnessCount{}; + bool _startingFight{}; }; void AddSC_boss_falric() { - new boss_falric(); + RegisterHallsOfReflectionCreatureAI(boss_falric); } diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp index 119dcef19..2f4b0784f 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp @@ -16,160 +16,136 @@ */ #include "CreatureScript.h" -#include "SpellScriptLoader.h" -#include "halls_of_reflection.h" #include "ScriptedCreature.h" #include "SpellAuraEffects.h" #include "SpellScript.h" +#include "SpellScriptLoader.h" +#include "halls_of_reflection.h" enum Yells { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_CORRUPTED_FLESH = 3, - SAY_CORRUPTED_WELL = 4, + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_CORRUPTED_FLESH = 3, + SAY_CORRUPTED_WELL = 4, }; enum Spells { - SPELL_OBLITERATE = 72360, - SPELL_WELL_OF_CORRUPTION = 72362, - SPELL_CORRUPTED_FLESH = 72363, - SPELL_SHARED_SUFFERING = 72368, + SPELL_OBLITERATE = 72360, + SPELL_WELL_OF_CORRUPTION = 72362, + SPELL_CORRUPTED_FLESH = 72363, + SPELL_SHARED_SUFFERING = 72368, }; enum Events { - EVENT_NONE, - EVENT_OBLITERATE, + EVENT_OBLITERATE = 1, EVENT_WELL_OF_CORRUPTION, EVENT_CORRUPTED_FLESH, EVENT_SHARED_SUFFERING, }; -class boss_marwyn : public CreatureScript +struct boss_marwyn : public BossAI { -public: - boss_marwyn() : CreatureScript("boss_marwyn") { } + boss_marwyn(Creature* creature) : BossAI(creature, DATA_MARWYN) { } - struct boss_marwynAI : public ScriptedAI + void Reset() override { - boss_marwynAI(Creature* creature) : ScriptedAI(creature) + BossAI::Reset(); + _startingFight = false; + me->SetImmuneToAll(true); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + me->SetImmuneToAll(false); + + events.ScheduleEvent(EVENT_OBLITERATE, 15s); + events.ScheduleEvent(EVENT_WELL_OF_CORRUPTION, 13s); + events.ScheduleEvent(EVENT_CORRUPTED_FLESH, 20s); + events.ScheduleEvent(EVENT_SHARED_SUFFERING, 5s); + } + + void DoAction(int32 action) override + { + if (action == 1) { - pInstance = creature->GetInstanceScript(); - } - - InstanceScript* pInstance; - EventMap events; - uint16 startFightTimer; - - void Reset() override - { - startFightTimer = 0; - me->SetImmuneToAll(true); - events.Reset(); - if (pInstance) - pInstance->SetData(DATA_MARWYN, NOT_STARTED); - } - - void JustEngagedWith(Unit* /*who*/) override - { - me->SetImmuneToAll(false); - - events.ScheduleEvent(EVENT_OBLITERATE, 15s); - events.ScheduleEvent(EVENT_WELL_OF_CORRUPTION, 13s); - events.ScheduleEvent(EVENT_CORRUPTED_FLESH, 20s); - events.ScheduleEvent(EVENT_SHARED_SUFFERING, 5s); - } - - void DoAction(int32 a) override - { - if (a == 1) + Talk(SAY_AGGRO); + _startingFight = true; + me->m_Events.AddEventAtOffset([this]() { - Talk(SAY_AGGRO); - startFightTimer = 8000; - } + _startingFight = false; + me->SetInCombatWithZone(); + }, 8s); } + } - void UpdateAI(uint32 diff) override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) { - if (startFightTimer) - { - if (startFightTimer <= diff) + case EVENT_OBLITERATE: + if (me->IsWithinMeleeRange(me->GetVictim())) { - startFightTimer = 0; - me->SetInCombatWithZone(); + DoCastVictim(SPELL_OBLITERATE); + events.Repeat(15s); } else - startFightTimer -= diff; - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_OBLITERATE: - if (me->IsWithinMeleeRange(me->GetVictim())) - { - me->CastSpell(me->GetVictim(), SPELL_OBLITERATE, false); - events.ScheduleEvent(EVENT_OBLITERATE, 15s); - } - else - events.ScheduleEvent(EVENT_OBLITERATE, 3s); - break; - case EVENT_WELL_OF_CORRUPTION: - Talk(SAY_CORRUPTED_WELL); - if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) - me->CastSpell(target, SPELL_WELL_OF_CORRUPTION, false); - events.ScheduleEvent(EVENT_WELL_OF_CORRUPTION, 13s); - break; - case EVENT_CORRUPTED_FLESH: - Talk(SAY_CORRUPTED_FLESH); - me->CastSpell((Unit*)nullptr, SPELL_CORRUPTED_FLESH, false); - events.ScheduleEvent(EVENT_CORRUPTED_FLESH, 20s); - break; - case EVENT_SHARED_SUFFERING: - if (Unit* target = SelectTargetFromPlayerList(200.0f, 0, true)) - me->CastSpell(target, SPELL_SHARED_SUFFERING, true); - events.ScheduleEvent(EVENT_SHARED_SUFFERING, 15s); - break; - } - - DoMeleeAttackIfReady(); + events.Repeat(3s); + break; + case EVENT_WELL_OF_CORRUPTION: + Talk(SAY_CORRUPTED_WELL); + if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) + DoCast(target, SPELL_WELL_OF_CORRUPTION); + events.Repeat(13s); + break; + case EVENT_CORRUPTED_FLESH: + Talk(SAY_CORRUPTED_FLESH); + DoCastAOE(SPELL_CORRUPTED_FLESH); + events.Repeat(20s); + break; + case EVENT_SHARED_SUFFERING: + if (Unit* target = SelectTargetFromPlayerList(200.0f, 0, true)) + DoCast(target, SPELL_SHARED_SUFFERING, true); + events.Repeat(15s); + break; } - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(DATA_MARWYN, DONE); - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - Talk(SAY_SLAY); - } - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - if (startFightTimer) - Reset(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); + DoMeleeAttackIfReady(); } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + Talk(SAY_SLAY); + } + + void EnterEvadeMode(EvadeReason why) override + { + BossAI::EnterEvadeMode(why); + if (_startingFight) + Reset(); + } + +private: + bool _startingFight{}; }; enum SharedSufferingAura @@ -186,25 +162,37 @@ class spell_hor_shared_suffering_aura : public AuraScript return ValidateSpellInfo({ SPELL_SHARED_SUFFERING_DAMAGE }); } - void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) { - if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_ENEMY_SPELL) // dispelled - if (Unit* caster = GetCaster()) - if (Map* map = caster->FindMap()) - if (Aura* a = aurEff->GetBase()) - { - uint32 count = 0; - uint32 ticks = 0; - uint32 dmgPerTick = a->GetSpellInfo()->Effects[0].BasePoints; - Map::PlayerList const& pl = map->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - if (p->IsAlive()) - ++count; - ticks = (a->GetDuration() / int32(a->GetSpellInfo()->Effects[0].Amplitude)) + 1; - int32 dmg = (ticks * dmgPerTick) / count; - caster->CastCustomSpell(GetTarget(), SPELL_SHARED_SUFFERING_DAMAGE, nullptr, &dmg, nullptr, true); - } + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL) + return; + + Unit* caster = GetCaster(); + if (!caster) + return; + + Map* map = caster->FindMap(); + if (!map) + return; + + Aura* aura = aurEff->GetBase(); + if (!aura) + return; + + uint32 count = 0; + uint32 dmgPerTick = aura->GetSpellInfo()->Effects[0].BasePoints; + Map::PlayerList const& pl = map->GetPlayers(); + for (auto itr = pl.begin(); itr != pl.end(); ++itr) + if (Player* p = itr->GetSource()) + if (p->IsAlive()) + ++count; + + if (!count) + return; + + uint32 ticks = (aura->GetDuration() / int32(aura->GetSpellInfo()->Effects[0].Amplitude)) + 1; + int32 dmg = (ticks * dmgPerTick) / count; + caster->CastCustomSpell(GetTarget(), SPELL_SHARED_SUFFERING_DAMAGE, nullptr, &dmg, nullptr, true); } void Register() override @@ -215,6 +203,6 @@ class spell_hor_shared_suffering_aura : public AuraScript void AddSC_boss_marwyn() { - new boss_marwyn(); + RegisterHallsOfReflectionCreatureAI(boss_marwyn); RegisterSpellScript(spell_hor_shared_suffering_aura); } diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp index 43547fcf0..95b2c4f5c 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -155,16 +155,12 @@ public: { QuestStatus status = player->GetQuestStatus(creature->GetEntry() == NPC_SYLVANAS_PART1 ? QUEST_DELIVRANCE_FROM_THE_PIT_H2 : QUEST_DELIVRANCE_FROM_THE_PIT_A2); if (status == QUEST_STATUS_COMPLETE || status == QUEST_STATUS_REWARDED) - { - AddGossipItemFor(player, creature->GetEntry() == NPC_SYLVANAS_PART1 ? GOSSIP_MENU_SYLVANAS : GOSISP_MENU_JAINA, GOSSIP_OPTION_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - } + AddGossipItemFor(player, creature->GetEntry() == NPC_SYLVANAS_PART1 ? GOSSIP_MENU_SYLVANAS : GOSISP_MENU_JAINA, GOSSIP_OPTION_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); // once last quest is completed, she offers this shortcut of the starting event status = player->GetQuestStatus(creature->GetEntry() == NPC_SYLVANAS_PART1 ? QUEST_WRATH_OF_THE_LICH_KING_H2 : QUEST_WRATH_OF_THE_LICH_KING_A2); if (status == QUEST_STATUS_COMPLETE || status == QUEST_STATUS_REWARDED) - { AddGossipItemFor(player, creature->GetEntry() == NPC_SYLVANAS_PART1 ? GOSSIP_MENU_SYLVANAS : GOSISP_MENU_JAINA, GOSSIP_OPTION_START_SKIP, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - } } SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); @@ -190,13 +186,13 @@ public: ClearGossipMenuFor(player); switch (uiAction) { - case GOSSIP_ACTION_INFO_DEF+1: + case GOSSIP_ACTION_INFO_DEF + 1: CloseGossipMenuFor(player); if (creature->AI()) creature->AI()->DoAction(ACTION_START_INTRO); creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); break; - case GOSSIP_ACTION_INFO_DEF+2: + case GOSSIP_ACTION_INFO_DEF + 2: CloseGossipMenuFor(player); if (creature->AI()) creature->AI()->DoAction(ACTION_SKIP_INTRO); @@ -216,13 +212,13 @@ public: { npc_hor_leaderAI(Creature* creature) : NullCreatureAI(creature) { - pInstance = me->GetInstanceScript(); - if (!pInstance) + instance = me->GetInstanceScript(); + if (!instance) me->IsAIEnabled = false; - first = (pInstance && !pInstance->GetData(DATA_INTRO)); + first = (instance && !instance->GetPersistentData(PERSISTENT_DATA_INTRO)); } - InstanceScript* pInstance; + InstanceScript* instance; EventMap events; bool first; bool shortver; @@ -262,7 +258,7 @@ public: switch (events.ExecuteEvent()) { case EVENT_PRE_INTRO_1: - if (pInstance) + if (instance) { me->SetSheath(SHEATH_STATE_MELEE); @@ -272,10 +268,8 @@ public: break; case EVENT_PRE_INTRO_2: if (me->GetEntry() == NPC_JAINA_PART1) - { Talk(SAY_JAINA_INTRO_1); - } - me->GetMotionMaster()->MovePoint(0, SpawnPos); + me->GetMotionMaster()->MovePoint(0, SpawnPos); break; case EVENT_TALK_LEADER_1: me->SetSheath(SHEATH_STATE_UNARMED); @@ -292,19 +286,16 @@ public: shortver = false; me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); me->GetMotionMaster()->MovePoint(0, MoveThronePos); - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) { - pLoralen->LoadEquipment(true); - pLoralen->SetVisible(true); - pLoralen->SetWalk(true); - pLoralen->GetMotionMaster()->MovePoint(0, LoralenMidleFollowPos); - + loralen->LoadEquipment(true); + loralen->SetVisible(true); + loralen->SetWalk(true); + loralen->GetMotionMaster()->MovePoint(0, LoralenMidleFollowPos); } // Begining of intro is differents between factions as the speech sequence and timers are differents. if (me->GetEntry() == NPC_JAINA_PART1) - { events.ScheduleEvent(EVENT_INTRO_A2_1, 10s); - } else { events.ScheduleEvent(EVENT_INTRO_H2_2, 9s); @@ -317,10 +308,8 @@ public: shortver = true; me->SetUInt32Value(UNIT_NPC_EMOTESTATE, (me->GetEntry() == NPC_JAINA_PART1 ? EMOTE_STATE_READY2H : EMOTE_STATE_READY1H)); me->GetMotionMaster()->MovePoint(0, MoveThronePos); - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) - { - pLoralen->GetMotionMaster()->MovePoint(0, LoralenFollowPos); - } + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) + loralen->GetMotionMaster()->MovePoint(0, LoralenFollowPos); events.ScheduleEvent(EVENT_INTRO_LK_1, 0ms); break; @@ -334,25 +323,25 @@ public: events.ScheduleEvent(EVENT_INTRO_A2_3, 10s); break; case EVENT_INTRO_A2_3: - pInstance->HandleGameObject(pInstance->GetGuidData(GO_FROSTMOURNE), true); - me->CastSpell(me, SPELL_FROSTMOURNE_SPAWN_SOUND, true); - //me->CastSpell(me, SPELL_ARCANE_CAST_VISUAL, false);// the sniff is missing from the alliance + instance->HandleGameObject(GO_FROSTMOURNE, true); + DoCastSelf(SPELL_FROSTMOURNE_SPAWN_SOUND, true); + //DoCastSelf(SPELL_ARCANE_CAST_VISUAL);// the sniff is missing from the alliance events.ScheduleEvent(EVENT_INTRO_A2_4, 10s); break; case EVENT_INTRO_A2_4: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - pUther->SetVisible(true); - if (Aura* a = pUther->AddAura(SPELL_SHADOWMOURNE_VISUAL, pUther)) + uther->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + uther->SetVisible(true); + if (Aura* a = uther->AddAura(SPELL_SHADOWMOURNE_VISUAL, uther)) a->SetDuration(8000); } events.ScheduleEvent(EVENT_INTRO_A2_5, 2s); break; case EVENT_INTRO_A2_5: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_1); + uther->AI()->Talk(SAY_UTHER_INTRO_A2_1); events.ScheduleEvent(EVENT_INTRO_A2_6, 3s); } break; @@ -361,10 +350,8 @@ public: events.ScheduleEvent(EVENT_INTRO_A2_7, 6s); break; case EVENT_INTRO_A2_7: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) - { - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_2); - } + if (Creature* uther = instance->GetCreature(NPC_UTHER)) + uther->AI()->Talk(SAY_UTHER_INTRO_A2_2); events.ScheduleEvent(EVENT_INTRO_A2_8, 6500ms); break; case EVENT_INTRO_A2_8: @@ -372,10 +359,8 @@ public: events.ScheduleEvent(EVENT_INTRO_A2_9, 2s); break; case EVENT_INTRO_A2_9: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) - { - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_3); - } + if (Creature* uther = instance->GetCreature(NPC_UTHER)) + uther->AI()->Talk(SAY_UTHER_INTRO_A2_3); events.ScheduleEvent(EVENT_INTRO_A2_10, 9s); break; case EVENT_INTRO_A2_10: @@ -383,10 +368,8 @@ public: events.ScheduleEvent(EVENT_INTRO_A2_11, 5s); break; case EVENT_INTRO_A2_11: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) - { - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_4); - } + if (Creature* uther = instance->GetCreature(NPC_UTHER)) + uther->AI()->Talk(SAY_UTHER_INTRO_A2_4); events.ScheduleEvent(EVENT_INTRO_A2_12, 11s); break; case EVENT_INTRO_A2_12: @@ -394,10 +377,8 @@ public: events.ScheduleEvent(EVENT_INTRO_A2_13, 4s); break; case EVENT_INTRO_A2_13: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) - { - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_5); - } + if (Creature* uther = instance->GetCreature(NPC_UTHER)) + uther->AI()->Talk(SAY_UTHER_INTRO_A2_5); events.ScheduleEvent(EVENT_INTRO_A2_14, 12s + 500ms); break; case EVENT_INTRO_A2_14: @@ -405,15 +386,13 @@ public: events.ScheduleEvent(EVENT_INTRO_A2_15, 10s); break; case EVENT_INTRO_A2_15: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) - { - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_6); - } + if (Creature* uther = instance->GetCreature(NPC_UTHER)) + uther->AI()->Talk(SAY_UTHER_INTRO_A2_6); events.ScheduleEvent(EVENT_INTRO_A2_16, 24s); break; case EVENT_INTRO_A2_16: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_7); + if (Creature* uther = instance->GetCreature(NPC_UTHER)) + uther->AI()->Talk(SAY_UTHER_INTRO_A2_7); events.ScheduleEvent(EVENT_INTRO_A2_17, 4s); break; case EVENT_INTRO_A2_17: @@ -421,10 +400,10 @@ public: events.ScheduleEvent(EVENT_INTRO_A2_18, 2s); break; case EVENT_INTRO_A2_18: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_8); + uther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + uther->AI()->Talk(SAY_UTHER_INTRO_A2_8); } events.ScheduleEvent(EVENT_INTRO_A2_19, 11s); break; @@ -435,17 +414,13 @@ public: // H2 Intro Events case EVENT_LORALEN_MOVE_1: - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) - { - pLoralen->GetMotionMaster()->MovePoint(0, LoralenFollowPos); - } + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) + loralen->GetMotionMaster()->MovePoint(0, LoralenFollowPos); break; case EVENT_LORALEN_MOVE_2: - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) - { - pLoralen->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); - } + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) + loralen->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); break; case EVENT_INTRO_H2_2: @@ -462,13 +437,13 @@ public: break; case EVENT_INTRO_H2_3: - me->CastSpell(me, SPELL_SUMMON_SOULS, false); + DoCastSelf(SPELL_SUMMON_SOULS); events.ScheduleEvent(EVENT_INTRO_H2_3_1, 5s); break; case EVENT_INTRO_H2_3_1: - me->CastSpell(me, SPELL_FROSTMOURNE_SPAWN_SOUND, false); - pInstance->HandleGameObject(pInstance->GetGuidData(GO_FROSTMOURNE), true); + DoCastSelf(SPELL_FROSTMOURNE_SPAWN_SOUND); + instance->HandleGameObject(GO_FROSTMOURNE, true); events.ScheduleEvent(EVENT_INTRO_H2_3_2, 3s); break; @@ -477,22 +452,20 @@ public: events.ScheduleEvent(EVENT_INTRO_H2_4, 2s); break; case EVENT_INTRO_H2_4: - if (Creature* pUther = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->SetVisible(true); - if (Aura* a = pUther->AddAura(SPELL_SHADOWMOURNE_VISUAL, pUther)) - { + uther->SetVisible(true); + if (Aura* a = uther->AddAura(SPELL_SHADOWMOURNE_VISUAL, uther)) a->SetDuration(8000); - } } events.ScheduleEvent(EVENT_INTRO_H2_5, 7s); break; case EVENT_INTRO_H2_5: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pUther->AI()->Talk(SAY_UTHER_INTRO_H2_1); + uther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + uther->AI()->Talk(SAY_UTHER_INTRO_H2_1); } events.ScheduleEvent(EVENT_INTRO_H2_6, 11s); break; @@ -502,10 +475,10 @@ public: events.ScheduleEvent(EVENT_INTRO_H2_7, 2s + 500ms); break; case EVENT_INTRO_H2_7: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pUther->AI()->Talk(SAY_UTHER_INTRO_H2_2); + uther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + uther->AI()->Talk(SAY_UTHER_INTRO_H2_2); } events.ScheduleEvent(EVENT_INTRO_H2_8, 9s);// break; @@ -515,10 +488,10 @@ public: events.ScheduleEvent(EVENT_INTRO_H2_9, 5s); break; case EVENT_INTRO_H2_9: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pUther->AI()->Talk(SAY_UTHER_INTRO_H2_3); + uther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + uther->AI()->Talk(SAY_UTHER_INTRO_H2_3); } events.ScheduleEvent(EVENT_INTRO_H2_10, 20s); break; @@ -528,10 +501,10 @@ public: events.ScheduleEvent(EVENT_INTRO_H2_11, 3s); break; case EVENT_INTRO_H2_11: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pUther->AI()->Talk(SAY_UTHER_INTRO_H2_4); + uther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + uther->AI()->Talk(SAY_UTHER_INTRO_H2_4); } events.ScheduleEvent(EVENT_INTRO_H2_12, 21s + 500ms); break; @@ -541,19 +514,18 @@ public: events.ScheduleEvent(EVENT_INTRO_H2_13, 3s + 500ms); break; case EVENT_INTRO_H2_13: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pUther->AI()->Talk(SAY_UTHER_INTRO_H2_5); - + uther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + uther->AI()->Talk(SAY_UTHER_INTRO_H2_5); } events.ScheduleEvent(EVENT_INTRO_H2_14, 12s + 500ms); break; case EVENT_INTRO_H2_14: - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pUther->AI()->Talk(SAY_UTHER_INTRO_H2_6); + uther->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + uther->AI()->Talk(SAY_UTHER_INTRO_H2_6); } events.ScheduleEvent(EVENT_INTRO_H2_15, 8s); break; @@ -565,25 +537,25 @@ public: // Remaining Intro Events common for both faction case EVENT_INTRO_LK_1: - if (Creature* pLichKing = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_EVENT))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) { - pInstance->HandleGameObject(pInstance->GetGuidData(GO_ARTHAS_DOOR), true); - pLichKing->SetVisible(true); + instance->HandleGameObject(GO_ARTHAS_DOOR, true); + lichKing->SetVisible(true); - pLichKing->GetMotionMaster()->MovePoint(0, LichKingMoveMidlelThronePos, FORCED_MOVEMENT_NONE, 0.f, false); + lichKing->GetMotionMaster()->MovePoint(0, LichKingMoveMidlelThronePos, FORCED_MOVEMENT_NONE, 0.f, false); } if (!shortver) - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + uther->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); if (me->GetEntry() == NPC_JAINA_PART1) - pUther->AI()->Talk(SAY_UTHER_INTRO_A2_9); + uther->AI()->Talk(SAY_UTHER_INTRO_A2_9); else - pUther->AI()->Talk(SAY_UTHER_INTRO_H2_7); + uther->AI()->Talk(SAY_UTHER_INTRO_H2_7); } events.ScheduleEvent(EVENT_INTRO_LK_1_1, 9s); @@ -593,102 +565,100 @@ public: break; case EVENT_INTRO_LK_1_1: - if (Creature* pLichKing = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_LICH_KING_EVENT))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) { - pLichKing->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); - pLichKing->AI()->Talk(SAY_LK_INTRO_1); + lichKing->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + lichKing->AI()->Talk(SAY_LK_INTRO_1); } break; case EVENT_INTRO_LK_1_2: if (!shortver) - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_COWER); - pUther->SetFacingTo(0.89f); + uther->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_COWER); + uther->SetFacingTo(0.89f); } break; case EVENT_INTRO_LK_1_3: - pInstance->HandleGameObject(pInstance->GetGuidData(GO_ARTHAS_DOOR), false); + instance->HandleGameObject(GO_ARTHAS_DOOR, false); break; case EVENT_INTRO_LK_2: if (!shortver) - if (Creature* pLichKing = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_LICH_KING_EVENT))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) { - pLichKing->SetVisible(true); - pLichKing->GetMotionMaster()->MovePoint(0, LichKingMoveThronePos, FORCED_MOVEMENT_NONE, 0.f, false); + lichKing->SetVisible(true); + lichKing->GetMotionMaster()->MovePoint(0, LichKingMoveThronePos, FORCED_MOVEMENT_NONE, 0.f, false); } events.ScheduleEvent(EVENT_INTRO_LK_2_1, 1s); break; case EVENT_INTRO_LK_2_1: if (!shortver) - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) + if (Creature* uther = instance->GetCreature(NPC_UTHER)) { - pUther->SendPlaySpellVisual(SPELL_UTHER_DESPAWN); - pUther->CastSpell(pUther, SPELL_UTHER_DESPAWN, true); + uther->SendPlaySpellVisual(SPELL_UTHER_DESPAWN); + uther->CastSpell(uther, SPELL_UTHER_DESPAWN, true); } events.ScheduleEvent(EVENT_INTRO_LK_3, 2s); break; case EVENT_INTRO_LK_3: if (!shortver) - if (Creature* pUther = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_UTHER))) - { - pUther->SetVisible(false); - } + if (Creature* uther = instance->GetCreature(NPC_UTHER)) + uther->SetVisible(false); events.ScheduleEvent(EVENT_INTRO_LK_4, 6s); break; case EVENT_INTRO_LK_4: - if (Creature* pLichKing = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_LICH_KING_EVENT))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) { - pLichKing->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); - pLichKing->AI()->Talk(SAY_LK_INTRO_2); + lichKing->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION); + lichKing->AI()->Talk(SAY_LK_INTRO_2); } events.ScheduleEvent(EVENT_INTRO_LK_4_2, 10s); break; case EVENT_INTRO_LK_4_2: - if (Creature* pLichKing = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_LICH_KING_EVENT))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) { - pLichKing->LoadEquipment(1, true); - pLichKing->SendMovementFlagUpdate(); - pLichKing->CastSpell(pLichKing, SPELL_FROSTMOURNE_EQUIP, false); - pInstance->HandleGameObject(pInstance->GetGuidData(GO_FROSTMOURNE), false); + lichKing->LoadEquipment(1, true); + lichKing->SendMovementFlagUpdate(); + lichKing->CastSpell(lichKing, SPELL_FROSTMOURNE_EQUIP); + instance->HandleGameObject(GO_FROSTMOURNE, false); events.ScheduleEvent(EVENT_INTRO_LK_4_3, 1750ms); } events.ScheduleEvent(EVENT_INTRO_LK_5, 6s); break; case EVENT_INTRO_LK_4_3: - if (GameObject* go = pInstance->instance->GetGameObject(pInstance->GetGuidData(GO_FROSTMOURNE))) + if (GameObject* go = instance->GetGameObject(GO_FROSTMOURNE)) go->SetPhaseMask(2, true); break; case EVENT_INTRO_LK_5: - if (Creature* pFalric = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_FALRIC))) + if (Creature* falric = instance->GetCreature(DATA_FALRIC)) { - pFalric->UpdatePosition(5274.9f, 2039.2f, 709.319f, 5.4619f, true); - pFalric->StopMovingOnCurrentPos(); - pFalric->SetVisible(true); - if (pFalric->IsAlive()) + falric->UpdatePosition(5274.9f, 2039.2f, 709.319f, 5.4619f, true); + falric->StopMovingOnCurrentPos(); + falric->SetVisible(true); + if (falric->IsAlive()) { - pFalric->GetMotionMaster()->MovePoint(0, FalricMovePos); - if (Aura* a = pFalric->AddAura(SPELL_SHADOWMOURNE_VISUAL, pFalric)) + falric->GetMotionMaster()->MovePoint(0, FalricMovePos); + if (Aura* a = falric->AddAura(SPELL_SHADOWMOURNE_VISUAL, falric)) a->SetDuration(8000); } } - if (Creature* pMarwyn = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_MARWYN))) + if (Creature* marwyn = instance->GetCreature(DATA_MARWYN)) { - pMarwyn->UpdatePosition(5343.77f, 1973.86f, 709.319f, 2.35173f, true); - pMarwyn->StopMovingOnCurrentPos(); - pMarwyn->SetVisible(true); - if (pMarwyn->IsAlive()) + marwyn->UpdatePosition(5343.77f, 1973.86f, 709.319f, 2.35173f, true); + marwyn->StopMovingOnCurrentPos(); + marwyn->SetVisible(true); + if (marwyn->IsAlive()) { - pMarwyn->GetMotionMaster()->MovePoint(0, MarwynMovePos); - if (Aura* a = pMarwyn->AddAura(SPELL_SHADOWMOURNE_VISUAL, pMarwyn)) + marwyn->GetMotionMaster()->MovePoint(0, MarwynMovePos); + if (Aura* a = marwyn->AddAura(SPELL_SHADOWMOURNE_VISUAL, marwyn)) a->SetDuration(8000); } } @@ -699,57 +669,55 @@ public: break; case EVENT_INTRO_LK_5_1: - if (Creature* pLichKing = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_LICH_KING_EVENT))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) { - pLichKing->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - pLichKing->AI()->Talk(SAY_LK_INTRO_3); + lichKing->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + lichKing->AI()->Talk(SAY_LK_INTRO_3); } break; case EVENT_INTRO_LK_5_2: - if (Creature* pLichKing = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_LICH_KING_EVENT))) - { - pLichKing->GetMotionMaster()->MovePoint(0, LichKingMoveAwayPos, FORCED_MOVEMENT_NONE, 0.f, false); - } + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) + lichKing->GetMotionMaster()->MovePoint(0, LichKingMoveAwayPos, FORCED_MOVEMENT_NONE, 0.f, false); break; case EVENT_INTRO_LK_5_3: me->SetSpeed(MOVE_RUN, 1.6); me->SetSheath(SHEATH_STATE_MELEE); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_READY1H); - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) { - pLoralen->SetSheath(SHEATH_STATE_MELEE); - pLoralen->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_READY1H); - pLoralen->SetWalk(false); - pLoralen->LoadEquipment(true); + loralen->SetSheath(SHEATH_STATE_MELEE); + loralen->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_READY1H); + loralen->SetWalk(false); + loralen->LoadEquipment(true); } break; case EVENT_INTRO_LK_6: - if (Creature* pFalric = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_FALRIC))) + if (Creature* falric = instance->GetCreature(DATA_FALRIC)) { - pFalric->HandleEmoteCommand(EMOTE_ONESHOT_BOW); - pFalric->AI()->Talk(SAY_FALRIC_INTRO_1); + falric->HandleEmoteCommand(EMOTE_ONESHOT_BOW); + falric->AI()->Talk(SAY_FALRIC_INTRO_1); } events.ScheduleEvent(EVENT_INTRO_LK_7, 2s); break; case EVENT_INTRO_LK_7: - if (Creature* pMarwyn = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_MARWYN))) + if (Creature* marwyn = instance->GetCreature(DATA_MARWYN)) { - pMarwyn->HandleEmoteCommand(EMOTE_ONESHOT_BOW); - pMarwyn->AI()->Talk(SAY_MARWYN_INTRO_1); + marwyn->HandleEmoteCommand(EMOTE_ONESHOT_BOW); + marwyn->AI()->Talk(SAY_MARWYN_INTRO_1); } events.ScheduleEvent(EVENT_INTRO_LK_8, 3s); break; case EVENT_INTRO_LK_8: - if (Creature* pFalric = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_FALRIC))) + if (Creature* falric = instance->GetCreature(DATA_FALRIC)) { - pFalric->AI()->Talk(SAY_FALRIC_INTRO_2); - pInstance->SetData(ACTION_SHOW_TRASH, 1); - pInstance->HandleGameObject(pInstance->GetGuidData(GO_ARTHAS_DOOR), true); + falric->AI()->Talk(SAY_FALRIC_INTRO_2); + instance->SetData(ACTION_SHOW_TRASH, 1); + instance->HandleGameObject(GO_ARTHAS_DOOR, true); } events.ScheduleEvent(EVENT_INTRO_LK_9, 6s); break; @@ -765,39 +733,28 @@ public: me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); me->GetMotionMaster()->MovePoint(0, LichKingMoveAwayPos, FORCED_MOVEMENT_NONE, 0.f, false); } - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) - { - pLoralen->GetMotionMaster()->MovePoint(0, LoralenFollowLk1, FORCED_MOVEMENT_NONE, 0.f, false); - } + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) + loralen->GetMotionMaster()->MovePoint(0, LoralenFollowLk1, FORCED_MOVEMENT_NONE, 0.f, false); events.ScheduleEvent(EVENT_INTRO_LK_10, 1s + 500ms); break; case EVENT_INTRO_LK_10: - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) - { - pLoralen->GetMotionMaster()->MovePoint(0, LoralenFollowLk2, FORCED_MOVEMENT_NONE, 0.f, false); - - } + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) + loralen->GetMotionMaster()->MovePoint(0, LoralenFollowLk2, FORCED_MOVEMENT_NONE, 0.f, false); events.ScheduleEvent(EVENT_INTRO_LK_11, 2s); break; case EVENT_INTRO_LK_11: - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) - { - pLoralen->GetMotionMaster()->MovePoint(0, LoralenFollowLk3, FORCED_MOVEMENT_NONE, 0.f, false); - } + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) + loralen->GetMotionMaster()->MovePoint(0, LoralenFollowLk3, FORCED_MOVEMENT_NONE, 0.f, false); events.ScheduleEvent(EVENT_INTRO_LK_12, 5s + 500ms); break; case EVENT_INTRO_LK_12: - if (Creature* pLichKing = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(NPC_LICH_KING_EVENT))) - { - pLichKing->SetVisible(false); - } - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) - { - pLoralen->GetMotionMaster()->MovePoint(0, LoralenFollowLkFinal, FORCED_MOVEMENT_NONE, 0.f, false); - } + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_EVENT)) + lichKing->SetVisible(false); + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) + loralen->GetMotionMaster()->MovePoint(0, LoralenFollowLkFinal, FORCED_MOVEMENT_NONE, 0.f, false); events.ScheduleEvent(EVENT_INTRO_LK_13, 2s); break; case EVENT_INTRO_LK_13: @@ -806,19 +763,19 @@ public: break; case EVENT_INTRO_END: - pInstance->HandleGameObject(pInstance->GetGuidData(GO_ARTHAS_DOOR), false); - pInstance->HandleGameObject(pInstance->GetGuidData(GO_FRONT_DOOR), false); + instance->HandleGameObject(GO_ARTHAS_DOOR, false); + instance->HandleGameObject(GO_FRONT_DOOR, false); events.ScheduleEvent(EVENT_INTRO_END_SET, 3s); break; case EVENT_INTRO_END_SET: - if (Creature* pLoralen = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_DARK_RANGER_LORALEN))) + if (Creature* loralen = instance->GetCreature(NPC_DARK_RANGER_LORALEN)) { - pLoralen->UpdatePosition(5369.71289f, 2083.6330f, 707.695129f, 0.188739f, true); - pLoralen->StopMovingOnCurrentPos(); - pLoralen->SetVisible(true); - pLoralen->KillSelf(pLoralen); + loralen->UpdatePosition(5369.71289f, 2083.6330f, 707.695129f, 0.188739f, true); + loralen->StopMovingOnCurrentPos(); + loralen->SetVisible(true); + loralen->KillSelf(loralen); } - pInstance->SetData(DATA_INTRO, DONE); + instance->SetData(DATA_INTRO, DONE); break; } } @@ -859,6 +816,21 @@ enum TrashSpells SPELL_CURSED_ARROW = 72222, SPELL_FROST_TRAP = 72215, SPELL_ICE_SHOT = 72268, + + // Phantom Mage (clone visual) + SPELL_HALLUCINATION_CLONE = 72343, + + // Raging Ghoul + SPELL_LEAP = 70150, + + // Risen Witch Doctor + SPELL_CURSE_OF_DOOM = 70144, + SPELL_SHADOW_BOLT_WD = 70080, + SPELL_SHADOW_BOLT_VOLLEY = 70145, + + // Lumbering Abomination + SPELL_VOMIT_SPRAY = 70176, + SPELL_CLEAVE = 40505, }; enum TrashEvents @@ -896,704 +868,591 @@ enum TrashEvents EVENT_ICE_SHOT, }; -class npc_ghostly_priest : public CreatureScript +struct npc_ghostly_priest: public ScriptedAI { -public: - npc_ghostly_priest() : CreatureScript("npc_ghostly_priest") { } + npc_ghostly_priest(Creature* creature) : ScriptedAI(creature) {} - CreatureAI* GetAI(Creature* creature) const override + void DoAction(int32 action) override { - return GetHallsOfReflectionAI(creature); + if (action == 1) + { + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) + AttackStart(target); + } } - struct npc_ghostly_priestAI: public ScriptedAI + void Reset() override { - npc_ghostly_priestAI(Creature* c) : ScriptedAI(c) {} + events.Reset(); + } - EventMap events; + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s); + events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 8s); + events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10s); + events.ScheduleEvent(EVENT_DARK_MENDING, 8s); + } - void DoAction(int32 a) override + void JustDied(Unit* /*killer*/) override + { + if (!urand(0,9)) + Talk(SAY_WAVE_DEATH); + } + + void AttackStart(Unit* who) override + { + if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return; + ScriptedAI::AttackStart(who); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + switch (events.ExecuteEvent()) { - if (a == 1) - { - me->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) - AttackStart(target); - } - } - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s); - events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 8s); - events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10s); - events.ScheduleEvent(EVENT_DARK_MENDING, 8s); - } - - void JustDied(Unit* /*killer*/) override - { - if (!urand(0,9)) + case EVENT_SHADOW_WORD_PAIN: + if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) + DoCast(target, SPELL_SHADOW_WORD_PAIN); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s); + break; + case EVENT_CIRCLE_OF_DESTRUCTION: + if (Unit* target = SelectTargetFromPlayerList(10.0f, 0, true)) + DoCast(target, SPELL_CIRCLE_OF_DESTRUCTION); + events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 12s); + break; + case EVENT_COWER_IN_FEAR: + if (Unit* target = SelectTargetFromPlayerList(20.0f, 0, true)) + DoCast(target, SPELL_COWER_IN_FEAR); + events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10s); + break; + case EVENT_DARK_MENDING: + if (Unit* target = DoSelectLowestHpFriendly(35.0f, DUNGEON_MODE(20000, 35000))) { - Talk(SAY_WAVE_DEATH); + DoCast(target, SPELL_DARK_MENDING); + events.ScheduleEvent(EVENT_DARK_MENDING, 6s); } + else + events.ScheduleEvent(EVENT_DARK_MENDING, 3s); + break; } - void AttackStart(Unit* who) override - { - if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return; - ScriptedAI::AttackStart(who); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SHADOW_WORD_PAIN: - if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) - me->CastSpell(target, SPELL_SHADOW_WORD_PAIN, false); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5s); - break; - case EVENT_CIRCLE_OF_DESTRUCTION: - if (Unit* target = SelectTargetFromPlayerList(10.0f, 0, true)) - me->CastSpell(target, SPELL_CIRCLE_OF_DESTRUCTION, false); - events.ScheduleEvent(EVENT_CIRCLE_OF_DESTRUCTION, 12s); - break; - case EVENT_COWER_IN_FEAR: - if (Unit* target = SelectTargetFromPlayerList(20.0f, 0, true)) - me->CastSpell(target, SPELL_COWER_IN_FEAR, false); - events.ScheduleEvent(EVENT_COWER_IN_FEAR, 10s); - break; - case EVENT_DARK_MENDING: - if (Unit* target = DoSelectLowestHpFriendly(35.0f, DUNGEON_MODE(20000, 35000))) - { - me->CastSpell(target, SPELL_DARK_MENDING, false); - events.ScheduleEvent(EVENT_DARK_MENDING, 6s); - } - else - events.ScheduleEvent(EVENT_DARK_MENDING, 3s); - break; - } - - DoMeleeAttackIfReady(); - } - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToAll(false); - } - } - }; -}; - -class npc_phantom_mage : public CreatureScript -{ -public: - npc_phantom_mage() : CreatureScript("npc_phantom_mage") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); + DoMeleeAttackIfReady(); } - struct npc_phantom_mageAI: public ScriptedAI + void EnterEvadeMode(EvadeReason why) override { - npc_phantom_mageAI(Creature* c) : ScriptedAI(c) {} - - EventMap events; - - void DoAction(int32 a) override + ScriptedAI::EnterEvadeMode(why); + if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) { - if (a == 1) - { + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToAll(false); + } + } +}; + +struct npc_phantom_mage: public ScriptedAI +{ + npc_phantom_mage(Creature* creature) : ScriptedAI(creature) {} + + void DoAction(int32 action) override + { + if (action == 1) + { + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) + AttackStart(target); + } + } + + void Reset() override + { + events.Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_FIREBALL, 3s); + events.ScheduleEvent(EVENT_FLAMESTRIKE, 6s); + events.ScheduleEvent(EVENT_FROSTBOLT, 9s); + events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 12s); + events.ScheduleEvent(EVENT_HALLUCINATION, 40s); + } + + void JustDied(Unit* /*killer*/) override + { + if (!urand(0,9)) + Talk(SAY_WAVE_DEATH); + } + + void AttackStart(Unit* who) override + { + if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return; + ScriptedAI::AttackStart(who); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_FIREBALL: + DoCastVictim(SPELL_FIREBALL); + events.ScheduleEvent(EVENT_FIREBALL, 6s); + break; + case EVENT_FLAMESTRIKE: + DoCastVictim(SPELL_FLAMESTRIKE); + events.ScheduleEvent(EVENT_FLAMESTRIKE, 15s); + break; + case EVENT_FROSTBOLT: + if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) + DoCast(target, SPELL_FROSTBOLT); + events.ScheduleEvent(EVENT_FROSTBOLT, 9s); + break; + case EVENT_CHAINS_OF_ICE: + if (Unit* target = SelectTargetFromPlayerList(100.0f, 0, true)) + DoCast(target, SPELL_CHAINS_OF_ICE); + events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 12s); + break; + case EVENT_HALLUCINATION: + //DoCastSelf(SPELL_HALLUCINATION); + me->SummonCreature(NPC_PHANTOM_HALLUCINATION, *me, TEMPSUMMON_TIMED_DESPAWN, 30000); + DoCastSelf(SPELL_HALLUCINATION_CLONE, true); + break; + } + + DoMeleeAttackIfReady(); + } + + void EnterEvadeMode(EvadeReason why) override + { + ScriptedAI::EnterEvadeMode(why); + if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) + { + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToAll(false); + } + } +}; + +struct npc_phantom_hallucination : public npc_phantom_mage +{ + npc_phantom_hallucination(Creature* creature) : npc_phantom_mage(creature) + { + numOfUpd = 2; + } + + uint8 numOfUpd; + + void JustDied(Unit* /*who*/) override + { + DoCastAOE(SPELL_HALLUCINATION_2); + } + + void UpdateAI(uint32 diff) override + { + if (numOfUpd) + { + if (--numOfUpd == 0) me->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) - AttackStart(target); - } + return; } - void Reset() override - { - events.Reset(); - } + npc_phantom_mage::UpdateAI(diff); + } - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_FIREBALL, 3s); - events.ScheduleEvent(EVENT_FLAMESTRIKE, 6s); - events.ScheduleEvent(EVENT_FROSTBOLT, 9s); - events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 12s); - events.ScheduleEvent(EVENT_HALLUCINATION, 40s); - } + void EnterEvadeMode(EvadeReason why) override + { + if (numOfUpd) + return; - void JustDied(Unit* /*killer*/) override + ScriptedAI::EnterEvadeMode(why); + if (me->IsSummon()) + me->ToTempSummon()->DespawnOrUnsummon(1ms); + } +}; + +struct npc_shadowy_mercenary: public ScriptedAI +{ + npc_shadowy_mercenary(Creature* creature) : ScriptedAI(creature) {} + + void DoAction(int32 action) override + { + if (action == 1) { - if (!urand(0,9)) + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) + AttackStart(target); + } + } + + void Reset() override + { + events.Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_SHADOW_STEP, 4s); + events.ScheduleEvent(EVENT_DEADLY_POISON, 6s); + events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10s); + events.ScheduleEvent(EVENT_KIDNEY_SHOT, 5s); + } + + void JustDied(Unit* /*killer*/) override + { + if (!urand(0,9)) + Talk(SAY_WAVE_DEATH); + } + + void AttackStart(Unit* who) override + { + if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return; + ScriptedAI::AttackStart(who); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_SHADOW_STEP: + if (Unit* target = SelectTargetFromPlayerList(100.0f, 0, true)) { - Talk(SAY_WAVE_DEATH); - } - } - - void AttackStart(Unit* who) override - { - if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return; - ScriptedAI::AttackStart(who); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_FIREBALL: - me->CastSpell(me->GetVictim(), SPELL_FIREBALL, false); - events.ScheduleEvent(EVENT_FIREBALL, 6s); - break; - case EVENT_FLAMESTRIKE: - me->CastSpell(me->GetVictim(), SPELL_FLAMESTRIKE, false); - events.ScheduleEvent(EVENT_FLAMESTRIKE, 15s); - break; - case EVENT_FROSTBOLT: - if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) - me->CastSpell(target, SPELL_FROSTBOLT, false); - events.ScheduleEvent(EVENT_FROSTBOLT, 9s); - break; - case EVENT_CHAINS_OF_ICE: - if (Unit* target = SelectTargetFromPlayerList(100.0f, 0, true)) - me->CastSpell(target, SPELL_CHAINS_OF_ICE, false); - events.ScheduleEvent(EVENT_CHAINS_OF_ICE, 12s); - break; - case EVENT_HALLUCINATION: - //me->CastSpell(me, SPELL_HALLUCINATION, false); - me->SummonCreature(NPC_PHANTOM_HALLUCINATION, *me, TEMPSUMMON_TIMED_DESPAWN, 30000); - me->CastSpell(me, SPELL_HALLUCINATION + 1, true); - break; - } - - DoMeleeAttackIfReady(); - } - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToAll(false); - } - } - }; -}; - -class npc_phantom_hallucination : public CreatureScript -{ -public: - npc_phantom_hallucination() : CreatureScript("npc_phantom_hallucination") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); - } - - struct npc_phantom_hallucinationAI : public npc_phantom_mage::npc_phantom_mageAI - { - npc_phantom_hallucinationAI(Creature* c) : npc_phantom_mage::npc_phantom_mageAI(c) - { - numOfUpd = 2; - } - - uint8 numOfUpd; - - void JustDied(Unit* /*who*/) override - { - me->CastSpell((Unit*)nullptr, SPELL_HALLUCINATION_2, false); - } - - void UpdateAI(uint32 diff) override - { - if (numOfUpd) - { - if (--numOfUpd == 0) - me->SetInCombatWithZone(); - return; - } - - npc_phantom_mageAI::UpdateAI(diff); - } - - void EnterEvadeMode(EvadeReason why) override - { - if (numOfUpd) - return; - - ScriptedAI::EnterEvadeMode(why); - if (me->IsSummon()) - me->ToTempSummon()->DespawnOrUnsummon(1ms); - } - }; -}; - -class npc_shadowy_mercenary : public CreatureScript -{ -public: - npc_shadowy_mercenary() : CreatureScript("npc_shadowy_mercenary") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); - } - - struct npc_shadowy_mercenaryAI: public ScriptedAI - { - npc_shadowy_mercenaryAI(Creature* c) : ScriptedAI(c) {} - - EventMap events; - - void DoAction(int32 a) override - { - if (a == 1) - { - me->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) + DoResetThreatList(); + me->AddThreat(target, 5000.0f); AttackStart(target); - } - } - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_SHADOW_STEP, 4s); - events.ScheduleEvent(EVENT_DEADLY_POISON, 6s); - events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10s); - events.ScheduleEvent(EVENT_KIDNEY_SHOT, 5s); - } - - void JustDied(Unit* /*killer*/) override - { - if (!urand(0,9)) - { - Talk(SAY_WAVE_DEATH); + DoCast(target, SPELL_SHADOW_STEP); } + events.ScheduleEvent(EVENT_SHADOW_STEP, 20s); + break; + case EVENT_DEADLY_POISON: + DoCastVictim(SPELL_DEADLY_POISON); + events.ScheduleEvent(EVENT_DEADLY_POISON, 4s); + break; + case EVENT_ENVENOMED_DAGGER_THROW: + if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) + DoCast(target, SPELL_ENVENOMED_DAGGER_THROW); + events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10s); + break; + case EVENT_KIDNEY_SHOT: + DoCastVictim(SPELL_KIDNEY_SHOT); + events.ScheduleEvent(EVENT_KIDNEY_SHOT, 10s); + break; } - void AttackStart(Unit* who) override - { - if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return; - ScriptedAI::AttackStart(who); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SHADOW_STEP: - if (Unit* target = SelectTargetFromPlayerList(100.0f, 0, true)) - { - DoResetThreatList(); - me->AddThreat(target, 5000.0f); - AttackStart(target); - me->CastSpell(target, SPELL_SHADOW_STEP, false); - } - events.ScheduleEvent(EVENT_SHADOW_STEP, 20s); - break; - case EVENT_DEADLY_POISON: - me->CastSpell(me->GetVictim(), SPELL_DEADLY_POISON, false); - events.ScheduleEvent(EVENT_DEADLY_POISON, 4s); - break; - case EVENT_ENVENOMED_DAGGER_THROW: - if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) - me->CastSpell(target, SPELL_ENVENOMED_DAGGER_THROW, false); - events.ScheduleEvent(EVENT_ENVENOMED_DAGGER_THROW, 10s); - break; - case EVENT_KIDNEY_SHOT: - me->CastSpell(me->GetVictim(), SPELL_KIDNEY_SHOT, false); - events.ScheduleEvent(EVENT_KIDNEY_SHOT, 10s); - break; - } - - DoMeleeAttackIfReady(); - } - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToAll(false); - } - } - }; -}; - -class npc_spectral_footman : public CreatureScript -{ -public: - npc_spectral_footman() : CreatureScript("npc_spectral_footman") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); + DoMeleeAttackIfReady(); } - struct npc_spectral_footmanAI: public ScriptedAI + void EnterEvadeMode(EvadeReason why) override { - npc_spectral_footmanAI(Creature* c) : ScriptedAI(c) {} - - EventMap events; - - void DoAction(int32 a) override + ScriptedAI::EnterEvadeMode(why); + if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) { - if (a == 1) - { - me->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) - AttackStart(target); - } + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToAll(false); } - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5s); - events.ScheduleEvent(EVENT_SHIELD_BASH, 6s); - events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15s); - } - - void JustDied(Unit* /*killer*/) override - { - if (!urand(0,9)) - { - Talk(SAY_WAVE_DEATH); - } - } - - void AttackStart(Unit* who) override - { - if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return; - ScriptedAI::AttackStart(who); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SPECTRAL_STRIKE: - me->CastSpell(me->GetVictim(), SPELL_SPECTRAL_STRIKE, false); - events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5s); - break; - case EVENT_SHIELD_BASH: - me->CastSpell(me->GetVictim(), SPELL_SHIELD_BASH, false); - events.ScheduleEvent(EVENT_SHIELD_BASH, 6s); - break; - case EVENT_TORTURED_ENRAGE: - me->CastSpell(me, SPELL_TORTURED_ENRAGE, false); - events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15s); - break; - } - - DoMeleeAttackIfReady(); - } - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToAll(false); - } - } - }; -}; - -class npc_tortured_rifleman : public CreatureScript -{ -public: - npc_tortured_rifleman() : CreatureScript("npc_tortured_rifleman") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); - } - - struct npc_tortured_riflemanAI : public ScriptedAI - { - npc_tortured_riflemanAI(Creature* c) : ScriptedAI(c) {} - - EventMap events; - - void DoAction(int32 a) override - { - if (a == 1) - { - me->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) - AttackStart(target); - } - } - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_CURSED_ARROW, 10s); - events.ScheduleEvent(EVENT_FROST_TRAP, 15s); - events.ScheduleEvent(EVENT_ICE_SHOT, 15s); - } - - void JustDied(Unit* /*killer*/) override - { - if (!urand(0,9)) - { - Talk(SAY_WAVE_DEATH); - } - } - - void AttackStart(Unit* who) override - { - if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return; - ScriptedAI::AttackStart(who); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_CURSED_ARROW: - me->CastSpell(me->GetVictim(), SPELL_CURSED_ARROW, false); - events.ScheduleEvent(EVENT_CURSED_ARROW, 10s); - break; - case EVENT_FROST_TRAP: - me->CastSpell((Unit*)nullptr, SPELL_FROST_TRAP, false); - events.ScheduleEvent(EVENT_FROST_TRAP, 30s); - break; - case EVENT_ICE_SHOT: - if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) - me->CastSpell(target, SPELL_ICE_SHOT, false); - events.ScheduleEvent(EVENT_ICE_SHOT, 8s); - break; - } - - DoSpellAttackIfReady(SPELL_SHOOT); - } - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToAll(false); - } - } - }; -}; - -class boss_frostsworn_general : public CreatureScript -{ -public: - boss_frostsworn_general() : CreatureScript("boss_frostsworn_general") { } - - struct boss_frostsworn_generalAI : public ScriptedAI - { - boss_frostsworn_generalAI(Creature* creature) : ScriptedAI(creature) - { - pInstance = creature->GetInstanceScript(); - } - - InstanceScript* pInstance; - EventMap events; - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_FROSTSWORN_GENERAL_AGGRO); - events.ScheduleEvent(EVENT_ACTIVATE_REFLECTIONS, 8s); - events.ScheduleEvent(EVENT_THROW_SHIELD, 6s); - pInstance->SetData(ACTION_SPIRITUAL_REFLECTIONS_COPY, 1); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - Position p = me->GetHomePosition(); - if (me->GetExactDist(&p) > 30.0f) - { - EnterEvadeMode(EVADE_REASON_OTHER); - return; - } - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_ACTIVATE_REFLECTIONS: - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_REFLECTIONS_DUMMY, false); - pInstance->SetData(ACTION_SPIRITUAL_REFLECTIONS_ACTIVATE, 1); - break; - case EVENT_THROW_SHIELD: - if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) - me->CastSpell(target, SPELL_THROW_SHIELD, false); - events.ScheduleEvent(EVENT_THROW_SHIELD, 10s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_FROSTSWORN_GENERAL_DEATH); - if (pInstance) - pInstance->SetData(DATA_FROSTSWORN_GENERAL, DONE); - } - - void EnterEvadeMode(EvadeReason why) override - { - pInstance->SetData(ACTION_SPIRITUAL_REFLECTIONS_HIDE, 1); - ScriptedAI::EnterEvadeMode(why); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetHallsOfReflectionAI(creature); } }; -class npc_hor_spiritual_reflection : public CreatureScript +struct npc_spectral_footman: public ScriptedAI { -public: - npc_hor_spiritual_reflection() : CreatureScript("npc_hor_spiritual_reflection") { } + npc_spectral_footman(Creature* creature) : ScriptedAI(creature) {} - struct npc_hor_spiritual_reflectionAI : public ScriptedAI + void DoAction(int32 action) override { - npc_hor_spiritual_reflectionAI(Creature* creature) : ScriptedAI(creature) + if (action == 1) { + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) + AttackStart(target); } + } - EventMap events; - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_BALEFUL_STRIKE, 4s, 7s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_BALEFUL_STRIKE: - me->CastSpell(me->GetVictim(), SPELL_BALEFUL_STRIKE, false); - events.ScheduleEvent(EVENT_BALEFUL_STRIKE, 4s, 7s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - me->CastSpell((Unit*)nullptr, SPELL_SPIRIT_BURST, false); - } - - void EnterEvadeMode(EvadeReason why) override - { - me->UpdatePosition(me->GetHomePosition(), true); - ScriptedAI::EnterEvadeMode(why); - me->SetVisible(false); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetHallsOfReflectionAI(creature); + events.Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5s); + events.ScheduleEvent(EVENT_SHIELD_BASH, 6s); + events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15s); + } + + void JustDied(Unit* /*killer*/) override + { + if (!urand(0,9)) + Talk(SAY_WAVE_DEATH); + } + + void AttackStart(Unit* who) override + { + if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return; + ScriptedAI::AttackStart(who); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_SPECTRAL_STRIKE: + DoCastVictim(SPELL_SPECTRAL_STRIKE); + events.ScheduleEvent(EVENT_SPECTRAL_STRIKE, 5s); + break; + case EVENT_SHIELD_BASH: + DoCastVictim(SPELL_SHIELD_BASH); + events.ScheduleEvent(EVENT_SHIELD_BASH, 6s); + break; + case EVENT_TORTURED_ENRAGE: + DoCastSelf(SPELL_TORTURED_ENRAGE); + events.ScheduleEvent(EVENT_TORTURED_ENRAGE, 15s); + break; + } + + DoMeleeAttackIfReady(); + } + + void EnterEvadeMode(EvadeReason why) override + { + ScriptedAI::EnterEvadeMode(why); + if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) + { + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToAll(false); + } + } +}; + +struct npc_tortured_rifleman : public ScriptedAI +{ + npc_tortured_rifleman(Creature* creature) : ScriptedAI(creature) {} + + void DoAction(int32 action) override + { + if (action == 1) + { + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 0.0f, true)) + AttackStart(target); + } + } + + void Reset() override + { + events.Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_CURSED_ARROW, 10s); + events.ScheduleEvent(EVENT_FROST_TRAP, 15s); + events.ScheduleEvent(EVENT_ICE_SHOT, 15s); + } + + void JustDied(Unit* /*killer*/) override + { + if (!urand(0,9)) + Talk(SAY_WAVE_DEATH); + } + + void AttackStart(Unit* who) override + { + if (!me->IsVisible() || me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return; + ScriptedAI::AttackStart(who); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_CURSED_ARROW: + DoCastVictim(SPELL_CURSED_ARROW); + events.ScheduleEvent(EVENT_CURSED_ARROW, 10s); + break; + case EVENT_FROST_TRAP: + DoCastAOE(SPELL_FROST_TRAP); + events.ScheduleEvent(EVENT_FROST_TRAP, 30s); + break; + case EVENT_ICE_SHOT: + if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) + DoCast(target, SPELL_ICE_SHOT); + events.ScheduleEvent(EVENT_ICE_SHOT, 8s); + break; + } + + DoSpellAttackIfReady(SPELL_SHOOT); + } + + void EnterEvadeMode(EvadeReason why) override + { + ScriptedAI::EnterEvadeMode(why); + if (me->GetInstanceScript()->GetData(DATA_WAVE_NUMBER)) + { + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToAll(false); + } + } +}; + +struct boss_frostsworn_general : public ScriptedAI +{ + boss_frostsworn_general(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + InstanceScript* instance; + + void Reset() override + { + events.Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + Talk(SAY_FROSTSWORN_GENERAL_AGGRO); + events.ScheduleEvent(EVENT_ACTIVATE_REFLECTIONS, 8s); + events.ScheduleEvent(EVENT_THROW_SHIELD, 6s); + instance->SetData(ACTION_SPIRITUAL_REFLECTIONS_COPY, 1); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + Position p = me->GetHomePosition(); + if (me->GetExactDist(&p) > 30.0f) + { + EnterEvadeMode(EVADE_REASON_OTHER); + return; + } + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_ACTIVATE_REFLECTIONS: + DoCastAOE(SPELL_SUMMON_REFLECTIONS_DUMMY); + instance->SetData(ACTION_SPIRITUAL_REFLECTIONS_ACTIVATE, 1); + break; + case EVENT_THROW_SHIELD: + if (Unit* target = SelectTargetFromPlayerList(40.0f, 0, true)) + DoCast(target, SPELL_THROW_SHIELD); + events.ScheduleEvent(EVENT_THROW_SHIELD, 10s); + break; + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_FROSTSWORN_GENERAL_DEATH); + if (instance) + instance->SetData(DATA_FROSTSWORN_GENERAL, DONE); + } + + void EnterEvadeMode(EvadeReason why) override + { + instance->SetData(ACTION_SPIRITUAL_REFLECTIONS_HIDE, 1); + ScriptedAI::EnterEvadeMode(why); + } +}; + +struct npc_hor_spiritual_reflection : public ScriptedAI +{ + npc_hor_spiritual_reflection(Creature* creature) : ScriptedAI(creature) {} + + void Reset() override + { + events.Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_BALEFUL_STRIKE, 4s, 7s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_BALEFUL_STRIKE: + DoCastVictim(SPELL_BALEFUL_STRIKE); + events.ScheduleEvent(EVENT_BALEFUL_STRIKE, 4s, 7s); + break; + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + DoCastAOE(SPELL_SPIRIT_BURST); + } + + void EnterEvadeMode(EvadeReason why) override + { + me->UpdatePosition(me->GetHomePosition(), true); + ScriptedAI::EnterEvadeMode(why); + me->SetVisible(false); } }; @@ -1604,14 +1463,11 @@ public: bool OnTrigger(Player* player, const AreaTrigger* /*at*/) override { - if (player->IsGameMaster()) - return false; - InstanceScript* inst = player->GetInstanceScript(); if (!inst) return false; - if (inst->GetData(DATA_FROSTSWORN_GENERAL) && !inst->GetData(DATA_LK_INTRO)) + if (inst->GetPersistentData(PERSISTENT_DATA_FROSTSWORN_GENERAL) && !inst->GetPersistentData(PERSISTENT_DATA_LK_INTRO)) inst->SetData(DATA_LK_INTRO, DONE); return false; @@ -1646,261 +1502,250 @@ enum eFightEvents EVENT_DECREASE_REQ_COUNT_BY_100, }; -class npc_hor_lich_king : public CreatureScript +struct npc_hor_lich_king : public NullCreatureAI { -public: - npc_hor_lich_king() : CreatureScript("npc_hor_lich_king") { } - - CreatureAI* GetAI(Creature* creature) const override + npc_hor_lich_king(Creature* creature) : NullCreatureAI(creature), summons(me) { - return GetHallsOfReflectionAI(creature); + instance = me->GetInstanceScript(); + if (!instance) + me->IsAIEnabled = false; } - struct npc_hor_lich_kingAI : public NullCreatureAI + InstanceScript* instance; + EventMap events; + SummonList summons; + uint8 currentWall; + uint8 reqKillCount; + uint8 div2; + + void Reset() override { - npc_hor_lich_kingAI(Creature* creature) : NullCreatureAI(creature), summons(me) + currentWall = 0; + reqKillCount = 0; + events.Reset(); + events.RescheduleEvent(EVENT_LK_CHECK_COMBAT, 1s); + } + void DoAction(int32 action) override + { + if (action == ACTION_START_LK_FIGHT_REAL) + events.ScheduleEvent(EVENT_LK_START_FOLLOWING, 1500ms); + else if ((action == ACTION_INFORM_TRASH_DIED && reqKillCount) || action == ACTION_CHECK_TRASH_DIED) { - pInstance = me->GetInstanceScript(); - if (!pInstance) - me->IsAIEnabled = false; - } - - InstanceScript* pInstance; - EventMap events; - SummonList summons; - uint8 currentWall; - uint8 reqKillCount; - uint8 div2; - - void Reset() override - { - currentWall = 0; - reqKillCount = 0; - events.Reset(); - events.RescheduleEvent(EVENT_LK_CHECK_COMBAT, 1s); - } - void DoAction(int32 a) override - { - if (a == ACTION_START_LK_FIGHT_REAL) - events.ScheduleEvent(EVENT_LK_START_FOLLOWING, 1500ms); - else if ((a == ACTION_INFORM_TRASH_DIED && reqKillCount) || a == ACTION_CHECK_TRASH_DIED) + if ((action == ACTION_CHECK_TRASH_DIED && reqKillCount == 0) || (action == ACTION_INFORM_TRASH_DIED && (--reqKillCount) == 0)) { - if ((a == ACTION_CHECK_TRASH_DIED && reqKillCount == 0) || (a == ACTION_INFORM_TRASH_DIED && (--reqKillCount) == 0)) + events.CancelEvent(EVENT_DECREASE_REQ_COUNT_BY_100); // just in case, magic happens sometimes + ++currentWall; + instance->SetData(ACTION_DELETE_ICE_WALL, 1); + if (currentWall <= 3) { - events.CancelEvent(EVENT_DECREASE_REQ_COUNT_BY_100); // just in case, magic happens sometimes - ++currentWall; - pInstance->SetData(ACTION_DELETE_ICE_WALL, 1); - if (currentWall <= 3) + events.ScheduleEvent(EVENT_LK_SUMMON_NEXT_ICE_WALL, 1s); + events.ScheduleEvent(EVENT_LK_SUMMON, currentWall == 3 ? 11s : 7500ms); + } + else + me->RemoveAura(SPELL_REMORSELESS_WINTER); + if (Creature* c = instance->GetCreature(NPC_SYLVANAS_PART2)) + c->AI()->DoAction(ACTION_INFORM_WALL_DESTROYED); + } + } + } + + void MovementInform(uint32 type, uint32 /*id*/) override + { + // Xinef: dont use 0, it is no longer the last point + // Xinef: if type is escort and spline is finalized, it means that we reached last point from the path + if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized()) + { + if (currentWall == 0) + { + Talk(SAY_LK_IW_1); + me->SetOrientation(4.15f); + if (Creature* icewall = instance->instance->GetCreature(instance->GetGuidData(NPC_ICE_WALL_TARGET))) + { + me->SetFacingToObject(icewall); + me->CastSpell(icewall, SPELL_SUMMON_ICE_WALL); + events.ScheduleEvent(EVENT_LK_REMORSELESS_WINTER, 4s); + } + } + else if (currentWall == 4) + { + Talk(SAY_LK_NOWHERE_TO_RUN); + instance->SetData(DATA_LICH_KING, DONE); + } + } + } + + void JustSummoned(Creature* s) override + { + + ++reqKillCount; + if (events.HasTimeUntilEvent(EVENT_DECREASE_REQ_COUNT_BY_100)) + events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s); + summons.Summon(s); + s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]); + s->GetMotionMaster()->MovePoint(0, PathWaypoints[WP_STOP[currentWall + 1]]); + s->SetInCombatWithZone(); + if (Unit* target = s->SelectNearestPlayer(350.0f)) + { + s->AddThreat(target, 1000.0f); + s->AI()->AttackStart(target); + } + s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]); + } + + void SummonedCreatureDespawn(Creature* s) override + { + summons.Despawn(s); + } + + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (target && target->IsAlive() && spell->Id == SPELL_LICH_KING_ZAP_PLAYER) + Unit::DealDamage(me, target, 10000); + } + + void UpdateAI(uint32 diff) override + { + if (me->HealthBelowPct(70)) + me->SetHealth(me->GetMaxHealth() * 3 / 4); + + events.Update(diff); + + if (me->IsNonMeleeSpellCast(false, true, true)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_LK_CHECK_COMBAT: + if (me->isActiveObject()) // during fight + { + if (Creature* leader = instance->GetCreature(NPC_SYLVANAS_PART2)) + if (leader->IsAlive() && leader->GetPositionX() < 5575.0f && me->GetExactDist2d(leader) <= 12.5f && !leader->HasAura(SPELL_HARVEST_SOUL) && me->HasAura(SPELL_REMORSELESS_WINTER)) + { + me->GetMotionMaster()->MovementExpired(); + me->StopMoving(); + reqKillCount = 255; + leader->InterruptNonMeleeSpells(true); + me->CastSpell(leader, SPELL_HARVEST_SOUL); + events.ScheduleEvent(EVENT_LK_KILL_LEADER, 3s); + events.ScheduleEvent(EVENT_LK_CHECK_COMBAT, 1s); + break; + } + if (instance->instance->HavePlayers()) { - events.ScheduleEvent(EVENT_LK_SUMMON_NEXT_ICE_WALL, 1s); - events.ScheduleEvent(EVENT_LK_SUMMON, currentWall == 3 ? 11s : 7500ms); + me->SetInCombatWithZone(); + ++div2; + if (div2 > 1) + { + div2 = 0; + if (me->HasAura(SPELL_REMORSELESS_WINTER)) // prevent going behind him during fight + { + Map::PlayerList const& pl = instance->instance->GetPlayers(); + for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) + if (Player* p = itr->GetSource()) + if (!p->IsGameMaster() && p->IsAlive() && (p->GetPositionX() - me->GetPositionX() + p->GetPositionY() - me->GetPositionY()) > 20.0f) + me->CastSpell(p, SPELL_LICH_KING_ZAP_PLAYER, true); + } + } } else - me->RemoveAura(SPELL_REMORSELESS_WINTER); - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_SYLVANAS_PART2))) - c->AI()->DoAction(ACTION_INFORM_WALL_DESTROYED); + { + summons.DespawnAll(); + instance->SetData(ACTION_STOP_LK_FIGHT, 1); + } } - } - } - - void MovementInform(uint32 type, uint32 /*id*/) override - { - // Xinef: dont use 0, it is no longer the last point - // Xinef: if type is escort and spline is finalized, it means that we reached last point from the path - if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized()) - { - if (currentWall == 0) + events.ScheduleEvent(EVENT_LK_CHECK_COMBAT, 1s); + break; + case EVENT_LK_KILL_LEADER: + if (Creature* leader = instance->GetCreature(NPC_SYLVANAS_PART2)) { - Talk(SAY_LK_IW_1); - me->SetOrientation(4.15f); - if (Creature* icewall = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_ICE_WALL_TARGET))) - { - me->SetFacingToObject(icewall); - me->CastSpell(icewall, SPELL_SUMMON_ICE_WALL, false); - events.ScheduleEvent(EVENT_LK_REMORSELESS_WINTER, 4s); - } + leader->CastSpell(leader, SPELL_HOR_SUICIDE, true); + Unit::Kill(me, leader); + me->InterruptNonMeleeSpells(true); + DoCastAOE(SPELL_FURY_OF_FROSTMOURNE); } - else if (currentWall == 4) + break; + case EVENT_LK_START_FOLLOWING: { - Talk(SAY_LK_NOWHERE_TO_RUN); - pInstance->SetData(DATA_LICH_KING, DONE); + me->SetSpeed(MOVE_RUN, 9.0f / 7.0f); + Movement::PointsArray path; + path.emplace_back(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); + for (uint8 i = 0; i <= 2; ++i) + path.emplace_back(PathWaypoints[i].GetPositionX(), PathWaypoints[i].GetPositionY(), PathWaypoints[i].GetPositionZ()); + me->GetMotionMaster()->MoveSplinePath(&path); } - } + break; + case EVENT_LK_REMORSELESS_WINTER: + { + me->SetSpeed(MOVE_RUN, me->GetCreatureTemplate()->speed_run); + Talk(SAY_LK_WINTER); + DoCastSelf(SPELL_REMORSELESS_WINTER, true); + Movement::PointsArray path; + path.emplace_back(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); + for (uint8 i = 3; i < PATH_WP_COUNT - 1; ++i) + path.emplace_back(PathWaypoints[i].GetPositionX(), PathWaypoints[i].GetPositionY(), PathWaypoints[i].GetPositionZ()); + me->GetMotionMaster()->MoveSplinePath(&path); + me->GetMotionMaster()->propagateSpeedChange(); + events.ScheduleEvent(EVENT_LK_SUMMON, 1s); + } + break; + case EVENT_LK_SUMMON: + switch (currentWall) + { + case 0: + events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 0ms); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 4s); + break; + case 1: + events.ScheduleEvent(EVENT_LK_SUMMON_LA, 0ms); + events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 100ms); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 4s); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 5100ms); + break; + case 2: + events.ScheduleEvent(EVENT_LK_SUMMON_LA, 0ms); + events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 100ms); + events.ScheduleEvent(EVENT_LK_SUMMON_LA, 4s); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 4100ms); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 5200ms); + break; + case 3: + events.ScheduleEvent(EVENT_LK_SUMMON_LA, 0ms); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 100ms); + events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 1200ms); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 5300ms); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 6400ms); + events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 12s + 500ms); + events.ScheduleEvent(EVENT_LK_SUMMON_LA, 16s + 500ms); + events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 16s + 600ms); + events.ScheduleEvent(EVENT_LK_SUMMON_LA, 17s + 700ms); + break; + } + if (currentWall <= 3) + { + reqKillCount = 100; + events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s); + } + break; + case EVENT_DECREASE_REQ_COUNT_BY_100: + reqKillCount = (reqKillCount <= 100 ? 0 : reqKillCount - 100); + DoAction(ACTION_CHECK_TRASH_DIED); + break; + case EVENT_LK_SUMMON_GHOULS: + DoCastAOE(SPELL_SUMMON_RAGING_GHOULS); + break; + case EVENT_LK_SUMMON_RWD: + DoCastAOE(SPELL_SUMMON_RISEN_WITCH_DOCTOR); + break; + case EVENT_LK_SUMMON_LA: + DoCastAOE(SPELL_SUMMON_LUMBERING_ABOMINATION); + break; + case EVENT_LK_SUMMON_NEXT_ICE_WALL: + Talk(SAY_LK_IW_1 + currentWall); + if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(NPC_ICE_WALL_TARGET + currentWall))) + me->CastSpell(c, SPELL_SUMMON_ICE_WALL); + break; } - - void JustSummoned(Creature* s) override - { - - ++reqKillCount; - if (events.HasTimeUntilEvent(EVENT_DECREASE_REQ_COUNT_BY_100)) - events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s); - summons.Summon(s); - s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]); - s->GetMotionMaster()->MovePoint(0, PathWaypoints[WP_STOP[currentWall + 1]]); - s->SetInCombatWithZone(); - if (Unit* target = s->SelectNearestPlayer(350.0f)) - { - s->AddThreat(target, 1000.0f); - s->AI()->AttackStart(target); - } - s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]); - } - - void SummonedCreatureDespawn(Creature* s) override - { - summons.Despawn(s); - } - - void SpellHitTarget(Unit* target, SpellInfo const* spell) override - { - if (target && target->IsAlive() && spell->Id == SPELL_LICH_KING_ZAP_PLAYER) - Unit::DealDamage(me, target, 10000); - } - - void UpdateAI(uint32 diff) override - { - if (me->HealthBelowPct(70)) - me->SetHealth(me->GetMaxHealth() * 3 / 4); - - events.Update(diff); - - if (me->IsNonMeleeSpellCast(false, true, true)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_LK_CHECK_COMBAT: - if (me->isActiveObject()) // during fight - { - if (Creature* leader = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_SYLVANAS_PART2))) - if (leader->IsAlive() && leader->GetPositionX() < 5575.0f && me->GetExactDist2d(leader) <= 12.5f && !leader->HasAura(SPELL_HARVEST_SOUL) && me->HasAura(SPELL_REMORSELESS_WINTER)) - { - me->GetMotionMaster()->MovementExpired(); - me->StopMoving(); - reqKillCount = 255; - leader->InterruptNonMeleeSpells(true); - me->CastSpell(leader, SPELL_HARVEST_SOUL, false); - events.ScheduleEvent(EVENT_LK_KILL_LEADER, 3s); - events.ScheduleEvent(EVENT_LK_CHECK_COMBAT, 1s); - break; - } - if (pInstance->instance->HavePlayers()) - { - me->SetInCombatWithZone(); - ++div2; - if (div2 > 1) - { - div2 = 0; - if (me->HasAura(SPELL_REMORSELESS_WINTER)) // prevent going behind him during fight - { - Map::PlayerList const& pl = pInstance->instance->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - if (!p->IsGameMaster() && p->IsAlive() && (p->GetPositionX() - me->GetPositionX() + p->GetPositionY() - me->GetPositionY()) > 20.0f) - me->CastSpell(p, SPELL_LICH_KING_ZAP_PLAYER, true); - } - } - } - else - { - summons.DespawnAll(); - pInstance->SetData(ACTION_STOP_LK_FIGHT, 1); - } - } - events.ScheduleEvent(EVENT_LK_CHECK_COMBAT, 1s); - break; - case EVENT_LK_KILL_LEADER: - if (Creature* leader = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_SYLVANAS_PART2))) - { - leader->CastSpell(leader, SPELL_HOR_SUICIDE, true); - Unit::Kill(me, leader); - me->InterruptNonMeleeSpells(true); - me->CastSpell((Unit*)nullptr, SPELL_FURY_OF_FROSTMOURNE, false); - } - break; - case EVENT_LK_START_FOLLOWING: - { - me->SetSpeed(MOVE_RUN, 9.0f / 7.0f); - Movement::PointsArray path; - path.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - for (uint8 i = 0; i <= 2; ++i) - path.push_back(G3D::Vector3(PathWaypoints[i].GetPositionX(), PathWaypoints[i].GetPositionY(), PathWaypoints[i].GetPositionZ())); - me->GetMotionMaster()->MoveSplinePath(&path); - } - break; - case EVENT_LK_REMORSELESS_WINTER: - { - me->SetSpeed(MOVE_RUN, me->GetCreatureTemplate()->speed_run); - Talk(SAY_LK_WINTER); - me->CastSpell(me, SPELL_REMORSELESS_WINTER, true); - Movement::PointsArray path; - path.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - for (uint8 i = 3; i < PATH_WP_COUNT - 1; ++i) - path.push_back(G3D::Vector3(PathWaypoints[i].GetPositionX(), PathWaypoints[i].GetPositionY(), PathWaypoints[i].GetPositionZ())); - me->GetMotionMaster()->MoveSplinePath(&path); - me->GetMotionMaster()->propagateSpeedChange(); - events.ScheduleEvent(EVENT_LK_SUMMON, 1s); - } - break; - case EVENT_LK_SUMMON: - switch (currentWall) - { - case 0: - events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 0ms); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 4s); - break; - case 1: - events.ScheduleEvent(EVENT_LK_SUMMON_LA, 0ms); - events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 100ms); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 4s); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 5100ms); - break; - case 2: - events.ScheduleEvent(EVENT_LK_SUMMON_LA, 0ms); - events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 100ms); - events.ScheduleEvent(EVENT_LK_SUMMON_LA, 4s); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 4100ms); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 5200ms); - break; - case 3: - events.ScheduleEvent(EVENT_LK_SUMMON_LA, 0ms); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 100ms); - events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 1200ms); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 5300ms); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 6400ms); - events.ScheduleEvent(EVENT_LK_SUMMON_GHOULS, 12s + 500ms); - events.ScheduleEvent(EVENT_LK_SUMMON_LA, 16s + 500ms); - events.ScheduleEvent(EVENT_LK_SUMMON_RWD, 16s + 600ms); - events.ScheduleEvent(EVENT_LK_SUMMON_LA, 17s + 700ms); - break; - } - if (currentWall <= 3) - { - reqKillCount = 100; - events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s); - } - break; - case EVENT_DECREASE_REQ_COUNT_BY_100: - reqKillCount = (reqKillCount <= 100 ? 0 : reqKillCount - 100); - DoAction(ACTION_CHECK_TRASH_DIED); - break; - case EVENT_LK_SUMMON_GHOULS: - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_RAGING_GHOULS, false); - break; - case EVENT_LK_SUMMON_RWD: - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_RISEN_WITCH_DOCTOR, false); - break; - case EVENT_LK_SUMMON_LA: - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_LUMBERING_ABOMINATION, false); - break; - case EVENT_LK_SUMMON_NEXT_ICE_WALL: - Talk(SAY_LK_IW_1 + currentWall); - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_ICE_WALL_TARGET + currentWall))) - me->CastSpell(c, SPELL_SUMMON_ICE_WALL, false); - break; - } - } - }; + } }; class npc_hor_leader_second : public CreatureScript @@ -1915,8 +1760,8 @@ public: ClearGossipMenuFor(player); - if (InstanceScript* pInstance = creature->GetInstanceScript()) - if (!pInstance->GetData(DATA_LICH_KING)) + if (InstanceScript* instance = creature->GetInstanceScript()) + if (instance->GetBossState(DATA_LICH_KING) != DONE) { creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); creature->AI()->DoAction(ACTION_START_LK_FIGHT_REAL); @@ -1935,13 +1780,13 @@ public: { npc_hor_leader_secondAI(Creature* creature) : NullCreatureAI(creature) { - pInstance = me->GetInstanceScript(); - if (!pInstance) + instance = me->GetInstanceScript(); + if (!instance) me->IsAIEnabled = false; } - InstanceScript* pInstance; + InstanceScript* instance; EventMap events; uint8 currentStopPoint; @@ -1986,9 +1831,9 @@ public: ++currentStopPoint; me->SetWalk(false); Movement::PointsArray path; - path.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); + path.emplace_back(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); for (uint8 i = WP_STOP[currentStopPoint - 1] + (currentStopPoint == 1 ? 0 : 1); i <= WP_STOP[currentStopPoint]; ++i) - path.push_back(G3D::Vector3(PathWaypoints[i].GetPositionX(), PathWaypoints[i].GetPositionY(), PathWaypoints[i].GetPositionZ())); + path.emplace_back(PathWaypoints[i].GetPositionX(), PathWaypoints[i].GetPositionY(), PathWaypoints[i].GetPositionZ()); me->GetMotionMaster()->MoveSplinePath(&path, FORCED_MOVEMENT_RUN); } @@ -2004,63 +1849,57 @@ public: switch (events.ExecuteEvent()) { case EVENT_LK_SAY_AGGRO: - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) { - me->Attack(lkboss, true), - lkboss->AI()->Talk(me->GetEntry() == NPC_JAINA_PART2 ? SAY_LK_AGGRO_ALLY : SAY_LK_AGGRO_HORDE); + me->Attack(lichKing, true), + lichKing->AI()->Talk(me->GetEntry() == NPC_JAINA_PART2 ? SAY_LK_AGGRO_ALLY : SAY_LK_AGGRO_HORDE); } break; case EVENT_LK_BATTLE_1: - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) - { - lkboss->CastSpell(lkboss, SPELL_SOUL_REAPER, false); - } + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) + lichKing->CastSpell(lichKing, SPELL_SOUL_REAPER); break; case EVENT_LK_BATTLE_2: //horda - if (Creature* leader = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_SYLVANAS_PART2))) - { + if (Creature* leader = instance->GetCreature(NPC_SYLVANAS_PART2)) leader->CastSpell(leader, SPELL_EVASION, true); - } - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) + lichKing->SetFacingToObject(me); + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) { - lkboss->SetFacingToObject(me); - } - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) - { - lkboss->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK2HTIGHT); - me->SetFacingToObject(lkboss); + lichKing->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK2HTIGHT); + me->SetFacingToObject(lichKing); } break; case EVENT_JAINA_IMMOBILIZE_LK: - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) { - lkboss->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY_SPELL_OMNI); + lichKing->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY_SPELL_OMNI); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - me->CastSpell(lkboss, SPELL_JAINA_ICE_PRISON, false); + me->CastSpell(lichKing, SPELL_JAINA_ICE_PRISON); events.ScheduleEvent(EVENT_SAY_LEAVE, 5s); } break; case EVENT_SYLVANAS_IMMOBILIZE_JUMP: - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) { me->AttackStop(); me->SetSheath(SHEATH_STATE_MELEE); - lkboss->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY_SPELL_OMNI); - lkboss->CastSpell(me, SPELL_BLIDING_RETREAT, false); + lichKing->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY_SPELL_OMNI); + lichKing->CastSpell(me, SPELL_BLIDING_RETREAT); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - me->KnockbackFrom(lkboss->GetPositionX(), lkboss->GetPositionY(), 34.3f, 4.0f); + me->KnockbackFrom(lichKing->GetPositionX(), lichKing->GetPositionY(), 34.3f, 4.0f); events.ScheduleEvent(EVENT_SYLVANAS_DARK_BINDING, 2s +500ms); } break; case EVENT_SYLVANAS_DARK_BINDING: - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) - me->CastSpell(lkboss, SPELL_SYLVANAS_DARK_BINDING, false); + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) + me->CastSpell(lichKing, SPELL_SYLVANAS_DARK_BINDING); events.ScheduleEvent(EVENT_SAY_LEAVE, 2s); break; case EVENT_SAY_LEAVE: { - Map::PlayerList const& pl = pInstance->instance->GetPlayers(); + Map::PlayerList const& pl = instance->instance->GetPlayers(); for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) if (Player* p = itr->GetSource()) p->KilledMonsterCredit(me->GetEntry()); //for quest @@ -2079,15 +1918,15 @@ public: break; case EVENT_START_RUN: { - pInstance->SetData(ACTION_START_LK_FIGHT, 1); + instance->SetData(ACTION_START_LK_FIGHT, 1); me->setActive(true); MoveToNextStopPoint(); - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) { - lkboss->setActive(true); - lkboss->SetInCombatWithZone(); - lkboss->RemoveAura(me->GetEntry() == NPC_JAINA_PART2 ? SPELL_JAINA_ICE_PRISON : SPELL_SYLVANAS_DARK_BINDING); - lkboss->AI()->DoAction(ACTION_START_LK_FIGHT_REAL); + lichKing->setActive(true); + lichKing->SetInCombatWithZone(); + lichKing->RemoveAura(me->GetEntry() == NPC_JAINA_PART2 ? SPELL_JAINA_ICE_PRISON : SPELL_SYLVANAS_DARK_BINDING); + lichKing->AI()->DoAction(ACTION_START_LK_FIGHT_REAL); } } break; @@ -2114,7 +1953,7 @@ public: } Talk(textId); if (currentStopPoint <= 4) - me->CastSpell((Unit*)nullptr, (me->GetEntry() == NPC_JAINA_PART2 ? SPELL_DESTROY_WALL_JAINA : SPELL_DESTROY_WALL_SYLVANAS), false); + DoCastAOE(me->GetEntry() == NPC_JAINA_PART2 ? SPELL_DESTROY_WALL_JAINA : SPELL_DESTROY_WALL_SYLVANAS); else { me->SetFacingTo(PathWaypoints[PATH_WP_COUNT - 1].GetOrientation()); @@ -2131,192 +1970,149 @@ public: }; }; -class npc_hor_raging_ghoul : public CreatureScript +struct npc_hor_raging_ghoul : public ScriptedAI { -public: - npc_hor_raging_ghoul() : CreatureScript("npc_hor_raging_ghoul") { } + npc_hor_raging_ghoul(Creature* creature) : ScriptedAI(creature) {} - struct npc_hor_raging_ghoulAI : public ScriptedAI + bool leaped; + + void Reset() override { - npc_hor_raging_ghoulAI(Creature* creature) : ScriptedAI(creature) {} + leaped = false; + } - bool leaped; - - void Reset() override - { - leaped = false; - } - - void UpdateAI(uint32 /*diff*/) override - { - if (!UpdateVictim()) - return; - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - if (!leaped) - if (Unit* target = me->SelectNearestPlayer(30.0f)) - { - AttackStart(target); - me->CastSpell(target, 70150, false); - leaped = true; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - me->SetCorpseDelay(10); - if (InstanceScript* pInstance = me->GetInstanceScript()) - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) - lkboss->AI()->DoAction(ACTION_INFORM_TRASH_DIED); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 /*diff*/) override { - return GetHallsOfReflectionAI(creature); + if (!UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + if (!leaped) + if (Unit* target = me->SelectNearestPlayer(30.0f)) + { + AttackStart(target); + DoCast(target, SPELL_LEAP); + leaped = true; + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + me->SetCorpseDelay(10); + if (InstanceScript* instance = me->GetInstanceScript()) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) + lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED); } }; -class npc_hor_risen_witch_doctor : public CreatureScript +struct npc_hor_risen_witch_doctor : public ScriptedAI { -public: - npc_hor_risen_witch_doctor() : CreatureScript("npc_hor_risen_witch_doctor") { } + npc_hor_risen_witch_doctor(Creature* creature) : ScriptedAI(creature) {} - struct npc_hor_risen_witch_doctorAI : public ScriptedAI + void Reset() override { - npc_hor_risen_witch_doctorAI(Creature* creature) : ScriptedAI(creature) {} + events.Reset(); + } - EventMap events; - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(1, 10s); - events.ScheduleEvent(2, 4500ms); - events.ScheduleEvent(3, 9s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - switch (events.ExecuteEvent()) - { - case 1: - if (Unit* target = SelectTargetFromPlayerList(30.0f, 0, true)) - me->CastSpell(target, 70144, false); - events.ScheduleEvent(1, 12s); - break; - case 2: - me->CastSpell(me->GetVictim(), 70080, false); - events.ScheduleEvent(2, 4500ms); - break; - case 3: - if (SelectTargetFromPlayerList(30.0f, 0, true)) - me->CastSpell(me->GetVictim(), 70145, false); - events.ScheduleEvent(3, 9s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - me->SetCorpseDelay(10); - if (InstanceScript* pInstance = me->GetInstanceScript()) - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) - { - lkboss->AI()->DoAction(ACTION_INFORM_TRASH_DIED); - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void JustEngagedWith(Unit* /*who*/) override { - return GetHallsOfReflectionAI(creature); + events.ScheduleEvent(1, 10s); + events.ScheduleEvent(2, 4500ms); + events.ScheduleEvent(3, 9s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + switch (events.ExecuteEvent()) + { + case 1: + if (Unit* target = SelectTargetFromPlayerList(30.0f, 0, true)) + DoCast(target, SPELL_CURSE_OF_DOOM); + events.ScheduleEvent(1, 12s); + break; + case 2: + DoCastVictim(SPELL_SHADOW_BOLT_WD); + events.ScheduleEvent(2, 4500ms); + break; + case 3: + if (SelectTargetFromPlayerList(30.0f, 0, true)) + DoCastVictim(SPELL_SHADOW_BOLT_VOLLEY); + events.ScheduleEvent(3, 9s); + break; + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override + { + me->SetCorpseDelay(10); + if (InstanceScript* instance = me->GetInstanceScript()) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) + lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED); } }; -class npc_hor_lumbering_abomination : public CreatureScript +struct npc_hor_lumbering_abomination : public ScriptedAI { -public: - npc_hor_lumbering_abomination() : CreatureScript("npc_hor_lumbering_abomination") { } + npc_hor_lumbering_abomination(Creature* creature) : ScriptedAI(creature) {} - struct npc_hor_lumbering_abominationAI : public ScriptedAI + void Reset() override { - npc_hor_lumbering_abominationAI(Creature* creature) : ScriptedAI(creature) {} + events.Reset(); + } - EventMap events; + void JustEngagedWith(Unit* /*who*/) override + { + events.ScheduleEvent(1, 5s); + } - void Reset() override + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) + return; + + switch (events.ExecuteEvent()) { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - events.ScheduleEvent(1, 5s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasFearAura() || me->IsCharmed() || me->isFrozen() || me->HasUnitState(UNIT_STATE_STUNNED) || me->HasUnitState(UNIT_STATE_CONFUSED)) - return; - - switch (events.ExecuteEvent()) - { - case 1: - if (me->IsWithinMeleeRange(me->GetVictim())) - { - me->CastSpell(me->GetVictim(), 70176, false); - events.ScheduleEvent(1, 18s); - } - else - events.ScheduleEvent(1, 3s); - break; - } - - if (me->GetVictim() && me->isAttackReady() && me->IsWithinMeleeRange(me->GetVictim()) && !me->HasUnitState(UNIT_STATE_CASTING)) - { - me->CastSpell(me->GetVictim(), 40505, false); - } - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - me->SetCorpseDelay(10); - if (InstanceScript* pInstance = me->GetInstanceScript()) - if (Creature* lkboss = pInstance->instance->GetCreature(pInstance->GetGuidData(NPC_LICH_KING_BOSS))) + case 1: + if (me->IsWithinMeleeRange(me->GetVictim())) { - lkboss->AI()->DoAction(ACTION_INFORM_TRASH_DIED); + DoCastVictim(SPELL_VOMIT_SPRAY); + events.ScheduleEvent(1, 18s); } + else + events.ScheduleEvent(1, 3s); + break; } - }; - CreatureAI* GetAI(Creature* creature) const override + if (me->GetVictim() && me->isAttackReady() && me->IsWithinMeleeRange(me->GetVictim()) && !me->HasUnitState(UNIT_STATE_CASTING)) + DoCastVictim(SPELL_CLEAVE); + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) override { - return GetHallsOfReflectionAI(creature); + me->SetCorpseDelay(10); + if (InstanceScript* instance = me->GetInstanceScript()) + if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) + lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED); } }; @@ -2339,9 +2135,7 @@ class spell_hor_gunship_cannon_fire_aura : public AuraScript PreventDefaultAction(); if (Unit* caster = GetCaster()) if (Creature* creature = caster->SummonCreature(WORLD_TRIGGER, CannonFirePos[caster->GetEntry() == NPC_JAINA_PART2 ? 0 : 1][urand(0, 2)], TEMPSUMMON_TIMED_DESPAWN, 1)) - { creature->CastSpell((Unit*)nullptr, SPELL_GUNSHIP_CANNON_FIRE, true); - } } void Register() override @@ -2359,7 +2153,7 @@ public: bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override { - if (player->HasAura(70013)) + if (player->HasAura(SPELL_QUELDELAR_COMPULSION)) if (InstanceScript* instance = player->GetInstanceScript()) instance->SetData(DATA_BATTERED_HILT, 2); return true; @@ -2373,13 +2167,13 @@ public: bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override { - if (player->HasAura(70013)) + if (player->HasAura(SPELL_QUELDELAR_COMPULSION)) if (InstanceScript* instance = player->GetInstanceScript()) { uint32 bhd = instance->GetData(DATA_BATTERED_HILT); if (bhd != BHSF_STARTED) return true; - player->CastSpell(player, 70698, true); + player->CastSpell(player, SPELL_QUELDELAR_WILL, true); player->DestroyItemCount(49766, 1, true); instance->SetData(DATA_BATTERED_HILT, 3); } @@ -2391,22 +2185,22 @@ void AddSC_halls_of_reflection() { new npc_hor_leader(); - new npc_ghostly_priest(); - new npc_phantom_mage(); - new npc_phantom_hallucination(); - new npc_shadowy_mercenary(); - new npc_spectral_footman(); - new npc_tortured_rifleman(); + RegisterHallsOfReflectionCreatureAI(npc_ghostly_priest); + RegisterHallsOfReflectionCreatureAI(npc_phantom_mage); + RegisterHallsOfReflectionCreatureAI(npc_phantom_hallucination); + RegisterHallsOfReflectionCreatureAI(npc_shadowy_mercenary); + RegisterHallsOfReflectionCreatureAI(npc_spectral_footman); + RegisterHallsOfReflectionCreatureAI(npc_tortured_rifleman); - new boss_frostsworn_general(); - new npc_hor_spiritual_reflection(); + RegisterHallsOfReflectionCreatureAI(boss_frostsworn_general); + RegisterHallsOfReflectionCreatureAI(npc_hor_spiritual_reflection); new at_hor_shadow_throne(); - new npc_hor_lich_king(); + RegisterHallsOfReflectionCreatureAI(npc_hor_lich_king); new npc_hor_leader_second(); - new npc_hor_raging_ghoul(); - new npc_hor_risen_witch_doctor(); - new npc_hor_lumbering_abomination(); + RegisterHallsOfReflectionCreatureAI(npc_hor_raging_ghoul); + RegisterHallsOfReflectionCreatureAI(npc_hor_risen_witch_doctor); + RegisterHallsOfReflectionCreatureAI(npc_hor_lumbering_abomination); RegisterSpellScript(spell_hor_gunship_cannon_fire_aura); new at_hor_battered_hilt_start(); diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h index fb5a722c1..dfd4c93c5 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h @@ -44,6 +44,7 @@ enum Data ACTION_DELETE_ICE_WALL, DATA_WAVE_NUMBER, DATA_LK_BATTLE,// in progress + DATA_SHIP_CAPTAIN, }; enum Creatures @@ -108,6 +109,17 @@ enum BatteredHiltStatusFlags BHSF_FINISHED = 4, }; +enum HoRPersistentData +{ + PERSISTENT_DATA_INTRO, + PERSISTENT_DATA_FROSTSWORN_GENERAL, + PERSISTENT_DATA_LK_INTRO, + PERSISTENT_DATA_BATTERED_HILT, + PERSISTENT_DATA_COUNT +}; + +constexpr uint8 MAX_SPIRITUAL_REFLECTIONS = 5; +constexpr uint8 MAX_ICE_WALL_TARGETS = 4; #define NUM_OF_TRASH 34 #define MAX_DIST_FROM_CENTER_IN_COMBAT 70.5f #define MAX_DIST_FROM_CENTER_TO_START 40.0f @@ -291,6 +303,11 @@ enum hMisc // Battered Hilt - Summon Quel'Delar SPELL_SUMMON_EVIL_QUEL = 69966, + SPELL_QUEL_DELAR_HATRED = 70300, + SPELL_FROSTMOURNE_ALTAR_GLOW = 70720, + SPELL_UTHER_HOLY_LIGHT_VISUAL = 73036, + SPELL_QUELDELAR_COMPULSION = 70013, + SPELL_QUELDELAR_WILL = 70698, }; const uint32 allowedCompositions[8][5] = @@ -390,4 +407,7 @@ inline AI* GetHallsOfReflectionAI(T* obj) return GetInstanceAI(obj, HallsOfReflectionScriptName); } +#define RegisterHallsOfReflectionCreatureAI(ai_name) \ + RegisterCreatureAIWithFactory(ai_name, GetHallsOfReflectionAI) + #endif diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index 261006135..a1bd4554e 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -46,12 +46,10 @@ public: case 3: _owner.CastSpell((Unit*)nullptr, SPELL_SUMMON_EVIL_QUEL, true); _owner.AI()->Talk(SAY_BATTERED_HILT_REALIZE); - if (InstanceScript* instance = _owner.GetInstanceScript()) - instance->SetData(DATA_BATTERED_HILT, 4); + if (InstanceScript* inst = _owner.GetInstanceScript()) + inst->SetData(DATA_BATTERED_HILT, 4); if (Creature* quel = _owner.FindNearestCreature(NPC_QUEL_DELAR, 50)) - { quel->AI()->Talk(EMOTE_QUEL_SPAWN); - } _owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, 4), 3500ms); break; case 4: @@ -64,26 +62,22 @@ public: _owner.SetStandState(UNIT_STAND_STATE_KNEEL); break; case 6: - if (InstanceScript* instance = _owner.GetInstanceScript()) - instance->SetData(DATA_BATTERED_HILT, 6); + if (InstanceScript* inst = _owner.GetInstanceScript()) + inst->SetData(DATA_BATTERED_HILT, 6); _owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, 7), 2s); break; case 7: - if (InstanceScript* instance = _owner.GetInstanceScript()) - { - instance->SetData(DATA_BATTERED_HILT, 7); - } + if (InstanceScript* inst = _owner.GetInstanceScript()) + inst->SetData(DATA_BATTERED_HILT, 7); if (Creature* quel = _owner.FindNearestCreature(NPC_QUEL_DELAR, 50)) - { quel->AI()->Talk(EMOTE_QUEL_PREPARE); - } _owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, 8), 4s); break; case 8: _owner.SetReactState(REACT_AGGRESSIVE); _owner.SetImmuneToAll(false); - if (InstanceScript* instance = _owner.GetInstanceScript()) - instance->SetData(DATA_BATTERED_HILT, 8); + if (InstanceScript* inst = _owner.GetInstanceScript()) + inst->SetData(DATA_BATTERED_HILT, 8); break; case 9: _owner.AI()->Talk(SAY_BATTERED_HILT_OUTRO1); @@ -102,22 +96,22 @@ public: _owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, _eventId + 1), 5s); break; case 13: - _owner.CastSpell((Unit*)nullptr, 73036, true); + _owner.CastSpell((Unit*)nullptr, SPELL_UTHER_HOLY_LIGHT_VISUAL, true); _owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, _eventId + 1), 3s); break; case 14: - { - Position homePos = _owner.GetHomePosition(); - _owner.SetReactState(REACT_PASSIVE); - _owner.SetImmuneToAll(true); - _owner.SetVisible(false); - _owner.UpdatePosition(homePos.GetPositionX(), homePos.GetPositionY(), homePos.GetPositionZ(), homePos.GetOrientation(), true); - _owner.StopMovingOnCurrentPos(); - _owner.GetMotionMaster()->Clear(); - if (InstanceScript* instance = _owner.GetInstanceScript()) - instance->SetData(DATA_BATTERED_HILT, 9); - } + { + Position homePos = _owner.GetHomePosition(); + _owner.SetReactState(REACT_PASSIVE); + _owner.SetImmuneToAll(true); + _owner.SetVisible(false); + _owner.UpdatePosition(homePos.GetPositionX(), homePos.GetPositionY(), homePos.GetPositionZ(), homePos.GetOrientation(), true); + _owner.StopMovingOnCurrentPos(); + _owner.GetMotionMaster()->Clear(); + if (InstanceScript* inst = _owner.GetInstanceScript()) + inst->SetData(DATA_BATTERED_HILT, 9); break; + } } return true; } @@ -127,1073 +121,1012 @@ private: uint8 _eventId; }; -class instance_halls_of_reflection : public InstanceMapScript +ObjectData const creatureData[] = +{ + { NPC_FALRIC, DATA_FALRIC }, + { NPC_MARWYN, DATA_MARWYN }, + { NPC_SYLVANAS_PART1, NPC_SYLVANAS_PART1 }, + { NPC_DARK_RANGER_LORALEN, NPC_DARK_RANGER_LORALEN }, + { NPC_UTHER, NPC_UTHER }, + { NPC_LICH_KING_EVENT, NPC_LICH_KING_EVENT }, + { NPC_FROSTSWORN_GENERAL, NPC_FROSTSWORN_GENERAL }, + { NPC_LICH_KING_BOSS, NPC_LICH_KING_BOSS }, + { NPC_SYLVANAS_PART2, NPC_SYLVANAS_PART2 }, + { NPC_ALTAR_BUNNY, NPC_ALTAR_BUNNY }, + { NPC_QUEL_DELAR, NPC_QUEL_DELAR }, + { NPC_HIGH_CAPTAIN_JUSTIN_BARLETT, DATA_SHIP_CAPTAIN }, + { NPC_SKY_REAVER_KORM_BLACKSKAR, DATA_SHIP_CAPTAIN }, + { 0, 0 } +}; + +ObjectData const gameObjectData[] = +{ + { GO_FROSTMOURNE, GO_FROSTMOURNE }, + { GO_FROSTMOURNE_ALTAR, GO_FROSTMOURNE_ALTAR }, + { GO_FRONT_DOOR, GO_FRONT_DOOR }, + { GO_ARTHAS_DOOR, GO_ARTHAS_DOOR }, + { GO_CAVE_IN, GO_CAVE_IN }, + { GO_DOOR_BEFORE_THRONE, GO_DOOR_BEFORE_THRONE }, + { GO_DOOR_AFTER_THRONE, GO_DOOR_AFTER_THRONE }, + { GO_ICE_WALL, GO_ICE_WALL }, + { 0, 0 } +}; + +class instance_halls_of_reflection : public InstanceScript { public: - instance_halls_of_reflection() : InstanceMapScript("instance_halls_of_reflection", MAP_HALLS_OF_REFLECTION) { } - - InstanceScript* GetInstanceScript(InstanceMap* pMap) const override + instance_halls_of_reflection(Map* map) : InstanceScript(map) { - return new instance_halls_of_reflection_InstanceMapScript(pMap); + SetHeaders(DataHeader); + SetBossNumber(MAX_ENCOUNTER); + SetPersistentDataCount(PERSISTENT_DATA_COUNT); + LoadObjectData(creatureData, gameObjectData); } - struct instance_halls_of_reflection_InstanceMapScript : public InstanceScript + void Initialize() override { - instance_halls_of_reflection_InstanceMapScript(Map* pMap) : InstanceScript(pMap) + std::fill(std::begin(_trashActive), std::end(_trashActive), false); + _trashCounter = 0; + std::memset(&_chosenComposition, 0, sizeof(_chosenComposition)); + _waveNumber = 0; + _nextWaveTimer = 0; + _playerCheckTimer = 5000; + _waveResumeTimer = 0; + _waveResumeStep = 0; + _falricPhaseComplete = false; + _remainingTrashKills = 0; + _isLichKingFightActive = false; + _batteredHiltStatus = 0; + _outroTimer = 0; + _outroStep = 0; + _transport = nullptr; + } + + bool IsEncounterInProgress() const override + { + return (instance->HavePlayers() && _waveNumber) || _isLichKingFightActive; + } + + void OnCreatureCreate(Creature* creature) override + { + InstanceScript::OnCreatureCreate(creature); + + switch (creature->GetEntry()) { - SetHeaders(DataHeader); - }; - - uint32 EncounterMask; - ObjectGuid NPC_FalricGUID; - ObjectGuid NPC_MarwynGUID; - ObjectGuid NPC_LichKingIntroGUID; - ObjectGuid NPC_LeaderIntroGUID; - ObjectGuid NPC_GuardGUID; - ObjectGuid NPC_UtherGUID; - ObjectGuid NPC_LichKingGUID; - ObjectGuid NPC_LeaderGUID; - ObjectGuid NPC_IceWallTargetGUID[4]; - ObjectGuid NPC_AltarBunnyGUID; - ObjectGuid NPC_QuelDelarGUID; - ObjectGuid NPC_ShipCaptainGUID; - ObjectGuid GO_FrostmourneGUID; - ObjectGuid GO_FrostmourneAltarGUID; - ObjectGuid GO_FrontDoorGUID; - ObjectGuid GO_ArthasDoorGUID; - ObjectGuid GO_CaveInGUID; - ObjectGuid GO_DoorBeforeThroneGUID; - ObjectGuid GO_DoorAfterThroneGUID; - ObjectGuid GO_IceWallGUID; - - ObjectGuid NPC_TrashGUID[NUM_OF_TRASH]; - bool TrashActive[NUM_OF_TRASH]; - uint8 TrashCounter; - uint32 chosenComposition[8][5]; - uint8 WaveNumber; - uint32 NextWaveTimer; - uint16 CheckPlayersTimer; - uint16 ResumeFirstEventTimer; - uint8 ResumeFirstEventStep; - bool bFinished5Waves; - uint8 reqKillCount; - bool IsDuringLKFight; - uint32 BatteredHiltStatus; - - ObjectGuid NPC_FrostswornGeneralGUID; - ObjectGuid NPC_SpiritualReflectionGUID[5]; - - uint32 outroTimer; - uint8 outroStep; - MotionTransport* T1; - - void Initialize() override - { - EncounterMask = 0; - memset(&TrashActive, 0, sizeof(TrashActive)); - TrashCounter = 0; - memset(&chosenComposition, 0, sizeof(chosenComposition)); - WaveNumber = 0; - NextWaveTimer = 0; - CheckPlayersTimer = 5000; - ResumeFirstEventTimer = 0; - ResumeFirstEventStep = 0; - bFinished5Waves = false; - reqKillCount = 0; - IsDuringLKFight = false; - BatteredHiltStatus = 0; - - outroTimer = 0; - outroStep = 0; - T1 = nullptr; - } - - bool IsEncounterInProgress() const override - { - return (instance->HavePlayers() && WaveNumber) || IsDuringLKFight; // during LK fight npcs are active and will unset this variable - } - - void OnCreatureCreate(Creature* creature) override - { - switch (creature->GetEntry()) - { - case NPC_SYLVANAS_PART1: + case NPC_SYLVANAS_PART1: + creature->SetVisible(false); + creature->SetSpeed(MOVE_RUN, 1.1); + if (GetTeamIdInInstance() == TEAM_ALLIANCE) + creature->UpdateEntry(NPC_JAINA_PART1); + creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); + creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + break; + case NPC_DARK_RANGER_LORALEN: + creature->SetVisible(false); + if (GetTeamIdInInstance() == TEAM_ALLIANCE) + creature->UpdateEntry(NPC_ARCHMAGE_KORELN); + break; + case NPC_UTHER: + creature->SetVisible(false); + creature->SetReactState(REACT_PASSIVE); + break; + case NPC_LICH_KING_EVENT: + creature->SetVisible(false); + creature->SetReactState(REACT_PASSIVE); + creature->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); + break; + case NPC_FALRIC: + creature->SetVisible(false); + creature->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); + break; + case NPC_MARWYN: + creature->SetVisible(false); + creature->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); + break; + case NPC_WAVE_MERCENARY: + case NPC_WAVE_FOOTMAN: + case NPC_WAVE_RIFLEMAN: + case NPC_WAVE_PRIEST: + case NPC_WAVE_MAGE: + if (_trashCounter < NUM_OF_TRASH) + _trashGUID[_trashCounter++] = creature->GetGUID(); + if (GetBossState(DATA_MARWYN) != DONE && !creature->IsAlive()) + creature->Respawn(); + creature->SetVisible(false); + break; + case NPC_FROSTSWORN_GENERAL: + if (GetBossState(DATA_MARWYN) != DONE) + { creature->SetVisible(false); - creature->SetSpeed(MOVE_RUN, 1.1); - NPC_LeaderIntroGUID = creature->GetGUID(); - if (GetTeamIdInInstance() == TEAM_ALLIANCE) - creature->UpdateEntry(NPC_JAINA_PART1); + creature->SetReactState(REACT_PASSIVE); + } + break; + case NPC_SPIRITUAL_REFLECTION: + for (uint8 i = 0; i < MAX_SPIRITUAL_REFLECTIONS; ++i) + if (!_spiritualReflectionGUID[i]) + { + _spiritualReflectionGUID[i] = creature->GetGUID(); + break; + } + creature->SetVisible(false); + break; + case NPC_LICH_KING_BOSS: + if (!GetPersistentData(PERSISTENT_DATA_FROSTSWORN_GENERAL)) + creature->SetVisible(false); + if (!GetPersistentData(PERSISTENT_DATA_LK_INTRO)) + { + creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK2HTIGHT); + creature->CastSpell(creature, SPELL_SOUL_REAPER, true); + } + else if (GetBossState(DATA_LICH_KING) != DONE) + creature->AddAura(GetTeamIdInInstance() == TEAM_ALLIANCE ? SPELL_JAINA_ICE_PRISON : SPELL_SYLVANAS_DARK_BINDING, creature); + else + creature->SetVisible(false); + + creature->SetHealth((creature->GetMaxHealth() * 3) / 4); + creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + break; + case NPC_SYLVANAS_PART2: + if (!creature->IsAlive()) + creature->Respawn(); + creature->SetWalk(false); + creature->SetSheath(SHEATH_STATE_MELEE); + if (GetTeamIdInInstance() == TEAM_ALLIANCE) + creature->UpdateEntry(NPC_JAINA_PART2); + creature->SetHealth(creature->GetMaxHealth() / 20); + if (!GetPersistentData(PERSISTENT_DATA_FROSTSWORN_GENERAL)) + creature->SetVisible(false); + if (!GetPersistentData(PERSISTENT_DATA_LK_INTRO)) + { + creature->SetSheath(SHEATH_STATE_MELEE); + creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, GetTeamIdInInstance() == TEAM_ALLIANCE ? EMOTE_ONESHOT_ATTACK2HTIGHT : EMOTE_ONESHOT_ATTACK1H); creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); - creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + creature->CastSpell(creature, GetTeamIdInInstance() == TEAM_ALLIANCE ? SPELL_JAINA_ICE_BARRIER : SPELL_SYLVANAS_CLOAK_OF_DARKNESS, true); + } + else if (GetBossState(DATA_LICH_KING) != DONE) + { + creature->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); + creature->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + creature->UpdatePosition(LeaderEscapePos, true); + creature->StopMovingOnCurrentPos(); + } + else + { + creature->UpdatePosition(PathWaypoints[PATH_WP_COUNT - 1], true); + creature->StopMovingOnCurrentPos(); + } + creature->SetSheath(SHEATH_STATE_MELEE); + creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + break; + case NPC_ICE_WALL_TARGET: + if (creature->GetPositionX() > 5525.0f) + _iceWallTargetGUID[0] = creature->GetGUID(); + else if (creature->GetPositionX() > 5475.0f) + _iceWallTargetGUID[1] = creature->GetGUID(); + else if (creature->GetPositionX() > 5400.0f) + _iceWallTargetGUID[2] = creature->GetGUID(); + else + _iceWallTargetGUID[3] = creature->GetGUID(); + break; + case NPC_QUEL_DELAR: + creature->SetReactState(REACT_PASSIVE); + break; + } + } + + void OnGameObjectCreate(GameObject* go) override + { + switch (go->GetEntry()) + { + case GO_FROSTMOURNE: + HandleGameObject(ObjectGuid::Empty, false, go); + if (GetPersistentData(PERSISTENT_DATA_INTRO)) + go->SetPhaseMask(2, true); + break; + case GO_FRONT_DOOR: + HandleGameObject(ObjectGuid::Empty, true, go); + break; + case GO_ARTHAS_DOOR: + HandleGameObject(ObjectGuid::Empty, GetBossState(DATA_MARWYN) == DONE, go); + break; + } + + InstanceScript::OnGameObjectCreate(go); + } + + bool SetBossState(uint32 type, EncounterState state) override + { + if (!InstanceScript::SetBossState(type, state)) + return false; + + switch (type) + { + case DATA_FALRIC: + if (_waveNumber) + { + if (state == NOT_STARTED) + HandleWaveWipe(); + else if (state == DONE) + _nextWaveTimer = 60000; + } + break; + case DATA_MARWYN: + if (_waveNumber) + { + if (state == NOT_STARTED) + HandleWaveWipe(); + else if (state == DONE) + { + HandleGameObject(GO_FRONT_DOOR, true); + HandleGameObject(GO_ARTHAS_DOOR, true); + if (Creature* general = GetCreature(NPC_FROSTSWORN_GENERAL)) + { + general->SetVisible(true); + general->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + general->SetReactState(REACT_AGGRESSIVE); + general->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE); + } + _waveNumber = 0; + DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVES_ENABLED, 0); + + instance->DoForAllPlayers([](Player* player) + { + player->CastSpell(player, player->GetTeamId() == TEAM_ALLIANCE ? SPELL_HOR_START_QUEST_ALLY : SPELL_HOR_START_QUEST_HORDE, true); + }); + } + } + break; + case DATA_LICH_KING: + if (state == DONE) + { + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + leader->setActive(false); + if (Creature* lichKing = GetCreature(NPC_LICH_KING_BOSS)) + lichKing->setActive(false); + _isLichKingFightActive = false; + _outroStep = 1; + _outroTimer = 0; + } + break; + } + + return true; + } + + void SetData(uint32 type, uint32 data) override + { + switch (type) + { + case DATA_INTRO: + StorePersistentData(PERSISTENT_DATA_INTRO, 1); + StartNextWave(); + break; + case ACTION_SHOW_TRASH: + RandomizeCompositionsAndShow(); + break; + case DATA_FROSTSWORN_GENERAL: + StorePersistentData(PERSISTENT_DATA_FROSTSWORN_GENERAL, 1); + if (data == DONE) + { + if (Creature* lichKing = GetCreature(NPC_LICH_KING_BOSS)) + lichKing->SetVisible(true); + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + leader->SetVisible(true); + } + break; + case ACTION_SPIRITUAL_REFLECTIONS_COPY: + { + uint8 reflectionIdx = 0; + instance->DoForAllPlayers([&](Player* player) + { + if (reflectionIdx >= MAX_SPIRITUAL_REFLECTIONS || !player->IsAlive() || player->IsGameMaster()) + return; + Creature* reflection = instance->GetCreature(_spiritualReflectionGUID[reflectionIdx++]); + if (!reflection) + return; + if (!reflection->IsAlive()) + reflection->Respawn(); + reflection->SetCanFly(true); + reflection->SetDisableGravity(true); + reflection->SetVisible(true); + + Item* weapon = player->GetWeaponForAttack(BASE_ATTACK); + reflection->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, weapon ? weapon->GetEntry() : 0); + weapon = player->GetWeaponForAttack(OFF_ATTACK); + reflection->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, weapon ? weapon->GetEntry() : 0); + weapon = player->GetWeaponForAttack(RANGED_ATTACK); + reflection->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, weapon ? weapon->GetEntry() : 0); + player->CastSpell(reflection, SPELL_HOR_CLONE, true); + player->CastSpell(reflection, SPELL_HOR_CLONE_NAME, true); + }); + break; + } + case ACTION_SPIRITUAL_REFLECTIONS_ACTIVATE: + { + Creature* general = GetCreature(NPC_FROSTSWORN_GENERAL); + if (!general) break; - case NPC_DARK_RANGER_LORALEN: - creature->SetVisible(false); - NPC_GuardGUID = creature->GetGUID(); - if (GetTeamIdInInstance() == TEAM_ALLIANCE) - creature->UpdateEntry(NPC_ARCHMAGE_KORELN); - break; - case NPC_UTHER: - creature->SetVisible(false); - NPC_UtherGUID = creature->GetGUID(); - creature->SetReactState(REACT_PASSIVE); - break; - case NPC_LICH_KING_EVENT: - creature->SetVisible(false); - NPC_LichKingIntroGUID = creature->GetGUID(); - creature->SetReactState(REACT_PASSIVE); - creature->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); - break; - case NPC_FALRIC: - creature->SetVisible(false); - NPC_FalricGUID = creature->GetGUID(); - creature->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); - break; - case NPC_MARWYN: - creature->SetVisible(false); - NPC_MarwynGUID = creature->GetGUID(); - creature->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); + for (uint8 i = 0; i < MAX_SPIRITUAL_REFLECTIONS; ++i) + if (Creature* reflection = instance->GetCreature(_spiritualReflectionGUID[i])) + if (reflection->IsVisible()) + { + reflection->SetInCombatWithZone(); + reflection->SetCanFly(false); + reflection->SetDisableGravity(false); + reflection->GetMotionMaster()->MoveJump(general->GetPositionX(), general->GetPositionY(), general->GetPositionZ(), 20.0f, 10.0f); + } + break; + } + case ACTION_SPIRITUAL_REFLECTIONS_HIDE: + for (uint8 i = 0; i < MAX_SPIRITUAL_REFLECTIONS; ++i) + if (Creature* reflection = instance->GetCreature(_spiritualReflectionGUID[i])) + reflection->AI()->EnterEvadeMode(); + break; + case DATA_LK_INTRO: + StorePersistentData(PERSISTENT_DATA_LK_INTRO, 1); + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + leader->AI()->DoAction(ACTION_START_INTRO); + break; + case ACTION_START_LK_FIGHT: + _isLichKingFightActive = true; + DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT); + DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT); + break; + case ACTION_STOP_LK_FIGHT: + if (!_isLichKingFightActive) break; + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + { + if (!leader->IsAlive()) + { + leader->Respawn(); + if (GetTeamIdInInstance() == TEAM_ALLIANCE) + leader->UpdateEntry(NPC_JAINA_PART2); + } + leader->GetThreatMgr().ClearAllThreat(); + leader->CombatStop(true); + leader->InterruptNonMeleeSpells(true); + leader->GetMotionMaster()->Clear(); + leader->GetMotionMaster()->MoveIdle(); + leader->UpdatePosition(LeaderEscapePos, true); + leader->StopMovingOnCurrentPos(); + leader->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); + leader->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + leader->SetHealth(leader->GetMaxHealth() / 20); + leader->AI()->Reset(); + leader->setActive(false); + leader->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + leader->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + } + if (Creature* lichKing = GetCreature(NPC_LICH_KING_BOSS)) + { + lichKing->GetThreatMgr().ClearAllThreat(); + lichKing->CombatStop(true); + lichKing->InterruptNonMeleeSpells(true); + lichKing->GetMotionMaster()->Clear(); + lichKing->GetMotionMaster()->MoveIdle(); + lichKing->UpdatePosition(lichKing->GetHomePosition(), true); + lichKing->StopMovingOnCurrentPos(); + lichKing->RemoveAllAuras(); + lichKing->AddAura(GetTeamIdInInstance() == TEAM_ALLIANCE ? SPELL_JAINA_ICE_PRISON : SPELL_SYLVANAS_DARK_BINDING, lichKing); + lichKing->AI()->Reset(); + lichKing->setActive(false); + lichKing->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + lichKing->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + lichKing->SetSpeed(MOVE_RUN, lichKing->GetCreatureTemplate()->speed_run); + } + _isLichKingFightActive = false; + _outroTimer = 0; + _outroStep = 0; + [[fallthrough]]; + case ACTION_DELETE_ICE_WALL: + HandleGameObject(GO_ICE_WALL, true); + break; + case DATA_LICH_KING: + SetBossState(DATA_LICH_KING, static_cast(data)); + break; + case DATA_BATTERED_HILT: + { + if (GetPersistentData(PERSISTENT_DATA_BATTERED_HILT)) + return; + + switch (data) + { + case 1: + StorePersistentData(PERSISTENT_DATA_BATTERED_HILT, 1); + break; + case 2: + if (_batteredHiltStatus) + break; + _batteredHiltStatus |= BHSF_STARTED; + if (Creature* bunny = GetCreature(NPC_ALTAR_BUNNY)) + bunny->CastSpell(bunny, SPELL_FROSTMOURNE_ALTAR_GLOW, true); + if (Creature* uther = GetCreature(NPC_UTHER)) + uther->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*uther, 1), 3s); + break; + case 3: + if (!(_batteredHiltStatus & BHSF_STARTED) || (_batteredHiltStatus & BHSF_THROWN)) + break; + _batteredHiltStatus |= BHSF_THROWN; + if (Creature* uther = GetCreature(NPC_UTHER)) + uther->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*uther, 3), 5500ms); + break; + case 4: + if (Creature* quelDelar = GetCreature(NPC_QUEL_DELAR)) + { + quelDelar->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + quelDelar->SetSpeed(MOVE_RUN, 2.5f); + } + break; + case 5: + if (Creature* uther = GetCreature(NPC_UTHER)) + uther->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*uther, 6), 3s); + break; + case 6: + if (Creature* quelDelar = GetCreature(NPC_QUEL_DELAR)) + { + quelDelar->SetSpeed(MOVE_RUN, quelDelar->GetCreatureTemplate()->speed_run); + quelDelar->GetMotionMaster()->MoveLand(0, quelDelar->GetPositionX(), quelDelar->GetPositionY(), 707.70f, 7.0f); + } + break; + case 7: + if (Creature* quelDelar = GetCreature(NPC_QUEL_DELAR)) + { + quelDelar->SetReactState(REACT_AGGRESSIVE); + quelDelar->SetImmuneToAll(false); + quelDelar->RemoveAurasDueToSpell(SPELL_QUEL_DELAR_HATRED); + } + break; + case 8: + if (Creature* quelDelar = GetCreature(NPC_QUEL_DELAR)) + quelDelar->SetInCombatWithZone(); + break; + case 9: + StorePersistentData(PERSISTENT_DATA_BATTERED_HILT, 1); + _batteredHiltStatus |= BHSF_FINISHED; + break; + } + return; + } + } + } + + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_WAVE_NUMBER: + return static_cast(_waveNumber); + case DATA_BATTERED_HILT: + return _batteredHiltStatus; + } + + return 0; + } + + ObjectGuid GetGuidData(uint32 type) const override + { + switch (type) + { + case NPC_ICE_WALL_TARGET: + case NPC_ICE_WALL_TARGET + 1: + case NPC_ICE_WALL_TARGET + 2: + case NPC_ICE_WALL_TARGET + 3: + return _iceWallTargetGUID[type - NPC_ICE_WALL_TARGET]; + default: + return GetObjectGuid(type); + } + } + + void OnUnitDeath(Unit* unit) override + { + if (_waveNumber && _remainingTrashKills) + { + switch (unit->GetEntry()) + { case NPC_WAVE_MERCENARY: case NPC_WAVE_FOOTMAN: case NPC_WAVE_RIFLEMAN: case NPC_WAVE_PRIEST: case NPC_WAVE_MAGE: - if (TrashCounter < NUM_OF_TRASH) - NPC_TrashGUID[TrashCounter++] = creature->GetGUID(); - if (!(EncounterMask & (1 << DATA_MARWYN)) && !creature->IsAlive()) - creature->Respawn(); - creature->SetVisible(false); - break; - case NPC_FROSTSWORN_GENERAL: - if (!(EncounterMask & (1 << DATA_MARWYN))) - { - creature->SetVisible(false); - creature->SetReactState(REACT_PASSIVE); - } - NPC_FrostswornGeneralGUID = creature->GetGUID(); - break; - case NPC_SPIRITUAL_REFLECTION: - for (uint8 i = 0; i < 5; ++i) - if (!NPC_SpiritualReflectionGUID[i]) - { - NPC_SpiritualReflectionGUID[i] = creature->GetGUID(); - break; - } - creature->SetVisible(false); - break; - case NPC_LICH_KING_BOSS: - if (!(EncounterMask & (1 << DATA_FROSTSWORN_GENERAL))) - creature->SetVisible(false); - if (!(EncounterMask & (1 << DATA_LK_INTRO))) - { - creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK2HTIGHT); //the fight cannot be in the form of an emote, it is causing bugs - creature->CastSpell(creature, SPELL_SOUL_REAPER, true); - } - else if (!(EncounterMask & (1 << DATA_LICH_KING))) - creature->AddAura(GetTeamIdInInstance() == TEAM_ALLIANCE ? SPELL_JAINA_ICE_PRISON : SPELL_SYLVANAS_DARK_BINDING, creature); - else - creature->SetVisible(false); - - NPC_LichKingGUID = creature->GetGUID(); - creature->SetHealth((creature->GetMaxHealth() * 3) / 4); - creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - break; - case NPC_SYLVANAS_PART2: - if (!creature->IsAlive()) - creature->Respawn(); - NPC_LeaderGUID = creature->GetGUID(); - creature->SetWalk(false); - creature->SetSheath(SHEATH_STATE_MELEE); - if (GetTeamIdInInstance() == TEAM_ALLIANCE) - creature->UpdateEntry(NPC_JAINA_PART2); - creature->SetWalk(false); - creature->SetHealth(creature->GetMaxHealth() / 20); - if (!(EncounterMask & (1 << DATA_FROSTSWORN_GENERAL))) - creature->SetVisible(false); - if (!(EncounterMask & (1 << DATA_LK_INTRO))) - { - creature->SetSheath(SHEATH_STATE_MELEE); - creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, GetTeamIdInInstance() == TEAM_ALLIANCE ? EMOTE_ONESHOT_ATTACK2HTIGHT : EMOTE_ONESHOT_ATTACK1H); //the fight cannot be in the form of an emote, it is causing bugs. - creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); - creature->CastSpell(creature, GetTeamIdInInstance() == TEAM_ALLIANCE ? SPELL_JAINA_ICE_BARRIER : SPELL_SYLVANAS_CLOAK_OF_DARKNESS, true); - } - else if (!(EncounterMask & (1 << DATA_LICH_KING))) - { - creature->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); - creature->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - creature->UpdatePosition(LeaderEscapePos, true); - creature->StopMovingOnCurrentPos(); - } - else - { - creature->UpdatePosition(PathWaypoints[PATH_WP_COUNT - 1], true); - creature->StopMovingOnCurrentPos(); - } - creature->SetSheath(SHEATH_STATE_MELEE); - creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - break; - case NPC_ICE_WALL_TARGET: - if (creature->GetPositionX() > 5525.0f) - NPC_IceWallTargetGUID[0] = creature->GetGUID(); - else if (creature->GetPositionX() > 5475.0f) - NPC_IceWallTargetGUID[1] = creature->GetGUID(); - else if (creature->GetPositionX() > 5400.0f) - NPC_IceWallTargetGUID[2] = creature->GetGUID(); - else - NPC_IceWallTargetGUID[3] = creature->GetGUID(); - break; - case NPC_ALTAR_BUNNY: - NPC_AltarBunnyGUID = creature->GetGUID(); - break; - case NPC_QUEL_DELAR: - NPC_QuelDelarGUID = creature->GetGUID(); - creature->SetReactState(REACT_PASSIVE); - break; - case NPC_HIGH_CAPTAIN_JUSTIN_BARLETT: - case NPC_SKY_REAVER_KORM_BLACKSKAR: - NPC_ShipCaptainGUID = creature->GetGUID(); + if ((--_remainingTrashKills) == 0 && _waveNumber % 5 && _nextWaveTimer > 5000) + _nextWaveTimer = 5000; break; } } - void OnGameObjectCreate(GameObject* go) override - { - switch (go->GetEntry()) + if (unit->GetEntry() == NPC_QUEL_DELAR) + if (Creature* uther = GetCreature(NPC_UTHER)) { - case GO_FROSTMOURNE: - GO_FrostmourneGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, false, go); - if (EncounterMask & (1 << DATA_INTRO)) - go->SetPhaseMask(2, true); - break; - case GO_FROSTMOURNE_ALTAR: - GO_FrostmourneAltarGUID = go->GetGUID(); - break; - case GO_FRONT_DOOR: - GO_FrontDoorGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, true, go); - break; - case GO_ARTHAS_DOOR: - GO_ArthasDoorGUID = go->GetGUID(); - HandleGameObject(ObjectGuid::Empty, (EncounterMask & (1 << DATA_MARWYN)), go); - break; - case GO_CAVE_IN: - GO_CaveInGUID = go->GetGUID(); - break; - case GO_DOOR_BEFORE_THRONE: - GO_DoorBeforeThroneGUID = go->GetGUID(); - break; - case GO_DOOR_AFTER_THRONE: - GO_DoorAfterThroneGUID = go->GetGUID(); - break; - case GO_ICE_WALL: - GO_IceWallGUID = go->GetGUID(); - break; + uther->SetStandState(UNIT_STAND_STATE_STAND); + uther->SetWalk(false); + uther->GetMotionMaster()->MovePoint(0, 5313.92f, 1989.36f, 707.70f); + uther->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*uther, 9), 7s); } - } + } - void SetData(uint32 type, uint32 data) override + void RandomizeCompositionsAndShow() + { + uint8 r1 = urand(0, 1); + uint8 r2 = urand(2, 3); + for (uint8 i = 0; i < MAX_SPIRITUAL_REFLECTIONS; ++i) { - switch (type) - { - case DATA_INTRO: - EncounterMask |= (1 << DATA_INTRO); - AddWave1(); - break; - case ACTION_SHOW_TRASH: - RandomizeCompositionsAndShow(); - break; - case DATA_FALRIC: - if (WaveNumber) - { - if (data == NOT_STARTED) - DoWipe1(); - else if (data == DONE) - { - NextWaveTimer = 60000; - EncounterMask |= (1 << DATA_FALRIC); - } - } - break; - case DATA_MARWYN: - if (WaveNumber) - { - if (data == NOT_STARTED) - DoWipe1(); - else if (data == DONE) - { - EncounterMask |= (1 << DATA_MARWYN); - HandleGameObject(GO_FrontDoorGUID, true); - HandleGameObject(GO_ArthasDoorGUID, true); - if (Creature* c = instance->GetCreature(NPC_FrostswornGeneralGUID)) - { - c->SetVisible(true); - c->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - c->SetReactState(REACT_AGGRESSIVE); - } - if (Creature* c = instance->GetCreature(NPC_FrostswornGeneralGUID)) - { - c->HandleEmoteCommand(EMOTE_ONESHOT_EMERGE); - } - WaveNumber = 0; - DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVES_ENABLED, 0); - - // give quest - Map::PlayerList const& pl = instance->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - p->CastSpell(p, p->GetTeamId() == TEAM_ALLIANCE ? SPELL_HOR_START_QUEST_ALLY : SPELL_HOR_START_QUEST_HORDE, true); - } - } - break; - case DATA_FROSTSWORN_GENERAL: - EncounterMask |= (1 << DATA_FROSTSWORN_GENERAL); - if (data == DONE) - { - if (Creature* c = instance->GetCreature(NPC_LichKingGUID)) - c->SetVisible(true); - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - c->SetVisible(true); - } - break; - case ACTION_SPIRITUAL_REFLECTIONS_COPY: - { - uint8 i = 0; - Map::PlayerList const& pl = instance->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - if (p->IsAlive() && !p->IsGameMaster()) - if (Creature* c = instance->GetCreature(NPC_SpiritualReflectionGUID[i++])) - { - if (!c->IsAlive()) - c->Respawn(); - c->SetCanFly(true); - c->SetDisableGravity(true); - c->SetVisible(true); - - Item* i; - i = p->GetWeaponForAttack(BASE_ATTACK); - c->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, i ? i->GetEntry() : 0); - i = p->GetWeaponForAttack(OFF_ATTACK); - c->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, i ? i->GetEntry() : 0); - i = p->GetWeaponForAttack(RANGED_ATTACK); - c->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, i ? i->GetEntry() : 0); - p->CastSpell(c, SPELL_HOR_CLONE, true); - p->CastSpell(c, SPELL_HOR_CLONE_NAME, true); - } - } - break; - case ACTION_SPIRITUAL_REFLECTIONS_ACTIVATE: - if (Creature* fg = instance->GetCreature(NPC_FrostswornGeneralGUID)) - for (uint8 i = 0; i < 5; ++i) - if (Creature* c = instance->GetCreature(NPC_SpiritualReflectionGUID[i])) - if (c->IsVisible()) - { - c->SetInCombatWithZone(); - c->SetCanFly(false); - c->SetDisableGravity(false); - c->GetMotionMaster()->MoveJump(fg->GetPositionX(), fg->GetPositionY(), fg->GetPositionZ(), 20.0f, 10.0f); - } - break; - case ACTION_SPIRITUAL_REFLECTIONS_HIDE: - for (uint8 i = 0; i < 5; ++i) - if (Creature* c = instance->GetCreature(NPC_SpiritualReflectionGUID[i])) - c->AI()->EnterEvadeMode(); - break; - case DATA_LK_INTRO: - EncounterMask |= (1 << DATA_LK_INTRO); - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - c->AI()->DoAction(ACTION_START_INTRO); - break; - case ACTION_START_LK_FIGHT: - IsDuringLKFight = true; - DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT); - DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT); - break; - case ACTION_STOP_LK_FIGHT: - if (!IsDuringLKFight) + _chosenComposition[0][i] = allowedCompositions[r1][i]; + _chosenComposition[1][i] = allowedCompositions[r1 == 0 ? 1 : 0][i]; + _chosenComposition[2][i] = allowedCompositions[r2][i]; + _chosenComposition[3][i] = allowedCompositions[r2 == 2 ? 3 : 2][i]; + } + bool left[4] = {true, true, true, true}; + for (uint8 k = 4; k > 0; --k) + { + uint8 r = urand(0, k - 1); + uint8 ur = 0; + for (uint8 j = 0; j < 4; ++j) + if (left[j]) + { + if (ur == r) { + left[j] = false; + for (uint8 i = 0; i < MAX_SPIRITUAL_REFLECTIONS; ++i) + _chosenComposition[8 - k][i] = allowedCompositions[j + 4][i]; break; } - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - { - if (!c->IsAlive()) - { - c->Respawn(); - if (GetTeamIdInInstance() == TEAM_ALLIANCE) - c->UpdateEntry(NPC_JAINA_PART2); - } - c->GetThreatMgr().ClearAllThreat(); - c->CombatStop(true); - c->InterruptNonMeleeSpells(true); - c->GetMotionMaster()->Clear(); - c->GetMotionMaster()->MoveIdle(); - c->UpdatePosition(LeaderEscapePos, true); - c->StopMovingOnCurrentPos(); - c->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); - c->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - c->SetHealth(c->GetMaxHealth() / 20); - c->AI()->Reset(); - c->setActive(false); - c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - c->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - } - if (Creature* c = instance->GetCreature(NPC_LichKingGUID)) - { - c->GetThreatMgr().ClearAllThreat(); - c->CombatStop(true); - c->InterruptNonMeleeSpells(true); - c->GetMotionMaster()->Clear(); - c->GetMotionMaster()->MoveIdle(); - c->UpdatePosition(c->GetHomePosition(), true); - c->StopMovingOnCurrentPos(); - c->RemoveAllAuras(); - c->AddAura(GetTeamIdInInstance() == TEAM_ALLIANCE ? SPELL_JAINA_ICE_PRISON : SPELL_SYLVANAS_DARK_BINDING, c); - c->AI()->Reset(); - c->setActive(false); - c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - c->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - c->SetSpeed(MOVE_RUN, c->GetCreatureTemplate()->speed_run); - } - IsDuringLKFight = false; - outroTimer = 0; - outroStep = 0; - [[fallthrough]]; - case ACTION_DELETE_ICE_WALL: - HandleGameObject(GO_IceWallGUID, true); - GO_IceWallGUID.Clear(); - break; - case DATA_LICH_KING: - if (data == DONE) - { - EncounterMask |= (1 << DATA_LICH_KING); - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - c->setActive(false); - if (Creature* c = instance->GetCreature(NPC_LichKingGUID)) - c->setActive(false); - IsDuringLKFight = false; - outroStep = 1; - outroTimer = 0; - } - break; - case DATA_BATTERED_HILT: - { - if (EncounterMask & (1 << DATA_BATTERED_HILT)) - return; - - switch (data) - { - case 1: // talked to leader - EncounterMask |= (1 << DATA_BATTERED_HILT); - SaveToDB(); - break; - case 2: - if (BatteredHiltStatus) - break; - BatteredHiltStatus |= BHSF_STARTED; - if (Creature* c = instance->GetCreature(NPC_AltarBunnyGUID)) - c->CastSpell(c, 70720, true); - if (Creature* c = instance->GetCreature(NPC_UtherGUID)) - c->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*c, 1), 3s); - break; - case 3: - if ((BatteredHiltStatus & BHSF_STARTED) == 0 || (BatteredHiltStatus & BHSF_THROWN)) - break; - BatteredHiltStatus |= BHSF_THROWN; - if (Creature* c = instance->GetCreature(NPC_UtherGUID)) - { - c->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*c, 3), 5500ms); - } - break; - case 4: - if (Creature* c = instance->GetCreature(NPC_QuelDelarGUID)) - { - c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - c->SetSpeed(MOVE_RUN, 2.5f); - } - break; - case 5: - if (Creature* c = instance->GetCreature(NPC_UtherGUID)) - c->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*c, 6), 3s); - break; - case 6: - if (Creature* c = instance->GetCreature(NPC_QuelDelarGUID)) - { - c->SetSpeed(MOVE_RUN, c->GetCreatureTemplate()->speed_run); - c->GetMotionMaster()->MoveLand(0, c->GetPositionX(), c->GetPositionY(), 707.70f, 7.0f); - } - break; - case 7: - if (Creature* c = instance->GetCreature(NPC_QuelDelarGUID)) - { - c->SetReactState(REACT_AGGRESSIVE); - c->SetImmuneToAll(false); - c->RemoveAurasDueToSpell(70300); - } - break; - case 8: - if (Creature* c = instance->GetCreature(NPC_QuelDelarGUID)) - c->SetInCombatWithZone(); - break; - case 9: - EncounterMask |= (1 << DATA_BATTERED_HILT); - BatteredHiltStatus |= BHSF_FINISHED; - SaveToDB(); - break; - } - } - return; - } - - if (data == DONE) - SaveToDB(); - } - - uint32 GetData(uint32 type) const override - { - switch (type) - { - case DATA_INTRO: - case DATA_FALRIC: - case DATA_MARWYN: - case DATA_FROSTSWORN_GENERAL: - case DATA_LK_INTRO: - case DATA_LICH_KING: - return EncounterMask & (1 << type); - case DATA_WAVE_NUMBER: - return (uint32)WaveNumber; - case DATA_BATTERED_HILT: - return BatteredHiltStatus; - } - - return 0; - } - - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case NPC_DARK_RANGER_LORALEN: - return NPC_GuardGUID; - case NPC_LICH_KING_EVENT: - return NPC_LichKingIntroGUID; - case NPC_UTHER: - return NPC_UtherGUID; - case DATA_FALRIC: - return NPC_FalricGUID; - case DATA_MARWYN: - return NPC_MarwynGUID; - case NPC_LICH_KING_BOSS: - return NPC_LichKingGUID; - case NPC_SYLVANAS_PART2: - return NPC_LeaderGUID; - case NPC_ICE_WALL_TARGET: - case NPC_ICE_WALL_TARGET+1: - case NPC_ICE_WALL_TARGET+2: - case NPC_ICE_WALL_TARGET+3: - return NPC_IceWallTargetGUID[type - NPC_ICE_WALL_TARGET]; - case GO_FROSTMOURNE: - return GO_FrostmourneGUID; - case GO_ARTHAS_DOOR: - return GO_ArthasDoorGUID; - case GO_FRONT_DOOR: - return GO_FrontDoorGUID; - } - return ObjectGuid::Empty; - } - - void ReadSaveDataMore(std::istringstream& data) override - { - data >> EncounterMask; - BatteredHiltStatus = (EncounterMask & (1 << DATA_BATTERED_HILT)) ? BHSF_FINISHED : BHSF_NONE; - } - - void WriteSaveDataMore(std::ostringstream& data) override - { - data << EncounterMask; - } - - void OnUnitDeath(Unit* unit) override - { - if (WaveNumber && reqKillCount) - if (unit->GetEntry() == NPC_WAVE_MERCENARY || unit->GetEntry() == NPC_WAVE_FOOTMAN || unit->GetEntry() == NPC_WAVE_RIFLEMAN || unit->GetEntry() == NPC_WAVE_PRIEST || unit->GetEntry() == NPC_WAVE_MAGE) - if ((--reqKillCount) == 0 && WaveNumber % 5 && NextWaveTimer > 5000) - NextWaveTimer = 5000; - - if (unit->GetEntry() == NPC_QUEL_DELAR) - if (Creature* c = instance->GetCreature(NPC_UtherGUID)) - { - c->SetStandState(UNIT_STAND_STATE_STAND); - c->SetWalk(false); - c->GetMotionMaster()->MovePoint(0, 5313.92f, 1989.36f, 707.70f); - c->m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(*c, 9), 7s); + ++ur; } } - - void RandomizeCompositionsAndShow() + if (_falricPhaseComplete) { - uint8 r1 = urand(0, 1), r2 = urand(2, 3); - for (uint8 i = 0; i < 5; ++i) + for (; _waveNumber < 4; ++_waveNumber) { - chosenComposition[0][i] = allowedCompositions[r1][i]; - chosenComposition[1][i] = allowedCompositions[r1 == 0 ? 1 : 0][i]; - chosenComposition[2][i] = allowedCompositions[r2][i]; - chosenComposition[3][i] = allowedCompositions[r2 == 2 ? 3 : 2][i]; - } - bool left[4] = {true, true, true, true}; - for (uint8 k = 4; k > 0; --k) - { - uint8 r = urand(0, k - 1); - uint8 ur = 0; - for (uint8 j = 0; j < 4; ++j) - if (left[j]) - { - if (ur == r) - { - left[j] = false; - for (uint8 i = 0; i < 5; ++i) - chosenComposition[8 - k][i] = allowedCompositions[j + 4][i]; - break; - } - ++ur; - } - } - if (bFinished5Waves) - { - for (; WaveNumber < 4; ++WaveNumber) + uint8 numToActivate = (_waveNumber <= 1) ? 3 : 4; + + for (uint8 i = 0; i < numToActivate; ++i) { - uint8 num_to_activate; - if (WaveNumber <= 1) - num_to_activate = 3; - else - num_to_activate = 4; - - for (uint8 i = 0; i < num_to_activate; ++i) - { - uint32 entry = chosenComposition[WaveNumber][i]; - bool forward = !!urand(0, 1); - for (int8 j = (forward ? 0 : NUM_OF_TRASH - 1); (forward ? j= 0); (forward ? ++j : --j)) - if (!TrashActive[j]) - if (Creature* c = instance->GetCreature(NPC_TrashGUID[j])) - if (c->GetEntry() == entry) - { - TrashActive[j] = true; - Unit::Kill(c, c); - break; - } - } - } - WaveNumber = 5; - } - - for (uint8 i = 0; i < NUM_OF_TRASH; ++i) - if (!TrashActive[i]) - if (Creature* c = instance->GetCreature(NPC_TrashGUID[i])) - { - c->SetVisible(true); - c->CastSpell(c, SPELL_WELL_OF_SOULS_VISUAL, false); - } - } - - void AddWave1() - { - if (WaveNumber >= 10) - return; - - ++WaveNumber; - if (WaveNumber >= 6) - bFinished5Waves = true; - - DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVES_ENABLED, 1); - DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVE_COUNT, WaveNumber); - HandleGameObject(GO_FrontDoorGUID, false); - - // some of them could go back to spawn due to vanish, etc. - // on activating next wave make those attack again - for (uint8 i = 0; i < NUM_OF_TRASH; ++i) - if (TrashActive[i]) - if (Creature* c = instance->GetCreature(NPC_TrashGUID[i])) - if (c->IsAlive() && !c->IsInCombat()) - c->AI()->DoAction(1); - - if (WaveNumber == 5 || WaveNumber == 10) - { - NextWaveTimer = 0; - if (WaveNumber == 5) - { - if (Creature* falric = instance->GetCreature(NPC_FalricGUID)) - { - if (falric->IsAlive()) - falric->AI()->DoAction(1); - else - NextWaveTimer = 1; - } - } - else - { - if (Creature* marwyn = instance->GetCreature(NPC_MarwynGUID)) - if (marwyn->IsAlive()) // should always be true, but just in case - marwyn->AI()->DoAction(1); - } - } - else - { - NextWaveTimer = 150000; - - uint8 num_to_activate = 5; - if (WaveNumber <= 2) - num_to_activate = 3; - else if (WaveNumber <= 4) - num_to_activate = 4; - reqKillCount += num_to_activate; - for (uint8 i = 0; i < num_to_activate; ++i) - { - uint32 entry = chosenComposition[WaveNumber - (WaveNumber > 5 ? 2 : 1)][i]; + uint32 entry = _chosenComposition[_waveNumber][i]; bool forward = !!urand(0, 1); - for (int8 j = (forward ? 0 : NUM_OF_TRASH - 1); (forward ? j= 0); (forward ? ++j : --j)) - if (!TrashActive[j]) - if (Creature* c = instance->GetCreature(NPC_TrashGUID[j])) + for (int8 j = (forward ? 0 : NUM_OF_TRASH - 1); (forward ? j < NUM_OF_TRASH : j >= 0); (forward ? ++j : --j)) + if (!_trashActive[j]) + if (Creature* c = instance->GetCreature(_trashGUID[j])) if (c->GetEntry() == entry) { - TrashActive[j] = true; - c->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - c->SetImmuneToAll(false); - c->AI()->DoAction(1); + _trashActive[j] = true; + Unit::Kill(c, c); break; } } } + _waveNumber = 5; } - void DoWipe1() - { - if (!WaveNumber) - return; - - DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVES_ENABLED, 0); - DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVE_COUNT, 0); - HandleGameObject(GO_FrontDoorGUID, true); - - TrashCounter = NUM_OF_TRASH; - WaveNumber = 0; - NextWaveTimer = 0; - memset(&chosenComposition, 0, sizeof(chosenComposition)); - - for (uint8 i = 0; i < NUM_OF_TRASH; ++i) - if (TrashActive[i]) - if (Creature* c = instance->GetCreature(NPC_TrashGUID[i])) - { - c->GetThreatMgr().ClearAllThreat(); - c->CombatStop(true); - c->InterruptNonMeleeSpells(true); - c->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - c->SetImmuneToAll(true); - c->Respawn(true); - c->UpdatePosition(c->GetHomePosition(), true); - c->StopMovingOnCurrentPos(); - } - memset(&TrashActive, 0, sizeof(TrashActive)); - if (Creature* falric = instance->GetCreature(NPC_FalricGUID)) - falric->AI()->EnterEvadeMode(); - if (Creature* marwyn = instance->GetCreature(NPC_MarwynGUID)) - marwyn->AI()->EnterEvadeMode(); - - ResumeFirstEventTimer = 5000; - ResumeFirstEventStep = 2; - reqKillCount = 0; - } - - void Update(uint32 diff) override - { - if (!instance->HavePlayers()) - return; - - if (CheckPlayersTimer <= diff) - { - CheckPlayersTimer = 5000; - if ((EncounterMask & (1 << DATA_INTRO)) && !(EncounterMask & (1 << DATA_MARWYN))) // first event + for (uint8 i = 0; i < NUM_OF_TRASH; ++i) + if (!_trashActive[i]) + if (Creature* c = instance->GetCreature(_trashGUID[i])) { - Map::PlayerList const& pl = instance->GetPlayers(); - if (WaveNumber || NextWaveTimer) - { - bool allDead = true; - bool outOfRange = false; - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - { - if (p->IsGameMaster()) - continue; - if (p->IsAlive()) - allDead = false; - if (p->GetExactDist2d(&CenterPos) > MAX_DIST_FROM_CENTER_IN_COMBAT) - { - outOfRange = true; - break; - } - } - if (allDead || outOfRange) - DoWipe1(); - } - else if (!ResumeFirstEventTimer) - { - bool allInRangeAndAlive = (instance->GetPlayersCountExceptGMs() > 0); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - if (!p->IsGameMaster() && (p->GetExactDist2d(&CenterPos) > MAX_DIST_FROM_CENTER_TO_START || !p->IsAlive())) - { - allInRangeAndAlive = false; - break; - } - if (allInRangeAndAlive) - { - ResumeFirstEventTimer = 1; - ResumeFirstEventStep = 0; - } - } + c->SetVisible(true); + c->CastSpell(c, SPELL_WELL_OF_SOULS_VISUAL, false); + } + } + + void StartNextWave() + { + if (_waveNumber >= 10) + return; + + ++_waveNumber; + if (_waveNumber >= 6) + _falricPhaseComplete = true; + + DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVES_ENABLED, 1); + DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVE_COUNT, _waveNumber); + HandleGameObject(GO_FRONT_DOOR, false); + + for (uint8 i = 0; i < NUM_OF_TRASH; ++i) + if (_trashActive[i]) + if (Creature* c = instance->GetCreature(_trashGUID[i])) + if (c->IsAlive() && !c->IsInCombat()) + c->AI()->DoAction(1); + + if (_waveNumber == 5 || _waveNumber == 10) + { + _nextWaveTimer = 0; + if (_waveNumber == 5) + { + if (Creature* falric = GetCreature(DATA_FALRIC)) + { + if (falric->IsAlive()) + falric->AI()->DoAction(1); + else + _nextWaveTimer = 1; } } else - CheckPlayersTimer -= diff; - - if (NextWaveTimer) { - if (NextWaveTimer <= diff) - { - NextWaveTimer = 0; - AddWave1(); - } - else - NextWaveTimer -= diff; - } - - if (ResumeFirstEventTimer) - { - if (ResumeFirstEventTimer <= diff) - { - switch (ResumeFirstEventStep) // After a wipe - { - case 0: - if (Creature* pFalric = instance->GetCreature(NPC_FalricGUID)) - { - pFalric->UpdatePosition(5274.9f, 2039.2f, 709.319f, 5.4619f, true); - pFalric->StopMovingOnCurrentPos(); - pFalric->SetVisible(true); - if (pFalric->IsAlive()) - { - pFalric->GetMotionMaster()->MovePoint(0, FalricMovePos); - if (Aura* a = pFalric->AddAura(SPELL_SHADOWMOURNE_VISUAL, pFalric)) - a->SetDuration(8000); - } - } - if (Creature* pMarwyn = instance->GetCreature(NPC_MarwynGUID)) - { - pMarwyn->UpdatePosition(5343.77f, 1973.86f, 709.319f, 2.35173f, true); - pMarwyn->StopMovingOnCurrentPos(); - pMarwyn->SetVisible(true); - if (pMarwyn->IsAlive()) - { - pMarwyn->GetMotionMaster()->MovePoint(0, MarwynMovePos); - if (Aura* a = pMarwyn->AddAura(SPELL_SHADOWMOURNE_VISUAL, pMarwyn)) - a->SetDuration(8000); - } - pMarwyn->AI()->Talk(EMOTE_MARWYN_INTRO_SPIRIT); - } - ++ResumeFirstEventStep; - ResumeFirstEventTimer = 7500; - break; - case 1: - if (Creature* pFalric = instance->GetCreature(NPC_FalricGUID)) - { - if (pFalric->IsAlive()) - { - pFalric->AI()->Talk(SAY_FALRIC_INTRO_2); // Between wave 1 and 4 - } - else - { - if (Creature* marwyn = instance->GetCreature(NPC_MarwynGUID)) - { - marwyn->AI()->Talk(SAY_MARWYN_WIPE_AFTER_FALRIC); // Between wave 6 and 9 - } - } - } - SetData(ACTION_SHOW_TRASH, 1); - ResumeFirstEventStep = 0; - ResumeFirstEventTimer = 0; - NextWaveTimer = 7000; - break; - default: - for (uint8 i = 0; i < NUM_OF_TRASH; ++i) - if (Creature* c = instance->GetCreature(NPC_TrashGUID[i])) - c->SetVisible(false); - if (Creature* falric = instance->GetCreature(NPC_FalricGUID)) - falric->SetVisible(false); - if (Creature* marwyn = instance->GetCreature(NPC_MarwynGUID)) - marwyn->SetVisible(false); - ResumeFirstEventStep = 0; - ResumeFirstEventTimer = 0; - break; - } - } - else - ResumeFirstEventTimer -= diff; - } - if (outroStep) - { - if (outroTimer <= diff) - { - switch (outroStep) - { - case 1: - if (Creature* lk = instance->GetCreature(NPC_LichKingGUID)) - { - lk->UpdatePosition(PathWaypoints[PATH_WP_COUNT - 2], true); - lk->StopMovingOnCurrentPos(); - lk->RemoveAllAuras(); - lk->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_CUSTOM_SPELL_02); - if (!lk->IsVisible()) - lk->SetVisible(true); - if (Creature* leader = instance->GetCreature(NPC_LeaderGUID)) - { - leader->UpdatePosition(PathWaypoints[PATH_WP_COUNT - 1], true); - leader->StopMovingOnCurrentPos(); - leader->RemoveAllAuras(); - leader->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_READY1H); - if (!leader->IsVisible()) - leader->SetVisible(true); - lk->CastSpell(leader, SPELL_HARVEST_SOUL, false); - } - } - ++outroStep; - outroTimer = 500; - break; - case 2: - { - uint32 entry = GetTeamIdInInstance() == TEAM_ALLIANCE ? GO_THE_SKYBREAKER : GO_ORGRIMS_HAMMER; - T1 = sTransportMgr->CreateTransport(entry, 0, instance); - - ++outroStep; - outroTimer = GetTeamIdInInstance() == TEAM_ALLIANCE ? 10000 : 10500; - } - break; - case 3: - if (T1) - T1->EnableMovement(false); - if (Creature* c = instance->GetCreature(NPC_ShipCaptainGUID)) - c->AI()->Talk(GetTeamIdInInstance() == TEAM_ALLIANCE ? SAY_FIRE_ALLY : SAY_FIRE_HORDE); - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - { - c->RemoveAllAuras(); - c->CastSpell(c, SPELL_GUNSHIP_CANNON_FIRE_PERIODIC, true); - } - if (Creature* c = instance->GetCreature(NPC_LichKingGUID)) - { - c->InterruptNonMeleeSpells(true); - c->RemoveAllAuras(); - } - ++outroStep; - outroTimer = 5000; - break; - case 4: - HandleGameObject(GO_CaveInGUID, false); - ++outroStep; - outroTimer = 3000; - break; - case 5: - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - c->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - if (Creature* c = instance->GetCreature(NPC_LichKingGUID)) - { - c->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - c->GetThreatMgr().ClearAllThreat(); - c->CombatStop(true); - c->InterruptNonMeleeSpells(true); - c->SetVisible(false); - } - if (instance->IsHeroic()) - instance->ToInstanceMap()->PermBindAllPlayers(); - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - c->CastSpell(c, SPELL_ACHIEVEMENT_CHECK, true); - ++outroStep; - outroTimer = 1000; - break; - case 6: - if (T1) - T1->EnableMovement(true); - ++outroStep; - outroTimer = 3500; - break; - case 7: - if (T1) - T1->EnableMovement(false); - if (Creature* leader = instance->GetCreature(NPC_LeaderGUID)) - { - uint8 index = GetTeamIdInInstance() == TEAM_ALLIANCE ? 0 : 1; - for (uint8 i = 0; i < 3; ++i) - if (StairsPos[index][i].GetPositionX()) - if (GameObject* go = leader->SummonGameObject(GetTeamIdInInstance() == TEAM_ALLIANCE ? GO_STAIRS_ALLIANCE : GO_STAIRS_HORDE, StairsPos[index][i].GetPositionX(), StairsPos[index][i].GetPositionY(), StairsPos[index][i].GetPositionZ(), StairsPos[index][i].GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 86400, false)) - go->SetGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE); - //Position pos = TeamIdInInstance == TEAM_ALLIANCE ? AllyPortalPos : HordePortalPos; - //leader->SummonGameObject(GO_PORTAL_TO_DALARAN, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 86400); - //pos = TeamIdInInstance == TEAM_ALLIANCE ? AllyChestPos : HordeChestPos; - //leader->SummonGameObject(instance->GetSpawnMode() ? GO_CHEST_HEROIC : GO_CHEST_NORMAL, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 86400); - } - ++outroStep; - outroTimer = 1000; - break; - case 8: - if (Creature* c = instance->GetCreature(NPC_ShipCaptainGUID)) - c->AI()->Talk(GetTeamIdInInstance() == TEAM_ALLIANCE ? SAY_ONBOARD_ALLY : SAY_ONBOARD_HORDE); - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - { - c->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); - c->GetMotionMaster()->MovePoint(0, WalkCaveInPos); - c->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); // need gossip ID 10931 - } - ++outroStep; - outroTimer = 6000; - break; - case 9: - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - c->AI()->Talk(GetTeamIdInInstance() == TEAM_ALLIANCE ? SAY_JAINA_FINAL_1 : SAY_SYLVANA_FINAL); - HandleGameObject(GO_CaveInGUID, true); - ++outroStep; - outroTimer = 11000; - break; - case 10: - ++outroStep; - outroTimer = 0; - for (Map::PlayerList::const_iterator itr = instance->GetPlayers().begin(); itr != instance->GetPlayers().end(); ++itr) - if (Player* p = itr->GetSource()) - p->KilledMonsterCredit(NPC_WRATH_OF_THE_LICH_KING_CREDIT); - if (GetTeamIdInInstance() == TEAM_ALLIANCE) - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - { - c->AI()->Talk(SAY_JAINA_FINAL_2); - outroTimer = 10000; - } - break; - case 11: - if (Creature* c = instance->GetCreature(NPC_LeaderGUID)) - c->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); - ++outroStep; - outroTimer = 300 * 1000; - break; - case 12: - outroStep = 0; - outroTimer = 0; - if (T1) - T1->setActive(false); - break; - } - } - else - outroTimer -= diff; + if (Creature* marwyn = GetCreature(DATA_MARWYN)) + if (marwyn->IsAlive()) + marwyn->AI()->DoAction(1); } } - }; + else + { + _nextWaveTimer = 150000; + + uint8 numToActivate = 5; + if (_waveNumber <= 2) + numToActivate = 3; + else if (_waveNumber <= 4) + numToActivate = 4; + _remainingTrashKills += numToActivate; + for (uint8 i = 0; i < numToActivate; ++i) + { + uint32 entry = _chosenComposition[_waveNumber - (_waveNumber > 5 ? 2 : 1)][i]; + bool forward = !!urand(0, 1); + for (int8 j = (forward ? 0 : NUM_OF_TRASH - 1); (forward ? j < NUM_OF_TRASH : j >= 0); (forward ? ++j : --j)) + if (!_trashActive[j]) + if (Creature* c = instance->GetCreature(_trashGUID[j])) + if (c->GetEntry() == entry) + { + _trashActive[j] = true; + c->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + c->SetImmuneToAll(false); + c->AI()->DoAction(1); + break; + } + } + } + } + + void HandleWaveWipe() + { + if (!_waveNumber) + return; + + DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVES_ENABLED, 0); + DoUpdateWorldState(WORLD_STATE_HALLS_OF_REFLECTION_WAVE_COUNT, 0); + HandleGameObject(GO_FRONT_DOOR, true); + + _trashCounter = NUM_OF_TRASH; + _waveNumber = 0; + _nextWaveTimer = 0; + std::memset(&_chosenComposition, 0, sizeof(_chosenComposition)); + + for (uint8 i = 0; i < NUM_OF_TRASH; ++i) + if (_trashActive[i]) + if (Creature* c = instance->GetCreature(_trashGUID[i])) + { + c->GetThreatMgr().ClearAllThreat(); + c->CombatStop(true); + c->InterruptNonMeleeSpells(true); + c->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + c->SetImmuneToAll(true); + c->Respawn(true); + c->UpdatePosition(c->GetHomePosition(), true); + c->StopMovingOnCurrentPos(); + } + std::fill(std::begin(_trashActive), std::end(_trashActive), false); + if (Creature* falric = GetCreature(DATA_FALRIC)) + falric->AI()->EnterEvadeMode(); + if (Creature* marwyn = GetCreature(DATA_MARWYN)) + marwyn->AI()->EnterEvadeMode(); + + _waveResumeTimer = 5000; + _waveResumeStep = 2; + _remainingTrashKills = 0; + } + + void Update(uint32 diff) override + { + if (!instance->HavePlayers()) + return; + + if (_playerCheckTimer <= diff) + { + _playerCheckTimer = 5000; + if ((GetPersistentData(PERSISTENT_DATA_INTRO)) && GetBossState(DATA_MARWYN) != DONE) + { + Map::PlayerList const& pl = instance->GetPlayers(); + if (_waveNumber || _nextWaveTimer) + { + bool allDead = true; + bool outOfRange = false; + for (auto itr = pl.begin(); itr != pl.end(); ++itr) + { + Player* p = itr->GetSource(); + if (!p || p->IsGameMaster()) + continue; + if (p->IsAlive()) + allDead = false; + if (p->GetExactDist2d(&CenterPos) > MAX_DIST_FROM_CENTER_IN_COMBAT) + { + outOfRange = true; + break; + } + } + if (allDead || outOfRange) + HandleWaveWipe(); + } + else if (!_waveResumeTimer) + { + bool allInRange = (instance->GetPlayersCountExceptGMs() > 0); + for (auto itr = pl.begin(); itr != pl.end(); ++itr) + { + Player* p = itr->GetSource(); + if (!p || p->IsGameMaster()) + continue; + if (p->GetExactDist2d(&CenterPos) > MAX_DIST_FROM_CENTER_TO_START || !p->IsAlive()) + { + allInRange = false; + break; + } + } + if (allInRange) + { + _waveResumeTimer = 1; + _waveResumeStep = 0; + } + } + } + } + else + _playerCheckTimer -= diff; + + if (_nextWaveTimer) + { + if (_nextWaveTimer <= diff) + { + _nextWaveTimer = 0; + StartNextWave(); + } + else + _nextWaveTimer -= diff; + } + + if (_waveResumeTimer) + { + if (_waveResumeTimer <= diff) + { + switch (_waveResumeStep) + { + case 0: + if (Creature* falric = GetCreature(DATA_FALRIC)) + { + falric->UpdatePosition(5274.9f, 2039.2f, 709.319f, 5.4619f, true); + falric->StopMovingOnCurrentPos(); + falric->SetVisible(true); + if (falric->IsAlive()) + { + falric->GetMotionMaster()->MovePoint(0, FalricMovePos); + if (Aura* a = falric->AddAura(SPELL_SHADOWMOURNE_VISUAL, falric)) + a->SetDuration(8000); + } + } + if (Creature* marwyn = GetCreature(DATA_MARWYN)) + { + marwyn->UpdatePosition(5343.77f, 1973.86f, 709.319f, 2.35173f, true); + marwyn->StopMovingOnCurrentPos(); + marwyn->SetVisible(true); + if (marwyn->IsAlive()) + { + marwyn->GetMotionMaster()->MovePoint(0, MarwynMovePos); + if (Aura* a = marwyn->AddAura(SPELL_SHADOWMOURNE_VISUAL, marwyn)) + a->SetDuration(8000); + } + marwyn->AI()->Talk(EMOTE_MARWYN_INTRO_SPIRIT); + } + ++_waveResumeStep; + _waveResumeTimer = 7500; + break; + case 1: + if (Creature* falric = GetCreature(DATA_FALRIC)) + { + if (falric->IsAlive()) + falric->AI()->Talk(SAY_FALRIC_INTRO_2); + else if (Creature* marwyn = GetCreature(DATA_MARWYN)) + marwyn->AI()->Talk(SAY_MARWYN_WIPE_AFTER_FALRIC); + } + SetData(ACTION_SHOW_TRASH, 1); + _waveResumeStep = 0; + _waveResumeTimer = 0; + _nextWaveTimer = 7000; + break; + default: + for (uint8 i = 0; i < NUM_OF_TRASH; ++i) + if (Creature* c = instance->GetCreature(_trashGUID[i])) + c->SetVisible(false); + if (Creature* falric = GetCreature(DATA_FALRIC)) + falric->SetVisible(false); + if (Creature* marwyn = GetCreature(DATA_MARWYN)) + marwyn->SetVisible(false); + _waveResumeStep = 0; + _waveResumeTimer = 0; + break; + } + } + else + _waveResumeTimer -= diff; + } + + if (_outroStep) + { + if (_outroTimer <= diff) + UpdateOutro(); + else + _outroTimer -= diff; + } + } + +private: + void UpdateOutro() + { + switch (_outroStep) + { + case 1: + if (Creature* lk = GetCreature(NPC_LICH_KING_BOSS)) + { + lk->UpdatePosition(PathWaypoints[PATH_WP_COUNT - 2], true); + lk->StopMovingOnCurrentPos(); + lk->RemoveAllAuras(); + lk->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_CUSTOM_SPELL_02); + if (!lk->IsVisible()) + lk->SetVisible(true); + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + { + leader->UpdatePosition(PathWaypoints[PATH_WP_COUNT - 1], true); + leader->StopMovingOnCurrentPos(); + leader->RemoveAllAuras(); + leader->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_READY1H); + if (!leader->IsVisible()) + leader->SetVisible(true); + lk->CastSpell(leader, SPELL_HARVEST_SOUL, false); + } + } + ++_outroStep; + _outroTimer = 500; + break; + case 2: + { + uint32 entry = GetTeamIdInInstance() == TEAM_ALLIANCE ? GO_THE_SKYBREAKER : GO_ORGRIMS_HAMMER; + _transport = sTransportMgr->CreateTransport(entry, 0, instance); + ++_outroStep; + _outroTimer = GetTeamIdInInstance() == TEAM_ALLIANCE ? 10000 : 10500; + break; + } + case 3: + if (_transport) + _transport->EnableMovement(false); + if (Creature* captain = GetCreature(DATA_SHIP_CAPTAIN)) + captain->AI()->Talk(GetTeamIdInInstance() == TEAM_ALLIANCE ? SAY_FIRE_ALLY : SAY_FIRE_HORDE); + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + { + leader->RemoveAllAuras(); + leader->CastSpell(leader, SPELL_GUNSHIP_CANNON_FIRE_PERIODIC, true); + } + if (Creature* lichKing = GetCreature(NPC_LICH_KING_BOSS)) + { + lichKing->InterruptNonMeleeSpells(true); + lichKing->RemoveAllAuras(); + } + ++_outroStep; + _outroTimer = 5000; + break; + case 4: + HandleGameObject(GO_CAVE_IN, false); + ++_outroStep; + _outroTimer = 3000; + break; + case 5: + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + leader->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + if (Creature* lichKing = GetCreature(NPC_LICH_KING_BOSS)) + { + lichKing->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + lichKing->GetThreatMgr().ClearAllThreat(); + lichKing->CombatStop(true); + lichKing->InterruptNonMeleeSpells(true); + lichKing->SetVisible(false); + } + if (instance->IsHeroic()) + instance->ToInstanceMap()->PermBindAllPlayers(); + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + leader->CastSpell(leader, SPELL_ACHIEVEMENT_CHECK, true); + ++_outroStep; + _outroTimer = 1000; + break; + case 6: + if (_transport) + _transport->EnableMovement(true); + ++_outroStep; + _outroTimer = 3500; + break; + case 7: + if (_transport) + _transport->EnableMovement(false); + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + { + uint8 index = GetTeamIdInInstance() == TEAM_ALLIANCE ? 0 : 1; + for (uint8 i = 0; i < 3; ++i) + if (StairsPos[index][i].GetPositionX()) + if (GameObject* go = leader->SummonGameObject(GetTeamIdInInstance() == TEAM_ALLIANCE ? GO_STAIRS_ALLIANCE : GO_STAIRS_HORDE, StairsPos[index][i].GetPositionX(), StairsPos[index][i].GetPositionY(), StairsPos[index][i].GetPositionZ(), StairsPos[index][i].GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 86400, false)) + go->SetGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE); + } + ++_outroStep; + _outroTimer = 1000; + break; + case 8: + if (Creature* captain = GetCreature(DATA_SHIP_CAPTAIN)) + captain->AI()->Talk(GetTeamIdInInstance() == TEAM_ALLIANCE ? SAY_ONBOARD_ALLY : SAY_ONBOARD_HORDE); + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + { + leader->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); + leader->GetMotionMaster()->MovePoint(0, WalkCaveInPos); + leader->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + } + ++_outroStep; + _outroTimer = 6000; + break; + case 9: + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + leader->AI()->Talk(GetTeamIdInInstance() == TEAM_ALLIANCE ? SAY_JAINA_FINAL_1 : SAY_SYLVANA_FINAL); + HandleGameObject(GO_CAVE_IN, true); + ++_outroStep; + _outroTimer = 11000; + break; + case 10: + ++_outroStep; + _outroTimer = 0; + instance->DoForAllPlayers([](Player* player) + { + player->KilledMonsterCredit(NPC_WRATH_OF_THE_LICH_KING_CREDIT); + }); + if (GetTeamIdInInstance() == TEAM_ALLIANCE) + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + { + leader->AI()->Talk(SAY_JAINA_FINAL_2); + _outroTimer = 10000; + } + break; + case 11: + if (Creature* leader = GetCreature(NPC_SYLVANAS_PART2)) + leader->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); + ++_outroStep; + _outroTimer = 300 * 1000; + break; + case 12: + _outroStep = 0; + _outroTimer = 0; + if (_transport) + _transport->setActive(false); + break; + } + } + + // GUIDs not handled by ObjectData (arrays with position-based assignment) + ObjectGuid _spiritualReflectionGUID[MAX_SPIRITUAL_REFLECTIONS]; + ObjectGuid _iceWallTargetGUID[MAX_ICE_WALL_TARGETS]; + ObjectGuid _trashGUID[NUM_OF_TRASH]; + + // Runtime state (not persisted) + uint32 _batteredHiltStatus{}; + + // Wave system + bool _trashActive[NUM_OF_TRASH]{}; + uint8 _trashCounter{}; + uint32 _chosenComposition[8][5]{}; + uint8 _waveNumber{}; + uint32 _nextWaveTimer{}; + uint16 _playerCheckTimer{}; + uint16 _waveResumeTimer{}; + uint8 _waveResumeStep{}; + bool _falricPhaseComplete{}; + uint8 _remainingTrashKills{}; + + // Lich King fight + bool _isLichKingFightActive{}; + uint32 _outroTimer{}; + uint8 _outroStep{}; + MotionTransport* _transport{}; }; void AddSC_instance_halls_of_reflection() { - new instance_halls_of_reflection(); + RegisterInstanceScript(instance_halls_of_reflection, MAP_HALLS_OF_REFLECTION); } diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp index ca9ec626d..e7e08d273 100644 --- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp @@ -116,6 +116,7 @@ public: // start real fight me->RemoveAllAuras(); me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetImmuneToPC(false); DoZoneInCombat(); me->CastSpell(me, 43979, true); Talk(SAY_AGGRO); diff --git a/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp b/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp index 7cb576a4a..2b7fd9805 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp @@ -363,7 +363,7 @@ public: ScriptedAI::MoveInLineOfSight(who); } - void AttackStart(Unit* who) override + void JustEngagedWith(Unit* who) override { if (me->ToTempSummon()) { @@ -373,6 +373,14 @@ public: return; } + ScriptedAI::JustEngagedWith(who); + } + + void AttackStart(Unit* who) override + { + if (me->ToTempSummon()) + return; + ScriptedAI::AttackStart(who); } diff --git a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp index c84207926..e21b5835c 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp @@ -43,12 +43,7 @@ Position const EckCombatStartPosition = { 1638.55f, 919.76f, 104.95f, 0.00f }; struct boss_eck : public BossAI { boss_eck(Creature* creature) : BossAI(creature, DATA_ECK_THE_FEROCIOUS) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void InitializeAI() override { diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index a80ff3250..9b6926c96 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -1176,8 +1176,7 @@ public: { if (InstanceScript* instance = player->GetInstanceScript()) if (instance->GetBossState(DATA_LADY_DEATHWHISPER) != DONE) - if (!player->IsGameMaster()) - if (Creature* ladyDeathwhisper = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_LADY_DEATHWHISPER))) + if (Creature* ladyDeathwhisper = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_LADY_DEATHWHISPER))) ladyDeathwhisper->AI()->DoAction(ACTION_START_INTRO); return true; } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 94ca3e5c4..77f6c7069 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -791,6 +791,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -3592,7 +3593,7 @@ public: bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override { if (InstanceScript* instance = player->GetInstanceScript()) - if (instance->GetBossState(DATA_SINDRAGOSA_GAUNTLET) == NOT_STARTED && !player->IsGameMaster()) + if (instance->GetBossState(DATA_SINDRAGOSA_GAUNTLET) == NOT_STARTED) if (Creature* gauntlet = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_SINDRAGOSA_GAUNTLET))) gauntlet->AI()->DoAction(ACTION_START_GAUNTLET); return true; @@ -3607,7 +3608,7 @@ public: bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override { if (InstanceScript* instance = player->GetInstanceScript()) - if (instance->GetData(DATA_PUTRICIDE_TRAP_STATE) == NOT_STARTED && !player->IsGameMaster()) + if (instance->GetData(DATA_PUTRICIDE_TRAP_STATE) == NOT_STARTED) if (Creature* trap = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_PUTRICADES_TRAP))) trap->AI()->DoAction(ACTION_START_GAUNTLET); return true; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index 6125ba262..f2be10718 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -111,7 +111,9 @@ enum Events // Intro EVENT_INTRO_2 = 15, EVENT_INTRO_3 = 16, - EVENT_INTRO_4 = 17 + EVENT_INTRO_4 = 17, + // Resume after teleport + EVENT_RESUME_ATTACK = 18 }; const uint32 gothikWaves[24][2] = @@ -172,21 +174,6 @@ const Position PosSummonDead[5] = #define POS_X_SOUTH 2633.84f #define IN_LIVE_SIDE(who) (who->GetPositionY() < POS_Y_GATE) -// Predicate function to check that the r efzr unit is NOT on the same side as the source. -struct NotOnSameSide -{ -public: - explicit NotOnSameSide(Unit* pSource) : m_inLiveSide(IN_LIVE_SIDE(pSource)) { } - - bool operator() (Unit const* pTarget) - { - return (m_inLiveSide != IN_LIVE_SIDE(pTarget)); - } - -private: - bool m_inLiveSide; -}; - class boss_gothik : public CreatureScript { public: @@ -206,6 +193,7 @@ public: SummonList summons; bool secondPhase{}; bool gateOpened{}; + bool lastTeleportDead{}; uint8 waveCount{}; bool IsInRoom() @@ -228,6 +216,7 @@ public: me->SetReactState(REACT_PASSIVE); secondPhase = false; gateOpened = false; + lastTeleportDead = false; waveCount = 0; me->NearTeleportTo(2642.139f, -3386.959f, 285.492f, 6.265f); } @@ -366,6 +355,18 @@ public: return false; } + void OpenGate() + { + if (gateOpened) + return; + + if (GameObject* go = instance->GetGameObject(DATA_GOTHIK_INNER_GATE)) + go->SetGoState(GO_STATE_ACTIVE); + + gateOpened = true; + Talk(EMOTE_GATE_OPENED); + } + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override { if (!secondPhase) @@ -382,6 +383,35 @@ public: if (!UpdateVictim()) return; + // Safety net: if our victim is on the wrong side, find a + // same-side target or open the gate + if (secondPhase && me->HasReactState(REACT_AGGRESSIVE) && !gateOpened + && !IN_LIVE_SIDE(me) != !IN_LIVE_SIDE(me->GetVictim())) + { + Map::PlayerList const& pList = me->GetMap()->GetPlayers(); + Player* newTarget = nullptr; + for (auto const& itr : pList) + { + Player* player = itr.GetSource(); + if (player && player->IsAlive() && !player->IsGameMaster() + && me->IsValidAttackTarget(player) + && IN_LIVE_SIDE(me) == IN_LIVE_SIDE(player)) + { + newTarget = player; + break; + } + } + + if (newTarget) + { + me->GetThreatMgr().ResetAllThreat(); + me->GetThreatMgr().AddThreat(newTarget, 1.0f); + AttackStart(newTarget); + } + else + OpenGate(); + } + events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; @@ -407,32 +437,29 @@ public: break; case EVENT_TELEPORT: { + if (me->HealthBelowPct(30)) + break; + + me->CastStop(); me->AttackStop(); - if (IN_LIVE_SIDE(me)) - me->CastSpell(me, SPELL_TELEPORT_DEAD, false); - else - me->CastSpell(me, SPELL_TELEPORT_LIVE, false); + me->SetReactState(REACT_PASSIVE); + me->GetThreatMgr().ResetAllThreat(); + me->CastSpell(me, lastTeleportDead ? SPELL_TELEPORT_LIVE : SPELL_TELEPORT_DEAD, false); + lastTeleportDead = !lastTeleportDead; - // Clear threat from targets not on the same side as Gothik - NotOnSameSide notOnSameSide(me); - for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList()) - if (notOnSameSide(ref->GetVictim())) - me->GetThreatMgr().ClearThreat(ref->GetVictim()); - - if (Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0)) - { - me->GetThreatMgr().AddThreat(pTarget, 100.0f); - AttackStart(pTarget); - } + events.CancelEvent(EVENT_SHADOW_BOLT); + events.ScheduleEvent(EVENT_RESUME_ATTACK, 2s); events.Repeat(20s); break; } + case EVENT_RESUME_ATTACK: + me->SetReactState(REACT_AGGRESSIVE); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 0s); + return UpdateAI(0); case EVENT_CHECK_HEALTH: if (me->HealthBelowPct(30)) { - if (GameObject* go = instance->GetGameObject(DATA_GOTHIK_INNER_GATE)) - go->SetGoState(GO_STATE_ACTIVE); - + OpenGate(); events.CancelEvent(EVENT_TELEPORT); break; } @@ -464,10 +491,7 @@ public: case EVENT_CHECK_PLAYERS: if (!CheckGroupSplitted()) { - if (GameObject* go = instance->GetGameObject(DATA_GOTHIK_INNER_GATE)) - go->SetGoState(GO_STATE_ACTIVE); - - gateOpened = true; + OpenGate(); summons.DoForAllSummons([&](WorldObject* summon) { if (Creature* gothikMinion = summon->ToCreature()) @@ -481,7 +505,6 @@ public: } } }); - Talk(EMOTE_GATE_OPENED); } break; } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp index e4d642000..99c3f731e 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp @@ -159,13 +159,13 @@ struct boss_heigan : public BossAI void UpdateAI(uint32 diff) override { if (!UpdateVictim()) - { return; - } _eruptionScheduler.Update(diff); - BossAI::UpdateAI(diff); + scheduler.Update(diff); + + DoMeleeAttackIfReady(); } void CheckSafetyDance() diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp index d71e12468..c1dd286dc 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp @@ -446,6 +446,7 @@ public: else { me->SetHealth(me->GetMaxHealth()); + me->GetThreatMgr().ResetAllThreat(); } } } diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 0829623ba..df57c3368 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -1232,18 +1232,15 @@ class spell_malygos_vortex_visual : public AuraScript if (!caster) return; - for (auto const* ref : caster->GetThreatMgr().GetUnsortedThreatList()) + if (InstanceScript* instance = caster->GetInstanceScript()) { - if (Player* player = ref->GetVictim()->ToPlayer()) + if (Creature* trigger = ObjectAccessor::GetCreature(*caster, instance->GetGuidData(DATA_VORTEX_TRIGGER))) { - if (player->IsGameMaster()) - continue; - - if (InstanceScript* instance =caster->GetInstanceScript()) + caster->GetMap()->DoForAllPlayers([&](Player* player) { - if (Creature* trigger =ObjectAccessor::GetCreature(*caster, instance->GetGuidData(DATA_VORTEX_TRIGGER))) + if (player->IsAlive() && !player->IsGameMaster()) trigger->CastSpell(player, SPELL_VORTEX_TELEPORT, true); - } + }); } } diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp index 4cec1ceb0..17de6107e 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp @@ -72,31 +72,22 @@ struct instance_eye_of_eternity : public InstanceScript void VortexHandling() { - Creature* malygos = GetCreature(DATA_MALYGOS); - if (!malygos) + if (_vortexTriggers.empty()) return; - for (ObjectGuid const& guid : _vortexTriggers) + size_t triggerIndex = 0; + size_t triggerCount = _vortexTriggers.size(); + + instance->DoForAllPlayers([&](Player* player) { - uint8 counter = 0; - if (Creature* trigger = instance->GetCreature(guid)) - { - for (auto* ref : malygos->GetThreatMgr().GetUnsortedThreatList()) - { - if (counter >= 5) - break; + if (!player->IsAlive() || player->IsGameMaster()) + return; - if (Player* player = ref->GetVictim()->ToPlayer()) - { - if (player->IsGameMaster() || player->HasAura(SPELL_VORTEX_4)) - continue; + if (Creature* trigger = instance->GetCreature(_vortexTriggers[triggerIndex])) + player->CastSpell(trigger, SPELL_VORTEX_4, true); - player->CastSpell(trigger, SPELL_VORTEX_4, true); - counter++; - } - } - } - } + triggerIndex = (triggerIndex + 1) % triggerCount; + }); } void OnPlayerEnter(Player* player) override diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp index 7ed99cfb4..57eeb4b17 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp @@ -213,6 +213,7 @@ public: me->LoadCreaturesAddon(true); me->SetLootRecipient(nullptr); me->ResetPlayerDamageReq(); + EngagementOver(); } void JustDied(Unit* /*killer*/) override @@ -369,6 +370,10 @@ public: void EnterEvadeMode(EvadeReason why) override { + // Block evade during outer platform transitions + if (lock) + return; + me->SetCanFly(false); me->SetDisableGravity(false); me->SetControlled(false, UNIT_STATE_ROOT); diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 0faf90823..392425bbb 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -817,7 +817,8 @@ class spell_oculus_rider_aura : public AuraScript _drakeGUID = drake->GetGUID(); caster->AddAura(SPELL_DRAKE_FLAG_VISUAL, caster); caster->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - caster->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); + if (caster->IsInDisallowedMountForm()) + caster->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); drake->CastSpell(drake, SPELL_SOAR_TRIGGER); if (drake->GetEntry() == NPC_RUBY_DRAKE) drake->CastSpell(drake, SPELL_RUBY_EVASIVE_AURA); diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp index cd8c36c9e..55ea322f0 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp @@ -267,6 +267,7 @@ struct boss_bjarngrim : public npc_escortAI m_uiStance = stance; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 Point) override { if (Point == 1) diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp index 7d5f74059..41f9cf1f0 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp @@ -312,6 +312,7 @@ public: TalkEvent = false; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 id) override; void InitializeEvent(); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index d6833a913..0af5863dc 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -267,12 +267,12 @@ struct boss_algalon_the_observer : public ScriptedAI _fedOnTears = true; _firstPull = true; _fightWon = false; - m_pInstance = me->GetInstanceScript(); + _instance = me->GetInstanceScript(); } EventMap events; SummonList summons; - InstanceScript* m_pInstance; + InstanceScript* _instance; bool _firstPull; bool _fightWon; @@ -363,8 +363,8 @@ struct boss_algalon_the_observer : public ScriptedAI return; } - if (m_pInstance) - m_pInstance->SetData(BOSS_ALGALON, FAIL); + if (_instance) + _instance->SetData(BOSS_ALGALON, FAIL); ScriptedAI::EnterEvadeMode(why); } @@ -385,13 +385,13 @@ struct boss_algalon_the_observer : public ScriptedAI _phaseTwo = false; _heraldOfTheTitans = true; - if (m_pInstance->GetBossState(BOSS_ALGALON) == FAIL) + if (_instance->GetBossState(BOSS_ALGALON) == FAIL) { _firstPull = false; } - if (m_pInstance) - m_pInstance->SetData(BOSS_ALGALON, NOT_STARTED); + if (_instance) + _instance->SetData(BOSS_ALGALON, NOT_STARTED); } void KilledUnit(Unit* victim) override @@ -445,8 +445,8 @@ struct boss_algalon_the_observer : public ScriptedAI me->SetFaction(FACTION_FRIENDLY); me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); me->InterruptNonMeleeSpells(false); - if (m_pInstance) - m_pInstance->SetData(BOSS_ALGALON, NOT_STARTED); + if (_instance) + _instance->SetData(BOSS_ALGALON, NOT_STARTED); break; case ACTION_INIT_ALGALON: _firstPull = false; @@ -473,7 +473,7 @@ struct boss_algalon_the_observer : public ScriptedAI if (_fightWon) return; - if (!m_pInstance) + if (!_instance) { EnterEvadeMode(EVADE_REASON_OTHER); return; @@ -499,7 +499,7 @@ struct boss_algalon_the_observer : public ScriptedAI Talk(SAY_ALGALON_START_TIMER); introDelay = 22s; events.ScheduleEvent(EVENT_START_COMBAT, 14s); - m_pInstance->SetData(DATA_DESPAWN_ALGALON, 0); + _instance->SetData(DATA_DESPAWN_ALGALON, 0); } events.ScheduleEvent(EVENT_REMOVE_UNNATTACKABLE, introDelay - 500ms); @@ -656,11 +656,11 @@ struct boss_algalon_the_observer : public ScriptedAI case EVENT_INTRO_FINISH: events.Reset(); me->SetImmuneToPC(false); - if (Creature* brann = m_pInstance->GetCreature(DATA_BRANN_BRONZEBEARD_ALG)) + if (Creature* brann = _instance->GetCreature(DATA_BRANN_BRONZEBEARD_ALG)) brann->AI()->DoAction(ACTION_FINISH_INTRO); break; case EVENT_START_COMBAT: - m_pInstance->SetData(BOSS_ALGALON, IN_PROGRESS); + _instance->SetData(BOSS_ALGALON, IN_PROGRESS); Talk(SAY_ALGALON_AGGRO); break; case EVENT_REMOVE_UNNATTACKABLE: @@ -735,10 +735,10 @@ struct boss_algalon_the_observer : public ScriptedAI ScriptedAI::EnterEvadeMode(); return; case EVENT_OUTRO_START: - if (m_pInstance) + if (_instance) { - m_pInstance->SetData(BOSS_ALGALON, DONE); - m_pInstance->SetData(DATA_ALGALON_DEFEATED, 1); + _instance->SetData(BOSS_ALGALON, DONE); + _instance->SetData(DATA_ALGALON_DEFEATED, 1); } break; case EVENT_OUTRO_1: diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index 7b1728615..610dce8ba 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -187,7 +187,7 @@ const Position homePos = {322.39f, -14.5f, 409.8f, 3.14f}; struct boss_flame_leviathan : public BossAI { - boss_flame_leviathan(Creature* pCreature) : BossAI(pCreature, BOSS_LEVIATHAN), vehicle(me->GetVehicleKit()) + boss_flame_leviathan(Creature* creature) : BossAI(creature, BOSS_LEVIATHAN), vehicle(me->GetVehicleKit()) { assert(vehicle); } @@ -214,7 +214,7 @@ struct boss_flame_leviathan : public BossAI { _JustReachedHome(); // For achievement - instance->SetData(DATA_UNBROKEN_ACHIEVEMENT, 0); + instance->StorePersistentData(PERSISTENT_DATA_UNBROKEN, 0); me->setActive(false); } @@ -974,6 +974,7 @@ struct npc_mimirons_inferno : public npc_escortAI void AttackStart(Unit*) override { } void MoveInLineOfSight(Unit*) override { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*waypointId*/) override { } void DoAction(int32 param) override @@ -1238,7 +1239,7 @@ class spell_auto_repair : public SpellScript // Achievement if (InstanceScript* instance = vehicle->GetBase()->GetInstanceScript()) - instance->SetData(DATA_UNBROKEN_ACHIEVEMENT, 0); + instance->StorePersistentData(PERSISTENT_DATA_UNBROKEN, 0); } void Register() override @@ -1716,10 +1717,9 @@ public: bool OnCheck(Player* player, Unit*, uint32 /*criteria_id*/) override { - if (player->GetInstanceScript()) - if (player->GetInstanceScript()->GetData(DATA_UNBROKEN_ACHIEVEMENT)) - return true; - return false; + InstanceScript* instance = player->GetInstanceScript(); + return instance + && instance->GetPersistentData(PERSISTENT_DATA_UNBROKEN); } }; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp index 1fed3825c..849576a15 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp @@ -108,7 +108,7 @@ enum VaporsText struct boss_vezax : public BossAI { - boss_vezax(Creature* pCreature) : BossAI(pCreature, BOSS_VEZAX) { } + boss_vezax(Creature* creature) : BossAI(creature, BOSS_VEZAX) { } uint8 vaporsCount; bool hardmodeAvailable; @@ -131,7 +131,7 @@ struct boss_vezax : public BossAI me->setActive(false); } - void JustEngagedWith(Unit* /*pWho*/) override + void JustEngagedWith(Unit* /*who*/) override { me->setActive(true); _JustEngagedWith(); @@ -326,13 +326,6 @@ struct boss_vezax : public BossAI { _JustDied(); Talk(SAY_DEATH); - - if (GameObject* door = me->FindNearestGameObject(GO_VEZAX_DOOR, 500.0f)) - if (door->GetGoState() != GO_STATE_ACTIVE) - { - door->SetLootState(GO_READY); - door->UseDoorOrButton(0, false); - } } void KilledUnit(Unit* who) override @@ -346,21 +339,21 @@ struct boss_vezax : public BossAI struct npc_ulduar_saronite_vapors : public NullCreatureAI { - npc_ulduar_saronite_vapors(Creature* pCreature) : NullCreatureAI(pCreature) + npc_ulduar_saronite_vapors(Creature* creature) : NullCreatureAI(creature) { - pInstance = pCreature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); me->GetMotionMaster()->MoveRandom(4.0f); } - InstanceScript* pInstance; + InstanceScript* _instance; void JustDied(Unit* /*killer*/) override { me->CastSpell(me, SPELL_SARONITE_VAPORS_AURA, true); // killed saronite vapors, hard mode unavailable - if (pInstance) - if (Creature* vezax = pInstance->GetCreature(BOSS_VEZAX)) + if (_instance) + if (Creature* vezax = _instance->GetCreature(BOSS_VEZAX)) vezax->AI()->DoAction(1); } @@ -372,25 +365,25 @@ struct npc_ulduar_saronite_vapors : public NullCreatureAI struct npc_ulduar_saronite_animus : public ScriptedAI { - npc_ulduar_saronite_animus(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_saronite_animus(Creature* creature) : ScriptedAI(creature) { - pInstance = pCreature->GetInstanceScript(); - if (pInstance) - if (Creature* vezax = pInstance->GetCreature(BOSS_VEZAX)) + _instance = creature->GetInstanceScript(); + if (_instance) + if (Creature* vezax = _instance->GetCreature(BOSS_VEZAX)) vezax->AI()->JustSummoned(me); timer = 0; me->SetInCombatWithZone(); } - InstanceScript* pInstance; + InstanceScript* _instance; uint16 timer; void JustDied(Unit* /*killer*/) override { me->DespawnOrUnsummon(3s); - if (pInstance) - if (Creature* vezax = pInstance->GetCreature(BOSS_VEZAX)) + if (_instance) + if (Creature* vezax = _instance->GetCreature(BOSS_VEZAX)) vezax->AI()->DoAction(2); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp index 391a73146..46f9a3367 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp @@ -199,7 +199,7 @@ HodirHelperData hhd[4][4] = struct boss_hodir : public BossAI { - boss_hodir(Creature* pCreature) : BossAI(pCreature, BOSS_HODIR) + boss_hodir(Creature* creature) : BossAI(creature, BOSS_HODIR) { if (!me->IsAlive()) instance->SetBossState(BOSS_HODIR, DONE); @@ -217,6 +217,14 @@ struct boss_hodir : public BossAI const Position ENTRANCE_DOOR{ 1999.160034f, -297.792999f, 431.960999f, 0 }; const Position EXIT_DOOR{ 1999.709961f, -166.259003f, 432.822998f, 0 }; + void JustExitedCombat() override + { + EngagementOver(); + if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) + return; + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + void Reset() override { _Reset(); @@ -231,17 +239,12 @@ struct boss_hodir : public BossAI me->RemoveAllAuras(); instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_BITING_COLD_PLAYER_AURA); - if (GameObject* go = me->FindNearestGameObject(GO_HODIR_FRONTDOOR, 900.0f)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - // Reset helpers if (!summons.size()) SpawnHelpers(); } - void JustEngagedWith(Unit* /*pWho*/) override + void JustEngagedWith(Unit* /*who*/) override { me->CastSpell(me, SPELL_BITING_COLD_BOSS_AURA, true); SmallIcicles(true); @@ -253,14 +256,7 @@ struct boss_hodir : public BossAI Talk(TEXT_AGGRO); if (instance->GetBossState(BOSS_HODIR) != DONE) - { instance->SetBossState(BOSS_HODIR, IN_PROGRESS); - } - - if (GameObject* go = me->FindNearestGameObject(GO_HODIR_FRONTDOOR, 300.0f)) - { - go->SetGoState(GO_STATE_READY); - } } GameObject* GetHardmodeChest() @@ -342,28 +338,6 @@ struct boss_hodir : public BossAI events.Reset(); summons.DespawnAll(); - if (GameObject* d = me->FindNearestGameObject(GO_HODIR_FROZEN_DOOR, 250.0f)) - { - if (d->GetGoState() != GO_STATE_ACTIVE ) - { - d->SetLootState(GO_READY); - d->UseDoorOrButton(0, false); - } - } - if (GameObject* d = me->FindNearestGameObject(GO_HODIR_DOOR, 250.0f)) - { - if (d->GetGoState() != GO_STATE_ACTIVE ) - { - d->SetLootState(GO_READY); - d->UseDoorOrButton(0, false); - } - } - - if (GameObject* go = me->FindNearestGameObject(GO_HODIR_FRONTDOOR, 300.0f)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - Talk(TEXT_DEATH); scheduler.Schedule(14s, [this](TaskContext /*context*/) { @@ -592,7 +566,7 @@ struct boss_hodir : public BossAI struct npc_ulduar_icicle : public NullCreatureAI { - npc_ulduar_icicle(Creature* pCreature) : NullCreatureAI(pCreature) + npc_ulduar_icicle(Creature* creature) : NullCreatureAI(creature) { timer1 = 2000; timer2 = 5000; @@ -624,20 +598,20 @@ struct npc_ulduar_icicle : public NullCreatureAI struct npc_ulduar_flash_freeze : public NullCreatureAI { - npc_ulduar_flash_freeze(Creature* pCreature) : NullCreatureAI(pCreature) + npc_ulduar_flash_freeze(Creature* creature) : NullCreatureAI(creature) { timer = 2500; - pInstance = me->GetInstanceScript(); + _instance = me->GetInstanceScript(); } - InstanceScript* pInstance; + InstanceScript* _instance; uint16 timer; void DamageTaken(Unit* doneBy, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override { - if (pInstance && doneBy) - if (pInstance->GetBossState(BOSS_HODIR) == NOT_STARTED) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance && doneBy) + if (_instance->GetBossState(BOSS_HODIR) == NOT_STARTED) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) hodir->AI()->AttackStart(doneBy); } @@ -673,7 +647,7 @@ struct npc_ulduar_flash_freeze : public NullCreatureAI struct npc_ulduar_toasty_fire : public NullCreatureAI { - npc_ulduar_toasty_fire(Creature* pCreature) : NullCreatureAI(pCreature) + npc_ulduar_toasty_fire(Creature* creature) : NullCreatureAI(creature) { me->CastSpell(me, SPELL_MAGE_TOASTY_FIRE_AURA, true); } @@ -705,15 +679,15 @@ struct npc_ulduar_toasty_fire : public NullCreatureAI struct npc_ulduar_hodir_priest : public ScriptedAI { - npc_ulduar_hodir_priest(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_hodir_priest(Creature* creature) : ScriptedAI(creature) { - pInstance = me->GetInstanceScript(); + _instance = me->GetInstanceScript(); events.Reset(); me->SetReactState(REACT_PASSIVE); } EventMap events; - InstanceScript* pInstance; + InstanceScript* _instance; void AttackStart(Unit* who) override { @@ -750,8 +724,8 @@ struct npc_ulduar_hodir_priest : public ScriptedAI case EVENT_TRY_FREE_HELPER: { if (!me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC)) - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) { AttackStart(hodir); ScheduleAbilities(); @@ -783,23 +757,23 @@ struct npc_ulduar_hodir_priest : public ScriptedAI void JustDied(Unit* /*killer*/) override { - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) hodir->AI()->SetData(4, 1); } }; struct npc_ulduar_hodir_druid : public ScriptedAI { - npc_ulduar_hodir_druid(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_hodir_druid(Creature* creature) : ScriptedAI(creature) { - pInstance = me->GetInstanceScript(); + _instance = me->GetInstanceScript(); events.Reset(); me->SetReactState(REACT_PASSIVE); } EventMap events; - InstanceScript* pInstance; + InstanceScript* _instance; void AttackStart(Unit* who) override { @@ -835,8 +809,8 @@ struct npc_ulduar_hodir_druid : public ScriptedAI case EVENT_TRY_FREE_HELPER: { if (!me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC)) - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) { AttackStart(hodir); ScheduleAbilities(); @@ -869,23 +843,23 @@ struct npc_ulduar_hodir_druid : public ScriptedAI void JustDied(Unit* /*killer*/) override { - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) hodir->AI()->SetData(4, 1); } }; struct npc_ulduar_hodir_shaman : public ScriptedAI { - npc_ulduar_hodir_shaman(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_hodir_shaman(Creature* creature) : ScriptedAI(creature) { - pInstance = me->GetInstanceScript(); + _instance = me->GetInstanceScript(); events.Reset(); me->SetReactState(REACT_PASSIVE); } EventMap events; - InstanceScript* pInstance; + InstanceScript* _instance; void AttackStart(Unit* who) override { @@ -929,8 +903,8 @@ struct npc_ulduar_hodir_shaman : public ScriptedAI case EVENT_TRY_FREE_HELPER: { if (!me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC)) - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) { AttackStart(hodir); ScheduleAbilities(); @@ -962,23 +936,23 @@ struct npc_ulduar_hodir_shaman : public ScriptedAI void JustDied(Unit* /*killer*/) override { - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) hodir->AI()->SetData(4, 1); } }; struct npc_ulduar_hodir_mage : public ScriptedAI { - npc_ulduar_hodir_mage(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_hodir_mage(Creature* creature) : ScriptedAI(creature) { - pInstance = me->GetInstanceScript(); + _instance = me->GetInstanceScript(); events.Reset(); me->SetReactState(REACT_PASSIVE); } EventMap events; - InstanceScript* pInstance; + InstanceScript* _instance; void AttackStart(Unit* who) override { @@ -1015,8 +989,8 @@ struct npc_ulduar_hodir_mage : public ScriptedAI case EVENT_TRY_FREE_HELPER: { if (!me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC)) - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) { AttackStart(hodir); ScheduleAbilities(); @@ -1066,8 +1040,8 @@ struct npc_ulduar_hodir_mage : public ScriptedAI void JustDied(Unit* /*killer*/) override { - if (pInstance) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (_instance) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) hodir->AI()->SetData(4, 1); } }; @@ -1162,8 +1136,8 @@ class spell_hodir_biting_cold_player_aura : public AuraScript if (_counter >= 4) { if (GetStackAmount() == 2) // increasing from 2 to 3 (not checking >= to improve performance) - if (InstanceScript* pInstance = target->GetInstanceScript()) - if (Creature* hodir = pInstance->GetCreature(BOSS_HODIR)) + if (InstanceScript* _instance = target->GetInstanceScript()) + if (Creature* hodir = _instance->GetCreature(BOSS_HODIR)) hodir->AI()->SetData(2, 1); ModStackAmount(1); _counter = 0; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp index a1854f5a1..804584415 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp @@ -572,10 +572,10 @@ struct boss_kologarn_eyebeam : public ScriptedAI { boss_kologarn_eyebeam(Creature* c) : ScriptedAI(c), _timer(1), _damaged(false) { - m_pInstance = (InstanceScript*)c->GetInstanceScript(); + _instance = c->GetInstanceScript(); } - InstanceScript* m_pInstance; + InstanceScript* _instance; uint32 _timer; bool _damaged; @@ -602,7 +602,7 @@ struct boss_kologarn_eyebeam : public ScriptedAI me->Attack(player, false); me->GetMotionMaster()->MoveChase(player); - if (Creature* cr = m_pInstance->GetCreature(BOSS_KOLOGARN)) + if (Creature* cr = _instance->GetCreature(BOSS_KOLOGARN)) { me->CastSpell(cr, me->GetEntry() == NPC_EYE_LEFT ? SPELL_FOCUSED_EYEBEAM_LEFT : SPELL_FOCUSED_EYEBEAM_RIGHT, true); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 8958c9af8..0eba106d7 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -264,35 +264,24 @@ Position const ACUSummonPos = { 2742.6265f, 2568.0571f, 377.22076f, 0.0f }; /// struct boss_mimiron : public BossAI { - boss_mimiron(Creature* pCreature) : BossAI(pCreature, BOSS_MIMIRON) + boss_mimiron(Creature* creature) : BossAI(creature, BOSS_MIMIRON) { if (!me->IsAlive()) instance->SetBossState(BOSS_MIMIRON, DONE); - bIsEvading = false; + _isEvading = false; } - bool bIsEvading; - bool hardmode; - bool berserk; - bool bAchievProximityMine; - bool bAchievBombBot; - bool bAchievRocketStrike; - uint32 allowedFlameSpreadTime; - bool changeAllowedFlameSpreadTime; - uint8 minutesTalkNum; - uint32 outofCombatTimer; - void Reset() override { - hardmode = false; - berserk = false; - bAchievProximityMine = false; - bAchievBombBot = false; - bAchievRocketStrike = false; - allowedFlameSpreadTime = 0; - outofCombatTimer = 0; - changeAllowedFlameSpreadTime = false; + _hardmode = false; + _berserk = false; + _achievProximityMine = false; + _achievBombBot = false; + _achievRocketStrike = false; + _allowedFlameSpreadTime = 0; + _outOfCombatTimer = 0; + _changeAllowedFlameSpreadTime = false; ResetGameObjects(); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); @@ -339,7 +328,7 @@ struct boss_mimiron : public BossAI } CloseDoorAndButton(); - if (!hardmode) + if (!_hardmode) { Talk(SAY_MKII_ACTIVATE); events.ScheduleEvent(EVENT_SIT_LMK2, 6s); @@ -354,10 +343,10 @@ struct boss_mimiron : public BossAI computer->AI()->Talk(TALK_COMPUTER_INITIATED); events.ScheduleEvent(EVENT_COMPUTER_SAY_MINUTES, 3s); - minutesTalkNum = Is25ManRaid() ? TALK_COMPUTER_TEN : TALK_COMPUTER_EIGHT; - for (uint32 i = 0; i < uint32(TALK_COMPUTER_ZERO - minutesTalkNum - 1); ++i) + _minutesTalkNum = Is25ManRaid() ? TALK_COMPUTER_TEN : TALK_COMPUTER_EIGHT; + for (uint32 i = 0; i < uint32(TALK_COMPUTER_ZERO - _minutesTalkNum - 1); ++i) events.ScheduleEvent(EVENT_COMPUTER_SAY_MINUTES, Milliseconds((i + 1) * 60000)); - events.ScheduleEvent(EVENT_COMPUTER_SAY_MINUTES, Milliseconds((TALK_COMPUTER_ZERO - minutesTalkNum) * 60000)); + events.ScheduleEvent(EVENT_COMPUTER_SAY_MINUTES, Milliseconds((TALK_COMPUTER_ZERO - _minutesTalkNum) * 60000)); } // ensure LMK2 is at proper position @@ -375,10 +364,10 @@ struct boss_mimiron : public BossAI { if (!me->IsInCombat()) { - outofCombatTimer += diff; - if (outofCombatTimer >= 10000) + _outOfCombatTimer += diff; + if (_outOfCombatTimer >= 10000) { - outofCombatTimer = 0; + _outOfCombatTimer = 0; if (Creature* c = GetLMK2()) me->CastSpell(c, RAND(SPELL_ENTER_VEHICLE_0, SPELL_ENTER_VEHICLE_1, SPELL_ENTER_VEHICLE_2, SPELL_ENTER_VEHICLE_4), true); } @@ -400,7 +389,7 @@ struct boss_mimiron : public BossAI break; case EVENT_COMPUTER_SAY_MINUTES: if (Creature* computer = me->SummonCreature(NPC_COMPUTER, 2746.7f, 2569.44f, 410.39f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 1000)) - computer->AI()->Talk(minutesTalkNum++); + computer->AI()->Talk(_minutesTalkNum++); break; case EVENT_MIMIRON_SAY_HARDMODE: Talk(SAY_HARDMODE_ON); @@ -409,8 +398,8 @@ struct boss_mimiron : public BossAI break; case EVENT_SPAWN_FLAMES_INITIAL: { - if (changeAllowedFlameSpreadTime) - allowedFlameSpreadTime = GameTime::GetGameTime().count(); + if (_changeAllowedFlameSpreadTime) + _allowedFlameSpreadTime = GameTime::GetGameTime().count(); std::vector pg; Map::PlayerList const& pl = me->GetMap()->GetPlayers(); @@ -438,9 +427,9 @@ struct boss_mimiron : public BossAI } break; case EVENT_BERSERK: - berserk = true; + _berserk = true; Talk(SAY_BERSERK); - if (hardmode) + if (_hardmode) me->SummonCreature(33576, 2744.78f, 2569.47f, 364.32f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 120000); events.ScheduleEvent(EVENT_BERSERK_2, 0ms); break; @@ -470,7 +459,7 @@ struct boss_mimiron : public BossAI case EVENT_SIT_LMK2_INTERVAL: if (Creature* LMK2 = GetLMK2()) { - if (hardmode) + if (_hardmode) { LMK2->CastSpell(LMK2, SPELL_EMERGENCY_MODE, true); if (Vehicle* veh = LMK2->GetVehicleKit()) @@ -550,7 +539,7 @@ struct boss_mimiron : public BossAI case EVENT_VX001_START_FIGHT: if (Creature* VX001 = GetVX001()) { - if (hardmode) + if (_hardmode) VX001->CastSpell(VX001, SPELL_EMERGENCY_MODE, true); VX001->AI()->SetData(1, 2); me->SetInCombatWithZone(); @@ -579,7 +568,7 @@ struct boss_mimiron : public BossAI EnterEvadeMode(EVADE_REASON_OTHER); break; case EVENT_SAY_VX001_DEAD: - changeAllowedFlameSpreadTime = true; + _changeAllowedFlameSpreadTime = true; Talk(SAY_VX001_DEATH); events.ScheduleEvent(EVENT_ENTER_ACU, 7s); break; @@ -599,7 +588,7 @@ struct boss_mimiron : public BossAI case EVENT_ACU_START_ATTACK: if (Creature* ACU = GetACU()) { - if (hardmode) + if (_hardmode) ACU->CastSpell(ACU, SPELL_EMERGENCY_MODE, true); ACU->AI()->SetData(1, 3); me->SetInCombatWithZone(); @@ -687,7 +676,7 @@ struct boss_mimiron : public BossAI LMK2->CastSpell(LMK2, SPELL_SELF_REPAIR, true); //LMK2->SetHealth( LMK2->GetMaxHealth()/2 ); VX001->CastSpell(VX001, SPELL_SELF_REPAIR, true); //VX001->SetHealth( VX001->GetMaxHealth()/2 ); ACU->CastSpell(ACU, SPELL_SELF_REPAIR, true); //ACU->SetHealth( ACU->GetMaxHealth()/2 ); - if (hardmode) + if (_hardmode) { LMK2->CastSpell(LMK2, SPELL_EMERGENCY_MODE, true); VX001->CastSpell(VX001, SPELL_EMERGENCY_MODE, true); @@ -740,18 +729,9 @@ struct boss_mimiron : public BossAI DoCastSelf(SPELL_SLEEP_VISUAL_1); - if (instance) - for( uint16 i = 0; i < 3; ++i ) - if (GameObject* door = instance->GetGameObject(DATA_GO_MIMIRON_DOOR_1 + i)) - if (door->GetGoState() != GO_STATE_ACTIVE ) - { - door->SetLootState(GO_READY); - door->UseDoorOrButton(0, false); - } - instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, NPC_LEVIATHAN_MKII, 1, me); - if (hardmode) + if (_hardmode) if (Creature* computer = me->SummonCreature(NPC_COMPUTER, 2746.7f, 2569.44f, 410.39f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 1000)) computer->AI()->Talk(TALK_COMPUTER_TERMINATED); @@ -770,7 +750,7 @@ struct boss_mimiron : public BossAI me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); instance->SetBossState(BOSS_MIMIRON, DONE); // spawn chest - if (uint32 chestId = (hardmode ? RAID_MODE(GO_MIMIRON_CHEST_HARD, GO_MIMIRON_CHEST_HERO_HARD) : RAID_MODE(GO_MIMIRON_CHEST, GO_MIMIRON_CHEST_HERO))) + if (uint32 chestId = (_hardmode ? RAID_MODE(GO_MIMIRON_CHEST_HARD, GO_MIMIRON_CHEST_HERO_HARD) : RAID_MODE(GO_MIMIRON_CHEST, GO_MIMIRON_CHEST_HERO))) { if (GameObject* go = me->SummonGameObject(chestId, 2744.65f, 2569.46f, 364.397f, 0, 0, 0, 0, 0, 0)) { @@ -800,9 +780,9 @@ struct boss_mimiron : public BossAI void EnterEvadeMode(EvadeReason why) override { - if (bIsEvading) + if (_isEvading) return; - bIsEvading = true; + _isEvading = true; if (Creature* c = GetLMK2()) c->AI()->EnterEvadeMode(why); @@ -823,19 +803,11 @@ struct boss_mimiron : public BossAI me->ExitVehicle(); BossAI::EnterEvadeMode(why); - bIsEvading = false; + _isEvading = false; } void ResetGameObjects() { - for (uint16 i = 0; i < 3; ++i) - if (GameObject* door = instance->GetGameObject(DATA_GO_MIMIRON_DOOR_1 + i)) - if (door->GetGoState() != GO_STATE_ACTIVE) - { - door->SetLootState(GO_READY); - door->UseDoorOrButton(0, false); - } - if (GameObject* elevator = me->FindNearestGameObject(GO_MIMIRON_ELEVATOR, 200.0f)) { if (elevator->GetGoState() != GO_STATE_ACTIVE ) @@ -857,14 +829,6 @@ struct boss_mimiron : public BossAI void CloseDoorAndButton() { - for (uint16 i = 0; i < 3; ++i) - if (GameObject* door = instance->GetGameObject(DATA_GO_MIMIRON_DOOR_1 + i)) - if (door->GetGoState() != GO_STATE_READY) - { - door->SetLootState(GO_READY); - door->UseDoorOrButton(0, false); - } - if (GameObject* button = me->FindNearestGameObject(GO_BUTTON, 200.0f)) if (button->GetGoState() != GO_STATE_ACTIVE) { @@ -907,16 +871,16 @@ struct boss_mimiron : public BossAI } break; case 7: - hardmode = true; + _hardmode = true; break; case 11: - bAchievProximityMine = true; + _achievProximityMine = true; break; case 12: - bAchievBombBot = true; + _achievBombBot = true; break; case 13: - bAchievRocketStrike = true; + _achievRocketStrike = true; break; } } @@ -926,38 +890,45 @@ struct boss_mimiron : public BossAI switch (id) { case 1: - return (hardmode ? 1 : 0); + return (_hardmode ? 1 : 0); case 2: - return (berserk ? 1 : 0); + return (_berserk ? 1 : 0); case 10: - return allowedFlameSpreadTime; + return _allowedFlameSpreadTime; case 11: - return (bAchievProximityMine ? 1 : 0); + return (_achievProximityMine ? 1 : 0); case 12: - return (bAchievBombBot ? 1 : 0); + return (_achievBombBot ? 1 : 0); case 13: - return (bAchievRocketStrike ? 1 : 0); + return (_achievRocketStrike ? 1 : 0); } return 0; } + +private: + bool _isEvading; + bool _hardmode; + bool _berserk; + bool _achievProximityMine; + bool _achievBombBot; + bool _achievRocketStrike; + uint32 _allowedFlameSpreadTime; + bool _changeAllowedFlameSpreadTime; + uint8 _minutesTalkNum; + uint32 _outOfCombatTimer; }; struct npc_ulduar_leviathan_mkii : public ScriptedAI { - npc_ulduar_leviathan_mkii(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_leviathan_mkii(Creature* creature) : ScriptedAI(creature) { instance = me->GetInstanceScript(); - bIsEvading = false; + _isEvading = false; } - InstanceScript* instance; - EventMap events; - bool bIsEvading; - uint8 Phase; - void Reset() override { - Phase = 0; + _phase = 0; if (Unit* c = GetS3()) c->ExitVehicle(); // this should never happen! if (Creature* c = me->SummonCreature(NPC_LEVIATHAN_MKII_CANNON, *me, TEMPSUMMON_MANUAL_DESPAWN)) @@ -967,7 +938,7 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI me->SetReactState(REACT_AGGRESSIVE); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - events.Reset(); + _events.Reset(); } void SetData(uint32 id, uint32 value) override @@ -977,35 +948,35 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI switch (value) { case 0: - Phase = 0; - events.Reset(); + _phase = 0; + _events.Reset(); break; case 1: - Phase = 1; + _phase = 1; me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); if (Unit* target = SelectTargetFromPlayerList(75.0f)) AttackStart(target); DoZoneInCombat(); - events.Reset(); - events.ScheduleEvent(EVENT_SPELL_NAPALM_SHELL, 3s); - events.ScheduleEvent(EVENT_SPELL_PLASMA_BLAST, 10s); - events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 20s); - events.ScheduleEvent(EVENT_PROXIMITY_MINES_1, 6s); + _events.Reset(); + _events.ScheduleEvent(EVENT_SPELL_NAPALM_SHELL, 3s); + _events.ScheduleEvent(EVENT_SPELL_PLASMA_BLAST, 10s); + _events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 20s); + _events.ScheduleEvent(EVENT_PROXIMITY_MINES_1, 6s); if (Creature* c = GetMimiron()) if (c->AI()->GetData(1)) - events.ScheduleEvent(EVENT_FLAME_SUPPRESSION_50000, 60s); + _events.ScheduleEvent(EVENT_FLAME_SUPPRESSION_50000, 60s); break; case 4: me->SetReactState(REACT_AGGRESSIVE); DoResetThreatList(); - Phase = 4; + _phase = 4; me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); if (Unit* target = SelectTargetFromPlayerList(75.0f)) AttackStart(target); DoZoneInCombat(); - events.Reset(); - events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 20s); - events.ScheduleEvent(EVENT_PROXIMITY_MINES_1, 6s); + _events.Reset(); + _events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 20s); + _events.ScheduleEvent(EVENT_PROXIMITY_MINES_1, 6s); break; } } @@ -1019,7 +990,7 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI if (me->GetReactState() == REACT_PASSIVE) return; me->SetReactState(REACT_PASSIVE); - if (Phase == 1) + if (_phase == 1) { if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) { @@ -1037,7 +1008,7 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI c->AI()->SetData(0, 1); } } - else if (Phase == 4) + else if (_phase == 4) { if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) { @@ -1063,7 +1034,7 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (!me->HasUnitState(UNIT_STATE_CASTING)) DoMeleeAttackIfReady(); @@ -1072,29 +1043,29 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI if (!cannon || cannon->HasUnitState(UNIT_STATE_CASTING) || me->HasUnitState(UNIT_STATE_CASTING) || me->HasSilenceAura()) return; - switch (events.ExecuteEvent()) + switch (_events.ExecuteEvent()) { case 0: break; case EVENT_SPELL_NAPALM_SHELL: { - Player* pTarget = nullptr; - std::vector pList; + Player* target = nullptr; + std::vector playerList; Map::PlayerList const& pl = me->GetMap()->GetPlayers(); for( Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr ) if (Player* plr = itr->GetSource()) if (plr->IsAlive() && plr->GetDistance2d(me) > 15.0f ) - pList.push_back(plr); + playerList.push_back(plr); - if (!pList.empty()) - pTarget = pList[urand(0, pList.size() - 1)]; + if (!playerList.empty()) + target = playerList[urand(0, playerList.size() - 1)]; else - pTarget = (Player*)SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true); + target = (Player*)SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true); - if (pTarget) - cannon->CastSpell(pTarget, SPELL_NAPALM_SHELL, false); + if (target) + cannon->CastSpell(target, SPELL_NAPALM_SHELL, false); - events.Repeat(14s); + _events.Repeat(14s); } break; case EVENT_SPELL_PLASMA_BLAST: @@ -1103,12 +1074,12 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI Talk(EMOTE_PLASMA_BLAST); cannon->CastSpell(victim, SPELL_PLASMA_BLAST, false); } - events.Repeat(22s); + _events.Repeat(22s); break; case EVENT_SPELL_SHOCK_BLAST: me->CastSpell(me->GetVictim(), SPELL_SHOCK_BLAST, false); - events.Repeat(30s); - events.ScheduleEvent(EVENT_PROXIMITY_MINES_1, 8s); + _events.Repeat(30s); + _events.ScheduleEvent(EVENT_PROXIMITY_MINES_1, 8s); break; case EVENT_PROXIMITY_MINES_1: for (uint8 i = 0; i < 10; ++i) @@ -1129,7 +1100,7 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI if (who->IsPlayer()) if (Creature* c = GetMimiron()) { - if (Phase == 1) + if (_phase == 1) { c->AI()->Talk(SAY_MKII_SLAY); } @@ -1142,9 +1113,9 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI void EnterEvadeMode(EvadeReason why) override { - if (bIsEvading) + if (_isEvading) return; - bIsEvading = true; + _isEvading = true; me->RemoveAllAuras(); me->ExitVehicle(); @@ -1153,7 +1124,7 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI if (Creature* mimiron = GetMimiron()) mimiron->AI()->EnterEvadeMode(why); - bIsEvading = false; + _isEvading = false; } void PassengerBoarded(Unit* p, int8 /*seat*/, bool apply) override @@ -1182,33 +1153,31 @@ struct npc_ulduar_leviathan_mkii : public ScriptedAI me->SetReactState(REACT_AGGRESSIVE); } } + +private: + InstanceScript* instance; + EventMap _events; + bool _isEvading; + uint8 _phase; }; struct npc_ulduar_vx001 : public ScriptedAI { - npc_ulduar_vx001(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_vx001(Creature* creature) : ScriptedAI(creature) { instance = me->GetInstanceScript(); - bIsEvading = false; + _isEvading = false; } - InstanceScript* instance; - EventMap events; - bool bIsEvading; - uint8 Phase; - bool fighting; - bool leftarm; - uint32 spinningUpOrientation; - uint16 spinningUpTimer; - void Reset() override { - Phase = 0; - fighting = false; - leftarm = false; - spinningUpTimer = 0; + _phase = 0; + _fighting = false; + _leftArm = false; + _spinningUpOrientation = 0; + _spinningUpTimer = 0; me->SetRegeneratingHealth(false); - events.Reset(); + _events.Reset(); } void AttackStart(Unit* /*who*/) override {} @@ -1220,41 +1189,41 @@ struct npc_ulduar_vx001 : public ScriptedAI switch (value) { case 0: - Phase = 0; - fighting = false; + _phase = 0; + _fighting = false; me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - events.Reset(); + _events.Reset(); break; case 2: - Phase = 2; - fighting = true; + _phase = 2; + _fighting = true; me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_SPELL_CAST_OMNI); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - events.Reset(); - events.ScheduleEvent(EVENT_SPELL_HEAT_WAVE, 10s); - events.ScheduleEvent(EVENT_SPELL_ROCKET_STRIKE, 16s); - events.ScheduleEvent(EVENT_SPELL_RAPID_BURST, 0ms); - events.ScheduleEvent(EVENT_SPELL_SPINNING_UP, 30s); - events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 3s); + _events.Reset(); + _events.ScheduleEvent(EVENT_SPELL_HEAT_WAVE, 10s); + _events.ScheduleEvent(EVENT_SPELL_ROCKET_STRIKE, 16s); + _events.ScheduleEvent(EVENT_SPELL_RAPID_BURST, 0ms); + _events.ScheduleEvent(EVENT_SPELL_SPINNING_UP, 30s); + _events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 3s); if (Creature* c = GetMimiron()) if (c->AI()->GetData(1)) { - events.ScheduleEvent(EVENT_FLAME_SUPPRESSION_10, 7s); - events.ScheduleEvent(EVENT_FROST_BOMB, 1s); + _events.ScheduleEvent(EVENT_FLAME_SUPPRESSION_10, 7s); + _events.ScheduleEvent(EVENT_FROST_BOMB, 1s); } break; case 4: - Phase = 4; - fighting = true; + _phase = 4; + _fighting = true; me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - events.Reset(); - events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 3s); - events.ScheduleEvent(EVENT_SPELL_ROCKET_STRIKE, 16s); - events.ScheduleEvent(EVENT_HAND_PULSE, 1ms); - events.ScheduleEvent(EVENT_SPELL_SPINNING_UP, 30s); + _events.Reset(); + _events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 3s); + _events.ScheduleEvent(EVENT_SPELL_ROCKET_STRIKE, 16s); + _events.ScheduleEvent(EVENT_HAND_PULSE, 1ms); + _events.ScheduleEvent(EVENT_SPELL_SPINNING_UP, 30s); if (Creature* c = GetMimiron()) if (c->AI()->GetData(1)) - events.ScheduleEvent(EVENT_FROST_BOMB, 1s); + _events.ScheduleEvent(EVENT_FROST_BOMB, 1s); break; } } @@ -1262,7 +1231,7 @@ struct npc_ulduar_vx001 : public ScriptedAI uint32 GetData(uint32 /*id*/) const override { - return spinningUpOrientation; + return _spinningUpOrientation; } void DoAction(int32 action) override @@ -1283,7 +1252,7 @@ struct npc_ulduar_vx001 : public ScriptedAI if (me->GetReactState() == REACT_PASSIVE) return; me->SetReactState(REACT_PASSIVE); - if (Phase == 2) + if (_phase == 2) { if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) { @@ -1298,7 +1267,7 @@ struct npc_ulduar_vx001 : public ScriptedAI c->AI()->SetData(0, 2); } } - else if (Phase == 4) + else if (_phase == 4) { if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) { @@ -1321,41 +1290,41 @@ struct npc_ulduar_vx001 : public ScriptedAI void UpdateAI(uint32 diff) override { - if (!fighting) + if (!_fighting) return; - events.Update(diff); + _events.Update(diff); - if (spinningUpTimer) // executed about a second after starting casting to ensure players can see the correct direction + if (_spinningUpTimer) // executed about a second after starting casting to ensure players can see the correct direction { - if (spinningUpTimer <= diff) + if (_spinningUpTimer <= diff) { - float angle = (spinningUpOrientation * 2 * M_PI) / 100.0f; + float angle = (_spinningUpOrientation * 2 * M_PI) / 100.0f; me->SetFacingTo(angle); - spinningUpTimer = 0; + _spinningUpTimer = 0; } else - spinningUpTimer -= diff; + _spinningUpTimer -= diff; } if (me->HasUnitState(UNIT_STATE_CASTING)) return; - switch (events.ExecuteEvent()) + switch (_events.ExecuteEvent()) { case 0: break; case EVENT_SPELL_HEAT_WAVE: me->CastSpell(me, SPELL_HEAT_WAVE, true); - events.Repeat(10s); + _events.Repeat(10s); break; case EVENT_SPELL_ROCKET_STRIKE: if (Vehicle* vk = me->GetVehicleKit()) { - for( int i = 0; i < (Phase / 2); ++i ) + for( int i = 0; i < (_phase / 2); ++i ) { - uint8 index = (Phase == 2 ? rand() % 2 : i); + uint8 index = (_phase == 2 ? rand() % 2 : i); if (Unit* r = vk->GetPassenger(5 + index)) if (Player* temp = SelectTargetFromPlayerList(100.0f)) { @@ -1364,15 +1333,15 @@ struct npc_ulduar_vx001 : public ScriptedAI Position exitPos = r->GetPosition(); exitPos.m_positionX += cos(me->GetOrientation()) * 2.35f; exitPos.m_positionY += std::sin(me->GetOrientation()) * 2.35f; - exitPos.m_positionZ += 2.0f * Phase; + exitPos.m_positionZ += 2.0f * _phase; r->_ExitVehicle(&exitPos); me->RemoveAurasByType(SPELL_AURA_CONTROL_VEHICLE, r->GetGUID()); if (r->IsCreature()) r->ToCreature()->AI()->SetData(0, 0); } } - events.Repeat(20s); - events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 10s); + _events.Repeat(20s); + _events.ScheduleEvent(EVENT_REINSTALL_ROCKETS, 10s); } break; case EVENT_REINSTALL_ROCKETS: @@ -1391,7 +1360,7 @@ struct npc_ulduar_vx001 : public ScriptedAI me->CastSpell(p, SPELL_RAPID_BURST, true); me->SetFacingToObject(p); } - events.Repeat(3200ms); + _events.Repeat(3200ms); break; case EVENT_HAND_PULSE: if (Player* p = SelectTargetFromPlayerList(80.0f)) @@ -1402,7 +1371,7 @@ struct npc_ulduar_vx001 : public ScriptedAI vb->SendMeleeAttackStop(); vb->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - if (!leftarm) + if (!_leftArm) { vb->HandleEmoteCommand(EMOTE_ONESHOT_CUSTOM_SPELL_03); me->CastSpell(p, SPELL_HAND_PULSE_R, false); @@ -1414,18 +1383,18 @@ struct npc_ulduar_vx001 : public ScriptedAI } } - leftarm = !leftarm; + _leftArm = !_leftArm; } - events.Repeat(1750ms); + _events.Repeat(1750ms); break; case EVENT_SPELL_SPINNING_UP: - events.Repeat(45s); + _events.Repeat(45s); if (Player* p = SelectTargetFromPlayerList(80.0f)) { float angle = me->GetAngle(p); - spinningUpOrientation = (uint32)((angle * 100.0f) / (2 * M_PI)); - spinningUpTimer = 1500; + _spinningUpOrientation = (uint32)((angle * 100.0f) / (2 * M_PI)); + _spinningUpTimer = 1500; me->SetFacingTo(angle); me->CastSpell(p, SPELL_SPINNING_UP, true); if (Unit* vehicle = me->GetVehicleBase()) @@ -1433,16 +1402,16 @@ struct npc_ulduar_vx001 : public ScriptedAI vehicle->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_CUSTOM_SPELL_01); vehicle->HandleEmoteCommand(EMOTE_STATE_CUSTOM_SPELL_01); } - events.RescheduleEvent((Phase == 2 ? EVENT_SPELL_RAPID_BURST : EVENT_HAND_PULSE), 14s + 500ms); + _events.RescheduleEvent((_phase == 2 ? EVENT_SPELL_RAPID_BURST : EVENT_HAND_PULSE), 14s + 500ms); } break; case EVENT_FLAME_SUPPRESSION_10: me->CastSpell(me, SPELL_FLAME_SUPPRESSANT_10yd, false); - events.Repeat(10s); + _events.Repeat(10s); break; case EVENT_FROST_BOMB: me->CastCustomSpell(SPELL_VX001_FROST_BOMB, SPELLVALUE_MAX_TARGETS, 1, (Unit*)nullptr, false); - events.Repeat(45s); + _events.Repeat(45s); break; } } @@ -1454,7 +1423,7 @@ struct npc_ulduar_vx001 : public ScriptedAI if (who->IsPlayer()) if (Creature* c = GetMimiron()) { - if (Phase == 2) + if (_phase == 2) { c->AI()->Talk(SAY_VX001_SLAY); } @@ -1467,9 +1436,9 @@ struct npc_ulduar_vx001 : public ScriptedAI void EnterEvadeMode(EvadeReason why) override { - if (bIsEvading) + if (_isEvading) return; - bIsEvading = true; + _isEvading = true; me->RemoveAllAuras(); me->ExitVehicle(); @@ -1478,7 +1447,7 @@ struct npc_ulduar_vx001 : public ScriptedAI if (Creature* mimiron = GetMimiron()) mimiron->AI()->EnterEvadeMode(why); - bIsEvading = false; + _isEvading = false; } void PassengerBoarded(Unit* p, int8 /*seat*/, bool apply) override @@ -1495,28 +1464,33 @@ struct npc_ulduar_vx001 : public ScriptedAI me->SetReactState(REACT_AGGRESSIVE); } } + +private: + InstanceScript* instance; + EventMap _events; + bool _isEvading; + bool _fighting; + bool _leftArm; + uint32 _spinningUpOrientation; + uint16 _spinningUpTimer; + uint8 _phase; }; struct npc_ulduar_aerial_command_unit : public ScriptedAI { - npc_ulduar_aerial_command_unit(Creature* pCreature) : ScriptedAI(pCreature), summons(me) + npc_ulduar_aerial_command_unit(Creature* creature) : ScriptedAI(creature), _summons(me) { instance = me->GetInstanceScript(); - bIsEvading = false; + _isEvading = false; me->SetDisableGravity(true); } - InstanceScript* instance; - EventMap events; - SummonList summons; - bool bIsEvading; - uint8 Phase; - void Reset() override { - Phase = 0; - events.Reset(); - summons.DespawnAll(); + _phase = 0; + _isDefeated = false; + _events.Reset(); + _summons.DespawnAll(); me->SetCombatMovement(false); /// @todo: research ACU behaviour } @@ -1527,31 +1501,31 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI switch (value) { case 0: - Phase = 0; - events.Reset(); + _phase = 0; + _events.Reset(); break; case 3: - Phase = 3; + _phase = 3; me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); DoZoneInCombat(); - events.Reset(); - events.ScheduleEvent(EVENT_SUMMON_BOMB_BOT, 15s); - events.ScheduleEvent(EVENT_SUMMON_ASSAULT_BOT, 1s); - events.ScheduleEvent(EVENT_SUMMON_JUNK_BOT, 10s); + _events.Reset(); + _events.ScheduleEvent(EVENT_SUMMON_BOMB_BOT, 15s); + _events.ScheduleEvent(EVENT_SUMMON_ASSAULT_BOT, 1s); + _events.ScheduleEvent(EVENT_SUMMON_JUNK_BOT, 10s); if (Creature* c = GetMimiron()) if (c->AI()->GetData(1)) - events.ScheduleEvent(EVENT_SUMMON_EMERGENCY_FIRE_BOTS, 0ms); + _events.ScheduleEvent(EVENT_SUMMON_EMERGENCY_FIRE_BOTS, 0ms); break; case 4: me->SetReactState(REACT_AGGRESSIVE); DoResetThreatList(); - Phase = 4; + _phase = 4; me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); if (Unit* target = SelectTargetFromPlayerList(75.0f)) AttackStart(target); DoZoneInCombat(); - events.Reset(); - events.ScheduleEvent(EVENT_SPELL_PLASMA_BALL, 0ms); + _events.Reset(); + _events.ScheduleEvent(EVENT_SPELL_PLASMA_BALL, 0ms); } } } @@ -1566,7 +1540,7 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI me->SetReactState(REACT_PASSIVE); me->SetDisableGravity(false); me->GetMotionMaster()->MoveFall(); - events.DelayEvents(25s); + _events.DelayEvents(25s); break; case DO_ENABLE_AERIAL: me->SetDisableGravity(true); @@ -1576,7 +1550,7 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI }, 2s); break; case 1337: - summons.DespawnAll(); + _summons.DespawnAll(); break; } } @@ -1586,10 +1560,11 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI if (damage >= me->GetHealth() || me->GetHealth() < 15000) { damage = 0; - if (me->GetReactState() == REACT_PASSIVE) + if (_isDefeated) return; + _isDefeated = true; me->SetReactState(REACT_PASSIVE); - if (Phase == 3) + if (_phase == 3) { if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) { @@ -1608,7 +1583,7 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI c->AI()->SetData(0, 3); } } - else if (Phase == 4) + else if (_phase == 4) { if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) { @@ -1637,30 +1612,30 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - switch (events.ExecuteEvent()) + switch (_events.ExecuteEvent()) { case 0: break; case EVENT_SUMMON_BOMB_BOT: me->CastSpell(me, SPELL_SUMMON_BOMB_BOT, false); - events.Repeat(15s); + _events.Repeat(15s); break; case EVENT_SUMMON_ASSAULT_BOT: if (GameObject* pad = me->FindNearestGameObject(RAND(194742, 194746, 194745), 200.0f)) if (Creature* trigger = me->SummonCreature(NPC_BOT_SUMMON_TRIGGER, *pad, TEMPSUMMON_TIMED_DESPAWN, 15000)) trigger->AI()->DoAction(2); - events.Repeat(30s); + _events.Repeat(30s); break; case EVENT_SUMMON_JUNK_BOT: if (GameObject* pad = me->FindNearestGameObject(RAND(194741, 194744, 194747), 200.0f)) if (Creature* trigger = me->SummonCreature(NPC_BOT_SUMMON_TRIGGER, *pad, TEMPSUMMON_TIMED_DESPAWN, 15000)) trigger->AI()->DoAction(1); - events.Repeat(10s); + _events.Repeat(10s); break; case EVENT_SUMMON_EMERGENCY_FIRE_BOTS: { @@ -1669,12 +1644,12 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI if (GameObject* pad = me->FindNearestGameObject(ids[i], 200.0f)) if (Creature* trigger = me->SummonCreature(NPC_BOT_SUMMON_TRIGGER, *pad, TEMPSUMMON_MANUAL_DESPAWN)) trigger->AI()->DoAction(3); - events.Repeat(45s); + _events.Repeat(45s); } break; } - DoSpellAttackIfReady(Phase == 3 ? SPELL_PLASMA_BALL_P1 : SPELL_PLASMA_BALL_P2); + DoSpellAttackIfReady(_phase == 3 ? SPELL_PLASMA_BALL_P1 : SPELL_PLASMA_BALL_P2); } void MoveInLineOfSight(Unit* /*mover*/) override {} @@ -1684,7 +1659,7 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI if (who->IsPlayer()) if (Creature* c = GetMimiron()) { - if (Phase == 3) + if (_phase == 3) { c->AI()->Talk(SAY_AERIAL_SLAY); } @@ -1697,9 +1672,9 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI void EnterEvadeMode(EvadeReason why) override { - if (bIsEvading) + if (_isEvading) return; - bIsEvading = true; + _isEvading = true; me->RemoveAllAuras(); me->ExitVehicle(); @@ -1708,44 +1683,49 @@ struct npc_ulduar_aerial_command_unit : public ScriptedAI if (Creature* mimiron = GetMimiron()) mimiron->AI()->EnterEvadeMode(why); - bIsEvading = false; + _isEvading = false; } void JustSummoned(Creature* s) override { - summons.Summon(s); + _summons.Summon(s); if (s->GetEntry() == NPC_BOMB_BOT) s->m_positionZ = 364.34f; } void SummonedCreatureDespawn(Creature* s) override { - summons.Despawn(s); + _summons.Despawn(s); } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { if (spell->Id == SPELL_SELF_REPAIR) { + _isDefeated = false; me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); me->SetReactState(REACT_AGGRESSIVE); } } + +private: + InstanceScript* instance; + EventMap _events; + SummonList _summons; + bool _isEvading; + bool _isDefeated; + uint8 _phase; }; struct npc_ulduar_proximity_mine : public ScriptedAI { - npc_ulduar_proximity_mine(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_proximity_mine(Creature* creature) : ScriptedAI(creature) { - exploded = false; - timer = 2500; - timer2 = 35000; + _exploded = false; + _timer = 2500; + _timer2 = 35000; } - bool exploded; - uint16 timer; - uint16 timer2; - void AttackStart(Unit* /*who*/) override {} void MoveInLineOfSight(Unit* /*who*/) override {} bool CanAIAttack(Unit const* /*target*/) const override { return false; } @@ -1753,30 +1733,35 @@ struct npc_ulduar_proximity_mine : public ScriptedAI // MoveInLineOfSight is checked every few yards, can't use it void UpdateAI(uint32 diff) override { - if (timer2 <= diff) + if (_timer2 <= diff) { - timer2 = 35000; - if (!exploded) + _timer2 = 35000; + if (!_exploded) { - exploded = true; + _exploded = true; me->CastSpell(me, SPELL_MINE_EXPLOSION, false); } } else - timer2 -= diff; + _timer2 -= diff; - if (timer <= diff) + if (_timer <= diff) { - timer = 500; - if (!exploded && SelectTargetFromPlayerList(1.9f)) + _timer = 500; + if (!_exploded && SelectTargetFromPlayerList(1.9f)) { - exploded = true; + _exploded = true; me->CastSpell(me, SPELL_MINE_EXPLOSION, false); } } else - timer -= diff; + _timer -= diff; } + +private: + bool _exploded; + uint16 _timer; + uint16 _timer2; }; class spell_ulduar_mimiron_mine_explosion : public SpellScript @@ -1799,7 +1784,7 @@ class spell_ulduar_mimiron_mine_explosion : public SpellScript struct npc_ulduar_mimiron_rocket : public NullCreatureAI { - npc_ulduar_mimiron_rocket(Creature* pCreature) : NullCreatureAI(pCreature) {} + npc_ulduar_mimiron_rocket(Creature* creature) : NullCreatureAI(creature) {} void InitializeAI() override { @@ -1831,15 +1816,12 @@ struct npc_ulduar_mimiron_rocket : public NullCreatureAI struct npc_ulduar_bot_summon_trigger : public NullCreatureAI { - npc_ulduar_bot_summon_trigger(Creature* pCreature) : NullCreatureAI(pCreature) { } - - uint32 timer; - uint8 option; + npc_ulduar_bot_summon_trigger(Creature* creature) : NullCreatureAI(creature) { } void Reset() override { - timer = 8000; - option = 0; + _timer = 8000; + _option = 0; } void DoAction(int32 param) override @@ -1848,29 +1830,29 @@ struct npc_ulduar_bot_summon_trigger : public NullCreatureAI { case 1: me->CastSpell(me, SPELL_BEAM_GREEN, true); - option = 1; + _option = 1; break; case 2: me->CastSpell(me, SPELL_BEAM_YELLOW, true); - option = 2; + _option = 2; break; case 3: me->CastSpell(me, SPELL_BEAM_BLUE, true); - option = 3; + _option = 3; break; } } void UpdateAI(uint32 diff) override { - if (timer <= diff) + if (_timer <= diff) { uint32 option_npcid[3] = {NPC_JUNK_BOT, NPC_ASSAULT_BOT, NPC_EMERGENCY_FIRE_BOT}; InstanceScript* instance = me->GetInstanceScript(); if (Creature* ACU = GetACU()) // ACU summons for easy removing - if (Creature* bot = ACU->SummonCreature( option_npcid[option - 1], *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 25000)) + if (Creature* bot = ACU->SummonCreature( option_npcid[_option - 1], *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 25000)) { - if (option < 3) + if (_option < 3) bot->SetInCombatWithZone(); if (Creature* m = GetMimiron()) if (m->AI()->GetData(1)) // hardmode @@ -1878,11 +1860,15 @@ struct npc_ulduar_bot_summon_trigger : public NullCreatureAI } me->DespawnOrUnsummon(500ms); - timer = 99999; + _timer = 99999; } else - timer -= diff; + _timer -= diff; } + +private: + uint32 _timer; + uint8 _option; }; // 64444 - Magnetic Core Summon @@ -2051,22 +2037,18 @@ public: struct npc_ulduar_flames_initial : public NullCreatureAI { - npc_ulduar_flames_initial(Creature* pCreature) : NullCreatureAI(pCreature) + npc_ulduar_flames_initial(Creature* creature) : NullCreatureAI(creature) { - CreateTime = GameTime::GetGameTime().count(); - events.Reset(); - events.ScheduleEvent(EVENT_FLAMES_SPREAD, 5750ms); + _createTime = GameTime::GetGameTime().count(); + _events.Reset(); + _events.ScheduleEvent(EVENT_FLAMES_SPREAD, 5750ms); if (Creature* flame = me->SummonCreature(NPC_FLAMES_SPREAD, me->GetPositionX(), me->GetPositionY(), 364.32f, 0.0f)) { - FlameList.push_back(flame->GetGUID()); + _flameList.push_back(flame->GetGUID()); flame->CastSpell(flame, SPELL_FLAMES_AURA, true); } } - GuidList FlameList; - EventMap events; - uint32 CreateTime; - void DoAction(int32 action) override { if (action == 1337) @@ -2077,7 +2059,7 @@ struct npc_ulduar_flames_initial : public NullCreatureAI { if (Creature* flame = me->SummonCreature(NPC_FLAMES_SPREAD, x, y, 364.32f, 0.0f)) { - FlameList.push_back(flame->GetGUID()); + _flameList.push_back(flame->GetGUID()); if (Creature* c = me->FindNearestCreature(NPC_FLAMES_SPREAD, 10.0f)) if (c->GetExactDist2d(flame->GetPositionX(), flame->GetPositionY()) <= 4.0f) return; @@ -2087,15 +2069,15 @@ struct npc_ulduar_flames_initial : public NullCreatureAI void RemoveFlame(ObjectGuid guid) { - FlameList.remove(guid); + _flameList.remove(guid); } void RemoveAll() { - for (ObjectGuid const& guid : FlameList) + for (ObjectGuid const& guid : _flameList) if (Creature* c = ObjectAccessor::GetCreature(*me, guid)) c->DespawnOrUnsummon(); - FlameList.clear(); + _flameList.clear(); me->DespawnOrUnsummon(); } @@ -2108,15 +2090,15 @@ struct npc_ulduar_flames_initial : public NullCreatureAI return; } - events.Update(diff); + _events.Update(diff); - switch (events.ExecuteEvent()) + switch (_events.ExecuteEvent()) { case 0: break; case EVENT_FLAMES_SPREAD: { - if (FlameList.empty()) + if (_flameList.empty()) { me->DespawnOrUnsummon(); return; @@ -2124,10 +2106,10 @@ struct npc_ulduar_flames_initial : public NullCreatureAI if (InstanceScript* instance = me->GetInstanceScript()) if (Creature* mimiron = GetMimiron()) - if (CreateTime < mimiron->AI()->GetData(10)) + if (_createTime < mimiron->AI()->GetData(10)) break; - Creature* last = ObjectAccessor::GetCreature(*me, FlameList.back()); + Creature* last = ObjectAccessor::GetCreature(*me, _flameList.back()); if (last) { float prevdist = 100.0f; @@ -2149,16 +2131,21 @@ struct npc_ulduar_flames_initial : public NullCreatureAI } } - events.Repeat(5750ms); + _events.Repeat(5750ms); } break; } } + +private: + GuidList _flameList; + EventMap _events; + uint32 _createTime; }; struct npc_ulduar_flames_spread : public NullCreatureAI { - npc_ulduar_flames_spread(Creature* pCreature) : NullCreatureAI(pCreature) {} + npc_ulduar_flames_spread(Creature* creature) : NullCreatureAI(creature) {} void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { @@ -2189,53 +2176,54 @@ struct npc_ulduar_flames_spread : public NullCreatureAI struct npc_ulduar_emergency_fire_bot : public ScriptedAI { - npc_ulduar_emergency_fire_bot(Creature* pCreature) : ScriptedAI(pCreature) + npc_ulduar_emergency_fire_bot(Creature* creature) : ScriptedAI(creature) { - events.Reset(); - events.ScheduleEvent(EVENT_EMERGENCY_BOT_CHECK, 1s); + _events.Reset(); + _events.ScheduleEvent(EVENT_EMERGENCY_BOT_CHECK, 1s); } - EventMap events; - void MoveInLineOfSight(Unit*) override {} void AttackStart(Unit*) override {} void MovementInform(uint32 type, uint32 id) override { if (type == POINT_MOTION_TYPE && id == 1) - events.ScheduleEvent(EVENT_EMERGENCY_BOT_ATTACK, 0ms); + _events.ScheduleEvent(EVENT_EMERGENCY_BOT_ATTACK, 0ms); } void UpdateAI(uint32 diff) override { - events.Update(diff); - switch (events.ExecuteEvent()) + _events.Update(diff); + switch (_events.ExecuteEvent()) { case 0: break; case EVENT_EMERGENCY_BOT_CHECK: - events.Repeat(15s); + _events.Repeat(15s); if (Creature* flame = me->FindNearestCreature(NPC_FLAMES_SPREAD, 150.0f, true)) { me->SetOrientation(me->GetAngle(flame->GetPositionX(), flame->GetPositionY())); float dist = me->GetExactDist2d(flame); if (dist <= 5.0f) - events.ScheduleEvent(EVENT_EMERGENCY_BOT_ATTACK, 0ms); + _events.ScheduleEvent(EVENT_EMERGENCY_BOT_ATTACK, 0ms); else me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + (dist - 5.0f)*cos(me->GetOrientation()), me->GetPositionY() + (dist - 5.0f)*sin(me->GetOrientation()), 364.32f); } break; case EVENT_EMERGENCY_BOT_ATTACK: me->CastSpell((Unit*)nullptr, SPELL_WATER_SPRAY, false); - events.RescheduleEvent(EVENT_EMERGENCY_BOT_CHECK, 5s); + _events.RescheduleEvent(EVENT_EMERGENCY_BOT_CHECK, 5s); break; } } + +private: + EventMap _events; }; struct npc_ulduar_rocket_strike_trigger : public NullCreatureAI { - npc_ulduar_rocket_strike_trigger(Creature* pCreature) : NullCreatureAI(pCreature) {} + npc_ulduar_rocket_strike_trigger(Creature* creature) : NullCreatureAI(creature) {} void SpellHitTarget(Unit* target, SpellInfo const* spell) override { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp index 23c3d5549..1030753aa 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp @@ -181,6 +181,7 @@ struct boss_razorscale : public BossAI void JustEngagedWith(Unit* who) override { + me->SetImmuneToPC(false); BossAI::JustEngagedWith(who); events.ScheduleEvent(EVENT_COMMANDER_SAY_AGGRO, 5s); events.ScheduleEvent(EVENT_EE_SAY_MOVE_OUT, 10s); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp index 6b94236bd..a73aafa68 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp @@ -399,6 +399,14 @@ struct boss_thorim : public BossAI go->SetGoState(GO_STATE_ACTIVE); } + void JustExitedCombat() override + { + EngagementOver(); + if (_encounterFinished) + return; + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + void EnterEvadeMode(EvadeReason why) override { DisableThorim(false); @@ -887,6 +895,7 @@ struct boss_thorim_lightning_orb : public npc_escortAI me->CastSpell(me, SPELL_LIGHTNING_DESTRUCTION, true); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*point*/) override { } @@ -952,6 +961,7 @@ struct boss_thorim_sif_blizzard : public npc_escortAI me->CastSpell(me, SPELL_BLIZZARD, true); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*point*/) override { } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp index 3e458823e..3fd86f9de 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp @@ -285,7 +285,6 @@ const Position KeepersPos[4] = const uint32 TABLE_KEEPER_ENTRY[4] = {NPC_FREYA_KEEPER, NPC_HODIR_KEEPER, NPC_MIMIRON_KEEPER, NPC_THORIM_KEEPER}; const uint32 TABLE_GOSSIP_ENTRY[4] = {NPC_FREYA_GOSSIP, NPC_HODIR_GOSSIP, NPC_MIMIRON_GOSSIP, NPC_THORIM_GOSSIP}; -const uint32 TABLE_KEEPER_TYPE[4] = {BOSS_FREYA, BOSS_HODIR, BOSS_MIMIRON, BOSS_THORIM}; static LocationsXY yoggPortalLoc[] = { @@ -366,12 +365,12 @@ const Position Middle = {1980.28f, -25.5868f, 329.397f, M_PI * 1.5f}; struct boss_yoggsaron_sara : public ScriptedAI { - boss_yoggsaron_sara(Creature* pCreature) : ScriptedAI(pCreature), summons(pCreature) + boss_yoggsaron_sara(Creature* creature) : ScriptedAI(creature), summons(creature) { - m_pInstance = pCreature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } - InstanceScript* m_pInstance; + InstanceScript* _instance; EventMap events; SummonList summons; @@ -464,27 +463,27 @@ struct boss_yoggsaron_sara : public ScriptedAI _currentIllusion = urand(1, 3); _isIllusionReversed = urand(0, 1); - if (m_pInstance) + if (_instance) { - m_pInstance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, CRITERIA_NOT_GETTING_OLDER); - m_pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SANITY); - m_pInstance->SetData(BOSS_YOGGSARON, NOT_STARTED); - if (GameObject* go = m_pInstance->GetGameObject(DATA_YOGG_SARON_DOORS)) + _instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, CRITERIA_NOT_GETTING_OLDER); + _instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SANITY); + _instance->SetData(BOSS_YOGGSARON, NOT_STARTED); + if (GameObject* go = _instance->GetGameObject(DATA_YOGG_SARON_DOORS)) go->SetGoState(GO_STATE_ACTIVE); } } void InitFight(Unit* target) { - if (!m_pInstance) + if (!_instance) return; // some simple hack checks - if (m_pInstance->GetBossState(BOSS_VEZAX) != DONE || m_pInstance->GetBossState(BOSS_XT002) != DONE) + if (_instance->GetBossState(BOSS_VEZAX) != DONE || _instance->GetBossState(BOSS_XT002) != DONE) return; - m_pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, CRITERIA_NOT_GETTING_OLDER); - m_pInstance->SetData(BOSS_YOGGSARON, IN_PROGRESS); + _instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, CRITERIA_NOT_GETTING_OLDER); + _instance->SetData(BOSS_YOGGSARON, IN_PROGRESS); me->SetInCombatWithZone(); AttackStart(target); @@ -505,24 +504,30 @@ struct boss_yoggsaron_sara : public ScriptedAI void DespawnGossipKeepers() { + static uint32 const gossipData[] = + { + DATA_FREYA_GOSSIP, DATA_HODIR_GOSSIP, + DATA_MIMIRON_GOSSIP, DATA_THORIM_GOSSIP + }; for (uint8 i = KEEPER_FREYA; i <= KEEPER_THORIM; i++) + { summons.DespawnEntry(TABLE_GOSSIP_ENTRY[i]); + if (Creature* keeper = _instance->GetCreature(gossipData[i])) + keeper->DespawnOrUnsummon(); + } } void UpdateKeeperSpawns() { + uint32 watchersMask = _instance->GetPersistentData( + PERSISTENT_DATA_WATCHERS_MASK); for (uint8 i = KEEPER_FREYA; i <= KEEPER_THORIM; i++) { - if (m_pInstance->GetData(TYPE_WATCHERS) & (1 << i)) + if (watchersMask & (1 << i)) { if (!summons.HasEntry(TABLE_KEEPER_ENTRY[i])) me->SummonCreature(TABLE_KEEPER_ENTRY[i], KeepersPos[i]); } - else if (m_pInstance->GetData(TABLE_KEEPER_TYPE[i]) == DONE) - { - if (!summons.HasEntry(TABLE_GOSSIP_ENTRY[i])) - me->SummonCreature(TABLE_GOSSIP_ENTRY[i], GossipKeepersPos[i]); - } } } @@ -614,8 +619,10 @@ struct boss_yoggsaron_sara : public ScriptedAI if (param == DATA_GET_KEEPERS_COUNT) { uint8 _count = 0; + uint32 watchersMask = _instance->GetPersistentData( + PERSISTENT_DATA_WATCHERS_MASK); for (uint8 i = 0; i < 4; ++i) - if (m_pInstance->GetData(TYPE_WATCHERS) & (1 << i)) + if (watchersMask & (1 << i)) ++_count; return _count; } @@ -738,7 +745,7 @@ struct boss_yoggsaron_sara : public ScriptedAI if (!SelectTargetFromPlayerList(90, SPELL_INSANE1)) { - m_pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_INSANE1); + _instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_INSANE1); EnterEvadeMode(EVADE_REASON_OTHER); return; } @@ -784,8 +791,8 @@ struct boss_yoggsaron_sara : public ScriptedAI // Whispers of YS me->SummonCreature(NPC_VOICE_OF_YOGG_SARON, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); - if (m_pInstance) - if (GameObject* go = m_pInstance->GetGameObject(DATA_YOGG_SARON_DOORS)) + if (_instance) + if (GameObject* go = _instance->GetGameObject(DATA_YOGG_SARON_DOORS)) go->SetGoState(GO_STATE_READY); events.ScheduleEvent(EVENT_SARA_P1_SPELLS, 0ms, 1, EVENT_PHASE_ONE); @@ -909,7 +916,7 @@ struct boss_yoggsaron_sara : public ScriptedAI struct boss_yoggsaron_cloud : public npc_escortAI { - boss_yoggsaron_cloud(Creature* pCreature) : npc_escortAI(pCreature) + boss_yoggsaron_cloud(Creature* creature) : npc_escortAI(creature) { InitWaypoint(); Reset(); @@ -931,6 +938,7 @@ struct boss_yoggsaron_cloud : public npc_escortAI void MoveInLineOfSight(Unit* /*who*/) override {} void AttackStart(Unit* /*who*/) override {} + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*point*/) override {} void Reset() override @@ -994,7 +1002,7 @@ struct boss_yoggsaron_cloud : public npc_escortAI struct boss_yoggsaron_guardian_of_ys : public ScriptedAI { - boss_yoggsaron_guardian_of_ys(Creature* pCreature) : ScriptedAI(pCreature) { } + boss_yoggsaron_guardian_of_ys(Creature* creature) : ScriptedAI(creature) { } uint32 _spellTimer; @@ -1027,9 +1035,9 @@ struct boss_yoggsaron_guardian_of_ys : public ScriptedAI struct boss_yoggsaron : public ScriptedAI { - boss_yoggsaron(Creature* pCreature) : ScriptedAI(pCreature), summons(pCreature) + boss_yoggsaron(Creature* creature) : ScriptedAI(creature), summons(creature) { - m_pInstance = me->GetInstanceScript(); + _instance = me->GetInstanceScript(); _thirdPhase = false; _usedInsane = false; summons.DespawnAll(); @@ -1037,10 +1045,12 @@ struct boss_yoggsaron : public ScriptedAI uint8 _count = 4; me->SetLootMode(31); // 1 + 2 + 4 + 8 + 16, remove with watchers addition - if (m_pInstance) + if (_instance) { + uint32 watchersMask = _instance->GetPersistentData( + PERSISTENT_DATA_WATCHERS_MASK); for (uint8 i = 0; i < 4; ++i) - if (m_pInstance->GetData(TYPE_WATCHERS) & (1 << i)) + if (watchersMask & (1 << i)) { me->RemoveLootMode(1 << _count); --_count; @@ -1048,7 +1058,7 @@ struct boss_yoggsaron : public ScriptedAI } } - InstanceScript* m_pInstance; + InstanceScript* _instance; EventMap events; SummonList summons; bool _thirdPhase; @@ -1073,17 +1083,17 @@ struct boss_yoggsaron : public ScriptedAI Talk(SAY_YOGG_SARON_DEATH); - if (m_pInstance) + if (_instance) { - m_pInstance->SetData(BOSS_YOGGSARON, DONE); - if (Creature* sara = m_pInstance->GetCreature(DATA_SARA)) + _instance->SetData(BOSS_YOGGSARON, DONE); + if (Creature* sara = _instance->GetCreature(DATA_SARA)) sara->AI()->DoAction(ACTION_YOGG_SARON_DEATH); - if (GameObject* go = m_pInstance->GetGameObject(DATA_YOGG_SARON_DOORS)) + if (GameObject* go = _instance->GetGameObject(DATA_YOGG_SARON_DOORS)) go->SetGoState(GO_STATE_ACTIVE); } - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) + Map::PlayerList const& playerList = me->GetMap()->GetPlayers(); + for(Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) { itr->GetSource()->RemoveAura(SPELL_SANITY); itr->GetSource()->RemoveAura(SPELL_INSANE1); @@ -1196,7 +1206,7 @@ struct boss_yoggsaron : public ScriptedAI struct boss_yoggsaron_brain : public NullCreatureAI { - boss_yoggsaron_brain(Creature* pCreature) : NullCreatureAI(pCreature), summons(pCreature) + boss_yoggsaron_brain(Creature* creature) : NullCreatureAI(creature), summons(creature) { me->SetDisableGravity(true); _tentacleCount = 0; @@ -1430,7 +1440,7 @@ struct boss_yoggsaron_brain : public NullCreatureAI struct boss_yoggsaron_death_orb : public NullCreatureAI { - boss_yoggsaron_death_orb(Creature* pCreature) : NullCreatureAI(pCreature) + boss_yoggsaron_death_orb(Creature* creature) : NullCreatureAI(creature) { me->CastSpell(me, SPELL_DEATH_RAY_WARNING, true); _startTimer = 1; @@ -1459,7 +1469,7 @@ struct boss_yoggsaron_death_orb : public NullCreatureAI struct boss_yoggsaron_crusher_tentacle : public ScriptedAI { - boss_yoggsaron_crusher_tentacle(Creature* pCreature) : ScriptedAI(pCreature) + boss_yoggsaron_crusher_tentacle(Creature* creature) : ScriptedAI(creature) { me->SetCombatMovement(false); me->CastSpell(me, SPELL_CRUSH, true); @@ -1510,7 +1520,7 @@ struct boss_yoggsaron_crusher_tentacle : public ScriptedAI struct boss_yoggsaron_corruptor_tentacle : public ScriptedAI { - boss_yoggsaron_corruptor_tentacle(Creature* pCreature) : ScriptedAI(pCreature) + boss_yoggsaron_corruptor_tentacle(Creature* creature) : ScriptedAI(creature) { me->SetCombatMovement(false); } @@ -1524,10 +1534,10 @@ struct boss_yoggsaron_corruptor_tentacle : public ScriptedAI Unit* SelectCorruptionTarget() { Player* target = nullptr; - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - uint8 num = urand(0, pList.getSize() - 1); + Map::PlayerList const& playerList = me->GetMap()->GetPlayers(); + uint8 num = urand(0, playerList.getSize() - 1); uint8 count = 0; - for (Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr, ++count) + for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr, ++count) { if (me->GetDistance(itr->GetSource()) > 200 || itr->GetSource()->GetPositionZ() < 300 || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster()) continue; @@ -1556,7 +1566,7 @@ struct boss_yoggsaron_corruptor_tentacle : public ScriptedAI struct boss_yoggsaron_constrictor_tentacle : public ScriptedAI { - boss_yoggsaron_constrictor_tentacle(Creature* pCreature) : ScriptedAI(pCreature) + boss_yoggsaron_constrictor_tentacle(Creature* creature) : ScriptedAI(creature) { me->SetCombatMovement(false); _checkTimer = 1; @@ -1569,10 +1579,10 @@ struct boss_yoggsaron_constrictor_tentacle : public ScriptedAI Unit* SelectConstrictTarget() { Player* target = nullptr; - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - uint8 num = urand(0, pList.getSize() - 1); + Map::PlayerList const& playerList = me->GetMap()->GetPlayers(); + uint8 num = urand(0, playerList.getSize() - 1); uint8 count = 0; - for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr, ++count) + for(Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr, ++count) { if (me->GetDistance(itr->GetSource()) > 10 || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster()) continue; @@ -1709,7 +1719,7 @@ private: struct boss_yoggsaron_influence_tentacle : public NullCreatureAI { - boss_yoggsaron_influence_tentacle(Creature* pCreature) : NullCreatureAI(pCreature) + boss_yoggsaron_influence_tentacle(Creature* creature) : NullCreatureAI(creature) { me->CastSpell(me, SPELL_GRIM_REPRISAL, true); } @@ -1730,7 +1740,7 @@ struct boss_yoggsaron_influence_tentacle : public NullCreatureAI struct boss_yoggsaron_immortal_guardian : public ScriptedAI { - boss_yoggsaron_immortal_guardian(Creature* pCreature) : ScriptedAI(pCreature) + boss_yoggsaron_immortal_guardian(Creature* creature) : ScriptedAI(creature) { Reset(); } @@ -2039,7 +2049,7 @@ struct boss_yoggsaron_neltharion : public ScriptedAI struct boss_yoggsaron_voice : public NullCreatureAI { - boss_yoggsaron_voice(Creature* pCreature) : NullCreatureAI(pCreature) + boss_yoggsaron_voice(Creature* creature) : NullCreatureAI(creature) { _targets.clear(); _current = 0; @@ -2123,13 +2133,13 @@ class spell_yogg_saron_brain_link : public SpellScript void FilterTargets(std::list& targets) { - std::list tempList; + std::list templayerList; for (std::list::iterator itr = targets.begin(); itr != targets.end(); ++itr) if ((*itr)->GetPositionZ() > 300.0f) - tempList.push_back(*itr); + templayerList.push_back(*itr); targets.clear(); - for (std::list::iterator itr = tempList.begin(); itr != tempList.end(); ++itr) + for (std::list::iterator itr = templayerList.begin(); itr != templayerList.end(); ++itr) targets.push_back(*itr); } @@ -2152,10 +2162,10 @@ class spell_yogg_saron_brain_link_aura : public AuraScript { PreventDefaultAction(); Player* target = nullptr; - Map::PlayerList const& pList = GetUnitOwner()->GetMap()->GetPlayers(); - uint8 _offset = urand(0, pList.getSize() - 1); + Map::PlayerList const& playerList = GetUnitOwner()->GetMap()->GetPlayers(); + uint8 _offset = urand(0, playerList.getSize() - 1); uint8 _counter = 0; - for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr, ++_counter) + for(Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr, ++_counter) { if (itr->GetSource() == GetUnitOwner() || GetUnitOwner()->GetDistance(itr->GetSource()) > 50.0f || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster()) continue; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index 7d4b0d1d3..80b0a0a48 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -30,13 +30,56 @@ DoorData const doorData[] = { - { GO_LEVIATHAN_DOORS, BOSS_LEVIATHAN, DOOR_TYPE_ROOM }, - { GO_XT002_DOORS, BOSS_XT002, DOOR_TYPE_ROOM }, - { GO_KOLOGARN_DOORS, BOSS_KOLOGARN, DOOR_TYPE_ROOM }, - { GO_ASSEMBLY_DOORS, BOSS_ASSEMBLY, DOOR_TYPE_ROOM }, - { GO_ARCHIVUM_DOORS, BOSS_ASSEMBLY, DOOR_TYPE_PASSAGE }, - { GO_YOGG_SARON_DOORS, BOSS_YOGGSARON, DOOR_TYPE_ROOM }, - { 0, 0, DOOR_TYPE_ROOM } + { GO_LEVIATHAN_DOORS, BOSS_LEVIATHAN, DOOR_TYPE_ROOM }, + { GO_LIGHTNING_WALL1, BOSS_LEVIATHAN, DOOR_TYPE_PASSAGE }, + { GO_XT002_DOORS, BOSS_XT002, DOOR_TYPE_ROOM }, + { GO_KOLOGARN_DOORS, BOSS_KOLOGARN, DOOR_TYPE_ROOM }, + { GO_ASSEMBLY_DOORS, BOSS_ASSEMBLY, DOOR_TYPE_ROOM }, + { GO_ARCHIVUM_DOORS, BOSS_ASSEMBLY, DOOR_TYPE_PASSAGE }, + { GO_MIMIRON_DOOR_1, BOSS_MIMIRON, DOOR_TYPE_ROOM }, + { GO_MIMIRON_DOOR_2, BOSS_MIMIRON, DOOR_TYPE_ROOM }, + { GO_MIMIRON_DOOR_3, BOSS_MIMIRON, DOOR_TYPE_ROOM }, + { GO_HODIR_FRONTDOOR, BOSS_HODIR, DOOR_TYPE_ROOM }, + { GO_HODIR_FROZEN_DOOR, BOSS_HODIR, DOOR_TYPE_PASSAGE }, + { GO_HODIR_DOOR, BOSS_HODIR, DOOR_TYPE_PASSAGE }, + { GO_KEEPERS_GATE, BOSS_HODIR, DOOR_TYPE_PASSAGE }, + { GO_KEEPERS_GATE, BOSS_MIMIRON, DOOR_TYPE_PASSAGE }, + { GO_KEEPERS_GATE, BOSS_THORIM, DOOR_TYPE_PASSAGE }, + { GO_KEEPERS_GATE, BOSS_FREYA, DOOR_TYPE_PASSAGE }, + { GO_VEZAX_DOOR, BOSS_VEZAX, DOOR_TYPE_PASSAGE }, + { GO_YOGG_SARON_DOORS, BOSS_YOGGSARON, DOOR_TYPE_ROOM }, + { GO_DOODAD_UL_SIGILDOOR_03, BOSS_ALGALON, DOOR_TYPE_ROOM }, + { GO_DOODAD_UL_UNIVERSEFLOOR_01, BOSS_ALGALON, DOOR_TYPE_ROOM }, + { GO_DOODAD_UL_UNIVERSEFLOOR_02, BOSS_ALGALON, DOOR_TYPE_SPAWN_HOLE }, + { GO_DOODAD_UL_UNIVERSEGLOBE01, BOSS_ALGALON, DOOR_TYPE_SPAWN_HOLE }, + { GO_DOODAD_UL_ULDUAR_TRAPDOOR_03, BOSS_ALGALON, DOOR_TYPE_SPAWN_HOLE }, + { 0, 0, DOOR_TYPE_ROOM } +}; + +// Observation Ring keeper positions, indexed by KEEPER_* constants +static Position const ObservationRingKeepersPos[4] = +{ + {1945.6823f, 33.342014f, 411.44083f, 5.270895f}, // Freya + {1945.7609f, -81.52171f, 411.4407f, 1.029744f}, // Hodir + {2028.7656f, 17.42014f, 411.44458f, 3.857178f}, // Mimiron + {2028.8219f, -65.73573f, 411.44257f, 2.460914f} // Thorim +}; + +static uint32 const ObservationRingKeeperEntry[4] = +{ + NPC_FREYA_GOSSIP, NPC_HODIR_GOSSIP, + NPC_MIMIRON_GOSSIP, NPC_THORIM_GOSSIP +}; + +static uint32 const ObservationRingKeeperData[4] = +{ + DATA_FREYA_GOSSIP, DATA_HODIR_GOSSIP, + DATA_MIMIRON_GOSSIP, DATA_THORIM_GOSSIP +}; + +static uint32 const ObservationRingKeeperBoss[4] = +{ + BOSS_FREYA, BOSS_HODIR, BOSS_MIMIRON, BOSS_THORIM }; ObjectData const creatureData[] = @@ -69,6 +112,11 @@ ObjectData const creatureData[] = // Yogg-Saron helpers { NPC_SARA, DATA_SARA }, { NPC_BRAIN_OF_YOGG_SARON, DATA_BRAIN_OF_YOGG_SARON }, + // Observation Ring Keepers + { NPC_FREYA_GOSSIP, DATA_FREYA_GOSSIP }, + { NPC_HODIR_GOSSIP, DATA_HODIR_GOSSIP }, + { NPC_MIMIRON_GOSSIP, DATA_MIMIRON_GOSSIP }, + { NPC_THORIM_GOSSIP, DATA_THORIM_GOSSIP }, // Algalon helpers { NPC_BRANN_BRONZBEARD_ALG, DATA_BRANN_BRONZEBEARD_ALG }, { NPC_BRANN_BASE_CAMP, DATA_BRANN_BASE_CAMP }, @@ -121,93 +169,68 @@ class instance_ulduar : public InstanceMapScript public: instance_ulduar() : InstanceMapScript("instance_ulduar", MAP_ULDUAR) { } - InstanceScript* GetInstanceScript(InstanceMap* pMap) const override + InstanceScript* GetInstanceScript(InstanceMap* map) const override { - return new instance_ulduar_InstanceMapScript(pMap); + return new instance_ulduar_InstanceMapScript(map); } struct instance_ulduar_InstanceMapScript : public InstanceScript { - instance_ulduar_InstanceMapScript(Map* pMap) : InstanceScript(pMap) + instance_ulduar_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); SetBossNumber(MAX_ENCOUNTER); + SetPersistentDataCount(MAX_PERSISTENT_DATA); LoadDoorData(doorData); LoadObjectData(creatureData, gameobjectData); Initialize(); - // 0: 10 man difficulty - // 1: 25 man difficulty - m_difficulty = (pMap->Is25ManRaid() ? 0 : 1); }; - // Only used for TYPE_WATCHERS bitmask - uint32 m_watchersMask; - uint32 C_of_Ulduar_MASK; - - int m_difficulty; - // Flame Leviathan - ObjectGuid m_leviathanVisualTowers[4][2]; - ObjectGuid m_RepairSGUID[2]; - bool m_leviathanTowers[4]; + ObjectGuid _leviathanVisualTowers[4][2]; + ObjectGuid _repairSGUID[2]; + bool _leviathanTowers[4]; GuidList _leviathanVehicles; - uint32 m_unbrokenAchievement; - uint32 m_mageBarrier; // Hodir - bool hmHodir; + bool _hmHodir; Position normalChestPosition = { 1967.152588f, -204.188461f, 432.686951f, 5.50957f }; Position hardChestPosition = { 2035.94600f, -202.084885f, 432.686859f, 3.164077f }; - // Freya - uint32 m_conspeedatoryAttempt; - - // Yogg-Saron - - // Algalon - uint32 m_algalonTimer; - // Ancient Gate - const Position triggerAncientGatePosition = { 1883.65f, 269.272f, 418.406f }; + Position const triggerAncientGatePosition = { 1883.65f, 269.272f, 418.406f }; // Shared EventMap _events; - bool m_mimironTramUsed; - ObjectGuid m_keepersGossipGUID[4]; + bool _mimironTramUsed; void Initialize() override { - // Bosses - m_watchersMask = 0; - C_of_Ulduar_MASK = 0; - // Flame Leviathan for (uint8 i = 0; i < 4; ++i) - m_leviathanTowers[i] = true; + _leviathanTowers[i] = true; _leviathanVehicles.clear(); - m_unbrokenAchievement = 1; - m_mageBarrier = 0; // Hodir - hmHodir = true; // If players fail the Hardmode then becomes false - - // Freya - m_conspeedatoryAttempt = 0; - - // Algalon - m_algalonTimer = 0; + _hmHodir = true; // If players fail the Hardmode then becomes false // Shared _events.Reset(); - m_mimironTramUsed = false; + _mimironTramUsed = false; } void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet) override { + uint32 algalonTimer = + GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER); packet.Worldstates.reserve(2); - packet.Worldstates.emplace_back(WORLD_STATE_ULDUAR_ALGALON_TIMER_ENABLED, (m_algalonTimer && m_algalonTimer <= 60) ? 1 : 0); - packet.Worldstates.emplace_back(WORLD_STATE_ULDUAR_ALGALON_DESPAWN_TIMER, std::min(m_algalonTimer, 60)); + packet.Worldstates.emplace_back( + WORLD_STATE_ULDUAR_ALGALON_TIMER_ENABLED, + (algalonTimer && algalonTimer <= 60) ? 1 : 0); + packet.Worldstates.emplace_back( + WORLD_STATE_ULDUAR_ALGALON_DESPAWN_TIMER, + std::min(algalonTimer, 60)); } void OnPlayerEnter(Player* player) override @@ -227,20 +250,37 @@ public: } } - if (!GetObjectGuid(BOSS_ALGALON) && m_algalonTimer && (m_algalonTimer <= 60 || m_algalonTimer == TIMER_ALGALON_TO_SUMMON)) + // Spawn Observation Ring keepers for defeated bosses + uint32 watchersMask = + GetPersistentData(PERSISTENT_DATA_WATCHERS_MASK); + for (uint8 i = KEEPER_FREYA; i <= KEEPER_THORIM; ++i) + if (IsBossDone(ObservationRingKeeperBoss[i]) + && !(watchersMask & (1 << i)) + && !GetObjectGuid(ObservationRingKeeperData[i])) + instance->SummonCreature( + ObservationRingKeeperEntry[i], + ObservationRingKeepersPos[i]); + + uint32 algalonTimer = + GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER); + if (!GetObjectGuid(BOSS_ALGALON) && algalonTimer + && (algalonTimer <= 60 + || algalonTimer == TIMER_ALGALON_TO_SUMMON)) { TempSummon* algalon = instance->SummonCreature(NPC_ALGALON, AlgalonLandPos); if (!algalon) return; - if (m_algalonTimer <= 60) + if (algalonTimer <= 60) { _events.RescheduleEvent(EVENT_UPDATE_ALGALON_TIMER, 1min); algalon->AI()->DoAction(ACTION_INIT_ALGALON); } - else // if (m_algalonTimer = TIMER_ALGALON_TO_SUMMON) + else // if (algalonTimer == TIMER_ALGALON_TO_SUMMON) { - m_algalonTimer = TIMER_ALGALON_SUMMONED; + StorePersistentData( + PERSISTENT_DATA_ALGALON_TIMER, + TIMER_ALGALON_SUMMONED); algalon->SetImmuneToPC(false); } } @@ -290,59 +330,29 @@ public: go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); } break; - case BOSS_ASSEMBLY: - // Assembly doors handled by DoorData, archivum - // doors stay open only after DONE - if (GameObject* go = GetGameObject(DATA_ARCHIVUM_DOORS)) - go->SetGoState(state == DONE ? GO_STATE_ACTIVE : GO_STATE_READY); - break; case BOSS_MIMIRON: if (state == IN_PROGRESS) - m_mimironTramUsed = true; + _mimironTramUsed = true; [[fallthrough]]; case BOSS_HODIR: case BOSS_THORIM: case BOSS_FREYA: - if (GetBossState(BOSS_MIMIRON) == DONE && GetBossState(BOSS_FREYA) == DONE && GetBossState(BOSS_HODIR) == DONE && GetBossState(BOSS_THORIM) == DONE) + if (AllBossesDone({BOSS_MIMIRON, BOSS_FREYA, BOSS_HODIR, BOSS_THORIM})) { scheduler.Schedule(45s, [this](TaskContext /*context*/) { - if (GameObject* go = GetGameObject(DATA_KEEPERS_GATE)) - { - go->RemoveGameObjectFlag(GO_FLAG_LOCKED); - if (Creature* trigger = instance->SummonCreature(NPC_ANCIENT_GATE_WORLD_TRIGGER, triggerAncientGatePosition, nullptr, 10 * IN_MILLISECONDS)) - trigger->AI()->Talk(EMOTE_ANCIENT_GATE_UNLOCKED); - } + if (Creature* trigger = instance->SummonCreature(NPC_ANCIENT_GATE_WORLD_TRIGGER, triggerAncientGatePosition, nullptr, 10 * IN_MILLISECONDS)) + trigger->AI()->Talk(EMOTE_ANCIENT_GATE_UNLOCKED); }); } if (type == BOSS_HODIR && state == DONE) setChestsLootable(BOSS_HODIR); - break; - case BOSS_ALGALON: - if (GameObject* go = GetGameObject(DATA_SIGILDOOR_03)) + if (state == DONE) { - go->SetGoState(state != IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY); - go->EnableCollision(false); - } - if (GameObject* go = GetGameObject(DATA_UNIVERSE_FLOOR_01)) - { - go->SetGoState(state != IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY); - go->EnableCollision(false); - } - if (GameObject* go = GetGameObject(DATA_UNIVERSE_FLOOR_02)) - { - go->SetGoState(state == IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY); - go->EnableCollision(false); - } - if (GameObject* go = GetGameObject(DATA_UNIVERSE_GLOBE)) - { - go->SetGoState(state == IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY); - go->EnableCollision(false); - } - if (GameObject* go = GetGameObject(DATA_ALGALON_TRAPDOOR)) - { - go->SetGoState(state == IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY); - go->EnableCollision(false); + uint8 keeperIdx = type - BOSS_FREYA; + instance->SummonCreature( + ObservationRingKeeperEntry[keeperIdx], + ObservationRingKeepersPos[keeperIdx]); } break; default: @@ -362,9 +372,6 @@ public: } } - if (state == DONE) - SaveToDB(); - if (type > BOSS_LEVIATHAN && type < MAX_ENCOUNTER && state == IN_PROGRESS) { instance->DoForAllPlayers([&](Player* player) @@ -405,7 +412,7 @@ public: hardChestPosition.GetOrientation(), 0, 0, 0, 0, 0)) { go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - hmHodir = true; + _hmHodir = true; } } break; @@ -434,7 +441,7 @@ public: hardChestPosition.GetOrientation(), 0, 0, 0, 0, 0)) { go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - hmHodir = true; + _hmHodir = true; } } break; @@ -466,7 +473,7 @@ public: } break; case NPC_ALGALON: - if (!m_algalonTimer) + if (!GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER)) creature->DespawnOrUnsummon(); break; //! These creatures are summoned by something else than Algalon @@ -511,46 +518,43 @@ public: // Flame Leviathan case GO_REPAIR_STATION_TRAP: { - if (m_RepairSGUID[0]) - m_RepairSGUID[1] = gameObject->GetGUID(); + if (_repairSGUID[0]) + _repairSGUID[1] = gameObject->GetGUID(); else - m_RepairSGUID[0] = gameObject->GetGUID(); + _repairSGUID[0] = gameObject->GetGUID(); break; } - case GO_LIGHTNING_WALL1: - OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - break; case GO_MIMIRONS_TARGETTING_CRYSTAL: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[3][0] = gameObject->GetGUID(); + _leviathanVisualTowers[3][0] = gameObject->GetGUID(); break; case GO_FREYAS_TARGETTING_CRYSTAL: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[0][0] = gameObject->GetGUID(); + _leviathanVisualTowers[0][0] = gameObject->GetGUID(); break; case GO_HODIRS_TARGETTING_CRYSTAL: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[2][0] = gameObject->GetGUID(); + _leviathanVisualTowers[2][0] = gameObject->GetGUID(); break; case GO_THORIMS_TARGETTING_CRYSTAL: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[1][0] = gameObject->GetGUID(); + _leviathanVisualTowers[1][0] = gameObject->GetGUID(); break; case GO_MIMIRONS_GENERATOR: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[3][1] = gameObject->GetGUID(); + _leviathanVisualTowers[3][1] = gameObject->GetGUID(); break; case GO_FREYAS_GENERATOR: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[0][1] = gameObject->GetGUID(); + _leviathanVisualTowers[0][1] = gameObject->GetGUID(); break; case GO_HODIRS_GENERATOR: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[2][1] = gameObject->GetGUID(); + _leviathanVisualTowers[2][1] = gameObject->GetGUID(); break; case GO_THORIMS_GENERATOR: OpenIfDone(BOSS_LEVIATHAN, gameObject, GO_STATE_ACTIVE); - m_leviathanVisualTowers[1][1] = gameObject->GetGUID(); + _leviathanVisualTowers[1][1] = gameObject->GetGUID(); break; case GO_LEVIATHAN_DOORS: if (GetBossState(BOSS_LEVIATHAN) >= DONE) @@ -559,54 +563,41 @@ public: case GO_KOLOGARN_BRIDGE: OpenIfDone(BOSS_KOLOGARN, gameObject, GO_STATE_READY); break; - case GO_ARCHIVUM_DOORS: - OpenIfDone(BOSS_ASSEMBLY, gameObject, GO_STATE_ACTIVE); - break; case GO_KEEPERS_GATE: - if (GetBossState(BOSS_MIMIRON) == DONE && GetBossState(BOSS_FREYA) == DONE && GetBossState(BOSS_HODIR) == DONE && GetBossState(BOSS_THORIM) == DONE) + if (AllBossesDone({BOSS_MIMIRON, BOSS_FREYA, BOSS_HODIR, BOSS_THORIM})) gameObject->RemoveGameObjectFlag(GO_FLAG_LOCKED); break; // Mimiron, Hodir, Vezax case GO_MIMIRON_ELEVATOR: gameObject->EnableCollision(false); break; - case GO_HODIR_FROZEN_DOOR: - case GO_HODIR_DOOR: - if (GetBossState(BOSS_HODIR) == DONE) - if (gameObject->GetGoState() != GO_STATE_ACTIVE ) - { - gameObject->SetLootState(GO_READY); - gameObject->UseDoorOrButton(0, false); - } - break; - case GO_VEZAX_DOOR: - if (GetBossState(BOSS_VEZAX) == DONE ) - if (gameObject->GetGoState() != GO_STATE_ACTIVE ) - { - gameObject->SetLootState(GO_READY); - gameObject->UseDoorOrButton(0, false); - } - break; case GO_SNOW_MOUND: gameObject->EnableCollision(false); break; // Mimiron Tram case GO_MIMIRON_TRAM: if (GetBossState(BOSS_MIMIRON) == DONE) - m_mimironTramUsed = true; + _mimironTramUsed = true; break; // Algalon the Observer case GO_CELESTIAL_PLANETARIUM_ACCESS_10: case GO_CELESTIAL_PLANETARIUM_ACCESS_25: - if (m_algalonTimer) + if (GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER)) gameObject->SetGameObjectFlag(GO_FLAG_IN_USE); break; + case GO_DOODAD_UL_SIGILDOOR_03: + case GO_DOODAD_UL_UNIVERSEFLOOR_01: + case GO_DOODAD_UL_UNIVERSEFLOOR_02: + case GO_DOODAD_UL_UNIVERSEGLOBE01: + case GO_DOODAD_UL_ULDUAR_TRAPDOOR_03: + gameObject->EnableCollision(false); + break; case GO_DOODAD_UL_SIGILDOOR_01: - if (m_algalonTimer) + if (GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER)) gameObject->SetGoState(GO_STATE_ACTIVE); break; case GO_DOODAD_UL_SIGILDOOR_02: - if (m_algalonTimer) + if (GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER)) gameObject->SetGoState(GO_STATE_ACTIVE); break; // Herbs @@ -628,7 +619,7 @@ public: switch (boss) { case BOSS_HODIR: - if (hmHodir) + if (_hmHodir) { if (GameObject* go = GetHodirChest(true)) { @@ -653,30 +644,27 @@ public: case TYPE_HODIR_HM_FAIL: if (GameObject* go = GetHodirChest(true)) { - hmHodir = false; + _hmHodir = false; go->Delete(); } break; - case TYPE_WATCHERS: - m_watchersMask |= 1 << data; - [[fallthrough]]; + case DATA_MAGE_BARRIER: + StorePersistentData( + PERSISTENT_DATA_MAGE_BARRIER, data); + break; case EVENT_KEEPER_TELEPORTED: if (Creature* sara = GetCreature(DATA_SARA)) sara->AI()->DoAction(ACTION_SARA_UPDATE_SUMMON_KEEPERS); break; - case DATA_MAGE_BARRIER: - m_mageBarrier = data; - break; - case EVENT_TOWER_OF_LIFE_DESTROYED: case EVENT_TOWER_OF_STORM_DESTROYED: case EVENT_TOWER_OF_FROST_DESTROYED: case EVENT_TOWER_OF_FLAMES_DESTROYED: { - m_leviathanTowers[type - EVENT_TOWER_OF_LIFE_DESTROYED] = data; + _leviathanTowers[type - EVENT_TOWER_OF_LIFE_DESTROYED] = data; for (uint8 i = 0; i < 2; ++i) { - if (GameObject *gameObject = instance->GetGameObject(m_leviathanVisualTowers[type - EVENT_TOWER_OF_LIFE_DESTROYED][i])) + if (GameObject *gameObject = instance->GetGameObject(_leviathanVisualTowers[type - EVENT_TOWER_OF_LIFE_DESTROYED][i])) { gameObject->SetGoState(GO_STATE_ACTIVE); } @@ -687,23 +675,20 @@ public: case DATA_VEHICLE_SPAWN: SpawnLeviathanEncounterVehicles(data); return; - case DATA_UNBROKEN_ACHIEVEMENT: - m_unbrokenAchievement = data; - SaveToDB(); - return; case DATA_DESPAWN_ALGALON: DoUpdateWorldState(WORLD_STATE_ULDUAR_ALGALON_TIMER_ENABLED, 1); DoUpdateWorldState(WORLD_STATE_ULDUAR_ALGALON_DESPAWN_TIMER, 60); - m_algalonTimer = 60; + StorePersistentData(PERSISTENT_DATA_ALGALON_TIMER, 60); _events.RescheduleEvent(EVENT_UPDATE_ALGALON_TIMER, 1min); - SaveToDB(); return; case DATA_ALGALON_SUMMON_STATE: case DATA_ALGALON_DEFEATED: DoUpdateWorldState(WORLD_STATE_ULDUAR_ALGALON_TIMER_ENABLED, 0); - m_algalonTimer = (type == DATA_ALGALON_DEFEATED ? TIMER_ALGALON_DEFEATED : TIMER_ALGALON_SUMMONED); + StorePersistentData(PERSISTENT_DATA_ALGALON_TIMER, + type == DATA_ALGALON_DEFEATED + ? TIMER_ALGALON_DEFEATED + : TIMER_ALGALON_SUMMONED); _events.CancelEvent(EVENT_UPDATE_ALGALON_TIMER); - SaveToDB(); return; // Achievement case DATA_DWARFAGEDDON: @@ -774,8 +759,6 @@ public: break; } - if (type == TYPE_WATCHERS) - SaveToDB(); } ObjectGuid GetGuidData(uint32 data) const override @@ -784,9 +767,9 @@ public: { // Flame Leviathan case DATA_REPAIR_STATION1: - return m_RepairSGUID[0]; + return _repairSGUID[0]; case DATA_REPAIR_STATION2: - return m_RepairSGUID[1]; + return _repairSGUID[1]; } @@ -797,23 +780,18 @@ public: { switch (type) { - case TYPE_WATCHERS: - return m_watchersMask; - case EVENT_TOWER_OF_LIFE_DESTROYED: case EVENT_TOWER_OF_STORM_DESTROYED: case EVENT_TOWER_OF_FROST_DESTROYED: case EVENT_TOWER_OF_FLAMES_DESTROYED: - return m_leviathanTowers[type - EVENT_TOWER_OF_LIFE_DESTROYED]; + return _leviathanTowers[type - EVENT_TOWER_OF_LIFE_DESTROYED]; case DATA_MAGE_BARRIER: - return m_mageBarrier; - - case DATA_UNBROKEN_ACHIEVEMENT: - return m_unbrokenAchievement; + return GetPersistentData( + PERSISTENT_DATA_MAGE_BARRIER); case DATA_CALL_TRAM: - return m_mimironTramUsed; + return _mimironTramUsed; } return 0; @@ -830,11 +808,15 @@ public: } else if (unit->IsCreature() && unit->GetAreaId() == AREA_THE_CONSERVATORY_OF_LIFE) { - if (GameTime::GetGameTime().count() > (m_conspeedatoryAttempt + DAY)) + uint32 conspeedatory = + GetPersistentData(PERSISTENT_DATA_CONSPEEDATORY); + if (GameTime::GetGameTime().count() > (conspeedatory + DAY)) { - DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, 21597 /*CON-SPEED-ATORY_TIMED_CRITERIA*/); - m_conspeedatoryAttempt = GameTime::GetGameTime().count(); - SaveToDB(); + DoStartTimedAchievement( + ACHIEVEMENT_TIMED_TYPE_EVENT, + 21597 /*CON-SPEED-ATORY_TIMED_CRITERIA*/); + StorePersistentData(PERSISTENT_DATA_CONSPEEDATORY, + GameTime::GetGameTime().count()); } } @@ -842,55 +824,52 @@ public: if (unit->IsPlayer()) for (uint8 i = 0; i <= 12; ++i) { - bool go = false; + bool inCombat = false; if (i == BOSS_LEVIATHAN) { if (Creature* c = GetCreature(BOSS_LEVIATHAN)) if (c->IsInCombat()) - go = true; + inCombat = true; } else - go = (GetBossState(i) == IN_PROGRESS); + inCombat = (GetBossState(i) == IN_PROGRESS); - if (go && (C_of_Ulduar_MASK & (1 << i)) == 0) - { - C_of_Ulduar_MASK |= (1 << i); - SaveToDB(); - } + uint32 mask = + GetPersistentData(PERSISTENT_DATA_C_OF_ULDUAR_MASK); + if (inCombat && (mask & (1 << i)) == 0) + StorePersistentData( + PERSISTENT_DATA_C_OF_ULDUAR_MASK, + mask | (1 << i)); } } - void ReadSaveDataMore(std::istringstream& data) override + void Load(char const* data) override { - // Boss states 0-13 are read by base InstanceScript. - data >> m_watchersMask; - data >> m_conspeedatoryAttempt; - data >> m_unbrokenAchievement; - data >> m_algalonTimer; + InstanceScript::Load(data); - if (m_algalonTimer == TIMER_ALGALON_SUMMONED) - m_algalonTimer = TIMER_ALGALON_TO_SUMMON; + if (!data) + StorePersistentData(PERSISTENT_DATA_UNBROKEN, 1); - if (m_algalonTimer && m_algalonTimer <= 60 && GetBossState(BOSS_ALGALON) != DONE) + uint32 algalonTimer = + GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER); + if (algalonTimer == TIMER_ALGALON_SUMMONED) { - DoUpdateWorldState(WORLD_STATE_ULDUAR_ALGALON_TIMER_ENABLED, 1); - DoUpdateWorldState(WORLD_STATE_ULDUAR_ALGALON_DESPAWN_TIMER, m_algalonTimer); + StorePersistentData( + PERSISTENT_DATA_ALGALON_TIMER, + TIMER_ALGALON_TO_SUMMON); } - data >> C_of_Ulduar_MASK; - data >> m_mageBarrier; - } - - void WriteSaveDataMore(std::ostringstream& data) override - { - // Boss states 0-13 are written by base InstanceScript. - // Only write extra non-boss data here. - data << m_watchersMask << ' ' - << m_conspeedatoryAttempt << ' ' - << m_unbrokenAchievement << ' ' - << m_algalonTimer << ' ' - << C_of_Ulduar_MASK << ' ' - << m_mageBarrier; + algalonTimer = + GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER); + if (algalonTimer && algalonTimer <= 60 + && GetBossState(BOSS_ALGALON) != DONE) + { + DoUpdateWorldState( + WORLD_STATE_ULDUAR_ALGALON_TIMER_ENABLED, 1); + DoUpdateWorldState( + WORLD_STATE_ULDUAR_ALGALON_DESPAWN_TIMER, + algalonTimer); + } } void Update(uint32 diff) override @@ -904,12 +883,19 @@ public: switch (_events.ExecuteEvent()) { case EVENT_UPDATE_ALGALON_TIMER: - if (m_algalonTimer == TIMER_ALGALON_DEFEATED) + { + uint32 algalonTimer = + GetPersistentData(PERSISTENT_DATA_ALGALON_TIMER); + if (algalonTimer == TIMER_ALGALON_DEFEATED) return; - SaveToDB(); - DoUpdateWorldState(WORLD_STATE_ULDUAR_ALGALON_DESPAWN_TIMER, --m_algalonTimer); - if (m_algalonTimer) + StorePersistentData( + PERSISTENT_DATA_ALGALON_TIMER, + --algalonTimer); + DoUpdateWorldState( + WORLD_STATE_ULDUAR_ALGALON_DESPAWN_TIMER, + algalonTimer); + if (algalonTimer) { _events.Repeat(1min); return; @@ -918,6 +904,7 @@ public: SetData(DATA_ALGALON_DEFEATED, 1); if (Creature* algalon = GetCreature(BOSS_ALGALON)) algalon->AI()->DoAction(ACTION_DESPAWN_ALGALON); + } } } @@ -925,47 +912,48 @@ public: bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override { + uint32 mask = GetPersistentData(PERSISTENT_DATA_C_OF_ULDUAR_MASK); switch (criteria_id) { case 10042: case 10352: - return (C_of_Ulduar_MASK & (1 << BOSS_LEVIATHAN)) == 0; + return (mask & (1 << BOSS_LEVIATHAN)) == 0; case 10342: case 10355: - return (C_of_Ulduar_MASK & (1 << BOSS_IGNIS)) == 0; + return (mask & (1 << BOSS_IGNIS)) == 0; case 10340: case 10353: - return (C_of_Ulduar_MASK & (1 << BOSS_RAZORSCALE)) == 0; + return (mask & (1 << BOSS_RAZORSCALE)) == 0; case 10341: case 10354: - return (C_of_Ulduar_MASK & (1 << BOSS_XT002)) == 0; + return (mask & (1 << BOSS_XT002)) == 0; case 10598: case 10599: - return (C_of_Ulduar_MASK & (1 << BOSS_ASSEMBLY)) == 0; + return (mask & (1 << BOSS_ASSEMBLY)) == 0; case 10348: case 10357: - return (C_of_Ulduar_MASK & (1 << BOSS_KOLOGARN)) == 0; + return (mask & (1 << BOSS_KOLOGARN)) == 0; case 10351: case 10363: - return (C_of_Ulduar_MASK & (1 << BOSS_AURIAYA)) == 0; + return (mask & (1 << BOSS_AURIAYA)) == 0; case 10439: case 10719: - return (C_of_Ulduar_MASK & (1 << BOSS_HODIR)) == 0; + return (mask & (1 << BOSS_HODIR)) == 0; case 10403: case 10404: - return (C_of_Ulduar_MASK & (1 << BOSS_THORIM)) == 0; + return (mask & (1 << BOSS_THORIM)) == 0; case 10582: case 10583: - return (C_of_Ulduar_MASK & (1 << BOSS_FREYA)) == 0; + return (mask & (1 << BOSS_FREYA)) == 0; case 10347: case 10361: - return (C_of_Ulduar_MASK & (1 << BOSS_MIMIRON)) == 0; + return (mask & (1 << BOSS_MIMIRON)) == 0; case 10349: case 10362: - return (C_of_Ulduar_MASK & (1 << BOSS_VEZAX)) == 0; + return (mask & (1 << BOSS_VEZAX)) == 0; case 10350: case 10364: - return (C_of_Ulduar_MASK & (1 << BOSS_YOGGSARON)) == 0; + return (mask & (1 << BOSS_YOGGSARON)) == 0; } return false; } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp index 0601363a2..411e9ac10 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.cpp @@ -137,7 +137,13 @@ public: if (spellInfo->Id == SPELL_TELEPORT) { me->DespawnOrUnsummon(); - me->GetInstanceScript()->SetData(TYPE_WATCHERS, _keeper); + InstanceScript* instance = me->GetInstanceScript(); + uint32 mask = instance->GetPersistentData( + PERSISTENT_DATA_WATCHERS_MASK); + instance->StorePersistentData( + PERSISTENT_DATA_WATCHERS_MASK, + mask | (1 << _keeper)); + instance->SetData(EVENT_KEEPER_TELEPORTED, 0); } } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h index 5307e6758..eb50851c8 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h @@ -143,6 +143,12 @@ enum UlduarData DATA_BRANN_MEMOTESAY = 801, DATA_BRANN_EASY_MODE = 802, DATA_BRANN_BASE_CAMP = 803, + + // Observation Ring Keepers + DATA_FREYA_GOSSIP = 810, + DATA_HODIR_GOSSIP = 811, + DATA_MIMIRON_GOSSIP = 812, + DATA_THORIM_GOSSIP = 813, }; enum UlduarNPCs @@ -300,6 +306,17 @@ enum UlduarGameObjects GO_GIFT_OF_THE_OBSERVER_25 = 194822, }; +enum UlduarPersistentData +{ + PERSISTENT_DATA_WATCHERS_MASK = 0, + PERSISTENT_DATA_CONSPEEDATORY, + PERSISTENT_DATA_UNBROKEN, + PERSISTENT_DATA_ALGALON_TIMER, + PERSISTENT_DATA_C_OF_ULDUAR_MASK, + PERSISTENT_DATA_MAGE_BARRIER, + MAX_PERSISTENT_DATA +}; + enum UlduarMisc { // Flame Leviathan diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp index 3af170ba6..9d57f7be0 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp @@ -191,6 +191,7 @@ public: cr->SetDisableGravity(true); cr->GetMotionMaster()->MovePoint(0, 275.4f, -453, 110); // ROOM CENTER events.RescheduleEvent(EVENT_UNFREEZE_MONSTER, 10s); + me->SetImmuneToPC(false); me->SetInCombatWithZone(); me->SetControlled(true, UNIT_STATE_STUNNED); } @@ -373,6 +374,7 @@ public: { me->RemoveAurasDueToSpell(SPELL_FREEZE); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_JORMUNGAR_ACID_SPIT, 3s); @@ -499,6 +501,7 @@ public: { me->RemoveAurasDueToSpell(SPELL_FREEZE); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_RHINO_STOMP, 3s); @@ -609,6 +612,7 @@ public: { me->RemoveAurasDueToSpell(SPELL_FREEZE); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_FURBOLG_CHAIN, 3s); @@ -717,6 +721,7 @@ public: { me->RemoveAurasDueToSpell(SPELL_FREEZE); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_WORGEN_MORTAL, 3s); diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp index efa101f38..c983d83a0 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp @@ -17,91 +17,129 @@ #include "CombatAI.h" #include "CreatureScript.h" -#include "GameObjectScript.h" +#include "GridNotifiers.h" #include "Player.h" #include "ScriptedCreature.h" -#include "SpellInfo.h" +#include "SpellAuras.h" +#include "SpellScript.h" +#include "SpellScriptLoader.h" #include "Vehicle.h" #include "utgarde_pinnacle.h" -enum Misc +enum Spells +{ + // Skadi + SPELL_CRUSH = 50234, + SPELL_POISONED_SPEAR = 50255, + SPELL_POISONED_SPEAR_PERIODIC = 50258, + SPELL_WHIRLWIND = 50228, + SPELL_LAUNCH_HARPOON = 48642, + SPELL_SKADI_TELEPORT = 61790, + + // Grauf + SPELL_FREEZING_CLOUD = 47579, + SPELL_FREEZING_CLOUD_RIGHT_PERIODIC = 47592, + SPELL_FREEZING_CLOUD_LEFT_PERIODIC = 47590, + SPELL_FREEZING_CLOUD_RIGHT_AREA = 47594, + SPELL_FREEZING_CLOUD_LEFT_AREA = 47574, + + // Gauntlet + SPELL_SUMMON_GAUNTLET_MOBS_PERIODIC = 59275, + SPELL_SUMMON_YMIRJAR_WARRIOR_W = 48631, + SPELL_SUMMON_YMIRJAR_WARRIOR_E = 48632, + SPELL_SUMMON_YMIRJAR_HARPOONER_W = 48633, + SPELL_SUMMON_YMIRJAR_HARPOONER_E = 48634, + SPELL_SUMMON_YMIRJAR_WITCH_DOCTOR_W = 48635, + SPELL_SUMMON_YMIRJAR_WITCH_DOCTOR_E = 48636, + SPELL_GAUNTLET_RESET_CHECK = 49308, + SPELL_GAUNTLET_EFFECT = 47547, +}; + +enum Texts { - // TEXTS SAY_AGGRO = 0, SAY_KILL = 1, SAY_DEATH = 3, SAY_DRAKE_DEATH = 5, SAY_DRAKE_BREATH = 6, - - // EMOTES EMOTE_DEEP_BREATH = 0, - EMOTE_RANGE = 1, - - // SPELLS - SPELL_CRUSH = 50234, - SPELL_POISONED_SPEAR = 50255, - SPELL_WHIRLWIND = 50228, - - SPELL_FREEZING_CLOUD_VISUAL = 47592, - SPELL_FREEZING_CLOUD = 47579, - - SPELL_LAUNCH_HARPOON = 48642, - - // NPCS - NPC_YMIRJAR_WARRIOR = 26690, - NPC_YMIRJAR_WITCH_DOCTOR = 26691, - NPC_YMIRJAR_HARPOONER = 26692, - NPC_GRAUF = 26893, - NPC_BREATH_TRIGGER = 28351, - EQUIP_MACE = 17193, - - // ACTIONS - ACTION_START_EVENT = 1, - ACTION_REMOVE_SKADI = 2, - ACTION_PHASE2 = 3, - ACTION_MYGIRL_ACHIEVEMENT = 4, - - // ACHIEVEMENTS - ACHIEV_TIMED_LODI_DODI = 17726, + EMOTE_ON_RANGE = 1, }; +enum Actions +{ + ACTION_START_ENCOUNTER = 1, + ACTION_DRAKE_BREATH = 2, + ACTION_PHASE2 = 3, + ACTION_HARPOON_HIT = 4, +}; + +enum CombatPhase +{ + PHASE_FLYING = 0, + PHASE_GROUND +}; + +enum MiscData +{ + NPC_GRAUF = 26893, + NPC_TRIGGER_RESET = 23472, + NPC_WORLD_TRIGGER = 22515, + NPC_COMBAT_TRIGGER = 38667, + + ACHIEV_TIMED_LODI_DODI = 17726, + + FIRST_WAVE_MAX_WARRIORS = 10, + FIRST_WAVE_SIZE = 13, +}; + +Position const GraufLoc = { 341.741f, -516.955f, 104.670f, 3.12414f }; +Position const SpawnLoc = { 477.581f, -484.559f, 104.822f, 4.67748f }; + +Position const FirstWaveLocations[FIRST_WAVE_SIZE] = +{ + { 458.532f, -516.254f, 104.617f }, + { 429.424f, -517.562f, 104.894f }, + { 394.496f, -514.614f, 104.724f }, + { 362.286f, -515.877f, 104.754f }, + { 333.537f, -514.794f, 104.478f }, + { 457.611f, -508.836f, 104.401f }, + { 427.403f, -510.772f, 104.880f }, + { 392.511f, -507.943f, 104.743f }, + { 362.951f, -508.412f, 104.722f }, + { 333.536f, -506.081f, 104.426f }, + { 478.310f, -511.049f, 104.724f, 3.26377f }, + { 482.250f, -514.127f, 104.723f, 3.26377f }, + { 481.388f, -507.109f, 104.724f, 3.26377f }, +}; + +Position const SecondaryWavesInitialPoint = { 478.743f, -505.576f, 104.724f }; + +enum GraufPoints +{ + POINT_BREACH = 0, + POINT_LEFT = 1, + POINT_RIGHT = 2, +}; + +uint32 const PATH_INITIAL = 2689300; +uint32 const PATH_RIGHT = 2689301; +uint32 const PATH_LEFT = 2689302; + +float const BreachFacing = 2.670354f; + enum Events { - // SKADI - EVENT_SKADI_START = 1, - EVENT_SKADI_CRUSH = 2, - EVENT_SKADI_SPEAR = 3, - EVENT_SKADI_WHIRLWIND = 4, + // Skadi + EVENT_SKADI_CRUSH = 1, + EVENT_SKADI_SPEAR = 2, + EVENT_SKADI_WHIRLWIND = 3, + EVENT_SKADI_RESET_CHECK = 4, - // GRAUF + // Grauf EVENT_GRAUF_START = 10, - EVENT_GRAUF_MOVE = 11, - EVENT_GRAUF_SUMMON_HELPERS = 12, - EVENT_GRAUF_CHECK = 13, - EVENT_GRAUF_REMOVE_SKADI = 14, -}; - -static Position TrashPosition[] = -{ - {441.236f, -512.000f, 104.930f, 0.0f}, - {478.436f, -494.475f, 104.730f, 0.0f} -}; - -static Position SkadiPosition[] = -{ - {338.679f, -507.254f, 124.122f, 0.0f}, - {338.679f, -513.254f, 124.122f, 0.0f}, - {490.096f, -510.86f, 123.368f, 0.0f}, - {490.76f, -517.389f, 123.368f, 0.0f} -}; - -enum phase -{ - PHASE_NONE, - PHASE_START, - PHASE_FLIGHT, - PHASE_LAND, - PHASE_GROUND + EVENT_GRAUF_LEAVE_BREACH = 11, + EVENT_GRAUF_REMOVE_AURA = 13, }; class boss_skadi : public CreatureScript @@ -109,155 +147,237 @@ class boss_skadi : public CreatureScript public: boss_skadi() : CreatureScript("boss_skadi") { } - CreatureAI* GetAI(Creature* pCreature) const override + CreatureAI* GetAI(Creature* creature) const override { - return GetUtgardePinnacleAI(pCreature); + return GetUtgardePinnacleAI(creature); } struct boss_skadiAI : public ScriptedAI { - boss_skadiAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me) + boss_skadiAI(Creature* creature) : ScriptedAI(creature), _summons(me) { - m_pInstance = pCreature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } - InstanceScript* m_pInstance; - EventMap events; - SummonList summons; - ObjectGuid GraufGUID; - bool SecondPhase, EventStarted; - void Reset() override { - events.Reset(); - summons.DespawnAll(); - if (Creature* cr = me->SummonCreature(NPC_GRAUF, 341.741f, -516.955f, 116.669f, 3.12414f)) - { - GraufGUID = cr->GetGUID(); - summons.Summon(cr); - } - SecondPhase = false; - EventStarted = false; + _events.Reset(); + _summons.DespawnAll(); + _phase = PHASE_GROUND; + _harpoonHit = 0; + _loveSkadi = 0; + _firstWaveSummoned = false; + _encounterStarted = false; + me->SetReactState(REACT_PASSIVE); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); - if (m_pInstance) + if (_instance) { - m_pInstance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_LODI_DODI); - m_pInstance->SetData(DATA_SKADI_THE_RUTHLESS, NOT_STARTED); - m_pInstance->SetData(SKADI_IN_RANGE, 0); - m_pInstance->SetData(SKADI_HITS, 0); - m_pInstance->SetData(DATA_SKADI_ACHIEVEMENT, false); + _instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_LODI_DODI); + _instance->SetData(DATA_SKADI_THE_RUTHLESS, NOT_STARTED); + _instance->SetData(DATA_SKADI_ACHIEVEMENT, false); } + + if (!_instance || !_instance->GetCreature(DATA_GRAUF)) + if (Creature* grauf = me->SummonCreature(NPC_GRAUF, GraufLoc)) + _summons.Summon(grauf); } - Creature* GetGrauf() { return ObjectAccessor::GetCreature(*me, GraufGUID); } + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) + { + case NPC_YMIRJAR_WARRIOR: + case NPC_YMIRJAR_WITCH_DOCTOR: + case NPC_YMIRJAR_HARPOONER: + if (_firstWaveSummoned) + summon->GetMotionMaster()->MovePoint(1, SecondaryWavesInitialPoint); + break; + default: + break; + } + _summons.Summon(summon); + } + + void SpawnFirstWave() + { + for (uint8 i = 0; i < FIRST_WAVE_MAX_WARRIORS; ++i) + if (Creature* summon = me->SummonCreature(NPC_YMIRJAR_WARRIOR, SpawnLoc, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) + summon->GetMotionMaster()->MovePoint(0, FirstWaveLocations[i]); + + if (Creature* crea = me->SummonCreature(NPC_YMIRJAR_WITCH_DOCTOR, SpawnLoc, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) + crea->GetMotionMaster()->MovePoint(0, FirstWaveLocations[10]); + if (Creature* crea = me->SummonCreature(NPC_YMIRJAR_HARPOONER, SpawnLoc, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) + crea->GetMotionMaster()->MovePoint(0, FirstWaveLocations[11]); + if (Creature* crea = me->SummonCreature(NPC_YMIRJAR_HARPOONER, SpawnLoc, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) + crea->GetMotionMaster()->MovePoint(0, FirstWaveLocations[12]); + + _firstWaveSummoned = true; + } + + void JustEngagedWith(Unit* /*who*/) override + { + DoAction(ACTION_START_ENCOUNTER); + } void DoAction(int32 param) override { - if (param == ACTION_START_EVENT) + switch (param) { - if (!EventStarted) - { - EventStarted = true; + case ACTION_START_ENCOUNTER: + if (_encounterStarted) + return; + + _encounterStarted = true; + _phase = PHASE_FLYING; Talk(SAY_AGGRO); - if (m_pInstance) + + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->setActive(true); + + SpawnFirstWave(); + + if (_instance) { if (IsHeroic()) - m_pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_LODI_DODI); - - m_pInstance->SetData(DATA_SKADI_THE_RUTHLESS, IN_PROGRESS); + _instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_LODI_DODI); + _instance->SetData(DATA_SKADI_THE_RUTHLESS, IN_PROGRESS); } - me->SetControlled(true, UNIT_STATE_ROOT); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - events.RescheduleEvent(EVENT_SKADI_START, 2s); - } - } - else if (param == ACTION_PHASE2) - { - SecondPhase = true; - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_SKADI_CRUSH, 8s); - events.ScheduleEvent(EVENT_SKADI_SPEAR, 10s); - events.ScheduleEvent(EVENT_SKADI_WHIRLWIND, 15s); + if (Creature* summonTrigger = me->SummonCreature(NPC_WORLD_TRIGGER, SpawnLoc)) + summonTrigger->CastSpell(summonTrigger, SPELL_SUMMON_GAUNTLET_MOBS_PERIODIC, true); - if (me->GetVictim()) - me->ResumeChasingVictim(); - else - me->SetInCombatWithZone(); + if (Creature* combatTrigger = me->SummonCreature(NPC_COMBAT_TRIGGER, SpawnLoc)) + combatTrigger->AI()->DoZoneInCombat(); + + _events.ScheduleEvent(EVENT_GRAUF_START, 2s); + _events.ScheduleEvent(EVENT_SKADI_RESET_CHECK, 6s); + break; + case ACTION_DRAKE_BREATH: + if (_loveSkadi == 1) + ++_loveSkadi; + Talk(SAY_DRAKE_BREATH); + break; + case ACTION_PHASE2: + Talk(SAY_DRAKE_DEATH); + DoCastSelf(SPELL_SKADI_TELEPORT); + _summons.DespawnEntry(NPC_WORLD_TRIGGER); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); + me->SetReactState(REACT_AGGRESSIVE); + _phase = PHASE_GROUND; + + _events.ScheduleEvent(EVENT_SKADI_CRUSH, 8s); + _events.ScheduleEvent(EVENT_SKADI_SPEAR, 11s); + _events.ScheduleEvent(EVENT_SKADI_WHIRLWIND, 23s); + break; + case ACTION_HARPOON_HIT: + ++_harpoonHit; + if (_harpoonHit == 1) + _loveSkadi = 1; + break; } } + uint32 GetData(uint32 id) const override + { + if (id == DATA_LOVE_TO_SKADI) + return _loveSkadi; + return 0; + } + void UpdateAI(uint32 diff) override { - if (!UpdateVictim() && SecondPhase) - return; + _events.Update(diff); - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) + if (_phase == PHASE_FLYING) { - case EVENT_SKADI_START: + switch (_events.ExecuteEvent()) + { + case EVENT_GRAUF_START: { - me->SetControlled(false, UNIT_STATE_ROOT); - if (Creature* cr = GetGrauf()) + if (Creature* grauf = _instance->GetCreature(DATA_GRAUF)) { - me->EnterVehicleUnattackable(cr, 0); - cr->AI()->DoAction(ACTION_START_EVENT); + me->EnterVehicleUnattackable(grauf, 0); + grauf->AI()->DoAction(ACTION_START_ENCOUNTER); } else EnterEvadeMode(); - break; } + case EVENT_SKADI_RESET_CHECK: + { + if (Creature* resetTrigger = me->FindNearestCreature(NPC_TRIGGER_RESET, 200.0f)) + resetTrigger->CastSpell(resetTrigger, SPELL_GAUNTLET_RESET_CHECK, true); + _events.Repeat(6s); + break; + } + } + return; + } + + if (!UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (_events.ExecuteEvent()) + { case EVENT_SKADI_CRUSH: - { - me->CastSpell(me->GetVictim(), SPELL_CRUSH, false); - events.Repeat(8s); - break; - } + DoCastVictim(SPELL_CRUSH); + _events.Repeat(8s); + break; case EVENT_SKADI_SPEAR: - { - if (Unit* tgt = SelectTarget(SelectTargetMethod::Random, 0)) - me->CastSpell(tgt, SPELL_POISONED_SPEAR, false); - - events.Repeat(10s); - break; - } + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + DoCast(target, SPELL_POISONED_SPEAR); + _events.Repeat(10s); + break; case EVENT_SKADI_WHIRLWIND: - { - me->CastSpell(me, SPELL_WHIRLWIND, false); - events.Repeat(15s, 20s); - events.DelayEvents(10s); - break; - } + DoCast(me, SPELL_WHIRLWIND); + _events.Repeat(15s, 20s); + _events.DelayEvents(10s); + break; } DoMeleeAttackIfReady(); } - void JustDied(Unit* /*pKiller*/) override + void JustDied(Unit* /*killer*/) override { - summons.DespawnAll(); + _summons.DespawnAll(); Talk(SAY_DEATH); - if (m_pInstance) + if (_instance) { - m_pInstance->SetData(DATA_SKADI_THE_RUTHLESS, DONE); - m_pInstance->HandleGameObject(m_pInstance->GetGuidData(SKADI_DOOR), true); + _instance->SetData(DATA_SKADI_THE_RUTHLESS, DONE); + _instance->HandleGameObject(_instance->GetGuidData(SKADI_DOOR), true); } } - void KilledUnit(Unit* /*pVictim*/) override + void KilledUnit(Unit* who) override { - if (urand(0, 1)) - return; - - Talk(SAY_KILL); + if (who->IsPlayer()) + Talk(SAY_KILL); } + + void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override + { + _summons.DespawnAll(); + ScriptedAI::EnterEvadeMode(); + } + + private: + InstanceScript* _instance; + EventMap _events; + SummonList _summons; + CombatPhase _phase; + uint8 _harpoonHit; + uint8 _loveSkadi; + bool _firstWaveSummoned; + bool _encounterStarted; }; }; @@ -266,263 +386,324 @@ class boss_skadi_grauf : public CreatureScript public: boss_skadi_grauf() : CreatureScript("boss_skadi_grauf") { } - CreatureAI* GetAI(Creature* pCreature) const override + CreatureAI* GetAI(Creature* creature) const override { - return GetUtgardePinnacleAI(pCreature); + return GetUtgardePinnacleAI(creature); } struct boss_skadi_graufAI : public VehicleAI { - boss_skadi_graufAI(Creature* pCreature) : VehicleAI(pCreature), summons(me) + boss_skadi_graufAI(Creature* creature) : VehicleAI(creature), _summons(me) { - m_pInstance = pCreature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } - InstanceScript* m_pInstance; - EventMap events; - SummonList summons; - uint8 currentPos; - uint8 AchievementHitCount; - void Reset() override { - events.Reset(); - summons.DespawnAll(); - currentPos = 0; - AchievementHitCount = 0; - me->RemoveAllAuras(); + _events.Reset(); + _summons.DespawnAll(); + me->SetReactState(REACT_PASSIVE); + me->SetSpeedRate(MOVE_RUN, 2.5f); } void DoAction(int32 param) override { - if (param == ACTION_START_EVENT) + if (param == ACTION_START_ENCOUNTER) { - events.RescheduleEvent(EVENT_GRAUF_CHECK, 5s); - events.RescheduleEvent(EVENT_GRAUF_START, 2s); - } - else if (param == ACTION_REMOVE_SKADI) - { - if (Unit* passenger = me->GetVehicleKit()->GetPassenger(0)) - if (Creature* skadi = passenger->ToCreature()) - skadi->AI()->Talk(SAY_DRAKE_DEATH); - me->GetMotionMaster()->MovePoint(10, 480.0f, -513.0f, 108.0f); - events.ScheduleEvent(EVENT_GRAUF_REMOVE_SKADI, 2s); - } - else if (param == ACTION_MYGIRL_ACHIEVEMENT) - { - AchievementHitCount++; - if (AchievementHitCount >= 3 && m_pInstance) - m_pInstance->SetData(DATA_SKADI_ACHIEVEMENT, true); + me->setActive(true); + me->SetCanFly(true); + me->SetDisableGravity(true); + me->GetMotionMaster()->Clear(true); + me->SetAnimTier(AnimTier::Fly); + me->GetMotionMaster()->MovePath(PATH_INITIAL, FORCED_MOVEMENT_RUN); } } - void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override { - if (spellInfo->Id == 47593) // SPELL_FREEZING_CLOUD_VISUAL trigger - target->CastSpell(target, SPELL_FREEZING_CLOUD, true); + if (spellInfo->Id == SPELL_LAUNCH_HARPOON) + if (Creature* skadi = _instance->GetCreature(DATA_SKADI_THE_RUTHLESS)) + skadi->AI()->DoAction(ACTION_HARPOON_HIT); } - void SpawnFlameTriggers(uint8 point) + void MovementInform(uint32 type, uint32 /*id*/) override { - for(uint8 j = 0; j < 50; ++j) + if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized()) { - if (point == 1) - me->SummonCreature(NPC_BREATH_TRIGGER, 480.0f - (j * 3), -518.0f + (j / 16.0f), 105.0f, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000); - else - me->SummonCreature(NPC_BREATH_TRIGGER, 480.0f - (j * 3), -510.0f + (j / 16.0f), 105.0f, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000); - } - // and out of loop, cover the small room - if (point == 0) - { - Creature* cr; - if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 483, -484.9f, 105, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000))) - cr->CastSpell(cr, SPELL_FREEZING_CLOUD, true); - if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 471.0f, -484.7f, 105, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000))) - cr->CastSpell(cr, SPELL_FREEZING_CLOUD, true); - - for (uint8 j = 0; j < 7; j++) - if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 477.0f, -507.0f + (j * 3), 105.0f, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000))) - cr->CastSpell(cr, SPELL_FREEZING_CLOUD, true); - } - } - - void MovementInform(uint32 /*uiType*/, uint32 Id) override - { - switch (Id) - { - case 0: - case 1: - me->RemoveAurasDueToSpell(SPELL_FREEZING_CLOUD_VISUAL); - me->SetFacingTo(M_PI * 2); - break; - case 2: - case 3: - if (m_pInstance) - m_pInstance->SetData(SKADI_IN_RANGE, 1); - Talk(EMOTE_RANGE); - me->SetFacingTo(M_PI); - break; - } - } - - uint8 SelectNextPos(uint8 Position) - { - switch (Position) - { - case 0: - case 1: - return 2 + urand(0, 1); - default: - if (me->GetPositionY() < -515.0f) - return 1; - else - return 0; - } - } - - void JustEngagedWith(Unit*) override - { - me->SetInCombatWithZone(); - } - - void RemoveSkadi(bool withEvade) - { - if (Unit* skadi = me->GetVehicleKit()->GetPassenger(0)) - { - summons.DespawnAll(); - skadi->ExitVehicle(); - if (withEvade) - { - skadi->ToCreature()->AI()->EnterEvadeMode(); - skadi->UpdatePosition(343.02f, -507.325f, 104.567f, M_PI, true); - } - else - skadi->ToCreature()->AI()->DoAction(ACTION_PHASE2); - - skadi->StopMovingOnCurrentPos(); - } - } - - void CheckPlayers() - { - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) - { - if (itr->GetSource()->GetPositionY() > -490.0f || itr->GetSource()->IsGameMaster() || !itr->GetSource()->IsAlive()) - continue; - - return; - } - - RemoveSkadi(true); - } - - void SpawnHelpers(uint8 Spot) - { - if (Creature* Harpooner = me->SummonCreature(NPC_YMIRJAR_HARPOONER, TrashPosition[Spot].GetPositionX() + rand() % 5, TrashPosition[Spot].GetPositionY() + rand() % 5, TrashPosition[Spot].GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000)) - { - Harpooner->SetInCombatWithZone(); - summons.Summon(Harpooner); - } - if (Creature* Second = me->SummonCreature((urand(0, 1) ? NPC_YMIRJAR_WARRIOR : NPC_YMIRJAR_WITCH_DOCTOR), TrashPosition[Spot].GetPositionX() + rand() % 5, TrashPosition[Spot].GetPositionY() + rand() % 5, TrashPosition[Spot].GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000)) - { - Second->SetInCombatWithZone(); - summons.Summon(Second); + me->SetFacingTo(BreachFacing); + Talk(EMOTE_ON_RANGE); + _events.ScheduleEvent(EVENT_GRAUF_LEAVE_BREACH, 10s); } } void UpdateAI(uint32 diff) override { - events.Update(diff); - switch (events.ExecuteEvent()) + _events.Update(diff); + switch (_events.ExecuteEvent()) { - case EVENT_GRAUF_CHECK: - { - CheckPlayers(); - events.Repeat(2s); - break; - } - case EVENT_GRAUF_START: - { - me->GetMotionMaster()->Clear(true); - me->GetMotionMaster()->MoveTakeoff(10, SkadiPosition[0].GetPositionX(), SkadiPosition[0].GetPositionY(), SkadiPosition[0].GetPositionZ(), 3.0f); + case EVENT_GRAUF_LEAVE_BREACH: + _lastSide = RAND(POINT_LEFT, POINT_RIGHT); + me->GetMotionMaster()->MovePath(_lastSide == POINT_LEFT ? PATH_LEFT : PATH_RIGHT, FORCED_MOVEMENT_RUN); - SpawnHelpers(0); - SpawnHelpers(0); - events.ScheduleEvent(EVENT_GRAUF_MOVE, 5s); - events.ScheduleEvent(EVENT_GRAUF_SUMMON_HELPERS, 20s); - break; - } - case EVENT_GRAUF_MOVE: - { - AchievementHitCount = 0; - uint8 targetPoint = SelectNextPos(currentPos); - me->GetMotionMaster()->MovePoint(targetPoint, SkadiPosition[targetPoint].GetPositionX(), SkadiPosition[targetPoint].GetPositionY(), SkadiPosition[targetPoint].GetPositionZ()); - if (targetPoint <= 1) - { - Talk(EMOTE_DEEP_BREATH); - SpawnFlameTriggers(targetPoint); - me->CastSpell(me, SPELL_FREEZING_CLOUD_VISUAL, false); - } + if (_lastSide == POINT_LEFT) + DoCast(me, SPELL_FREEZING_CLOUD_LEFT_PERIODIC); + else + DoCast(me, SPELL_FREEZING_CLOUD_RIGHT_PERIODIC); - if (m_pInstance) - m_pInstance->SetData(SKADI_IN_RANGE, 0); + if (Creature* skadi = _instance->GetCreature(DATA_SKADI_THE_RUTHLESS)) + skadi->AI()->DoAction(ACTION_DRAKE_BREATH); - currentPos = targetPoint; - events.Repeat(25s); - break; - } - case EVENT_GRAUF_SUMMON_HELPERS: - { - SpawnHelpers(1); - events.Repeat(15s); - break; - } - case EVENT_GRAUF_REMOVE_SKADI: - { - RemoveSkadi(false); - me->DespawnOrUnsummon(); - break; - } + _events.ScheduleEvent(EVENT_GRAUF_REMOVE_AURA, 10s); + break; + case EVENT_GRAUF_REMOVE_AURA: + me->RemoveAurasDueToSpell(SPELL_FREEZING_CLOUD_LEFT_PERIODIC); + me->RemoveAurasDueToSpell(SPELL_FREEZING_CLOUD_RIGHT_PERIODIC); + break; } } + + void JustDied(Unit* /*killer*/) override + { + if (Creature* skadi = _instance->GetCreature(DATA_SKADI_THE_RUTHLESS)) + { + skadi->ExitVehicle(); + skadi->AI()->DoAction(ACTION_PHASE2); + + if (skadi->AI()->GetData(DATA_LOVE_TO_SKADI) == 1) + _instance->SetData(DATA_SKADI_ACHIEVEMENT, true); + } + me->DespawnOrUnsummon(6s); + } + + private: + InstanceScript* _instance; + EventMap _events; + SummonList _summons; + uint8 _lastSide = POINT_LEFT; }; }; -class go_harpoon_canon : public GameObjectScript +// 48642 - Launch Harpoon +class spell_skadi_launch_harpoon : public SpellScript { -public: - go_harpoon_canon() : GameObjectScript("go_harpoon_canon") { } + PrepareSpellScript(spell_skadi_launch_harpoon); - bool OnGossipHello(Player* pPlayer, GameObject* go) override + void FilterTargets(std::list& targets) { - InstanceScript* m_pInstance = go->GetInstanceScript(); - if (m_pInstance && m_pInstance->GetData(DATA_SKADI_THE_RUTHLESS) == IN_PROGRESS) - if (m_pInstance->GetData(SKADI_IN_RANGE) == 1) - { - uint8 count = m_pInstance->GetData(SKADI_HITS) + 1; - m_pInstance->SetData(SKADI_HITS, count); + if (targets.size() >= 2) + targets.remove_if([](WorldObject* obj) { return obj->GetEntry() != NPC_GRAUF; }); + } - if (Creature* grauf = ObjectAccessor::GetCreature(*pPlayer, m_pInstance->GetGuidData(DATA_GRAUF))) - { - if (count >= 3) - { - m_pInstance->SetData(SKADI_IN_RANGE, 0); - grauf->AI()->DoAction(ACTION_REMOVE_SKADI); - } + void HandleDamageCalc() + { + if (Unit* target = GetHitUnit()) + SetHitDamage(target->CountPctFromMaxHealth(35)); + } - grauf->AI()->DoAction(ACTION_MYGIRL_ACHIEVEMENT); - } - go->CastSpell((Unit*)nullptr, SPELL_LAUNCH_HARPOON); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_skadi_launch_harpoon::FilterTargets, EFFECT_0, TARGET_UNIT_CONE_ENTRY); + OnHit += SpellHitFn(spell_skadi_launch_harpoon::HandleDamageCalc); + } +}; +// 50255 - Poisoned Spear +class spell_skadi_poisoned_spear : public SpellScript +{ + PrepareSpellScript(spell_skadi_poisoned_spear); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_POISONED_SPEAR_PERIODIC }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_POISONED_SPEAR_PERIODIC, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_skadi_poisoned_spear::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; + +// 47594 - Freezing Cloud (right side) +class spell_freezing_cloud_area_right : public SpellScript +{ + PrepareSpellScript(spell_freezing_cloud_area_right); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_FREEZING_CLOUD }); + } + + void FilterTargets(std::list& targets) + { + targets.remove_if([](WorldObject* obj) { return obj->GetPositionY() > -511.0f; }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_FREEZING_CLOUD, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_freezing_cloud_area_right::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_freezing_cloud_area_right::HandleScript, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } +}; + +// 47574 - Freezing Cloud (left side) +class spell_freezing_cloud_area_left : public SpellScript +{ + PrepareSpellScript(spell_freezing_cloud_area_left); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_FREEZING_CLOUD }); + } + + void FilterTargets(std::list& targets) + { + targets.remove_if([](WorldObject* obj) { return obj->GetPositionY() < -511.0f; }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_FREEZING_CLOUD, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_freezing_cloud_area_left::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_freezing_cloud_area_left::HandleScript, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } +}; + +// 47579, 60020 - Freezing Cloud (damage aura) +class spell_freezing_cloud_damage : public AuraScript +{ + PrepareAuraScript(spell_freezing_cloud_damage); + + bool CanBeAppliedOn(Unit* target) + { + if (Aura* aur = target->GetAura(GetId())) + if (aur->GetOwner() != GetOwner()) + return false; return true; } + + void Register() override + { + DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_freezing_cloud_damage::CanBeAppliedOn); + } +}; + +// 59275 - Summon Gauntlet Mobs Periodic +class spell_summon_gauntlet_mobs_periodic : public AuraScript +{ + PrepareAuraScript(spell_summon_gauntlet_mobs_periodic); + + void CastTheNextTwoSpells() + { + for (uint8 i = 0; i < 2; ++i) + { + uint32 spellId = _summonSpells.front(); + GetTarget()->CastSpell((Unit*)nullptr, spellId, true); + _summonSpells.push_back(spellId); + _summonSpells.pop_front(); + } + } + + void PushBackTheNextTwoSpells() + { + for (uint8 j = 0; j < 2; ++j) + { + _summonSpells.push_back(_summonSpells.front()); + _summonSpells.pop_front(); + } + } + + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + if (RAND(0, 1)) + { + CastTheNextTwoSpells(); + PushBackTheNextTwoSpells(); + } + else + { + PushBackTheNextTwoSpells(); + CastTheNextTwoSpells(); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_summon_gauntlet_mobs_periodic::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + +private: + std::deque _summonSpells = + { + SPELL_SUMMON_YMIRJAR_WARRIOR_E, + SPELL_SUMMON_YMIRJAR_HARPOONER_W, + SPELL_SUMMON_YMIRJAR_WARRIOR_W, + SPELL_SUMMON_YMIRJAR_HARPOONER_E, + SPELL_SUMMON_YMIRJAR_WARRIOR_W, + SPELL_SUMMON_YMIRJAR_WITCH_DOCTOR_E, + SPELL_SUMMON_YMIRJAR_WARRIOR_E, + SPELL_SUMMON_YMIRJAR_WITCH_DOCTOR_W + }; +}; + +// 49308 - Utgarde Pinnacle Gauntlet Reset Check +class spell_skadi_reset_check : public SpellScript +{ + PrepareSpellScript(spell_skadi_reset_check); + + void CountTargets(std::list& targets) + { + targets.remove_if(Acore::UnitAuraCheck(false, SPELL_GAUNTLET_EFFECT)); + _targetCount = targets.size(); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (_targetCount) + return; + + Creature* target = GetHitCreature(); + if (!target) + return; + + if (InstanceScript* instance = target->GetInstanceScript()) + if (instance->GetData(DATA_SKADI_THE_RUTHLESS) == IN_PROGRESS) + if (Creature* skadi = instance->GetCreature(DATA_SKADI_THE_RUTHLESS)) + skadi->AI()->EnterEvadeMode(); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_skadi_reset_check::CountTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_skadi_reset_check::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); + } + +private: + uint32 _targetCount = 0; }; void AddSC_boss_skadi() { new boss_skadi(); new boss_skadi_grauf(); - new go_harpoon_canon(); + RegisterSpellScript(spell_skadi_launch_harpoon); + RegisterSpellScript(spell_skadi_poisoned_spear); + RegisterSpellScript(spell_freezing_cloud_area_left); + RegisterSpellScript(spell_freezing_cloud_area_right); + RegisterSpellScript(spell_freezing_cloud_damage); + RegisterSpellScript(spell_summon_gauntlet_mobs_periodic); + RegisterSpellScript(spell_skadi_reset_check); } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp index cddc964ae..c332fccbd 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp @@ -23,9 +23,18 @@ ObjectData const creatureData[] = { { NPC_SKADI_THE_RUTHLESS, DATA_SKADI_THE_RUTHLESS }, + { NPC_GARUF, DATA_GRAUF }, { 0, 0 } }; +ObjectData const summonData[] = +{ + { NPC_YMIRJAR_WARRIOR, DATA_SKADI_THE_RUTHLESS }, + { NPC_YMIRJAR_WITCH_DOCTOR, DATA_SKADI_THE_RUTHLESS }, + { NPC_YMIRJAR_HARPOONER, DATA_SKADI_THE_RUTHLESS }, + { 0, 0 } +}; + class instance_utgarde_pinnacle : public InstanceMapScript { public: @@ -65,6 +74,7 @@ public: { SetHeaders(DataHeader); LoadObjectData(creatureData, nullptr); + LoadSummonData(summonData); SkadiHits = 0; SkadiInRange = 0; diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h index f05387598..cb67ff7e3 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h @@ -49,6 +49,7 @@ enum Data DATA_SVALA_ACHIEVEMENT = 50, DATA_SKADI_ACHIEVEMENT = 51, DATA_YMIRON_ACHIEVEMENT = 52, + DATA_LOVE_TO_SKADI = 53, }; enum Objects @@ -71,6 +72,9 @@ enum Objects NPC_MASSIVE_JORMUNGAR = 26685, NPC_FEROCIOUS_RHINO = 26686, NPC_GARUF = 26893, + NPC_YMIRJAR_WARRIOR = 26690, + NPC_YMIRJAR_WITCH_DOCTOR = 26691, + NPC_YMIRJAR_HARPOONER = 26692, }; template @@ -79,4 +83,6 @@ inline AI* GetUtgardePinnacleAI(T* obj) return GetInstanceAI(obj, UtgardePinnacleScriptName); } +#define RegisterUtgardePinnacleCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetUtgardePinnacleAI) + #endif diff --git a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp index 1c21394b4..1cbd12df6 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp @@ -244,7 +244,7 @@ public: { pSummoned->SetSpeed(MOVE_RUN, 0.3f); pSummoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - pSummoned->GetMotionMaster()->MoveFollow(me, 0, 0); + pSummoned->GetMotionMaster()->MoveFollow(me, 0, 0, MOTION_SLOT_ACTIVE, false, false); me->CastSpell(pSummoned, SPELL_CREATE_GLOBULE_VISUAL, true); // triggered should ignore los globules.Summon(pSummoned); if (pInstance) diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp index 4d1bc0924..c805c43f0 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -312,6 +312,7 @@ struct violet_hold_trashAI : public npc_escortAI CreatureStartAttackDoor(); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 id) override { if (PLoc < 6) @@ -1021,6 +1022,7 @@ public: uint32 timer; uint8 count; + using CreatureAI::WaypointReached; void WaypointReached(uint32 uiWPointId) override { if (!pInstance) diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index af5491db7..a3cab28af 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -377,6 +377,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -752,6 +753,7 @@ public: player->FailQuest(QUEST_ESCAPING_THE_MIST); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -849,6 +851,7 @@ public: else Bonker_agro = 0; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp index bccd217ad..c83884871 100644 --- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp +++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp @@ -75,6 +75,7 @@ public: summoned->AI()->AttackStart(me->GetVictim()); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index 928a0a71e..731f022e9 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -126,6 +126,7 @@ public: DoMeleeAttackIfReady(); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -210,6 +211,7 @@ public: Start(false, summonerGUID); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { if (waypointId != 26) diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp index 9afba1804..6b10781cc 100644 --- a/src/server/scripts/Northrend/zone_icecrown.cpp +++ b/src/server/scripts/Northrend/zone_icecrown.cpp @@ -747,6 +747,7 @@ public: summons.Despawn(summon); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 pointId) override { switch (pointId) @@ -1658,7 +1659,7 @@ enum BlessedBanner NPC_ARGENT_MASON = 30900, NPC_REANIMATED_CAPTAIN = 30986, NPC_SCOURGE_DRUDGE = 30984, - NPC_HIDEOUS_PLAGEBRINGER = 30987, + NPC_HIDEOUS_PLAGUEBRINGER = 30987, NPC_HALOF_THE_DEATHBRINGER = 30989, NPC_LK = 31013, @@ -1770,9 +1771,20 @@ public: void MoveInLineOfSight(Unit* /*who*/) override { } - void JustSummoned(Creature* Summoned) override + void JustSummoned(Creature* summon) override { - Summons.Summon(Summoned); + Summons.Summon(summon); + if (summon->GetEntry() == NPC_SCOURGE_DRUDGE || summon->GetEntry() == NPC_REANIMATED_CAPTAIN || + summon->GetEntry() == NPC_HIDEOUS_PLAGUEBRINGER || summon->GetEntry() == NPC_HALOF_THE_DEATHBRINGER) + { + summon->SetHomePosition(DalforsPos[2]); + summon->SetReactState(REACT_PASSIVE); + summon->EngageWithTarget(me); + summon->m_Events.AddEventAtOffset([summon]() + { + summon->SetReactState(REACT_AGGRESSIVE); + }, 2s); + } } void JustDied(Unit* /*killer*/) override @@ -1925,36 +1937,16 @@ public: if (Creature* LK = GetClosestCreatureWithEntry(me, NPC_LK, 100)) LK->AI()->Talk(LK_TALK_3); } - if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE, Mason3Pos[0])) - { - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); - } + DoSummon(NPC_SCOURGE_DRUDGE, Mason3Pos[0]); if (urand(0, 1) == 0) { - if (Creature* tempsum = DoSummon(NPC_HIDEOUS_PLAGEBRINGER, Mason1Pos[0])) - { - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); - } - if (Creature* tempsum = DoSummon(NPC_HIDEOUS_PLAGEBRINGER, Mason2Pos[0])) - { - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); - } + DoSummon(NPC_HIDEOUS_PLAGUEBRINGER, Mason1Pos[0]); + DoSummon(NPC_HIDEOUS_PLAGUEBRINGER, Mason2Pos[0]); } else { - if (Creature* tempsum = DoSummon(NPC_REANIMATED_CAPTAIN, Mason1Pos[0])) - { - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); - } - if (Creature* tempsum = DoSummon(NPC_REANIMATED_CAPTAIN, Mason2Pos[0])) - { - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); - } + DoSummon(NPC_REANIMATED_CAPTAIN, Mason1Pos[0]); + DoSummon(NPC_REANIMATED_CAPTAIN, Mason2Pos[0]); } PhaseCount++; @@ -1969,22 +1961,12 @@ public: { if (Creature* LK = GetClosestCreatureWithEntry(me, NPC_LK, 100)) LK->AI()->Talk(LK_TALK_4); - if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE, Mason1Pos[0])) - { - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); - } - if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE, Mason2Pos[0])) - { - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); - } + DoSummon(NPC_SCOURGE_DRUDGE, Mason1Pos[0]); + DoSummon(NPC_SCOURGE_DRUDGE, Mason2Pos[0]); if (Creature* tempsum = DoSummon(NPC_HALOF_THE_DEATHBRINGER, DalforsPos[0])) { HalofSpawned = true; guidHalof = tempsum->GetGUID(); - tempsum->SetHomePosition(DalforsPos[2]); - tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100)); } } break; @@ -2001,7 +1983,7 @@ public: if (Halof->isDead()) { DoCast(me, SPELL_CRUSADERS_SPIRE_VICTORY, true); - Summons.DespawnEntry(NPC_HIDEOUS_PLAGEBRINGER); + Summons.DespawnEntry(NPC_HIDEOUS_PLAGUEBRINGER); Summons.DespawnEntry(NPC_REANIMATED_CAPTAIN); Summons.DespawnEntry(NPC_SCOURGE_DRUDGE); Summons.DespawnEntry(NPC_HALOF_THE_DEATHBRINGER); diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index 4af05b4b9..a9de0bf08 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -521,6 +521,7 @@ public: uint32 m_uiChatTimer; + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 4b4b17728..b94dabc73 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -20,6 +20,7 @@ #include "CreatureScript.h" #include "Player.h" #include "ScriptedCreature.h" +#include "SpellMgr.h" #include "ScriptedEscortAI.h" #include "ScriptedGossip.h" #include "SpellAuraEffects.h" @@ -72,6 +73,7 @@ struct npc_frosthound : public npc_escortAI return; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -567,6 +569,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*waypointId*/) override { } void JustDied(Unit* /*killer*/) override { } void OnCharmed(bool /*apply*/) override { } @@ -1340,9 +1343,46 @@ class spell_eject_passenger_wild_wyrm : public SpellScript } }; +struct npc_oathbound_warder : public ScriptedAI +{ + npc_oathbound_warder(Creature* creature) : ScriptedAI(creature) { } + + void AttackStart(Unit* /*who*/) override { } + void JustEngagedWith(Unit* /*who*/) override { } + void UpdateAI(uint32 /*diff*/) override { } // Need so AI doesn't stop casting when hit in combat + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + me->SetReactState(REACT_PASSIVE); + + CharmInfo* charmInfo = me->GetCharmInfo(); + if (!charmInfo) + return; + + charmInfo->InitEmptyActionBar(false); + + uint32 slot = 0; + for (uint32 i = 0; i < MAX_CREATURE_SPELLS; ++i) + { + uint32 spellId = me->m_spells[i]; + if (!spellId) + continue; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (spellInfo && spellInfo->IsPassive()) + me->CastSpell(me, spellInfo, true); + + charmInfo->SetActionBar(6 + slot, spellId, ACT_PASSIVE); + ++slot; + } + } +}; + void AddSC_storm_peaks() { RegisterCreatureAI(npc_frosthound); + RegisterCreatureAI(npc_oathbound_warder); new npc_iron_watcher(); new npc_time_lost_proto_drake(); RegisterSpellScript(spell_q13007_iron_colossus); diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp index 66ac94100..f472847eb 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp @@ -16,7 +16,6 @@ */ #include "OutdoorPvPHP.h" -#include "CreatureScript.h" #include "Language.h" #include "MapMgr.h" #include "ObjectMgr.h" diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp index 33e2fb9fa..bbd72a023 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp @@ -16,7 +16,6 @@ */ #include "OutdoorPvPSI.h" -#include "CreatureScript.h" #include "GameObject.h" #include "Language.h" #include "MapMgr.h" diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPTF.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPTF.cpp index 77bf4b4fa..ab781a85d 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPTF.cpp +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPTF.cpp @@ -16,7 +16,6 @@ */ #include "OutdoorPvPTF.h" -#include "CreatureScript.h" #include "GameTime.h" #include "Language.h" #include "MapMgr.h" diff --git a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp index 67679c091..7413ed4f2 100644 --- a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp +++ b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp @@ -61,10 +61,6 @@ struct boss_exarch_maladaar : public BossAI boss_exarch_maladaar(Creature* creature) : BossAI(creature, DATA_EXARCH_MALADAAR) { _talked = false; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp index 03918e5e1..bb7bb9404 100644 --- a/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp +++ b/src/server/scripts/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp @@ -46,12 +46,7 @@ enum Misc struct boss_shirrak_the_dead_watcher : public BossAI { boss_shirrak_the_dead_watcher(Creature* creature) : BossAI(creature, DATA_SHIRRAK_THE_DEAD_WATCHER) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } ObjectGuid focusGUID; diff --git a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_nexusprince_shaffar.cpp b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_nexusprince_shaffar.cpp index 0986ba9c6..846f786a5 100644 --- a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_nexusprince_shaffar.cpp +++ b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_nexusprince_shaffar.cpp @@ -54,10 +54,6 @@ struct boss_nexusprince_shaffar : public BossAI boss_nexusprince_shaffar(Creature* creature) : BossAI(creature, DATA_NEXUSPRINCE_SHAFFAR), summons(me) { HasTaunted = false; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } SummonList summons; diff --git a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp index 2612c690e..04fc2baa3 100644 --- a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp +++ b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp @@ -55,12 +55,7 @@ constexpr uint8 MAX_VOID_BLAST = 5; struct boss_pandemonius : public BossAI { boss_pandemonius(Creature* creature) : BossAI(creature, DATA_PANDEMONIUS) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void JustEngagedWith(Unit* who) override { diff --git a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_tavarok.cpp b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_tavarok.cpp index 2bacc2b4d..e12658381 100644 --- a/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_tavarok.cpp +++ b/src/server/scripts/Outland/Auchindoun/ManaTombs/boss_tavarok.cpp @@ -29,12 +29,7 @@ enum Spells struct boss_tavarok : public BossAI { boss_tavarok(Creature* creature) : BossAI(creature, DATA_TAVAROK) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp index c201d5a0a..c4add0c19 100644 --- a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp +++ b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp @@ -68,10 +68,6 @@ struct boss_anzu : public BossAI talkTimer = 1; me->ReplaceAllUnitFlags(UNIT_FLAG_NON_ATTACKABLE); me->AddAura(SPELL_SHADOWFORM, me); - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } const Position AnzuSpiritPos[3] = diff --git a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_darkweaver_syth.cpp b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_darkweaver_syth.cpp index fb3600458..0992d4da3 100644 --- a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_darkweaver_syth.cpp +++ b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_darkweaver_syth.cpp @@ -43,12 +43,7 @@ enum Spells struct boss_darkweaver_syth : public BossAI { boss_darkweaver_syth(Creature* creature) : BossAI(creature, DATA_DARKWEAVER_SYTH) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp index 51cb46a99..9e6372481 100644 --- a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp +++ b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp @@ -46,12 +46,7 @@ enum Spells struct boss_talon_king_ikiss : public BossAI { boss_talon_king_ikiss(Creature* creature) : BossAI(creature, DATA_IKISS), _spoken(false) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp index 155ad15fd..cf6fe6102 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp @@ -45,12 +45,7 @@ enum Npc struct boss_blackheart_the_inciter : public BossAI { boss_blackheart_the_inciter(Creature* creature) : BossAI(creature, DATA_BLACKHEARTTHEINCITEREVENT) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } bool InciteChaos; diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp index a9c1471e9..30969ea8a 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp @@ -59,10 +59,6 @@ struct boss_murmur : public BossAI boss_murmur(Creature* creature) : BossAI(creature, DATA_MURMUR) { me->SetCombatMovement(false); - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index c41e1ecc2..2e0cfe9f0 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -1101,16 +1101,29 @@ struct npc_maiev_illidan : public ScriptedAI instance = creature->GetInstanceScript(); } + bool _outroActive{ false }; + void Reset() override { + if (_outroActive) + return; scheduler.CancelAll(); me->m_Events.KillAllEvents(false); } + void JustExitedCombat() override + { + EngagementOver(); + if (_outroActive) + return; + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + void DoAction(int32 param) override { if (param == ACTION_MAIEV_ENDING) { + _outroActive = true; scheduler.CancelAll(); me->SetReactState(REACT_PASSIVE); DoStopAttack(); diff --git a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp index 5a5796538..a81ab01a9 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp @@ -48,12 +48,7 @@ enum Supremus struct boss_supremus : public BossAI { boss_supremus(Creature* creature) : BossAI(creature, DATA_SUPREMUS) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp index 2e73787fa..353e357cf 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_fathomlord_karathress.cpp @@ -205,11 +205,6 @@ struct boss_fathomguard_sharkkis : public ScriptedAI { _instance = creature->GetInstanceScript(); SetBoundary(_instance->GetBossBoundary(DATA_FATHOM_LORD_KARATHRESS)); - - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp index 9fcbc68fc..286bb0c97 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp @@ -72,11 +72,6 @@ struct boss_lady_vashj : public BossAI { boss_lady_vashj(Creature* creature) : BossAI(creature, DATA_LADY_VASHJ) { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - _intro = false; } diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp index 5a1d6c0fa..be599ef83 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp @@ -249,12 +249,7 @@ private: struct npc_inner_demon : public ScriptedAI { npc_inner_demon(Creature* creature) : ScriptedAI(creature) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void IsSummonedBy(WorldObject* summoner) override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp index cc337beee..a923b81a9 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp @@ -55,12 +55,7 @@ const uint32 waterGlobuleIds[4] = {SPELL_SUMMON_WATER_GLOBULE_1, SPELL_SUMMON_WA struct boss_morogrim_tidewalker : public BossAI { boss_morogrim_tidewalker(Creature* creature) : BossAI(creature, DATA_MOROGRIM_TIDEWALKER) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_mennu_the_betrayer.cpp b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_mennu_the_betrayer.cpp index 547e0e442..2d210f3cd 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_mennu_the_betrayer.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_mennu_the_betrayer.cpp @@ -38,12 +38,7 @@ enum Text struct boss_mennu_the_betrayer : public BossAI { boss_mennu_the_betrayer(Creature* creature) : BossAI(creature, DATA_MENNU_THE_BETRAYER) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_quagmirran.cpp b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_quagmirran.cpp index 55fb6cd4d..75fe25dc3 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_quagmirran.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_quagmirran.cpp @@ -30,12 +30,7 @@ enum Spells struct boss_quagmirran : public BossAI { boss_quagmirran(Creature* creature) : BossAI(creature, DATA_QUAGMIRRAN) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void JustEngagedWith(Unit* /*who*/) override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_rokmar_the_crackler.cpp b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_rokmar_the_crackler.cpp index 5a33cdc19..e4e3aa967 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_rokmar_the_crackler.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_rokmar_the_crackler.cpp @@ -30,12 +30,7 @@ enum Spells struct boss_rokmar_the_crackler : public BossAI { explicit boss_rokmar_the_crackler(Creature* creature) : BossAI(creature, DATA_ROKMAR_THE_CRACKLER) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/SteamVault/boss_mekgineer_steamrigger.cpp b/src/server/scripts/Outland/CoilfangReservoir/SteamVault/boss_mekgineer_steamrigger.cpp index 5f9769e62..4d3595879 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SteamVault/boss_mekgineer_steamrigger.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SteamVault/boss_mekgineer_steamrigger.cpp @@ -41,12 +41,7 @@ enum MekgineerSteamrigger struct boss_mekgineer_steamrigger : public BossAI { boss_mekgineer_steamrigger(Creature* creature) : BossAI(creature, DATA_MEKGINEER_STEAMRIGGER) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void JustDied(Unit* /*killer*/) override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_ghazan.cpp b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_ghazan.cpp index e0f7ef243..5b660a1d1 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_ghazan.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_ghazan.cpp @@ -35,12 +35,7 @@ enum eBlackStalker struct boss_ghazan : public BossAI { boss_ghazan(Creature* creature) : BossAI(creature, DATA_GHAZAN) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void InitializeAI() override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_swamplord_muselek.cpp b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_swamplord_muselek.cpp index ee2e3d36f..1a4c19fe5 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_swamplord_muselek.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_swamplord_muselek.cpp @@ -47,12 +47,7 @@ enum Misc struct boss_swamplord_muselek : public BossAI { boss_swamplord_muselek(Creature* creature) : BossAI(creature, DATA_MUSELEK) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_the_black_stalker.cpp b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_the_black_stalker.cpp index 1bb2ee4b5..5bb097451 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_the_black_stalker.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_the_black_stalker.cpp @@ -54,12 +54,7 @@ enum eBlackStalker struct boss_the_black_stalker : public BossAI { boss_the_black_stalker(Creature* creature) : BossAI(creature, DATA_BLACK_STALKER) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void JustEngagedWith(Unit* /*who*/) override { diff --git a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp index 2679eb6ca..5dd35b908 100644 --- a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp +++ b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp @@ -64,12 +64,7 @@ enum HighKingMaulgar struct boss_high_king_maulgar : public BossAI { boss_high_king_maulgar(Creature* creature) : BossAI(creature, DATA_MAULGAR) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp index 41f77e9bb..acba63305 100644 --- a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_kelidan_the_breaker.cpp @@ -53,12 +53,7 @@ enum Actions struct boss_kelidan_the_breaker : public BossAI { boss_kelidan_the_breaker(Creature* creature) : BossAI(creature, DATA_KELIDAN) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_the_maker.cpp b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_the_maker.cpp index 7bb30a7c7..691a04724 100644 --- a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_the_maker.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/boss_the_maker.cpp @@ -35,12 +35,7 @@ enum Spells struct boss_the_maker : public BossAI { boss_the_maker(Creature* creature) : BossAI(creature, DATA_THE_MAKER) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void JustEngagedWith(Unit* /*who*/) override { diff --git a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_omor_the_unscarred.cpp b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_omor_the_unscarred.cpp index d5a01b296..914aee1bf 100644 --- a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_omor_the_unscarred.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_omor_the_unscarred.cpp @@ -42,10 +42,6 @@ struct boss_omor_the_unscarred : public BossAI boss_omor_the_unscarred(Creature* creature) : BossAI(creature, DATA_OMOR_THE_UNSCARRED) { me->SetCombatMovement(false); - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp index 448bdfa2b..3f64181e4 100644 --- a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_watchkeeper_gargolmar.cpp @@ -48,10 +48,6 @@ struct boss_watchkeeper_gargolmar : public BossAI boss_watchkeeper_gargolmar(Creature* creature) : BossAI(creature, DATA_WATCHKEEPER_GARGOLMAR) { _taunted = false; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp index f64c0fff5..83c7abefc 100644 --- a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp @@ -79,12 +79,7 @@ enum Actions struct boss_magtheridon : public BossAI { boss_magtheridon(Creature* creature) : BossAI(creature, DATA_MAGTHERIDON) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp index 5d87557be..50adf9ba2 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp @@ -100,12 +100,7 @@ PeonRoleplay PeonRoleplayData[3] = struct boss_grand_warlock_nethekurse : public BossAI { boss_grand_warlock_nethekurse(Creature* creature) : BossAI(creature, DATA_NETHEKURSE) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void Reset() override { diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp index 66fe098cd..ff6af2ceb 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp @@ -66,12 +66,7 @@ enum Phase struct boss_warbringer_omrogg : public BossAI { boss_warbringer_omrogg(Creature* creature) : BossAI(creature, DATA_OMROGG) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void HandleHeadTalk(HeadYells yell) { diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp index d275d6956..984e3d9bd 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp @@ -69,12 +69,7 @@ Position const kargathRespawnPos = { 231.25f, -83.6449f, 5.02341f }; struct boss_warchief_kargath_bladefist : public BossAI { boss_warchief_kargath_bladefist(Creature* creature) : BossAI(creature, DATA_KARGATH) - { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + { } void InitializeAI() override { diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 910d8b8cc..c7b7d1d72 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -98,10 +98,6 @@ struct boss_alar : public BossAI boss_alar(Creature* creature) : BossAI(creature, DATA_ALAR) { me->SetCombatMovement(false); - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp index d691dde4b..54235dda0 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp @@ -67,10 +67,6 @@ struct boss_high_astromancer_solarian : public BossAI boss_high_astromancer_solarian(Creature* creature) : BossAI(creature, DATA_ASTROMANCER) { callForHelpRange = 105.0f; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); } void Reset() override diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 31390d5a3..8328802bd 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -725,12 +725,7 @@ private: struct advisor_baseAI : public ScriptedAI { - advisor_baseAI(Creature* creature) : ScriptedAI(creature) { - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - } + advisor_baseAI(Creature* creature) : ScriptedAI(creature) { } virtual void ScheduleEvents() {} diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp index 6ccf91dca..3c4582d46 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_void_reaver.cpp @@ -42,11 +42,6 @@ struct boss_void_reaver : public BossAI boss_void_reaver(Creature* creature) : BossAI(creature, DATA_REAVER) { callForHelpRange = 105.0f; - scheduler.SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }); - me->ApplySpellImmune(0, IMMUNITY_DISPEL, DISPEL_POISON, true); me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_HEALTH_LEECH, true); me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_POWER_DRAIN, true); diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index f281cae2c..140ef72fe 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -195,6 +195,7 @@ public: npc_escortAI::MoveInLineOfSight(who); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -320,6 +321,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Outland/zone_nagrand.cpp b/src/server/scripts/Outland/zone_nagrand.cpp index 609391185..fbef4231c 100644 --- a/src/server/scripts/Outland/zone_nagrand.cpp +++ b/src/server/scripts/Outland/zone_nagrand.cpp @@ -114,6 +114,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) @@ -340,6 +341,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { switch (waypointId) diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp index 5136e89dd..9c3cf495e 100644 --- a/src/server/scripts/Outland/zone_netherstorm.cpp +++ b/src/server/scripts/Outland/zone_netherstorm.cpp @@ -164,6 +164,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 i) override { Player* player = GetPlayerForEscort(); @@ -626,6 +627,7 @@ public: player->FailQuest(Q_ALMABTRIEB); } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -709,6 +711,7 @@ public: uiTakeTimer = 3000; } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Outland/zone_shattrath_city.cpp b/src/server/scripts/Outland/zone_shattrath_city.cpp index 0cd938e3b..4447b2797 100644 --- a/src/server/scripts/Outland/zone_shattrath_city.cpp +++ b/src/server/scripts/Outland/zone_shattrath_city.cpp @@ -161,6 +161,7 @@ public: public: npc_kservantAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Outland/zone_terokkar_forest.cpp b/src/server/scripts/Outland/zone_terokkar_forest.cpp index 63f623cd5..0d032ce14 100644 --- a/src/server/scripts/Outland/zone_terokkar_forest.cpp +++ b/src/server/scripts/Outland/zone_terokkar_forest.cpp @@ -374,6 +374,7 @@ public: { npc_isla_starmaneAI(Creature* creature) : npc_escortAI(creature) { } + using CreatureAI::WaypointReached; void WaypointReached(uint32 waypointId) override { Player* player = GetPlayerForEscort(); @@ -612,43 +613,6 @@ public: } }; -/*###### -## npc_slim -######*/ - -enum Slim -{ - FACTION_CONSORTIUM = 933 -}; - -class npc_slim : public CreatureScript -{ -public: - npc_slim() : CreatureScript("npc_slim") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsVendor() && player->GetReputationRank(FACTION_CONSORTIUM) >= REP_FRIENDLY) - { - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - SendGossipMenuFor(player, 9896, creature->GetGUID()); - } - else - SendGossipMenuFor(player, 9895, creature->GetGUID()); - - return true; - } -}; - void AddSC_terokkar_forest() { RegisterSpellAndAuraScriptPair(spell_q10930_big_bone_worm, spell_q10930_big_bone_worm_aura); @@ -662,5 +626,4 @@ void AddSC_terokkar_forest() new npc_isla_starmane(); new go_skull_pile(); new go_ancient_skull_pile(); - new npc_slim(); } diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp index db6ef1361..f3d561f37 100644 --- a/src/server/scripts/Pet/pet_dk.cpp +++ b/src/server/scripts/Pet/pet_dk.cpp @@ -70,6 +70,25 @@ struct npc_pet_dk_ebon_gargoyle : ScriptedAI } } + void JustExitedCombat() override + { + EngagementOver(); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (!_EnterEvadeMode()) + return; + + me->ClearUnitState(UNIT_STATE_EVADE); + + if (Unit* owner = me->GetOwner()) + { + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE); + } + } + void InitializeAI() override { ScriptedAI::InitializeAI(); @@ -306,6 +325,8 @@ struct npc_pet_dk_army_of_the_dead : public AggressorAI { npc_pet_dk_army_of_the_dead(Creature* creature) : AggressorAI(creature) { } + // Restrict MoveInLineOfSight aggro to targets already fighting our owner, + // so ghouls don't pull extra packs on their own. bool CanAIAttack(Unit const* target) const override { if (!target) @@ -315,6 +336,26 @@ struct npc_pet_dk_army_of_the_dead : public AggressorAI return false; return AggressorAI::CanAIAttack(target); } + + // Owner started attacking a target — engage immediately. + // We bypass OnOwnerCombatInteraction because CanStartAttack -> CanAIAttack + // may reject the target before combat refs are established. + void OwnerAttacked(Unit* target) override + { + if (!target || !me->IsAlive() || me->HasReactState(REACT_PASSIVE)) + return; + if (me->IsValidAttackTarget(target)) + AttackStart(target); + } + + // Owner was attacked — help defend. + void OwnerAttackedBy(Unit* attacker) override + { + if (!attacker || !me->IsAlive() || me->HasReactState(REACT_PASSIVE)) + return; + if (me->IsValidAttackTarget(attacker)) + AttackStart(attacker); + } }; struct npc_pet_dk_dancing_rune_weapon : public NullCreatureAI diff --git a/src/server/scripts/Pet/pet_hunter.cpp b/src/server/scripts/Pet/pet_hunter.cpp index 239ae9660..d9dff32f5 100644 --- a/src/server/scripts/Pet/pet_hunter.cpp +++ b/src/server/scripts/Pet/pet_hunter.cpp @@ -20,12 +20,15 @@ * Scriptnames of files in this file should be prefixed with "npc_pet_hun_". */ +#include "CombatManager.h" +#include "Containers.h" #include "CreatureScript.h" #include "PetDefines.h" #include "ScriptedCreature.h" #include "SpellAuraEffects.h" #include "SpellScript.h" #include "SpellScriptLoader.h" +#include "TemporarySummon.h" enum HunterSpells { @@ -50,100 +53,94 @@ enum PetSpellsMisc struct npc_pet_hunter_snake_trap : public ScriptedAI { - npc_pet_hunter_snake_trap(Creature* creature) : ScriptedAI(creature) { _init = false; } + npc_pet_hunter_snake_trap(Creature* creature) : ScriptedAI(creature), _isViper(false), _spellTimer(0) { } - void Reset() override + void JustEngagedWith(Unit* /*who*/) override { } + + void InitializeAI() override { - _spellTimer = urand(1500, 3000); + _isViper = me->GetEntry() == NPC_VIPER; - // Start attacking attacker of owner on first ai update after spawn - move in line of sight may choose better target - if (!me->GetVictim()) - if (Unit* tgt = me->SelectNearestTarget(10.0f)) - { - me->AddThreat(tgt, 100000.0f); - AttackStart(tgt); - } + me->SetMaxHealth(uint32(107 * (me->GetLevel() - 40) * 0.025f)); + // Add delta to make them not all hit the same time + me->SetAttackTime(BASE_ATTACK, me->GetAttackTime(BASE_ATTACK) + urandms(0, 6)); + + if (!_isViper && !me->HasAura(SPELL_HUNTER_DEADLY_POISON_PASSIVE)) + DoCast(me, SPELL_HUNTER_DEADLY_POISON_PASSIVE, true); + + // Glyph of Snake Trap — apply AoE damage reduction scaling + if (Unit* owner = me->GetOwner()) + if (owner->GetAuraEffectDummy(SPELL_HUNTER_GLYPH_OF_SNAKE_TRAP)) + me->CastSpell(me, SPELL_HUNTER_PET_SCALING, true); } - void EnterEvadeMode(EvadeReason /*why*/) override - { - // _EnterEvadeMode(); - me->GetThreatMgr().ClearAllThreat(); - me->CombatStop(true); - me->LoadCreaturesAddon(true); - me->SetLootRecipient(nullptr); - me->ResetPlayerDamageReq(); - me->ClearLastLeashExtensionTimePtr(); - - me->AddUnitState(UNIT_STATE_EVADE); - me->GetMotionMaster()->MoveTargetedHome(); - - Reset(); - } - - //Redefined for random target selection: - void MoveInLineOfSight(Unit* who) override - { - if (!me->GetVictim() && who->isTargetableForAttack() && (me->IsHostileTo(who)) && who->isInAccessiblePlaceFor(me)) - { - if (me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) - return; - - if (me->IsWithinDistInMap(who, 10.0f)) - { - me->AddThreat(who, 100000.0f); - AttackStart(who); - } - } - } + // Redefined for random target selection: + void MoveInLineOfSight(Unit* /*who*/) override { } void UpdateAI(uint32 diff) override { + if (me->GetVictim() && me->GetVictim()->HasBreakableByDamageCrowdControlAura(me)) + { + me->GetThreatMgr().ClearFixate(); + me->InterruptNonMeleeSpells(false); + me->AttackStop(); + return; + } + + if (me->IsSummon() && !me->GetThreatMgr().GetFixateTarget()) + { + Unit* summoner = me->ToTempSummon()->GetSummonerUnit(); + if (summoner) + { + std::vector targets; + + auto addTargetIfValid = [this, &targets, summoner](CombatReference* ref) mutable + { + Unit* enemy = ref->GetOther(summoner); + if (!enemy->HasBreakableByDamageCrowdControlAura(me) && me->CanCreatureAttack(enemy) && me->IsWithinDistInMap(enemy, me->GetAttackDistance(enemy))) + targets.push_back(enemy); + }; + + for (auto const& [guid, ref] : summoner->GetCombatManager().GetPvPCombatRefs()) + addTargetIfValid(ref); + + if (targets.empty()) + for (auto const& [guid, ref] : summoner->GetCombatManager().GetPvECombatRefs()) + addTargetIfValid(ref); + + for (Unit* target : targets) + me->EngageWithTarget(target); + + if (!targets.empty()) + { + Unit* target = Acore::Containers::SelectRandomContainerElement(targets); + me->GetThreatMgr().FixateTarget(target); + } + } + } + if (!UpdateVictim()) return; - if (me->GetVictim()->HasBreakableByDamageCrowdControlAura(me)) + // Viper + if (_isViper) { - me->InterruptNonMeleeSpells(false); - return; - } + if (_spellTimer <= diff) + { + if (!urand(0, 2)) // 33% chance to cast + DoCastVictim(RAND(SPELL_HUNTER_MIND_NUMBING_POISON, SPELL_HUNTER_CRIPPLING_POISON)); - if (!_init) - { - _init = true; - - uint32 health = uint32(107 * (me->GetLevel() - 40) * 0.025f); - me->SetCreateHealth(health); - me->SetStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, (float)health); - me->SetMaxHealth(health); - - //Add delta to make them not all hit the same time - uint32 delta = urand(0, 700); - me->SetAttackTime(BASE_ATTACK, me->GetAttackTime(BASE_ATTACK) + delta); - - if (me->GetEntry() == NPC_VENOMOUS_SNAKE) - DoCastSelf(SPELL_HUNTER_DEADLY_POISON_PASSIVE, true); - - // Glyph of Snake Trap - if (Unit* owner = me->GetOwner()) - if (owner->GetAuraEffectDummy(SPELL_HUNTER_GLYPH_OF_SNAKE_TRAP)) - me->CastSpell(me, SPELL_HUNTER_PET_SCALING, true); - } - - _spellTimer += diff; - if (_spellTimer >= 3000) - { - if (urand(0, 2) == 0) // 33% chance to cast - DoCastVictim(RAND(SPELL_HUNTER_MIND_NUMBING_POISON, SPELL_HUNTER_CRIPPLING_POISON)); - - _spellTimer = 0; + _spellTimer = 3000; + } + else + _spellTimer -= diff; } DoMeleeAttackIfReady(); } private: - bool _init; + bool _isViper; uint32 _spellTimer; }; diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index ae2d2c517..442139783 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -16,7 +16,6 @@ */ #include "AreaDefines.h" -#include "CreatureScript.h" #include "PetDefines.h" #include "Player.h" #include "SpellAuraEffects.h" diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index fea4d869e..0670da786 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -16,7 +16,6 @@ */ #include "Containers.h" -#include "CreatureScript.h" #include "GameTime.h" #include "GridNotifiers.h" #include "Player.h" diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 7b72cf252..8dff3bba9 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -23,7 +23,6 @@ #include "Cell.h" #include "CellImpl.h" #include "Chat.h" -#include "CreatureScript.h" #include "GameTime.h" #include "GridNotifiers.h" #include "Group.h" diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index ed9556de2..a7147d328 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -17,7 +17,6 @@ #include "Cell.h" #include "CellImpl.h" -#include "CreatureScript.h" #include "GridNotifiers.h" #include "Pet.h" #include "SpellAuraEffects.h" diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index a94ee8382..dc9340479 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -17,7 +17,6 @@ #include "AreaDefines.h" #include "Battleground.h" -#include "CreatureScript.h" #include "ObjectMgr.h" #include "Pet.h" #include "Player.h" @@ -3565,6 +3564,24 @@ class spell_item_rocket_boots : public SpellScript } }; +// 55001 - Flexweave Underlay (Parachute) +class spell_item_flexweave_underlay : public SpellScript +{ + PrepareSpellScript(spell_item_flexweave_underlay); + + SpellCastResult CheckCast() + { + if (GetCaster()->IsFlying() || GetCaster()->IsFalling()) + return SPELL_CAST_OK; + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_item_flexweave_underlay::CheckCast); + } +}; + class spell_item_healing_injector : public SpellScript { PrepareSpellScript(spell_item_healing_injector); @@ -6327,6 +6344,7 @@ void AddSC_item_spell_scripts() RegisterSpellScript(spell_item_deathbringers_will_normal); RegisterSpellScript(spell_item_deathbringers_will_heroic); RegisterSpellScript(spell_item_discerning_eye_beast_dummy); + RegisterSpellScript(spell_item_flexweave_underlay); RegisterSpellScript(spell_item_frozen_shadoweave); RegisterSpellScript(spell_item_healing_touch_refund); RegisterSpellScript(spell_item_heartpierce); diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index f69a43210..f6d5f240a 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "CreatureScript.h" #include "Pet.h" #include "Player.h" #include "SpellAuraEffects.h" diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index bcd9673e4..4ab3a1611 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "CreatureScript.h" #include "GridNotifiers.h" #include "Player.h" #include "SpellAuraEffects.h" diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index 2ec1a3891..bbf9bb52e 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -16,7 +16,6 @@ */ #include "CellImpl.h" -#include "CreatureScript.h" #include "CreatureTextMgr.h" #include "GridNotifiers.h" #include "MapMgr.h" diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index 5d53b04d0..1d01f3028 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -17,7 +17,6 @@ #include "AreaDefines.h" #include "CellImpl.h" -#include "CreatureScript.h" #include "GameTime.h" #include "GridNotifiers.h" #include "SpellAuraEffects.h" diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index cb7da23d9..1a9c545a5 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "CreatureScript.h" #include "GridNotifiers.h" #include "ObjectAccessor.h" #include "Player.h" diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index b7b09b350..c3410d0da 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -16,7 +16,6 @@ */ #include "AreaDefines.h" -#include "CreatureScript.h" #include "Pet.h" #include "PetDefines.h" #include "Player.h" diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index b6a96008f..b6a4f53e1 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "CreatureScript.h" #include "Player.h" #include "SpellAuraEffects.h" #include "SpellInfo.h" diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 9242761b4..902193daf 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -18,7 +18,6 @@ #include "AreaDefines.h" #include "CellImpl.h" #include "Chat.h" -#include "CreatureScript.h" #include "GameEventMgr.h" #include "GameObjectAI.h" #include "GameObjectScript.h" diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp index 81b2cbb8d..c01767c2d 100644 --- a/src/server/scripts/World/item_scripts.cpp +++ b/src/server/scripts/World/item_scripts.cpp @@ -16,7 +16,6 @@ */ #include "AreaDefines.h" -#include "CreatureScript.h" #include "ItemScript.h" #include "Player.h" #include "ScriptedCreature.h" diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index ba6deb25d..3ddb27669 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -314,7 +314,11 @@ public: struct npc_training_dummy : NullCreatureAI { - npc_training_dummy(Creature* creature) : NullCreatureAI(creature) { } + npc_training_dummy(Creature* creature) : NullCreatureAI(creature) + { + // TODO: Remove once WorldObject casting is ported + me->SetIsCombatDisallowed(false); + } void JustEnteredCombat(Unit* who) override { @@ -359,6 +363,8 @@ struct npc_target_dummy : NullCreatureAI { npc_target_dummy(Creature* creature) : NullCreatureAI(creature) { + // TODO: Remove once WorldObject casting is ported + me->SetIsCombatDisallowed(false); _deathTimer = 15s; } @@ -1356,6 +1362,7 @@ public: } } + using CreatureAI::WaypointReached; void WaypointReached(uint32 /*waypointId*/) override { } diff --git a/src/server/scripts/World/server_mail.cpp b/src/server/scripts/World/server_mail.cpp index 1a8d418a4..3a40f7492 100644 --- a/src/server/scripts/World/server_mail.cpp +++ b/src/server/scripts/World/server_mail.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "CreatureScript.h" #include "Player.h" #include "PlayerScript.h" #include "QueryResult.h" diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index b03796f62..887528f0a 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -420,7 +420,7 @@ enum SpellAttr1 : uint32 SPELL_ATTR1_TOGGLE_FAR_SIGHT = 0x00002000, // TITLE Farsight aura (client only) SPELL_ATTR1_TRACK_TARGET_IN_CHANNEL = 0x00004000, // TITLE Track target while channeling DESCRIPTION While channeling, adjust facing to face target SPELL_ATTR1_IMMUNITY_PURGES_EFFECT = 0x00008000, // TITLE Immunity cancels preapplied auras DESCRIPTION For immunity spells, cancel all auras that this spell would make you immune to when the spell is applied - SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS = 0x00010000, // TITLE Unaffected by school immunities DESCRIPTION Will not pierce Divine Shield, Ice Block and other full invulnerabilities + SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS = 0x00010000, // TITLE Immunity to Hostile & Friendly Effects DESCRIPTION Immunity applied by this aura will also be checked for friendly spells (school immunity only) - used by Cyclone for example to cause friendly spells and healing over time to be immune SPELL_ATTR1_NO_AUTOCAST_AI = 0x00020000, // TITLE Cannot be autocast by pet DESCRIPTION (AI) SPELL_ATTR1_PREVENTS_ANIM = 0x00040000, // TITLE NYI, auras apply UNIT_FLAG_PREVENT_EMOTES_FROM_CHAT_TEXT SPELL_ATTR1_EXCLUDE_CASTER = 0x00080000, // TITLE Cannot be self-cast @@ -1285,7 +1285,7 @@ enum AuraStateType //AURA_STATE_UNKNOWN6 = 6, // | not used AURA_STATE_HUNTER_PARRY = 7, // C | //AURA_STATE_UNKNOWN7 = 7, // c | creature cheap shot / focused bursts spells - //AURA_STATE_UNKNOWN8 = 8, // t| test spells + AURA_STATE_BANISHED = 8, // c t| banished //AURA_STATE_UNKNOWN9 = 9, // | AURA_STATE_WARRIOR_VICTORY_RUSH = 10, // C | warrior victory rush //AURA_STATE_UNKNOWN11 = 11, // C t| 60348 - Maelstrom Ready!, test spells @@ -1342,17 +1342,34 @@ enum Mechanics : uint32 MECHANIC_IMMUNE_SHIELD = 29, // Divine (Blessing) Shield/Protection and Ice Block MECHANIC_SAPPED = 30, MECHANIC_ENRAGED = 31, - MAX_MECHANIC = 32 // SKIP + MECHANIC_WOUNDED = 32, + MECHANIC_INFECTED_2 = 33, + MECHANIC_INFECTED_3 = 34, + MECHANIC_INFECTED_4 = 35, + MECHANIC_TAUNTED = 36, + MAX_MECHANIC = 37 // SKIP }; // Used for spell 42292 Immune Movement Impairment and Loss of Control (0x49967ca6) -#define IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK (\ - (1<speed_run = 1.14286f; _fakeCreatureTemplate->speed_swim = 1.0f; _fakeCreatureTemplate->speed_flight = 1.0f; - _fakeCreatureTemplate->scale = 1.0f; _fakeCreatureTemplate->DamageModifier = 1.0f; _fakeCreatureTemplate->BaseAttackTime = 2000; _fakeCreatureTemplate->RangeAttackTime = 2000; diff --git a/src/test/server/game/Combat/CombatManagerTest.cpp b/src/test/server/game/Combat/CombatManagerTest.cpp index 537d68665..f060a83d6 100644 --- a/src/test/server/game/Combat/CombatManagerTest.cpp +++ b/src/test/server/game/Combat/CombatManagerTest.cpp @@ -42,6 +42,8 @@ protected: ON_CALL(*_worldMock, getIntConfig(_)).WillByDefault(Return(0)); ON_CALL(*_worldMock, getFloatConfig(_)).WillByDefault(Return(1.0f)); ON_CALL(*_worldMock, getBoolConfig(_)).WillByDefault(Return(false)); + static std::string emptyString; + ON_CALL(*_worldMock, GetDataPath()).WillByDefault(ReturnRef(emptyString)); sWorld.reset(_worldMock); diff --git a/src/test/server/game/Combat/SnakeTrapEvadeTest.cpp b/src/test/server/game/Combat/SnakeTrapEvadeTest.cpp new file mode 100644 index 000000000..9bfc3ab98 --- /dev/null +++ b/src/test/server/game/Combat/SnakeTrapEvadeTest.cpp @@ -0,0 +1,197 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +#include "CombatManager.h" +#include "ThreatManager.h" +#include "CreatureAI.h" +#include "DBCStores.h" +#include "TestCreature.h" +#include "TestMap.h" +#include "WorldMock.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace testing; + +namespace +{ + +// ============================================================================ +// Regression test for snake trap evade recursion crash. +// +// The old npc_pet_hunter_snake_trap::EnterEvadeMode called CombatStop(true) +// which triggers ClearInCombat -> EndAllCombat -> CombatReference::EndCombat +// -> JustExitedCombat -> EnterEvadeMode, causing deep recursion and a +// freeze-detector crash during Battleground::EndBattleground. +// +// The fix removes the custom EnterEvadeMode override entirely, using the base +// CreatureAI::EnterEvadeMode which properly guards against recursion via +// UNIT_STATE_EVADE before calling CombatStop. +// +// This test verifies that ending combat on a creature with multiple PvE refs +// does not cause unbounded recursion or leave stale combat state. +// ============================================================================ +class SnakeTrapEvadeTest : public ::testing::Test +{ +protected: + void SetUp() override + { + _previousWorld = std::move(sWorld); + _worldMock = new NiceMock(); + + ON_CALL(*_worldMock, getIntConfig(_)).WillByDefault(Return(0)); + ON_CALL(*_worldMock, getFloatConfig(_)).WillByDefault(Return(1.0f)); + ON_CALL(*_worldMock, getBoolConfig(_)).WillByDefault(Return(false)); + static std::string emptyString; + ON_CALL(*_worldMock, GetDataPath()).WillByDefault(ReturnRef(emptyString)); + + sWorld.reset(_worldMock); + + // Create two mutually hostile factions + auto* factionA = new FactionTemplateEntry{}; + factionA->ID = 90001; + factionA->faction = 90001; + factionA->factionFlags = 0; + factionA->ourMask = 1; + factionA->friendlyMask = 0; + factionA->hostileMask = 2; + for (auto& e : factionA->enemyFaction) e = 0; + for (auto& f : factionA->friendFaction) f = 0; + sFactionTemplateStore.SetEntry(90001, factionA); + + auto* factionB = new FactionTemplateEntry{}; + factionB->ID = 90002; + factionB->faction = 90002; + factionB->factionFlags = 0; + factionB->ourMask = 2; + factionB->friendlyMask = 0; + factionB->hostileMask = 1; + for (auto& e : factionB->enemyFaction) e = 0; + for (auto& f : factionB->friendFaction) f = 0; + sFactionTemplateStore.SetEntry(90002, factionB); + + TestMap::EnsureDBC(); + _map = new TestMap(); + + // Simulate a "snake trap snake" — a creature with multiple combat refs + _snake = new TestCreature(); + _snake->SetupForCombatTest(_map, 1, 19833); // NPC_VENOMOUS_SNAKE entry + _snake->SetFaction(90001); + + // Simulate two enemy targets (e.g., players in a BG) + _targetA = new TestCreature(); + _targetA->SetupForCombatTest(_map, 2, 50001); + _targetA->SetFaction(90002); + + _targetB = new TestCreature(); + _targetB->SetupForCombatTest(_map, 3, 50002); + _targetB->SetFaction(90002); + } + + void TearDown() override + { + _snake->CleanupCombatState(); + _targetA->CleanupCombatState(); + _targetB->CleanupCombatState(); + delete _snake; + delete _targetA; + delete _targetB; + delete _map; + sWorld = std::move(_previousWorld); + } + + std::unique_ptr _previousWorld; + NiceMock* _worldMock = nullptr; + TestMap* _map = nullptr; + TestCreature* _snake = nullptr; + TestCreature* _targetA = nullptr; + TestCreature* _targetB = nullptr; +}; + +// Verify that ending all combat on a creature with multiple refs completes +// without hanging or leaving stale state (regression: recursive EnterEvadeMode) +// cppcheck-suppress syntaxError +TEST_F(SnakeTrapEvadeTest, EndAllCombat_WithMultipleRefs_DoesNotRecurse) +{ + // Put the snake in combat with both targets + _snake->TestGetCombatMgr().SetInCombatWith(_targetA); + _snake->TestGetCombatMgr().SetInCombatWith(_targetB); + + ASSERT_TRUE(_snake->TestGetCombatMgr().HasCombat()); + ASSERT_TRUE(_snake->TestGetCombatMgr().IsInCombatWith(_targetA)); + ASSERT_TRUE(_snake->TestGetCombatMgr().IsInCombatWith(_targetB)); + + // This is the call path that caused the crash: + // EndAllCombat -> EndCombat -> JustExitedCombat -> EnterEvadeMode -> CombatStop -> EndAllCombat + // With the old custom EnterEvadeMode, this would recurse unboundedly. + _snake->TestGetCombatMgr().EndAllPvECombat(); + + // All combat state should be cleanly resolved + EXPECT_FALSE(_snake->TestGetCombatMgr().HasCombat()); + EXPECT_FALSE(_targetA->TestGetCombatMgr().HasCombat()); + EXPECT_FALSE(_targetB->TestGetCombatMgr().HasCombat()); +} + +// Verify that CombatStop on a target also clears the snake's refs cleanly +TEST_F(SnakeTrapEvadeTest, TargetCombatStop_ClearsSnakeRefs) +{ + _snake->TestGetCombatMgr().SetInCombatWith(_targetA); + _snake->TestGetCombatMgr().SetInCombatWith(_targetB); + + ASSERT_TRUE(_snake->TestGetCombatMgr().HasCombat()); + + // Simulate what Battleground::EndBattleground does: CombatStop on the target + // This triggers ClearInCombat -> EndAllCombat on _targetA, which calls + // EndCombat on the ref(targetA, snake), which triggers JustExitedCombat + // on the snake if it's the snake's last ref. + _targetA->TestGetCombatMgr().EndAllPvECombat(); + + // targetA should be out of combat + EXPECT_FALSE(_targetA->TestGetCombatMgr().HasCombat()); + // Snake should still be in combat with targetB + EXPECT_TRUE(_snake->TestGetCombatMgr().HasCombat()); + EXPECT_TRUE(_snake->TestGetCombatMgr().IsInCombatWith(_targetB)); + + // Now end targetB's combat too + _targetB->TestGetCombatMgr().EndAllPvECombat(); + + // Everything clean + EXPECT_FALSE(_snake->TestGetCombatMgr().HasCombat()); + EXPECT_FALSE(_targetB->TestGetCombatMgr().HasCombat()); +} + +// Verify that adding threat during evade is rejected (guards against +// the old Reset() -> AddThreat -> re-enter combat pattern) +TEST_F(SnakeTrapEvadeTest, AddThreat_DuringEvade_IsRejected) +{ + _snake->TestGetCombatMgr().SetInCombatWith(_targetA); + _snake->TestGetThreatMgr().AddThreat(_targetA, 100.0f); + + ASSERT_TRUE(_snake->TestGetThreatMgr().IsThreatenedBy(_targetA)); + + // Enter evade state + _snake->AddUnitState(UNIT_STATE_EVADE); + + // AddThreat should be rejected while in evade + _snake->TestGetThreatMgr().ClearAllThreat(); + _snake->AddThreat(_targetB, 100000.0f); + + // Should NOT have threat on targetB because UNIT_STATE_EVADE blocks AddThreat + EXPECT_FALSE(_snake->TestGetThreatMgr().IsThreatenedBy(_targetB)); + + _snake->ClearUnitState(UNIT_STATE_EVADE); +} + +} // namespace diff --git a/src/test/server/game/Combat/ThreatManagerTest.cpp b/src/test/server/game/Combat/ThreatManagerTest.cpp index 69a708340..af4e48aaf 100644 --- a/src/test/server/game/Combat/ThreatManagerTest.cpp +++ b/src/test/server/game/Combat/ThreatManagerTest.cpp @@ -42,6 +42,8 @@ protected: ON_CALL(*_worldMock, getIntConfig(_)).WillByDefault(Return(0)); ON_CALL(*_worldMock, getFloatConfig(_)).WillByDefault(Return(1.0f)); ON_CALL(*_worldMock, getBoolConfig(_)).WillByDefault(Return(false)); + static std::string emptyString; + ON_CALL(*_worldMock, GetDataPath()).WillByDefault(ReturnRef(emptyString)); sWorld.reset(_worldMock); @@ -2311,4 +2313,247 @@ TEST_F(ThreatManagerIntegrationTest, delete creatureC; } +// ============================================================================ +// TauntUpdate immediate victim reselection tests +// Verify that TauntUpdate() calls UpdateVictim() immediately (no 1-second +// delay), and that rapid/repeated calls do not cause recursion or crashes. +// +// Note: TauntUpdate() reads real auras via GetAuraEffectsByType(), which are +// not present in unit tests. Tests that need taunt state to persist use +// SetTauntStateForTesting() + UpdateVictim() to exercise the same code path +// (ReselectVictim → ProcessAIUpdates) that TauntUpdate now invokes. +// TauntUpdate()-specific tests verify no-crash / no-recursion behaviour. +// ============================================================================ + +TEST_F(ThreatManagerIntegrationTest, + UpdateVictim_ImmediateReselectAfterTauntState) +{ + TestCreature* creatureC = new TestCreature(); + creatureC->SetupForCombatTest(_map, 3, 12347); + creatureC->SetFaction(90002); + + // B has low threat, C has high threat + _creatureA->TestGetThreatMgr().AddThreat(_creatureB, 100.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 500.0f); + _creatureA->TestGetThreatMgr().Update( + ThreatManager::THREAT_UPDATE_INTERVAL); + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + creatureC); + + // Set taunt on B, then call UpdateVictim directly (the call that + // TauntUpdate now makes internally) — WITHOUT waiting for the + // 1-second Update() timer + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + _creatureB, ThreatReference::TAUNT_STATE_TAUNT); + _creatureA->TestGetThreatMgr().UpdateVictimForTesting(); + + // Victim should switch immediately to B (taunted) + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + _creatureB); + + creatureC->CleanupCombatState(); + delete creatureC; +} + +TEST_F(ThreatManagerIntegrationTest, + UpdateVictim_Idempotent_MultipleCalls) +{ + TestCreature* creatureC = new TestCreature(); + creatureC->SetupForCombatTest(_map, 3, 12347); + creatureC->SetFaction(90002); + + _creatureA->TestGetThreatMgr().AddThreat(_creatureB, 100.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 500.0f); + _creatureA->TestGetThreatMgr().Update( + ThreatManager::THREAT_UPDATE_INTERVAL); + + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + _creatureB, ThreatReference::TAUNT_STATE_TAUNT); + + // Call UpdateVictim many times in a row — must be idempotent + for (int i = 0; i < 50; ++i) + _creatureA->TestGetThreatMgr().UpdateVictimForTesting(); + + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + _creatureB); + + creatureC->CleanupCombatState(); + delete creatureC; +} + +TEST_F(ThreatManagerIntegrationTest, + UpdateVictim_RapidTauntToggle_NoCrash) +{ + TestCreature* creatureC = new TestCreature(); + creatureC->SetupForCombatTest(_map, 3, 12347); + creatureC->SetFaction(90002); + + _creatureA->TestGetThreatMgr().AddThreat(_creatureB, 100.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 500.0f); + _creatureA->TestGetThreatMgr().Update( + ThreatManager::THREAT_UPDATE_INTERVAL); + + // Rapidly toggle taunt state with immediate UpdateVictim each time + for (int i = 0; i < 50; ++i) + { + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + _creatureB, ThreatReference::TAUNT_STATE_TAUNT); + _creatureA->TestGetThreatMgr().UpdateVictimForTesting(); + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + _creatureB); + + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + _creatureB, ThreatReference::TAUNT_STATE_NONE); + _creatureA->TestGetThreatMgr().UpdateVictimForTesting(); + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + creatureC); + } + + creatureC->CleanupCombatState(); + delete creatureC; +} + +TEST_F(ThreatManagerIntegrationTest, + UpdateVictim_WithConcurrentThreatChanges_NoCrash) +{ + TestCreature* creatureC = new TestCreature(); + creatureC->SetupForCombatTest(_map, 3, 12347); + creatureC->SetFaction(90002); + + _creatureA->TestGetThreatMgr().AddThreat(_creatureB, 100.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 500.0f); + _creatureA->TestGetThreatMgr().Update( + ThreatManager::THREAT_UPDATE_INTERVAL); + + // Simulate taunt landing while threat is also being modified + // (e.g. damage events arriving in the same server tick) + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + _creatureB, ThreatReference::TAUNT_STATE_TAUNT); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 200.0f); + _creatureA->TestGetThreatMgr().UpdateVictimForTesting(); + + // B should be victim because taunt overrides threat + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + _creatureB); + + creatureC->CleanupCombatState(); + delete creatureC; +} + +TEST_F(ThreatManagerIntegrationTest, + UpdateVictim_MultipleTaunters_HighestStateWins) +{ + TestCreature* creatureC = new TestCreature(); + creatureC->SetupForCombatTest(_map, 3, 12347); + creatureC->SetFaction(90002); + + TestCreature* creatureD = new TestCreature(); + creatureD->SetupForCombatTest(_map, 4, 12348); + creatureD->SetFaction(90002); + + _creatureA->TestGetThreatMgr().AddThreat(_creatureB, 100.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 200.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureD, 300.0f); + _creatureA->TestGetThreatMgr().Update( + ThreatManager::THREAT_UPDATE_INTERVAL); + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + creatureD); + + // Two taunters: B (state=TAUNT) and C (state=TAUNT+1, i.e. "last taunt") + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + _creatureB, ThreatReference::TAUNT_STATE_TAUNT); + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + creatureC, static_cast( + ThreatReference::TAUNT_STATE_TAUNT + 1)); + _creatureA->TestGetThreatMgr().UpdateVictimForTesting(); + + // C has the higher taunt state — should be victim immediately + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + creatureC); + + creatureC->CleanupCombatState(); + creatureD->CleanupCombatState(); + delete creatureC; + delete creatureD; +} + +TEST_F(ThreatManagerIntegrationTest, + UpdateVictim_FixateStillTakesPrecedence) +{ + TestCreature* creatureC = new TestCreature(); + creatureC->SetupForCombatTest(_map, 3, 12347); + creatureC->SetFaction(90002); + + _creatureA->TestGetThreatMgr().AddThreat(_creatureB, 100.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 200.0f); + + // Fixate on C + _creatureA->TestGetThreatMgr().FixateTarget(creatureC); + _creatureA->TestGetThreatMgr().Update( + ThreatManager::THREAT_UPDATE_INTERVAL); + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + creatureC); + + // Taunt from B + immediate UpdateVictim — fixate should still win + _creatureA->TestGetThreatMgr().SetTauntStateForTesting( + _creatureB, ThreatReference::TAUNT_STATE_TAUNT); + _creatureA->TestGetThreatMgr().UpdateVictimForTesting(); + + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + creatureC); + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetFixateTarget(), + creatureC); + + creatureC->CleanupCombatState(); + delete creatureC; +} + +TEST_F(ThreatManagerIntegrationTest, + TauntUpdate_EmptyThreatList_NoCrash) +{ + // TauntUpdate on a creature with no threats should not crash + // even though UpdateVictim is now called internally + _creatureA->TestGetThreatMgr().TauntUpdate(); + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + nullptr); +} + +TEST_F(ThreatManagerIntegrationTest, + TauntUpdate_RapidCalls_NoCrash) +{ + TestCreature* creatureC = new TestCreature(); + creatureC->SetupForCombatTest(_map, 3, 12347); + creatureC->SetFaction(90002); + + _creatureA->TestGetThreatMgr().AddThreat(_creatureB, 100.0f); + _creatureA->TestGetThreatMgr().AddThreat(creatureC, 500.0f); + _creatureA->TestGetThreatMgr().Update( + ThreatManager::THREAT_UPDATE_INTERVAL); + + // Rapidly call TauntUpdate (no real auras — clears taunt state each + // time and calls UpdateVictim). Must not crash or recurse. + for (int i = 0; i < 50; ++i) + _creatureA->TestGetThreatMgr().TauntUpdate(); + + // C remains victim (highest threat, no taunt active) + EXPECT_EQ( + _creatureA->TestGetThreatMgr().GetCurrentVictim(), + creatureC); + + creatureC->CleanupCombatState(); + delete creatureC; +} + } // namespace diff --git a/src/test/server/game/Spells/SpellImmunityTest.cpp b/src/test/server/game/Spells/SpellImmunityTest.cpp new file mode 100644 index 000000000..a88e8c048 --- /dev/null +++ b/src/test/server/game/Spells/SpellImmunityTest.cpp @@ -0,0 +1,366 @@ +/* + * This file is part of the AzerothCore 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 . + */ + +/** + * @file SpellImmunityTest.cpp + * @brief Tests for spell immunity mechanics + */ + +#include "gtest/gtest.h" + +#include +#include +#include "Unit.h" // needed for SpellSchoolMask and mask helper + +namespace +{ + enum EffectType : uint8_t + { + EFFECT_NONE, + EFFECT_SCHOOL_DAMAGE, + EFFECT_HEALTH_LEECH, + EFFECT_POWER_DRAIN, + EFFECT_POWER_BURN, + EFFECT_NORMALIZED_WEAPON_DMG, + EFFECT_WEAPON_PERCENT_DAMAGE, + EFFECT_APPLY_AURA, + EFFECT_DUMMY + }; + + enum AuraType : uint8_t + { + AURA_NONE, + AURA_MOD_DECREASE_SPEED, + AURA_PERIODIC_DAMAGE, + AURA_TRANSFORM, + AURA_MOD_STUN + }; + + struct EffectDesc + { + EffectType effect = EFFECT_NONE; + AuraType aura = AURA_NONE; + }; + + struct SpellDesc + { + std::array effects{}; + }; + + bool IsDamageEffect(EffectType effect) + { + switch (effect) + { + case EFFECT_SCHOOL_DAMAGE: + case EFFECT_HEALTH_LEECH: + case EFFECT_POWER_DRAIN: + case EFFECT_POWER_BURN: + case EFFECT_NORMALIZED_WEAPON_DMG: + case EFFECT_WEAPON_PERCENT_DAMAGE: + return true; + default: + return false; + } + } + + bool HasOnlyDamageEffects(SpellDesc const& spell) + { + bool hasAny = false; + + for (EffectDesc const& e : spell.effects) + { + if (e.effect == EFFECT_NONE) + continue; + + hasAny = true; + if (!IsDamageEffect(e.effect)) + return false; + } + + return hasAny; + } + + // Helper to classify spells which apply a stun aura + bool IsStunSpell(SpellDesc const& spell) + { + for (EffectDesc const& e : spell.effects) + { + if (e.effect == EFFECT_APPLY_AURA && e.aura == AURA_MOD_STUN) + return true; + } + return false; + } + + bool IsEffectBlockedByStunImmunity(EffectDesc const& effect, bool immuneToStun) + { + if (!immuneToStun) + return false; + + return effect.effect == EFFECT_APPLY_AURA && effect.aura == AURA_MOD_STUN; + } + + bool IsFullyImmunedByStunImmunity(SpellDesc const& spell, bool immuneToStun) + { + bool hasAnyEffect = false; + + for (EffectDesc const& effect : spell.effects) + { + if (effect.effect == EFFECT_NONE) + continue; + + hasAnyEffect = true; + if (!IsEffectBlockedByStunImmunity(effect, immuneToStun)) + return false; + } + + return hasAnyEffect; + } + + // The last parameter defaults to false to avoid updating existing tests + // that don't care about mechanic immunities. Bladestorm grants a + // specific mechanic immunity (including stun) that should block + // a spell like Lethargy. + SpellMissInfo ComputeSpellHitResult(bool isImmunedToSpell, bool isImmunedToDamage, + SpellDesc const& spell, bool immuneToStun = false) + { + // Mirrors current core ordering: + // 1) full spell immunity check + // 2) full immunity from mechanic immunity (all effects blocked) + // 3) damage immunity only for damage-only spells + if (isImmunedToSpell) + return SPELL_MISS_IMMUNE; + + if (IsFullyImmunedByStunImmunity(spell, immuneToStun)) + return SPELL_MISS_IMMUNE; + + if (HasOnlyDamageEffects(spell) && isImmunedToDamage) + return SPELL_MISS_IMMUNE; + + return SPELL_MISS_NONE; + } + + struct EffectApplyResult + { + bool damageApplied = false; + bool slowApplied = false; + }; + + EffectApplyResult ApplyEffectsWithMovementImmunity(SpellDesc const& spell, bool immuneToMovementImpairing) + { + EffectApplyResult result; + + for (EffectDesc const& e : spell.effects) + { + if (e.effect == EFFECT_NONE) + continue; + + if (IsDamageEffect(e.effect)) + result.damageApplied = true; + + if (e.effect == EFFECT_APPLY_AURA && e.aura == AURA_MOD_DECREASE_SPEED) + { + if (!immuneToMovementImpairing) + result.slowApplied = true; + } + } + + return result; + } + + SpellDesc MakeDamageOnlySpell() + { + SpellDesc spell; + spell.effects[0] = { EFFECT_SCHOOL_DAMAGE, AURA_NONE }; + return spell; + } + + SpellDesc MakeFrostboltLikeSpell() + { + SpellDesc spell; + spell.effects[0] = { EFFECT_SCHOOL_DAMAGE, AURA_NONE }; + spell.effects[1] = { EFFECT_APPLY_AURA, AURA_MOD_DECREASE_SPEED }; + return spell; + } + + SpellDesc MakeCycloneLikeSpell() + { + SpellDesc spell; + spell.effects[0] = { EFFECT_APPLY_AURA, AURA_TRANSFORM }; + return spell; + } + + SpellDesc MakeSlowOnlySpell() + { + // represents effects such as Frost Trap or Desecration, which only slow + SpellDesc spell; + spell.effects[0] = { EFFECT_APPLY_AURA, AURA_MOD_DECREASE_SPEED }; + return spell; + } +} + +TEST(SpellImmunityTest, HasOnlyDamageEffects_TrueForPureDamage) +{ + SpellDesc spell = MakeDamageOnlySpell(); + EXPECT_TRUE(HasOnlyDamageEffects(spell)); +} + +TEST(SpellImmunityTest, HasOnlyDamageEffects_FalseForDamagePlusAura) +{ + SpellDesc spell = MakeFrostboltLikeSpell(); + EXPECT_FALSE(HasOnlyDamageEffects(spell)); +} + +TEST(SpellImmunityTest, SpellImmunity_BlocksAllSpells) +{ + SpellDesc damageOnly = MakeDamageOnlySpell(); + SpellDesc cycloneLike = MakeCycloneLikeSpell(); + + EXPECT_EQ(ComputeSpellHitResult(true, false, damageOnly), SPELL_MISS_IMMUNE); + EXPECT_EQ(ComputeSpellHitResult(true, false, cycloneLike), SPELL_MISS_IMMUNE); +} + +TEST(SpellImmunityTest, DamageImmunity_BlocksDamageOnlySpell) +{ + SpellDesc damageOnly = MakeDamageOnlySpell(); + + EXPECT_EQ(ComputeSpellHitResult(false, true, damageOnly), SPELL_MISS_IMMUNE); +} + +// Specific case for spell ID 16621 "Self Invulnerability". +// This aura grants immunity to melee/physical damage only. A rogue +// using Sinister Strike (a physical melee attack) should be completely +// blocked by the effect. The test uses the same simplified damage-only +// spell description as above but documents the physical context. +TEST(SpellImmunityTest, SelfInvulnerability_BlocksMeleeDamage) +{ + SpellDesc meleeAttack = MakeDamageOnlySpell(); + // simulate physical damage coming from a rogue melee ability + + EXPECT_EQ(ComputeSpellHitResult(false, true, meleeAttack), SPELL_MISS_IMMUNE); +} + +TEST(SpellImmunityTest, DamageImmunity_DoesNotMissMixedSpell) +{ + // This is the key fix: damage immunity must not force SPELL_MISS_IMMUNE + // for mixed spells that include non-damage effects. + SpellDesc frostboltLike = MakeFrostboltLikeSpell(); + + EXPECT_EQ(ComputeSpellHitResult(false, true, frostboltLike), SPELL_MISS_NONE); +} + +TEST(SpellImmunityTest, HandOfFreedomStyle_MovementImmunity_AllowsDamageBlocksSlow) +{ + SpellDesc frostboltLike = MakeFrostboltLikeSpell(); + + EffectApplyResult result = ApplyEffectsWithMovementImmunity(frostboltLike, true); + + EXPECT_TRUE(result.damageApplied); + EXPECT_FALSE(result.slowApplied); +} + +TEST(SpellImmunityTest, NoMovementImmunity_FrostboltStyle_AppliesDamageAndSlow) +{ + SpellDesc frostboltLike = MakeFrostboltLikeSpell(); + + EffectApplyResult result = ApplyEffectsWithMovementImmunity(frostboltLike, false); + + EXPECT_TRUE(result.damageApplied); + EXPECT_TRUE(result.slowApplied); +} + +TEST(SpellImmunityTest, CycloneLikeSpell_DivineShieldStyle_Immune) +{ + SpellDesc cycloneLike = MakeCycloneLikeSpell(); + + EXPECT_EQ(ComputeSpellHitResult(true, false, cycloneLike), SPELL_MISS_IMMUNE); +} + +// Regression test for issue #10671: +// Divine Shield (full spell immunity) should block purely slowing spells +// such as Hunter Frost Trap or DK Desecration. Previously the effect was +// applied because the core only checked damage-only spells when deciding +// immunity based on damage or spell state. +TEST(SpellImmunityTest, SpellImmunity_BlocksSlowOnlySpell) +{ + SpellDesc slowOnly = MakeSlowOnlySpell(); + EXPECT_EQ(ComputeSpellHitResult(true, false, slowOnly), SPELL_MISS_IMMUNE); +} + +// Ensure that damage-only immunity (e.g. from Hand of Protection) does not +// accidentally prevent slow-only spells. This covers the regression when +// Divine Shield was incorrectly modelled as damage immunity only. +TEST(SpellImmunityTest, DamageImmunity_DoesNotBlockSlowOnlySpell) +{ + SpellDesc slowOnly = MakeSlowOnlySpell(); + EXPECT_EQ(ComputeSpellHitResult(false, true, slowOnly), SPELL_MISS_NONE); +} + +// New coverage for school-mask logic. These exercises the helper used by +// Unit::IsImmunedToDamage to ensure broad masks are handled correctly. +TEST(SpellImmunityTest, ImmunityMask_PartialOverlapDoesNotCount) +{ + SpellSchoolMask immune = SPELL_SCHOOL_MASK_FROST; + SpellSchoolMask checkAll = SPELL_SCHOOL_MASK_ALL; + + // a frost-only immunity should *not* make you immune to all damage + EXPECT_FALSE(Unit::IsImmuneMaskFully(immune, checkAll)); +} + +TEST(SpellImmunityTest, ImmunityMask_FullCoverageAccepted) +{ + SpellSchoolMask immune = SPELL_SCHOOL_MASK_MAGIC; // holy+spell + SpellSchoolMask checkMagic = SPELL_SCHOOL_MASK_MAGIC; + SpellSchoolMask checkSpell = SPELL_SCHOOL_MASK_SPELL; + + EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkMagic)); + EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkSpell)); +} + +TEST(SpellImmunityTest, ImmunityMask_SupersetMatches) +{ + SpellSchoolMask immune = SPELL_SCHOOL_MASK_ALL; + SpellSchoolMask checkMagic = SPELL_SCHOOL_MASK_MAGIC; + + EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkMagic)); +} + +// Bladestorm grants a mechanic immunity mask which includes stuns (e.g. +// Lethargy 69133). The following test mirrors that behaviour by +// modelling a simple spell that applies a stun aura and exercising the +// new `immuneToStun` flag in ComputeSpellHitResult. +TEST(SpellImmunityTest, Bladestorm_ImmuneToStun) +{ + SpellDesc stunSpell; + stunSpell.effects[0] = {EFFECT_APPLY_AURA, AURA_MOD_STUN}; + + // without any special immunity the stun should land + EXPECT_EQ(ComputeSpellHitResult(false, false, stunSpell), SPELL_MISS_NONE); + + // with a bladestorm‑style stun immunity it is blocked + EXPECT_EQ(ComputeSpellHitResult(false, false, stunSpell, true), SPELL_MISS_IMMUNE); +} + +TEST(SpellImmunityTest, StunImmunity_DoesNotFullyBlockMixedSpell) +{ + SpellDesc mixedSpell; + mixedSpell.effects[0] = { EFFECT_SCHOOL_DAMAGE, AURA_NONE }; + mixedSpell.effects[1] = { EFFECT_APPLY_AURA, AURA_MOD_STUN }; + + // This models partial immunity: stun effect is blocked, damage still hits. + EXPECT_TRUE(IsStunSpell(mixedSpell)); + EXPECT_EQ(ComputeSpellHitResult(false, false, mixedSpell, true), SPELL_MISS_NONE); +} diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp index 6fb539b0f..c85a7933e 100644 --- a/src/tools/mmaps_generator/TerrainBuilder.cpp +++ b/src/tools/mmaps_generator/TerrainBuilder.cpp @@ -676,24 +676,20 @@ namespace MMAP /**************************************************************************/ bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData) { - IVMapMgr* vmapMgr = new VMapMgr2(); - int result = vmapMgr->loadMap(m_vmapsPath.c_str(), mapID, tileX, tileY); + std::string const mapFileName = VMapMgr2::getMapFileName(mapID); + std::unique_ptr staticTree = std::make_unique(mapID, m_vmapsPath); + if (!staticTree->InitMap(mapFileName)) + return false; + + staticTree->LoadMapTile(tileX, tileY); + bool retval = false; do { - if (result == VMAP_LOAD_RESULT_ERROR) - break; - - InstanceTreeMap instanceTrees; - ((VMapMgr2*)vmapMgr)->GetInstanceMapTree(instanceTrees); - - if (!instanceTrees[mapID]) - break; - ModelInstance* models = nullptr; uint32 count = 0; - instanceTrees[mapID]->GetModelInstances(models, count); + staticTree->GetModelInstances(models, count); if (!models) break; @@ -829,9 +825,6 @@ namespace MMAP } } while (false); - vmapMgr->unloadMap(mapID, tileX, tileY); - delete vmapMgr; - return retval; }