diff --git a/data/sql/updates/db_world/2022_06_13_05.sql b/data/sql/updates/db_world/2022_06_13_05.sql new file mode 100644 index 000000000..7b6ea1b06 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_13_05.sql @@ -0,0 +1,22 @@ +-- DB update 2022_06_13_04 -> 2022_06_13_05 +-- +DELETE FROM `spell_dbc` WHERE `ID` = 24081; +INSERT INTO `spell_dbc` (`ID`, `Category`, `DispelType`, `Mechanic`, `Attributes`, `AttributesEx`, `AttributesEx2`, `AttributesEx3`, `AttributesEx4`, `AttributesEx5`, `AttributesEx6`, `AttributesEx7`, `ShapeshiftMask`, `unk_320_2`, `ShapeshiftExclude`, `unk_320_3`, `Targets`, `TargetCreatureType`, `RequiresSpellFocus`, `FacingCasterFlags`, `CasterAuraState`, `TargetAuraState`, `ExcludeCasterAuraState`, `ExcludeTargetAuraState`, `CasterAuraSpell`, `TargetAuraSpell`, `ExcludeCasterAuraSpell`, `ExcludeTargetAuraSpell`, `CastingTimeIndex`, `RecoveryTime`, `CategoryRecoveryTime`, `InterruptFlags`, `AuraInterruptFlags`, `ChannelInterruptFlags`, `ProcTypeMask`, `ProcChance`, `ProcCharges`, `MaxLevel`, `BaseLevel`, `SpellLevel`, `DurationIndex`, `PowerType`, `ManaCost`, `ManaCostPerLevel`, `ManaPerSecond`, `ManaPerSecondPerLevel`, `RangeIndex`, `Speed`, `ModalNextSpell`, `CumulativeAura`, `Totem_1`, `Totem_2`, `Reagent_1`, `Reagent_2`, `Reagent_3`, `Reagent_4`, `Reagent_5`, `Reagent_6`, `Reagent_7`, `Reagent_8`, `ReagentCount_1`, `ReagentCount_2`, `ReagentCount_3`, `ReagentCount_4`, `ReagentCount_5`, `ReagentCount_6`, `ReagentCount_7`, `ReagentCount_8`, `EquippedItemClass`, `EquippedItemSubclass`, `EquippedItemInvTypes`, `Effect_1`, `Effect_2`, `Effect_3`, `EffectDieSides_1`, `EffectDieSides_2`, `EffectDieSides_3`, `EffectRealPointsPerLevel_1`, `EffectRealPointsPerLevel_2`, `EffectRealPointsPerLevel_3`, `EffectBasePoints_1`, `EffectBasePoints_2`, `EffectBasePoints_3`, `EffectMechanic_1`, `EffectMechanic_2`, `EffectMechanic_3`, `ImplicitTargetA_1`, `ImplicitTargetA_2`, `ImplicitTargetA_3`, `ImplicitTargetB_1`, `ImplicitTargetB_2`, `ImplicitTargetB_3`, `EffectRadiusIndex_1`, `EffectRadiusIndex_2`, `EffectRadiusIndex_3`, `EffectAura_1`, `EffectAura_2`, `EffectAura_3`, `EffectAuraPeriod_1`, `EffectAuraPeriod_2`, `EffectAuraPeriod_3`, `EffectMultipleValue_1`, `EffectMultipleValue_2`, `EffectMultipleValue_3`, `EffectChainTargets_1`, `EffectChainTargets_2`, `EffectChainTargets_3`, `EffectItemType_1`, `EffectItemType_2`, `EffectItemType_3`, `EffectMiscValue_1`, `EffectMiscValue_2`, `EffectMiscValue_3`, `EffectMiscValueB_1`, `EffectMiscValueB_2`, `EffectMiscValueB_3`, `EffectTriggerSpell_1`, `EffectTriggerSpell_2`, `EffectTriggerSpell_3`, `EffectPointsPerCombo_1`, `EffectPointsPerCombo_2`, `EffectPointsPerCombo_3`, `EffectSpellClassMaskA_1`, `EffectSpellClassMaskA_2`, `EffectSpellClassMaskA_3`, `EffectSpellClassMaskB_1`, `EffectSpellClassMaskB_2`, `EffectSpellClassMaskB_3`, `EffectSpellClassMaskC_1`, `EffectSpellClassMaskC_2`, `EffectSpellClassMaskC_3`, `SpellVisualID_1`, `SpellVisualID_2`, `SpellIconID`, `ActiveIconID`, `SpellPriority`, `Name_Lang_enUS`, `Name_Lang_enGB`, `Name_Lang_koKR`, `Name_Lang_frFR`, `Name_Lang_deDE`, `Name_Lang_enCN`, `Name_Lang_zhCN`, `Name_Lang_enTW`, `Name_Lang_zhTW`, `Name_Lang_esES`, `Name_Lang_esMX`, `Name_Lang_ruRU`, `Name_Lang_ptPT`, `Name_Lang_ptBR`, `Name_Lang_itIT`, `Name_Lang_Unk`, `Name_Lang_Mask`, `NameSubtext_Lang_enUS`, `NameSubtext_Lang_enGB`, `NameSubtext_Lang_koKR`, `NameSubtext_Lang_frFR`, `NameSubtext_Lang_deDE`, `NameSubtext_Lang_enCN`, `NameSubtext_Lang_zhCN`, `NameSubtext_Lang_enTW`, `NameSubtext_Lang_zhTW`, `NameSubtext_Lang_esES`, `NameSubtext_Lang_esMX`, `NameSubtext_Lang_ruRU`, `NameSubtext_Lang_ptPT`, `NameSubtext_Lang_ptBR`, `NameSubtext_Lang_itIT`, `NameSubtext_Lang_Unk`, `NameSubtext_Lang_Mask`, `Description_Lang_enUS`, `Description_Lang_enGB`, `Description_Lang_koKR`, `Description_Lang_frFR`, `Description_Lang_deDE`, `Description_Lang_enCN`, `Description_Lang_zhCN`, `Description_Lang_enTW`, `Description_Lang_zhTW`, `Description_Lang_esES`, `Description_Lang_esMX`, `Description_Lang_ruRU`, `Description_Lang_ptPT`, `Description_Lang_ptBR`, `Description_Lang_itIT`, `Description_Lang_Unk`, `Description_Lang_Mask`, `AuraDescription_Lang_enUS`, `AuraDescription_Lang_enGB`, `AuraDescription_Lang_koKR`, `AuraDescription_Lang_frFR`, `AuraDescription_Lang_deDE`, `AuraDescription_Lang_enCN`, `AuraDescription_Lang_zhCN`, `AuraDescription_Lang_enTW`, `AuraDescription_Lang_zhTW`, `AuraDescription_Lang_esES`, `AuraDescription_Lang_esMX`, `AuraDescription_Lang_ruRU`, `AuraDescription_Lang_ptPT`, `AuraDescription_Lang_ptBR`, `AuraDescription_Lang_itIT`, `AuraDescription_Lang_Unk`, `AuraDescription_Lang_Mask`, `ManaCostPct`, `StartRecoveryCategory`, `StartRecoveryTime`, `MaxTargetLevel`, `SpellClassSet`, `SpellClassMask_1`, `SpellClassMask_2`, `SpellClassMask_3`, `MaxTargets`, `DefenseType`, `PreventionType`, `StanceBarOrder`, `EffectChainAmplitude_1`, `EffectChainAmplitude_2`, `EffectChainAmplitude_3`, `MinFactionID`, `MinReputation`, `RequiredAuraVision`, `RequiredTotemCategoryID_1`, `RequiredTotemCategoryID_2`, `RequiredAreasID`, `SchoolMask`, `RuneCostID`, `SpellMissileID`, `PowerDisplayID`, `EffectBonusMultiplier_1`, `EffectBonusMultiplier_2`, `EffectBonusMultiplier_3`, `SpellDescriptionVariableID`, `SpellDifficultyID`) VALUES +(24081, 0, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 28, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 15041, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Summon Spawn of Mar\'li', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 0, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 0, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 0, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0); + +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_hatch_eggs', 'spell_enveloping_webs', 'spell_marli_transform'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(24083, 'spell_hatch_eggs'), +(24110, 'spell_enveloping_webs'), +(24084, 'spell_marli_transform'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 15041; +DELETE FROM `creature_text` WHERE `CreatureID` = 14510 AND `GroupID` = 4; +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(15041, 0, 0, '%s is fully grown!', 16, 0, 100, 0, 0, 0, 10445, 0, 'Spawn of Mar\'li - EMOTE_FULL_GROWN'), +(14510, 4, 0, 'The brood shall not fall!', 14, 0, 100, 0, 0, 0, 10444, 0, 'Mar\'li - SAY_TRANSFORM_BACK'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` IN (24082, 24083); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 24082, 0, 0, 31, 0, 5, 179985, 0, 0, 0, 0, '', 'Hatch Spider Egg targets Spider Egg'), +(13, 1, 24083, 0, 0, 31, 0, 5, 179985, 0, 0, 0, 0, '', 'Hatch Eggs targets Spider Egg'); diff --git a/data/sql/updates/db_world/2022_06_14_00.sql b/data/sql/updates/db_world/2022_06_14_00.sql new file mode 100644 index 000000000..87f068047 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_00.sql @@ -0,0 +1,5 @@ +-- DB update 2022_06_13_05 -> 2022_06_14_00 +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=24684; +INSERT INTO `spell_script_names` VALUES +(24684,'spell_chain_burn'); diff --git a/data/sql/updates/db_world/2022_06_14_01.sql b/data/sql/updates/db_world/2022_06_14_01.sql new file mode 100644 index 000000000..4e6b9fc9a --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_01.sql @@ -0,0 +1,57 @@ +-- DB update 2022_06_14_00 -> 2022_06_14_01 +-- +ALTER TABLE `gameobject_template_addon` + ADD COLUMN `artkit0` INT NOT NULL DEFAULT 0 AFTER `maxgold`, + ADD COLUMN `artkit1` INT NOT NULL DEFAULT 0 AFTER `artkit0`, + ADD COLUMN `artkit2` INT NOT NULL DEFAULT 0 AFTER `artkit1`, + ADD COLUMN `artkit3` INT NOT NULL DEFAULT 0 AFTER `artkit2`; + +DROP TABLE IF EXISTS `gameobjectartkit_dbc`; + +CREATE TABLE `gameobjectartkit_dbc` +( + `ID` INT NOT NULL DEFAULT 0, + `Texture_1` INT NOT NULL DEFAULT 0, + `Texture_2` INT NOT NULL DEFAULT 0, + `Texture_3` INT NOT NULL DEFAULT 0, + `Attach_Model_1` INT NOT NULL DEFAULT 0, + `Attach_Model_2` INT NOT NULL DEFAULT 0, + `Attach_Model_3` INT NOT NULL DEFAULT 0, + `Attach_Model_4` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`ID`) +) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4; + +-- Note: All of these should be targetable by spells 46904 and 46903, but conditions are only set for Stormwind (damn Horde fanatics) +UPDATE `gameobject_template_addon` SET `artkit0` = 121, `artkit1` = 122 WHERE `entry` IN ( + 188352, -- Flame of Shattrath + 188129, -- Flame of Silvermoon + 188128, -- Flame of the Exodar + 181567, -- Flame of the Wetlands + 181566, -- Flame of Hillsbrad + 181565, -- Flame of Westfall + 181564, -- Flame of Silverpine + 181563, -- Flame of Darkshore + 181562, -- Flame of Stonetalon + 181561, -- Flame of Ashenvale + 181560, -- Flame of the Barrens + 181349, -- Flame of the Scholomance + 181348, -- Flame of Stratholme + 181347, -- Flame of Blackrock Spire + 181346, -- Flame of Dire Maul + 181345, -- Flame of the Hinterlands + 181344, -- Flame of the Blasted Lands + 181343, -- Flame of Un'Goro + 181342, -- Flame of Azshara + 181341, -- Flame of Searing Gorge + 181340, -- Flame of Winterspring + 181339, -- Flame of Silithus + 181338, -- Flame of the Plaguelands + 181337, -- Flame of Thunder Bluff + 181336, -- Flame of Orgrimmar + 181335, -- Flame of the Undercity + 181334, -- Flame of Darnassus + 181333, -- Flame of Ironforge + 181332 -- Flame of Stormwind +); + +DELETE FROM `spell_script_names` WHERE `ScriptName`= "spell_banging_the_gong"; diff --git a/data/sql/updates/db_world/2022_06_14_02.sql b/data/sql/updates/db_world/2022_06_14_02.sql new file mode 100644 index 000000000..0211373bf --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_02.sql @@ -0,0 +1,13 @@ +-- DB update 2022_06_14_01 -> 2022_06_14_02 +-- +UPDATE `spell_target_position` SET `PositionX`=-11688.5, `PositionY`=-1737.74, `PositionZ`=8.409842 WHERE `id` IN (24325, 24593); +DELETE FROM `event_scripts` WHERE `id`=9104; + +DELETE FROM `spell_script_names` WHERE `spell_id`=24325; +INSERT INTO `spell_script_names` VALUES +(24325, 'spell_pagles_point_cast'); + +DELETE FROM `waypoint_data` WHERE `id`=151140; +INSERT INTO `waypoint_data` VALUES +(151140,1,-11697.263,-1759.001,10.364448,0,0,0,0,100,0), +(151140,2,-11689.899,-1776.087,12.593142,6.098,0,0,0,100,0); diff --git a/data/sql/updates/db_world/2022_06_14_03.sql b/data/sql/updates/db_world/2022_06_14_03.sql new file mode 100644 index 000000000..5b7195035 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_03.sql @@ -0,0 +1,3 @@ +-- DB update 2022_06_14_02 -> 2022_06_14_03 +-- Update movement for Tarindralla +UPDATE `creature` SET `wander_distance` = 7, `MovementType` = 1 WHERE `guid` = 47347 AND `id1` = 1992; diff --git a/data/sql/updates/db_world/2022_06_14_04.sql b/data/sql/updates/db_world/2022_06_14_04.sql new file mode 100644 index 000000000..e94883e49 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_04.sql @@ -0,0 +1,6 @@ +-- DB update 2022_06_14_03 -> 2022_06_14_04 +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 1540201) AND (`source_type` = 9) AND (`id` IN (4, 5, 6)); +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`, `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 +(1540201, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 12, 15958, 4, 15000, 0, 0, 0, 8, 0, 0, 0, 0, 8750.1, -7129.7, 35.2976, 3.8041, 'Apprentice Mirveda - Actionlist - Summon Creature \'Gharsul the Remorseless\''), +(1540201, 9, 5, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 12, 15656, 4, 15000, 0, 0, 0, 8, 0, 0, 0, 0, 8753.61, -7133.15, 35, 3.8576, 'Apprentice Mirveda - Actionlist - Summon Creature \'Angershade\''), +(1540201, 9, 6, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 12, 15656, 4, 15000, 0, 0, 0, 8, 0, 0, 0, 0, 8747.14, -7125.71, 35.848, 3.8576, 'Apprentice Mirveda - Actionlist - Summon Creature \'Angershade\''); diff --git a/data/sql/updates/db_world/2022_06_14_05.sql b/data/sql/updates/db_world/2022_06_14_05.sql new file mode 100644 index 000000000..1190456d7 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_05.sql @@ -0,0 +1,16 @@ +-- DB update 2022_06_14_04 -> 2022_06_14_05 +-- +DELETE FROM `creature_text` WHERE `CreatureID`=15111 AND `GroupID` IN (0,1); +INSERT INTO `creature_text` (`CreatureID`, `ID`, `Text`, `Type`, `Probability`, `BroadcastTextId`) VALUES +(15111, 0, 'I gonna make you into mojo!', 12, 100, 10435), +(15111, 1, 'Troll mojo da strongest mojo!', 12, 100, 10437); + +DELETE FROM `creature_template_spell` WHERE (`CreatureID` = 15111) AND (`Index` IN (0, 1)); +INSERT INTO `creature_template_spell` (`CreatureID`, `Index`, `Spell`, `VerifiedBuild`) VALUES +(15111, 0, 24611, 0), +(15111, 1, 24612, 0); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 15111; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 15111) AND (`source_type` = 0) AND (`id` IN (3)); +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`, `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 +(15111, 0, 3, 0, 4, 0, 15, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Mad Servant - On Aggro - Say Line GroupID 0'); diff --git a/data/sql/updates/db_world/2022_06_14_06.sql b/data/sql/updates/db_world/2022_06_14_06.sql new file mode 100644 index 000000000..9852273b7 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_06.sql @@ -0,0 +1,6 @@ +-- DB update 2022_06_14_05 -> 2022_06_14_06 +-- +DELETE FROM `spell_group` WHERE `spell_id` IN (8042,20005); +INSERT INTO `spell_group` VALUES +(1014,8042,0), +(1014,20005,0); diff --git a/data/sql/updates/db_world/2022_06_14_07.sql b/data/sql/updates/db_world/2022_06_14_07.sql new file mode 100644 index 000000000..1de0d55cd --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_07.sql @@ -0,0 +1,5 @@ +-- DB update 2022_06_14_06 -> 2022_06_14_07 +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=29341; +INSERT INTO `spell_script_names` VALUES +(29341,'spell_warl_shadowburn'); diff --git a/data/sql/updates/db_world/2022_06_14_08.sql b/data/sql/updates/db_world/2022_06_14_08.sql new file mode 100644 index 000000000..f31e8fec2 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_14_08.sql @@ -0,0 +1,24 @@ +-- DB update 2022_06_14_07 -> 2022_06_14_08 +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 4966; +DELETE FROM `smart_scripts` WHERE ((`entryorguid` = 496603) AND (`source_type` = 9) AND (`id` IN (0, 1, 2, 3))) OR ((`entryorguid` = 496600) AND (`source_type` = 9) AND (`id` IN (0, 1, 2, 3, 4, 5, 6))) OR ((`entryorguid` = 5184) AND (`source_type` = 0)) OR (`source_type` = 0 AND `entryorguid` = 4966 AND (`id` IN (0, 1, 2, 3, 4, 5, 6, 7))); +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`, `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 +(4966, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - On Respawn - Set Emote State 0'), +(4966, 0, 1, 2, 61, 0, 100, 512, 0, 0, 0, 0, 0, 82, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - On Respawn - Add Npc Flags Questgiver'), +(4966, 0, 2, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - On Respawn - Reset Invincibility Hp'), +(4966, 0, 3, 0, 19, 0, 100, 512, 1324, 0, 0, 0, 0, 80, 496600, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - On Quest \'The Missing Diplomat\' Taken - Run Script'), +(4966, 0, 4, 0, 7, 1, 100, 513, 0, 0, 0, 0, 0, 80, 496603, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - On Evade - Run Script (Phase 1) (No Repeat)'), +(4966, 0, 6, 0, 2, 1, 100, 512, 0, 20, 300, 500, 0, 80, 496601, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Between 0-20% Health - Run Script (Phase 1)'), +(4966, 0, 7, 0, 40, 2, 100, 512, 1, 496600, 0, 0, 0, 80, 496602, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - On Waypoint 1 Reached - Run Script (Phase 2)'), +(496600, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 83, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Remove Npc Flags Questgiver'), +(496600, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Set Invincibility Hp 1'), +(496600, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 2, 168, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Set Faction 168'), +(496600, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 11, 5184, 50, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Set Data 1 1'), +(496600, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Store Targetlist'), +(496600, 9, 5, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Start Attacking'), +(496600, 9, 6, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Set Event Phase 1'), +(5184, 0, 0, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 11, 7165, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Theramore Sentry - On Aggro - Cast \'Battle Stance\''), +(5184, 0, 1, 0, 38, 0, 100, 513, 3, 3, 1, 2, 0, 41, 60000, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Theramore Sentry - On Data Set 3 3 - Despawn In 60000 ms (No Repeat)'), +(496603, 9, 0, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 41, 1, 300, 0, 0, 0, 0, 19, 5184, 50, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Despawn In 1000 ms'), +(496603, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 6, 1324, 0, 0, 0, 0, 0, 12, 1, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Fail Quest \'The Missing Diplomat\''), +(496603, 9, 2, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 41, 1, 300, 0, 0, 0, 0, 19, 5184, 50, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Despawn Instant'), +(496603, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 41, 1, 300, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Private Hendel - Actionlist - Despawn Instant'); diff --git a/data/sql/updates/db_world/2022_06_15_00.sql b/data/sql/updates/db_world/2022_06_15_00.sql new file mode 100644 index 000000000..a241ea2c0 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_15_00.sql @@ -0,0 +1,3 @@ +-- DB update 2022_06_14_08 -> 2022_06_15_00 + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_gen_proc_once_per_cast'; diff --git a/data/sql/updates/db_world/2022_06_15_01.sql b/data/sql/updates/db_world/2022_06_15_01.sql new file mode 100644 index 000000000..7b5773553 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_15_01.sql @@ -0,0 +1,3 @@ +-- DB update 2022_06_15_00 -> 2022_06_15_01 +-- Adjust drop rate of Ritual salve to 72% (based by a sniffer with 325 kills) +UPDATE `creature_loot_template` SET `Chance` = 72 WHERE `Entry` = 2953 AND `Item` = 6634; diff --git a/data/sql/updates/db_world/2022_06_15_02.sql b/data/sql/updates/db_world/2022_06_15_02.sql new file mode 100644 index 000000000..ca8cb88b0 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_15_02.sql @@ -0,0 +1,4 @@ +-- DB update 2022_06_15_01 -> 2022_06_15_02 +DELETE FROM `spell_script_names` WHERE `spell_id` = 24834; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(24834, 'spell_shadow_bolt_whirl'); diff --git a/data/sql/updates/db_world/2022_06_16_00.sql b/data/sql/updates/db_world/2022_06_16_00.sql new file mode 100644 index 000000000..18a97b91c --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_00.sql @@ -0,0 +1,3 @@ +-- DB update 2022_06_15_02 -> 2022_06_16_00 +UPDATE `quest_template_addon` SET `RewardMailTemplateID` = 107 WHERE (`ID` = 5237); +UPDATE `quest_template_addon` SET `RewardMailTemplateID` = 101 WHERE (`ID` = 5238); diff --git a/data/sql/updates/db_world/2022_06_16_01.sql b/data/sql/updates/db_world/2022_06_16_01.sql new file mode 100644 index 000000000..c62e4fe55 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_01.sql @@ -0,0 +1,19 @@ +-- DB update 2022_06_16_00 -> 2022_06_16_01 +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 16332; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 16332); +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`, `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 +(16332, 0, 0, 1, 1, 0, 100, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Out of Combat - Disable Combat Movement'), +(16332, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Out of Combat - Stop Attacking'), +(16332, 0, 2, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 11, 6660, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - On Aggro - Cast \'Shoot\''), +(16332, 0, 3, 4, 9, 0, 100, 0, 5, 30, 2300, 3900, 0, 11, 6660, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 5-30 Range - Cast \'Shoot\''), +(16332, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 40, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 5-30 Range - Set Sheath Ranged'), +(16332, 0, 5, 6, 9, 0, 100, 0, 25, 80, 0, 0, 0, 21, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 25-80 Range - Enable Combat Movement'), +(16332, 0, 6, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 25-80 Range - Start Attacking'), +(16332, 0, 7, 8, 9, 0, 100, 0, 0, 5, 0, 0, 0, 21, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 0-5 Range - Enable Combat Movement'), +(16332, 0, 8, 9, 61, 0, 100, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 0-5 Range - Set Sheath Melee'), +(16332, 0, 9, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 0-5 Range - Start Attacking'), +(16332, 0, 10, 0, 9, 0, 100, 0, 5, 15, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 5-15 Range - Disable Combat Movement'), +(16332, 0, 11, 0, 9, 0, 100, 0, 0, 5, 5000, 9000, 0, 11, 11976, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Within 0-5 Range - Cast \'Strike\''), +(16332, 0, 12, 13, 2, 0, 100, 0, 0, 15, 0, 0, 0, 21, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Between 0-15% Health - Enable Combat Movement'), +(16332, 0, 13, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - Between 0-15% Health - Flee For Assist'), +(16332, 0, 14, 0, 7, 0, 100, 0, 0, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Huntress - On Evade - Set Sheath Melee'); diff --git a/data/sql/updates/db_world/2022_06_16_02.sql b/data/sql/updates/db_world/2022_06_16_02.sql new file mode 100644 index 000000000..bfb747fc4 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_02.sql @@ -0,0 +1,2 @@ +-- DB update 2022_06_16_01 -> 2022_06_16_02 +UPDATE `creature_template` SET `unit_flags` = `unit_flags`&~33554432 WHERE `entry` = 14467; diff --git a/data/sql/updates/db_world/2022_06_16_03.sql b/data/sql/updates/db_world/2022_06_16_03.sql new file mode 100644 index 000000000..ba16a6b88 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_03.sql @@ -0,0 +1,4 @@ +-- DB update 2022_06_16_02 -> 2022_06_16_03 +-- +UPDATE `creature_template` SET `unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|2 WHERE `entry`=13148; + diff --git a/data/sql/updates/db_world/2022_06_16_04.sql b/data/sql/updates/db_world/2022_06_16_04.sql new file mode 100644 index 000000000..3ab4502ef --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_04.sql @@ -0,0 +1,11 @@ +-- DB update 2022_06_16_03 -> 2022_06_16_04 +UPDATE `creature_template` SET `DamageModifier` = 29, `BaseAttackTime` = 1100 WHERE (`entry` = 15114); +UPDATE `creature_template` SET `DamageModifier` = 27, `BaseAttackTime` = 1300 WHERE (`entry` = 15083); +UPDATE `creature_template` SET `DamageModifier` = 24, `BaseAttackTime` = 2000 WHERE (`entry` = 15085); +UPDATE `creature_template` SET `DamageModifier` = 23, `BaseAttackTime` = 1108 WHERE (`entry` = 15084); +UPDATE `creature_template` SET `DamageModifier` = 20 WHERE `entry` IN (14834, 15082); +UPDATE `creature_template` SET `DamageModifier` = 17 WHERE `entry` IN (11382, 11380); +UPDATE `creature_template` SET `DamageModifier` = 15 WHERE `entry` IN (14517, 14515); +UPDATE `creature_template` SET `DamageModifier` = 14 WHERE `entry` IN (14510, 14507); +UPDATE `creature_template` SET `DamageModifier` = 13 WHERE (`entry` = 14509); +UPDATE `creature_template` SET `BaseAttackTime` = 1108 WHERE (`entry` = 15084); diff --git a/data/sql/updates/db_world/2022_06_16_05.sql b/data/sql/updates/db_world/2022_06_16_05.sql new file mode 100644 index 000000000..f6ddd8606 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_05.sql @@ -0,0 +1,10 @@ +-- DB update 2022_06_16_04 -> 2022_06_16_05 +-- +DELETE FROM `game_event_gameobject` WHERE `guid`=28704; +INSERT INTO `game_event_gameobject` VALUES +(27,28704), +(28,28704), +(29,28704), +(30,28704); + +UPDATE `gameobject_template` SET `ScriptName`='go_brazier_of_madness' WHERE `entry`=180327; diff --git a/data/sql/updates/db_world/2022_06_16_06.sql b/data/sql/updates/db_world/2022_06_16_06.sql new file mode 100644 index 000000000..c000d7d3f --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_06.sql @@ -0,0 +1,2 @@ +-- DB update 2022_06_16_05 -> 2022_06_16_06 +UPDATE `creature_template` SET `flags_extra` = `flags_extra`|4194304 WHERE `entry` = 15261; diff --git a/data/sql/updates/db_world/2022_06_16_07.sql b/data/sql/updates/db_world/2022_06_16_07.sql new file mode 100644 index 000000000..bc5b08313 --- /dev/null +++ b/data/sql/updates/db_world/2022_06_16_07.sql @@ -0,0 +1,11 @@ +-- DB update 2022_06_16_06 -> 2022_06_16_07 + +UPDATE `creature_template` SET `AIName`='SmartAI',`flags_extra`=2 WHERE `entry`=14989; +DELETE FROM `smart_scripts` WHERE `entryorguid`= 14989 AND `source_type`= 0 AND `id`= 0; +INSERT INTO `smart_scripts` (`entryorguid`, `event_type`, `event_flags`, `action_type`, `action_param1`, `target_type`, `comment`) VALUES +(14989, 54, 1, 11, 26744, 1, 'Poisonous Cloud - OOC - Cast \'Serverside - Poisonous Blood\''); + +DELETE FROM `smart_scripts` WHERE `entryorguid`= 11357 AND `source_type`= 0 AND `id`= 2; +INSERT INTO `smart_scripts` (`entryorguid`, `id`, `event_type`, `event_flags`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `target_type`, `comment`) VALUES +(11357, 2, 6, 512, 12, 14989, 3, 10000, 1, 'Son of Hakkar - On Just Died - Summon \'Poisonous Cloud\''); + diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 78ae7cb46..61ca3c80a 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -134,14 +134,6 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRange { creature->AddThreat(player, 0.0f); } - - /* Causes certain things to never leave the threat list (Priest Lightwell, etc): - for (Unit::ControlSet::const_iterator itr = player->m_Controlled.begin(); itr != player->m_Controlled.end(); ++itr) - { - creature->SetInCombatWith(*itr); - (*itr)->SetInCombatWith(creature); - creature->AddThreat(*itr, 0.0f); - }*/ } } } @@ -161,7 +153,7 @@ void CreatureAI::MoveInLineOfSight_Safe(Unit* who) void CreatureAI::MoveInLineOfSight(Unit* who) { - if (me->GetVictim()) + if (me->IsEngaged()) return; // pussywizard: civilian, non-combat pet or any other NOT HOSTILE TO ANYONE (!) @@ -182,7 +174,7 @@ void CreatureAI::TriggerAlert(Unit const* who) const if (!who || who->GetTypeId() != TYPEID_PLAYER) return; // If this unit isn't an NPC, is already distracted, is in combat, is confused, stunned or fleeing, do nothing - if (me->GetTypeId() != TYPEID_UNIT || me->IsInCombat() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED)) + if (me->GetTypeId() != TYPEID_UNIT || me->IsEngaged() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED)) return; // Only alert for hostiles! if (me->IsCivilian() || me->HasReactState(REACT_PASSIVE) || !me->IsHostileTo(who) || !me->_IsTargetAcceptable(who)) @@ -253,7 +245,7 @@ void CreatureAI::SetGazeOn(Unit* target) bool CreatureAI::UpdateVictimWithGaze() { - if (!me->IsInCombat()) + if (!me->IsEngaged()) return false; if (me->HasReactState(REACT_PASSIVE)) @@ -271,7 +263,7 @@ bool CreatureAI::UpdateVictimWithGaze() bool CreatureAI::UpdateVictim() { - if (!me->IsInCombat()) + if (!me->IsEngaged()) return false; if (!me->HasReactState(REACT_PASSIVE)) diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 6fac18ba3..f08e5eaf8 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -114,7 +114,7 @@ public: // Called for reaction at stopping attack at no attackers or targets virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER); - // Called for reaction at enter to combat if not in combat yet (enemy can be nullptr) + // Called for reaction when initially engaged virtual void EnterCombat(Unit* /*victim*/) {} // Called when the creature is killed @@ -196,6 +196,7 @@ public: virtual bool CanSeeAlways(WorldObject const* /*obj*/) { return false; } virtual bool CanBeSeen(Player const* /*seer*/) { return true; } + virtual bool CanAlwaysBeDetectable(WorldObject const* /*seer*/) { return false; } virtual void PetStopAttack() { } diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index a0b29741b..85b3c39f5 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -574,7 +574,7 @@ void BossAI::TeleportCheaters() void BossAI::JustSummoned(Creature* summon) { summons.Summon(summon); - if (me->IsInCombat()) + if (me->IsEngaged()) DoZoneInCombat(summon); } diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index fe49a3479..1f8cb7030 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -4044,18 +4044,18 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; case SMART_EVENT_UPDATE_OOC: - if (me && me->IsInCombat()) + if (me && me->IsEngaged()) return; ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; case SMART_EVENT_UPDATE_IC: - if (!me || !me->IsInCombat()) + if (!me || !me->IsEngaged()) return; ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; case SMART_EVENT_HEALTH_PCT: { - if (!me || !me->IsInCombat() || !me->GetMaxHealth()) + if (!me || !me->IsEngaged() || !me->GetMaxHealth()) return; uint32 perc = (uint32)me->GetHealthPct(); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) @@ -4065,7 +4065,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_TARGET_HEALTH_PCT: { - if (!me || !me->IsInCombat() || !me->GetVictim() || !me->GetVictim()->GetMaxHealth()) + if (!me || !me->IsEngaged() || !me->GetVictim() || !me->GetVictim()->GetMaxHealth()) return; uint32 perc = (uint32)me->GetVictim()->GetHealthPct(); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) @@ -4075,7 +4075,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_MANA_PCT: { - if (!me || !me->IsInCombat() || !me->GetMaxPower(POWER_MANA)) + if (!me || !me->IsEngaged() || !me->GetMaxPower(POWER_MANA)) return; uint32 perc = uint32(me->GetPowerPct(POWER_MANA)); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) @@ -4085,7 +4085,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_TARGET_MANA_PCT: { - if (!me || !me->IsInCombat() || !me->GetVictim() || !me->GetVictim()->GetMaxPower(POWER_MANA)) + if (!me || !me->IsEngaged() || !me->GetVictim() || !me->GetVictim()->GetMaxPower(POWER_MANA)) return; uint32 perc = uint32(me->GetVictim()->GetPowerPct(POWER_MANA)); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) @@ -4095,7 +4095,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_RANGE: { - if (!me || !me->IsInCombat() || !me->GetVictim()) + if (!me || !me->IsEngaged() || !me->GetVictim()) return; if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max)) @@ -4106,7 +4106,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_VICTIM_CASTING: { - if (!me || !me->IsInCombat()) + if (!me || !me->IsEngaged()) return; Unit* victim = me->GetVictim(); @@ -4124,7 +4124,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_FRIENDLY_HEALTH: { - if (!me || !me->IsInCombat()) + if (!me || !me->IsEngaged()) return; Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealth.radius, e.event.friendlyHealth.hpDeficit); @@ -4139,7 +4139,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_FRIENDLY_IS_CC: { - if (!me || !me->IsInCombat()) + if (!me || !me->IsEngaged()) return; std::list pList; @@ -4285,7 +4285,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_OOC_LOS: { - if (!me || me->IsInCombat()) + if (!me || me->IsEngaged()) return; //can trigger if closer than fMaxAllowedRange float range = (float)e.event.los.maxDist; @@ -4309,7 +4309,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_IC_LOS: { - if (!me || !me->IsInCombat()) + if (!me || !me->IsEngaged()) return; //can trigger if closer than fMaxAllowedRange float range = (float)e.event.los.maxDist; @@ -4507,7 +4507,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui } case SMART_EVENT_FRIENDLY_HEALTH_PCT: { - if (!me || !me->IsInCombat()) + if (!me || !me->IsEngaged()) return; Unit* target = nullptr; @@ -4701,10 +4701,10 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff) if (e.event.event_phase_mask && !IsInPhase(e.event.event_phase_mask)) return; - if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsInCombat())) + if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsEngaged())) return; - if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsInCombat()))//can be used with me=nullptr (go script) + if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsEngaged()))//can be used with me=nullptr (go script) return; if (e.timer < diff) @@ -4982,7 +4982,7 @@ void SmartScript::OnMoveInLineOfSight(Unit* who) if (!me) return; - ProcessEventsFor(me->IsInCombat() ? SMART_EVENT_IC_LOS : SMART_EVENT_OOC_LOS, who); + ProcessEventsFor(me->IsEngaged() ? SMART_EVENT_IC_LOS : SMART_EVENT_OOC_LOS, who); } /* diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index a705c2761..ccf220a9c 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -86,6 +86,8 @@ static FactionTeamMap sFactionTeamMap; DBCStorage sFactionStore(FactionEntryfmt); DBCStorage sFactionTemplateStore(FactionTemplateEntryfmt); +DBCStorage sGameObjectArtKitStore(GameObjectArtKitfmt); + DBCStorage sGameObjectDisplayInfoStore(GameObjectDisplayInfofmt); DBCStorage sGemPropertiesStore(GemPropertiesEntryfmt); DBCStorage sGlyphPropertiesStore(GlyphPropertiesfmt); @@ -306,6 +308,7 @@ void LoadDBCStores(const std::string& dataPath) LOAD_DBC(sEmotesTextSoundStore, "EmotesTextSound.dbc", "emotetextsound_dbc"); LOAD_DBC(sFactionStore, "Faction.dbc", "faction_dbc"); LOAD_DBC(sFactionTemplateStore, "FactionTemplate.dbc", "factiontemplate_dbc"); + LOAD_DBC(sGameObjectArtKitStore, "GameObjectArtKit.dbc", "gameobjectartkit_dbc"); LOAD_DBC(sGameObjectDisplayInfoStore, "GameObjectDisplayInfo.dbc", "gameobjectdisplayinfo_dbc"); LOAD_DBC(sGemPropertiesStore, "GemProperties.dbc", "gemproperties_dbc"); LOAD_DBC(sGlyphPropertiesStore, "GlyphProperties.dbc", "glyphproperties_dbc"); diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index af2747775..fbb22d64e 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -110,6 +110,7 @@ extern DBCStorage sEmotesTextStore; extern DBCStorage sEmotesTextSoundStore; extern DBCStorage sFactionStore; extern DBCStorage sFactionTemplateStore; +extern DBCStorage sGameObjectArtKitStore; extern DBCStorage sGameObjectDisplayInfoStore; extern DBCStorage sGemPropertiesStore; extern DBCStorage sGlyphPropertiesStore; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 631b1ea76..a81a28418 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -704,7 +704,7 @@ void Creature::Update(uint32 diff) } // periodic check to see if the creature has passed an evade boundary - if (IsAIEnabled && !IsInEvadeMode() && IsInCombat()) + if (IsAIEnabled && !IsInEvadeMode() && IsEngaged()) { if (diff >= m_boundaryCheckTime) { @@ -1810,6 +1810,21 @@ bool Creature::CanAlwaysSee(WorldObject const* obj) const return false; } +bool Creature::IsAlwaysDetectableFor(WorldObject const* seer) const +{ + if (Unit::IsAlwaysDetectableFor(seer)) + { + return true; + } + + if (IsAIEnabled && AI()->CanAlwaysBeDetectable(seer)) + { + return true; + } + + return false; +} + bool Creature::CanStartAttack(Unit const* who) const { if (IsCivilian()) @@ -1839,7 +1854,7 @@ bool Creature::CanStartAttack(Unit const* who) const // pussywizard: at this point we are either hostile to who or friendly to who->getAttackerForHelper() // pussywizard: if who is in combat and has an attacker, help him if the distance is right (help because who is hostile or help because attacker is friendly) bool assist = false; - if (who->IsInCombat() && IsWithinDist(who, ATTACK_DISTANCE)) + if (who->IsEngaged() && IsWithinDist(who, ATTACK_DISTANCE)) if (Unit* victim = who->getAttackerForHelper()) if (IsWithinDistInMap(victim, sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS))) assist = true; @@ -2358,7 +2373,7 @@ bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction / return false; // skip fighting creature - if (IsInCombat()) + if (IsEngaged()) return false; // only free creature @@ -2409,11 +2424,10 @@ bool Creature::_IsTargetAcceptable(Unit const* target) const return false; } - Unit const* myVictim = getAttackerForHelper(); Unit const* targetVictim = target->getAttackerForHelper(); // if I'm already fighting target, or I'm hostile towards the target, the target is acceptable - if (myVictim == target || targetVictim == this || IsHostileTo(target)) + if (IsEngagedBy(target) || IsHostileTo(target)) return true; // if the target's victim is friendly, and the target is neutral, the target is acceptable diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 28561f5a7..fea174f99 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -443,6 +443,7 @@ protected: [[nodiscard]] bool IsInvisibleDueToDespawn() const override; bool CanAlwaysSee(WorldObject const* obj) const override; + bool IsAlwaysDetectableFor(WorldObject const* seer) const override; private: void ForcedDespawn(uint32 timeMSToDespawn = 0, Seconds forcedRespawnTimer = 0s); diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp index 6bfcb211f..daaf52be0 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.cpp +++ b/src/server/game/Entities/Creature/CreatureGroups.cpp @@ -190,7 +190,7 @@ void CreatureGroup::RemoveMember(Creature* member) member->SetFormation(nullptr); } -void CreatureGroup::MemberAttackStart(Creature* member, Unit* target) +void CreatureGroup::MemberEngagingTarget(Creature* member, Unit* target) { uint8 const groupAI = sFormationMgr->CreatureGroupMap[member->GetSpawnId()].groupAI; if (member == m_leader) diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h index 26c4826ef..6cc3157b8 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.h +++ b/src/server/game/Entities/Creature/CreatureGroups.h @@ -106,7 +106,7 @@ public: void FormationReset(bool dismiss, bool initMotionMaster); void LeaderMoveTo(float x, float y, float z, bool run); - void MemberAttackStart(Creature* member, Unit* target); + void MemberEngagingTarget(Creature* member, Unit* target); void MemberEvaded(Creature* member); private: diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index cbff74a00..6f26f7245 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -25,6 +25,7 @@ #include "Object.h" #include "SharedDefines.h" #include "Unit.h" +#include class GameObjectAI; class Transport; @@ -676,6 +677,7 @@ struct GameObjectTemplateAddon uint32 flags; uint32 mingold; uint32 maxgold; + std::array artKits = {}; }; // Benchmarked: Faster than std::map (insert/find) @@ -736,6 +738,35 @@ enum GOState #define MAX_GO_STATE 3 +enum class GameObjectActions : uint32 +{ + // Name from client executable // Comments + None, // -NONE- + AnimateCustom0, // Animate Custom0 + AnimateCustom1, // Animate Custom1 + AnimateCustom2, // Animate Custom2 + AnimateCustom3, // Animate Custom3 + Disturb, // Disturb // Triggers trap + Unlock, // Unlock // Resets GO_FLAG_LOCKED + Lock, // Lock // Sets GO_FLAG_LOCKED + Open, // Open // Sets GO_STATE_ACTIVE + OpenAndUnlock, // Open + Unlock // Sets GO_STATE_ACTIVE and resets GO_FLAG_LOCKED + Close, // Close // Sets GO_STATE_READY + ToggleOpen, // Toggle Open + Destroy, // Destroy // Sets GO_STATE_DESTROYED + Rebuild, // Rebuild // Resets from GO_STATE_DESTROYED + Creation, // Creation + Despawn, // Despawn + MakeInert, // Make Inert // Disables interactions + MakeActive, // Make Active // Enables interactions + CloseAndLock, // Close + Lock // Sets GO_STATE_READY and sets GO_FLAG_LOCKED + UseArtKit0, // Use ArtKit0 // 46904: 121 + UseArtKit1, // Use ArtKit1 // 36639: 81, 46903: 122 + UseArtKit2, // Use ArtKit2 + UseArtKit3, // Use ArtKit3 + SetTapList, // Set Tap List +}; + // from `gameobject` struct GameObjectData { diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index b41974cc5..d38619932 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -85,6 +85,8 @@ void Totem::InitStats(uint32 duration) void Totem::InitSummon() { + Minion::InitSummon(); + if (m_type == TOTEM_PASSIVE && GetSpell()) CastSpell(this, GetSpell(), true); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 2be2a68d1..bbc83ebc0 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -316,6 +316,8 @@ Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), _oldFactionId = 0; + _isWalkingBeforeCharm = false; + _lastExtraAttackSpell = 0; } @@ -10482,8 +10484,12 @@ void Unit::SetCharm(Unit* charm, bool apply) if (!charm->AddGuidValue(UNIT_FIELD_CHARMEDBY, GetGUID())) LOG_FATAL("entities.unit", "Unit {} is being charmed, but it already has a charmer {}", charm->GetEntry(), charm->GetCharmerGUID().ToString()); - if (charm->HasUnitMovementFlag(MOVEMENTFLAG_WALKING)) + _isWalkingBeforeCharm = charm->IsWalking(); + if (_isWalkingBeforeCharm) + { charm->SetWalk(false); + charm->SendMovementFlagUpdate(); + } m_Controlled.insert(charm); } @@ -10521,6 +10527,12 @@ void Unit::SetCharm(Unit* charm, bool apply) charm->SetByteValue(UNIT_FIELD_BYTES_2, 1, 0); } + if (charm->IsWalking() != _isWalkingBeforeCharm) + { + charm->SetWalk(_isWalkingBeforeCharm); + charm->SendMovementFlagUpdate(true); // send packet to self, to update movement state on player. + } + m_Controlled.erase(charm); } } @@ -13168,7 +13180,7 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy, uint32 duration) creature->AI()->EnterCombat(enemy); if (creature->GetFormation()) - creature->GetFormation()->MemberAttackStart(creature, enemy); + creature->GetFormation()->MemberEngagingTarget(creature, enemy); } creature->RefreshSwimmingFlag(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 896fa4e70..7af6d71e4 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1326,6 +1326,7 @@ public: bool IsWithinCombatRange(Unit const* obj, float dist2compare) const; bool IsWithinMeleeRange(Unit const* obj, float dist = 0.f) const; float GetMeleeRange(Unit const* target) const; + [[nodiscard]] virtual SpellSchoolMask GetMeleeDamageSchoolMask() const; bool GetRandomContactPoint(Unit const* target, float& x, float& y, float& z, bool force = false) const; uint32 m_extraAttacks; bool m_canDualWield; @@ -1343,6 +1344,9 @@ public: if (GetVictim() != nullptr) return GetVictim(); + if (!IsEngaged()) + return nullptr; + if (!m_attackers.empty()) return *(m_attackers.begin()); @@ -1644,6 +1648,9 @@ public: [[nodiscard]] bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); } + bool IsEngaged() const { return IsInCombat(); } + bool IsEngagedBy(Unit const* who) const { return IsInCombatWith(who); } + [[nodiscard]] bool IsInCombat() const { return HasUnitFlag(UNIT_FLAG_IN_COMBAT); } bool IsInCombatWith(Unit const* who) const; @@ -2105,6 +2112,7 @@ public: void TauntApply(Unit* victim); void TauntFadeOut(Unit* taunter); ThreatMgr& GetThreatMgr() { return m_ThreatMgr; } + ThreatMgr const& GetThreatMgr() const { return m_ThreatMgr; } void addHatedBy(HostileReference* pHostileReference) { m_HostileRefMgr.insertFirst(pHostileReference); }; void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ } HostileRefMgr& getHostileRefMgr() { return m_HostileRefMgr; } @@ -2478,8 +2486,6 @@ protected: CharmInfo* m_charmInfo; SharedVisionList m_sharedVision; - [[nodiscard]] virtual SpellSchoolMask GetMeleeDamageSchoolMask() const; - MotionMaster* i_motionMaster; uint32 m_reactiveTimer[MAX_REACTIVE]; @@ -2549,6 +2555,7 @@ private: bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world uint32 _oldFactionId; ///< faction before charm + bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed? [[nodiscard]] float processDummyAuras(float TakenTotalMod) const; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 32eda9fe7..81161064b 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7183,8 +7183,8 @@ void ObjectMgr::LoadGameObjectTemplateAddons() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 - QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold FROM gameobject_template_addon"); + // 0 1 2 3 4 5 6 7 8 + QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3 FROM gameobject_template_addon"); if (!result) { @@ -7215,6 +7215,21 @@ void ObjectMgr::LoadGameObjectTemplateAddons() gameObjectAddon.mingold = fields[3].Get(); gameObjectAddon.maxgold = fields[4].Get(); + for (uint32 i = 0; i < gameObjectAddon.artKits.size(); i++) + { + uint32 artKitID = fields[5 + i].Get(); + if (!artKitID) + continue; + + if (!sGameObjectArtKitStore.LookupEntry(artKitID)) + { + LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid `artkit{}` {} defined, set to zero instead.", entry, i, artKitID); + continue; + } + + gameObjectAddon.artKits[i] = artKitID; + } + // checks if (gameObjectAddon.faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.faction)) LOG_ERROR("sql.sql", diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 2fb4afd0b..c329395ec 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2677,7 +2677,7 @@ void Spell::EffectDistract(SpellEffIndex /*effIndex*/) return; // Check for possible target - if (!unitTarget || unitTarget->IsInCombat()) + if (!unitTarget || unitTarget->IsEngaged()) return; // target must be OK to do this @@ -4263,7 +4263,7 @@ void Spell::EffectSummonPlayer(SpellEffIndex /*effIndex*/) player->GetSession()->SendPacket(&data); } -void Spell::EffectActivateObject(SpellEffIndex /*effIndex*/) +void Spell::EffectActivateObject(SpellEffIndex effIndex) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; @@ -4271,17 +4271,74 @@ void Spell::EffectActivateObject(SpellEffIndex /*effIndex*/) if (!gameObjTarget) return; - Player* player = m_caster->GetTypeId() == TYPEID_PLAYER ? m_caster->ToPlayer() : m_caster->GetCharmerOrOwnerPlayerOrPlayerItself(); - gameObjTarget->Use(player ? player : m_caster); + GameObjectActions action = GameObjectActions(m_spellInfo->Effects[effIndex].MiscValue); + switch (action) + { + case GameObjectActions::AnimateCustom0: + case GameObjectActions::AnimateCustom1: + case GameObjectActions::AnimateCustom2: + case GameObjectActions::AnimateCustom3: + gameObjTarget->SendCustomAnim(uint32(action) - uint32(GameObjectActions::AnimateCustom0)); + break; + case GameObjectActions::Disturb: // What's the difference with Open? + case GameObjectActions::Open: + if (Unit* unitCaster = m_caster->ToUnit()) + gameObjTarget->Use(unitCaster); + break; + case GameObjectActions::OpenAndUnlock: + if (Unit* unitCaster = m_caster->ToUnit()) + gameObjTarget->UseDoorOrButton(0, false, unitCaster); + [[fallthrough]]; + case GameObjectActions::Unlock: + case GameObjectActions::Lock: + gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED, action == GameObjectActions::Lock); + break; + case GameObjectActions::Close: + case GameObjectActions::Rebuild: + gameObjTarget->ResetDoorOrButton(); + break; + case GameObjectActions::Despawn: + gameObjTarget->DespawnOrUnsummon(); + break; + case GameObjectActions::MakeInert: + case GameObjectActions::MakeActive: + gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE, action == GameObjectActions::MakeInert); + break; + case GameObjectActions::CloseAndLock: + gameObjTarget->ResetDoorOrButton(); + gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + break; + case GameObjectActions::Destroy: + if (Unit* unitCaster = m_caster->ToUnit()) + gameObjTarget->UseDoorOrButton(0, true, unitCaster); + break; + case GameObjectActions::UseArtKit0: + case GameObjectActions::UseArtKit1: + case GameObjectActions::UseArtKit2: + case GameObjectActions::UseArtKit3: + { + GameObjectTemplateAddon const* templateAddon = gameObjTarget->GetTemplateAddon(); - //ScriptInfo activateCommand; - //activateCommand.command = SCRIPT_COMMAND_ACTIVATE_OBJECT; + uint32 artKitIndex = uint32(action) - uint32(GameObjectActions::UseArtKit0); - // int32 unk = m_spellInfo->Effects[effIndex].MiscValue; // This is set for EffectActivateObject spells; needs research + uint32 artKitValue = 0; + if (templateAddon) + artKitValue = templateAddon->artKits[artKitIndex]; - // xinef: pass player to allow gossip scripts to work - // - //gameObjTarget->GetMap()->ScriptCommandStart(activateCommand, 0, player ? player : m_caster, gameObjTarget); + if (artKitValue == 0) + LOG_ERROR("sql.sql", "GameObject {} hit by spell {} needs `artkit{}` in `gameobject_template_addon`", gameObjTarget->GetEntry(), m_spellInfo->Id, artKitIndex); + else + gameObjTarget->SetGoArtKit(artKitValue); + + break; + } + case GameObjectActions::None: + LOG_FATAL("spell", "Spell {} has action type NONE in effect {}", m_spellInfo->Id, int32(effIndex)); + break; + default: + LOG_ERROR("spell", "Spell {} has unhandled action {} in effect {}", m_spellInfo->Id, int32(action), int32(effIndex)); + break; + } } void Spell::EffectApplyGlyph(SpellEffIndex effIndex) diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp index 32b16bb08..43796d624 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp @@ -339,7 +339,7 @@ struct boss_priestess_lackey_commonAI : public ScriptedAI void EnterCombat(Unit* who) override { if (Creature* delrissa = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_DELRISSA))) - if (delrissa->IsAlive() && !delrissa->IsInCombat()) + if (delrissa->IsAlive() && !delrissa->IsEngaged()) delrissa->AI()->AttackStart(who); events.ScheduleEvent(EVENT_SPELL_HELPER_HEALING_POTION, 1000); diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp index aa883bb10..bd02ff14a 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp @@ -783,37 +783,9 @@ public: } }; -class spell_banging_the_gong : public SpellScriptLoader -{ -public: - spell_banging_the_gong() : SpellScriptLoader("spell_banging_the_gong") { } - - class spell_banging_the_gong_SpellScript : public SpellScript - { - PrepareSpellScript(spell_banging_the_gong_SpellScript); - - void Activate(SpellEffIndex index) - { - PreventHitDefaultEffect(index); - GetHitGObj()->SendCustomAnim(0); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_banging_the_gong_SpellScript::Activate, EFFECT_1, SPELL_EFFECT_ACTIVATE_OBJECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_banging_the_gong_SpellScript(); - } -}; - void AddSC_zulaman() { new npc_forest_frog(); new npc_zulaman_hostage(); new npc_harrison_jones(); - new spell_banging_the_gong(); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp index 86e4c473d..4cf8348d0 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp @@ -32,7 +32,8 @@ enum Spells SPELL_FROSTBREATH = 16099, SPELL_MASSIVEGEYSER = 22421, SPELL_SLAM = 24326, - SPELL_THRASH = 3417 // Triggers 3391 + SPELL_THRASH = 3417, // Triggers 3391 + SPELL_SPLASH = 24593 }; enum Events @@ -42,6 +43,11 @@ enum Events EVENT_SLAM = 3 }; +enum Misc +{ + GAMEOBJECT_MUDSKUNK_LURE = 180346 +}; + class boss_gahzranka : public CreatureScript { public: @@ -51,6 +57,11 @@ public: { boss_gahzrankaAI(Creature* creature) : BossAI(creature, DATA_GAHZRANKA) { } + void IsSummonedBy(Unit* /*summoner*/) override + { + me->GetMotionMaster()->MovePath(me->GetEntry() * 10, false); + } + void Reset() override { _Reset(); @@ -147,8 +158,45 @@ private: bool _wipeThreat = false; }; +class spell_pagles_point_cast : public SpellScript +{ + PrepareSpellScript(spell_pagles_point_cast); + + void OnEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + if (InstanceScript* instanceScript = caster->GetInstanceScript()) + { + if (!instanceScript->GetData(DATA_GAHZRANKA)) + { + caster->m_Events.AddEventAtOffset([caster]() + { + if (GameObject* lure = caster->SummonGameObject(GAMEOBJECT_MUDSKUNK_LURE, -11688.5f, -1737.74f, 10.409842f, 1.f, 0.f, 0.f, 0.f, 0.f, 30 * IN_MILLISECONDS)) + { + caster->m_Events.AddEventAtOffset([caster, lure]() + { + if (lure) + lure->DespawnOrUnsummon(); + caster->CastSpell(caster, SPELL_SPLASH, true); + caster->SummonCreature(NPC_GAHZRANKA, -11688.5f, -1723.74f, -5.78f, 0.f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5 * DAY * IN_MILLISECONDS); + }, 5s); + } + }, 2s); + } + } + } + } + + void Register() override + { + OnEffectLaunch += SpellEffectFn(spell_pagles_point_cast::OnEffect, EFFECT_1, SPELL_EFFECT_SEND_EVENT); + } +}; + void AddSC_boss_gahzranka() { new boss_gahzranka(); RegisterSpellScript(spell_gahzranka_slam); + RegisterSpellScript(spell_pagles_point_cast); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp index 136cd18be..d475bd966 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp @@ -24,19 +24,25 @@ EndScriptData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "zulgurub.h" enum Spells { - SPELL_MANABURN = 26046, - SPELL_SLEEP = 24664 + SPELL_SLEEP = 24664, + SPELL_EARTH_SHOCK = 24685, + SPELL_CHAIN_BURN = 24684, + SPELL_SUMMON_NIGHTMARE_ILLUSION_LEFT = 24681, + SPELL_SUMMON_NIGHTMARE_ILLUSION_BACK = 24728, + SPELL_SUMMON_NIGHTMARE_ILLUSION_RIGHT = 24729 }; enum Events { - EVENT_MANABURN = 1, - EVENT_SLEEP = 2, - EVENT_ILLUSIONS = 3 + EVENT_SLEEP = 1, + EVENT_EARTH_SHOCK = 2, + EVENT_CHAIN_BURN = 3, + EVENT_ILLUSIONS = 4 }; class boss_hazzarah : public CreatureScript @@ -48,22 +54,42 @@ public: { boss_hazzarahAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) { } - void Reset() override + void JustSummoned(Creature* summon) override { - _Reset(); - } + summons.Summon(summon); - void JustDied(Unit* /*killer*/) override - { - _JustDied(); + summon->SetCorpseDelay(10); + summon->SetReactState(REACT_PASSIVE); + summon->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE); + summon->SetVisible(false); + summon->m_Events.AddEventAtOffset([summon]() + { + summon->SetVisible(true); + }, 2s); + + summon->m_Events.AddEventAtOffset([summon]() + { + summon->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); + summon->SetReactState(REACT_AGGRESSIVE); + summon->SetInCombatWithZone(); + }, 3500ms); } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - events.ScheduleEvent(EVENT_MANABURN, urand(4000, 10000)); - events.ScheduleEvent(EVENT_SLEEP, urand(10000, 18000)); - events.ScheduleEvent(EVENT_ILLUSIONS, urand(10000, 18000)); + events.ScheduleEvent(EVENT_SLEEP, 12s, 15s); + events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s); + events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s); + events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s); + } + + bool CanAIAttack(Unit const* target) const override + { + if (me->GetThreatMgr().getThreatList().size() > 1 && me->GetThreatMgr().getOnlineContainer().getMostHated()->getTarget() == target) + return !target->HasAura(SPELL_SLEEP); + + return true; } void UpdateAI(uint32 diff) override @@ -80,27 +106,26 @@ public: { switch (eventId) { - case EVENT_MANABURN: - DoCastVictim(SPELL_MANABURN, true); - events.ScheduleEvent(EVENT_MANABURN, urand(8000, 16000)); - break; case EVENT_SLEEP: DoCastVictim(SPELL_SLEEP, true); - events.ScheduleEvent(EVENT_SLEEP, urand(12000, 20000)); + events.ScheduleEvent(EVENT_SLEEP, 24s, 32s); + return; + case EVENT_EARTH_SHOCK: + DoCastVictim(SPELL_EARTH_SHOCK); + events.ScheduleEvent(EVENT_EARTH_SHOCK, 8s, 18s); + break; + case EVENT_CHAIN_BURN: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, [&](Unit* u) { return u && !u->IsPet() && u->getPowerType() == POWER_MANA; })) + { + DoCast(target, SPELL_CHAIN_BURN, false); + } + events.ScheduleEvent(EVENT_CHAIN_BURN, 12s, 28s); break; case EVENT_ILLUSIONS: - // We will summon 3 illusions that will spawn on a random gamer and attack this gamer - // We will just use one model for the beginning - for (uint8 i = 0; i < 3; ++i) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - Creature* Illusion = me->SummonCreature(NPC_NIGHTMARE_ILLUSION, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000); - if (Illusion) - Illusion->AI()->AttackStart(target); - } - } - events.ScheduleEvent(EVENT_ILLUSIONS, urand(15000, 25000)); + DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_LEFT, true); + DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_BACK, true); + DoCastSelf(SPELL_SUMMON_NIGHTMARE_ILLUSION_RIGHT, true); + events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s); break; default: break; @@ -117,7 +142,28 @@ public: } }; +class spell_chain_burn : public SpellScript +{ + PrepareSpellScript(spell_chain_burn); + + void FilterTargets(std::list& targets) + { + Unit* caster = GetCaster(); + targets.remove_if([caster](WorldObject* target) -> bool + { + Unit* unit = target->ToUnit(); + return !unit || unit->getPowerType() != POWER_MANA || caster->GetVictim() == unit; + }); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_chain_burn::FilterTargets, EFFECT_0, TARGET_UNIT_TARGET_ENEMY); + } +}; + void AddSC_boss_hazzarah() { new boss_hazzarah(); + RegisterSpellScript(spell_chain_burn); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp index 2eb62367a..0b912aef1 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp @@ -86,6 +86,23 @@ public: Talk(SAY_AGGRO); } + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); + + switch (summon->GetEntry()) + { + case NPC_BRAIN_WASH_TOTEM: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1)) + { + summon->CastSpell(target, summon->m_spells[0], true); + } + break; + default: + break; + } + } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp index 140c99c23..6748979a8 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp @@ -15,45 +15,72 @@ * with this program. If not, see . */ -/* ScriptData -SDName: Boss_Marli -SD%Complete: 80 -SDComment: Charging healers and casters not working. Perhaps wrong Spell Timers. -SDCategory: Zul'Gurub -EndScriptData */ - +#include "GameObjectAI.h" +#include "SpellScript.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "TaskScheduler.h" #include "zulgurub.h" enum Says { + // Mar'li SAY_AGGRO = 0, SAY_TRANSFORM = 1, SAY_SPIDER_SPAWN = 2, - SAY_DEATH = 3 + SAY_DEATH = 3, + SAY_TRANSFORM_BACK = 4, + + // Spawn of Mar'li + EMOTE_FULL_GROWN = 0 }; enum Spells { + // Spider form SPELL_CHARGE = 22911, - SPELL_ASPECT_OF_MARLI = 24686, // A stun spell - SPELL_ENVOLWINGWEB = 24110, + SPELL_ENVELOPING_WEB = 24110, + SPELL_CORROSIVE_POISON = 24111, + SPELL_POISON_SHOCK = 24112, + + //Troll form SPELL_POISON_VOLLEY = 24099, + SPELL_DRAIN_LIFE = 24300, + SPELL_ENLARGE = 24109, + SPELL_SPIDER_EGGS = 24082, + + // All SPELL_SPIDER_FORM = 24084, - // The Spider Spell - SPELL_LEVELUP = 24312 // Not right Spell. + SPELL_TRANSFORM_BACK = 24085, + SPELL_THRASH = 3391, + SPELL_HATCH_SPIDER_EGG = 24082, + SPELL_HATCH_EGGS = 24083, + + // Spawn of Mar'li + SPELL_GROWTH = 24086, + SPELL_FULL_GROWN = 24088 }; enum Events { - EVENT_SPAWN_START_SPIDERS = 1, // Phase 1 - EVENT_POISON_VOLLEY = 2, // Phase All - EVENT_SPAWN_SPIDER = 3, // Phase All - EVENT_CHARGE_PLAYER = 4, // Phase 3 - EVENT_ASPECT_OF_MARLI = 5, // Phase 2 - EVENT_TRANSFORM = 6, // Phase 2 - EVENT_TRANSFORM_BACK = 7 // Phase 3 + // Spider form + EVENT_CHARGE_PLAYER = 7, + EVENT_ENVELOPING_WEB = 8, + EVENT_CORROSIVE_POISON = 9, + EVENT_POISON_SHOCK = 10, + + // Troll form + EVENT_POISON_VOLLEY = 11, + EVENT_DRAIN_LIFE = 12, + EVENT_ENLARGE = 13, + + // All + EVENT_SPAWN_START_SPIDERS = 1, + EVENT_TRANSFORM = 2, + EVENT_TRANSFORM_BACK = 3, + EVENT_HATCH_SPIDER_EGG = 4, + EVENT_THRASH = 5, + EVENT_TALK_FIRST_SPIDERS = 6, }; enum Phases @@ -63,208 +90,269 @@ enum Phases PHASE_THREE = 3 }; -class boss_marli : public CreatureScript +enum Misc { -public: - boss_marli() : CreatureScript("boss_marli") { } + GO_SPIDER_EGGS = 179985, +}; - struct boss_marliAI : public BossAI +struct boss_marli : public BossAI +{ + boss_marli(Creature* creature) : BossAI(creature, DATA_MARLI) { } + + void Reset() override { - boss_marliAI(Creature* creature) : BossAI(creature, DATA_MARLI) { } + if (events.IsInPhase(PHASE_THREE)) + me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack - void Reset() override + std::list eggs; + me->GetGameObjectListWithEntryInGrid(eggs, GO_SPIDER_EGGS, DEFAULT_VISIBILITY_INSTANCE); + + for (auto const& egg : eggs) { - if (events.IsInPhase(PHASE_THREE)) - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack - _Reset(); + egg->Respawn(); + egg->UpdateObjectVisibility(); } - void JustDied(Unit* /*killer*/) override + BossAI::Reset(); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); + events.ScheduleEvent(EVENT_SPAWN_START_SPIDERS, 1000, 0, PHASE_ONE); + Talk(SAY_AGGRO); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - _JustDied(); - Talk(SAY_DEATH); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - events.ScheduleEvent(EVENT_SPAWN_START_SPIDERS, 1000, 0, PHASE_ONE); - Talk(SAY_AGGRO); - } - - 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_SPAWN_START_SPIDERS: + events.ScheduleEvent(EVENT_TALK_FIRST_SPIDERS, 500, 0, PHASE_TWO); + DoCastAOE(SPELL_HATCH_EGGS); + events.ScheduleEvent(EVENT_TRANSFORM, 60000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_HATCH_SPIDER_EGG, 30000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_DRAIN_LIFE, 30000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_THRASH, urand(4000, 6000)); // all phases + events.ScheduleEvent(EVENT_ENLARGE, urand(10000, 20000), 0, PHASE_TWO); + events.SetPhase(PHASE_TWO); + break; + case EVENT_TALK_FIRST_SPIDERS: + Talk(SAY_SPIDER_SPAWN); + break; + case EVENT_POISON_VOLLEY: + DoCastVictim(SPELL_POISON_VOLLEY, true); + events.ScheduleEvent(EVENT_POISON_VOLLEY, urand(10000, 20000), 0, PHASE_TWO); + break; + case EVENT_HATCH_SPIDER_EGG: + DoCastSelf(SPELL_HATCH_SPIDER_EGG, true); + events.ScheduleEvent(EVENT_HATCH_SPIDER_EGG, 20000, 0, PHASE_TWO); + break; + case EVENT_DRAIN_LIFE: + DoCastRandomTarget(SPELL_DRAIN_LIFE); + events.ScheduleEvent(EVENT_DRAIN_LIFE, urand(20000, 50000), 0, PHASE_TWO); + break; + case EVENT_ENLARGE: { - case EVENT_SPAWN_START_SPIDERS: - - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - Talk(SAY_SPIDER_SPAWN); - Creature* Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); - if (Spider) - Spider->AI()->AttackStart(target); - Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); - if (Spider) - Spider->AI()->AttackStart(target); - Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); - if (Spider) - Spider->AI()->AttackStart(target); - Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); - if (Spider) - Spider->AI()->AttackStart(target); - } - events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000, 0, PHASE_TWO); - events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO); - events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000); - events.ScheduleEvent(EVENT_SPAWN_SPIDER, 30000); - events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO); - events.SetPhase(PHASE_TWO); - break; - case EVENT_POISON_VOLLEY: - DoCastVictim(SPELL_POISON_VOLLEY, true); - events.ScheduleEvent(EVENT_POISON_VOLLEY, urand(10000, 20000)); - break; - case EVENT_ASPECT_OF_MARLI: - DoCastVictim(SPELL_ASPECT_OF_MARLI, true); - events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, urand(13000, 18000), 0, PHASE_TWO); - break; - case EVENT_SPAWN_SPIDER: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - Creature* Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); - if (Spider) - Spider->AI()->AttackStart(target); - } - events.ScheduleEvent(EVENT_SPAWN_SPIDER, urand(12000, 17000)); - break; - case EVENT_TRANSFORM: - { - Talk(SAY_TRANSFORM); - DoCast(me, SPELL_SPIDER_FORM); // SPELL_AURA_TRANSFORM - /* - CreatureTemplate const* cinfo = me->GetCreatureTemplate(); - me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35))); - me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35))); - me->UpdateDamagePhysical(BASE_ATTACK); - */ - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); // hack - DoCastVictim(SPELL_ENVOLWINGWEB); - if (DoGetThreat(me->GetVictim())) - DoModifyThreatPercent(me->GetVictim(), -100); - events.ScheduleEvent(EVENT_CHARGE_PLAYER, 1500, 0, PHASE_THREE); - events.ScheduleEvent(EVENT_TRANSFORM_BACK, 25000, 0, PHASE_THREE); - events.SetPhase(PHASE_THREE); - break; - } - case EVENT_CHARGE_PLAYER: - { - Unit* target = nullptr; - int i = 0; - while (i++ < 3) // max 3 tries to get a random target with power_mana - { - target = SelectTarget(SelectTargetMethod::Random, 1, 100, true); // not aggro leader - if (target && target->getPowerType() == POWER_MANA) - break; - } - if (target) - { - DoCast(target, SPELL_CHARGE); - AttackStart(target); - } - events.ScheduleEvent(EVENT_CHARGE_PLAYER, 8000, 0, PHASE_THREE); - break; - } - case EVENT_TRANSFORM_BACK: - { - me->RemoveAura(SPELL_SPIDER_FORM); - /* - CreatureTemplate const* cinfo = me->GetCreatureTemplate(); - me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 1))); - me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 1))); - me->UpdateDamagePhysical(BASE_ATTACK); - */ - me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack - events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000, 0, PHASE_TWO); - events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO); - events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000); - events.ScheduleEvent(EVENT_SPAWN_SPIDER, 30000); - events.ScheduleEvent(EVENT_TRANSFORM, urand(35000, 60000), 0, PHASE_TWO); - events.SetPhase(PHASE_TWO); - break; - } - default: - break; + std::list targets = DoFindFriendlyMissingBuff(100.f, SPELL_ENLARGE); + if (targets.size() > 0) + DoCast(*(targets.begin()), SPELL_ENLARGE); + events.ScheduleEvent(EVENT_ENLARGE, urand(20000, 40000), 0, PHASE_TWO); + break; } + case EVENT_TRANSFORM: + Talk(SAY_TRANSFORM); + DoCastSelf(SPELL_SPIDER_FORM, true); + me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); // hack + events.ScheduleEvent(EVENT_ENVELOPING_WEB, 5000, 0, PHASE_THREE); + events.ScheduleEvent(EVENT_CHARGE_PLAYER, 6000, 0, PHASE_THREE); + events.ScheduleEvent(EVENT_CORROSIVE_POISON, 1000, 0, PHASE_THREE); + events.ScheduleEvent(EVENT_POISON_SHOCK, urand(5000, 10000), 0, PHASE_THREE); + events.ScheduleEvent(EVENT_TRANSFORM_BACK, 60000, 0, PHASE_THREE); + events.SetPhase(PHASE_THREE); + break; + case EVENT_ENVELOPING_WEB: + DoCastAOE(SPELL_ENVELOPING_WEB); + events.ScheduleEvent(EVENT_CHARGE_PLAYER, 500, 0, PHASE_THREE); + events.ScheduleEvent(EVENT_ENVELOPING_WEB, urand(15000, 20000), 0, PHASE_THREE); + break; + case EVENT_CHARGE_PLAYER: + { + Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [this](Unit* target) -> bool + { + if (target->GetTypeId() != TYPEID_PLAYER || target->getPowerType() != Powers::POWER_MANA) + return false; + if (me->IsWithinMeleeRange(target) || me->GetVictim() == target) + return false; + return true; + }); + if (target) + { + DoCast(target, SPELL_CHARGE); + AttackStart(target); + } + break; + } + case EVENT_CORROSIVE_POISON: + DoCastVictim(SPELL_CORROSIVE_POISON); + events.ScheduleEvent(EVENT_CORROSIVE_POISON, urand(25000, 35000), 0, PHASE_THREE); + break; + case EVENT_POISON_SHOCK: + DoCastRandomTarget(SPELL_POISON_SHOCK); + events.ScheduleEvent(EVENT_POISON_SHOCK, 10000, 0, PHASE_THREE); + break; + case EVENT_TRANSFORM_BACK: + me->RemoveAura(SPELL_SPIDER_FORM); + DoCastSelf(SPELL_TRANSFORM_BACK, true); + Talk(SAY_TRANSFORM_BACK); + me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack + events.ScheduleEvent(EVENT_TRANSFORM, 60000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_HATCH_SPIDER_EGG, 30000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_DRAIN_LIFE, 30000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_ENLARGE, urand(10000, 20000), 0, PHASE_TWO); + events.SetPhase(PHASE_TWO); + break; + case EVENT_THRASH: + DoCastVictim(SPELL_THRASH); + events.ScheduleEvent(EVENT_THRASH, urand(10000, 20000)); + break; + default: + break; } - - DoMeleeAttackIfReady(); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulGurubAI(creature); + DoMeleeAttackIfReady(); } }; -// Spawn of Marli -class npc_spawn_of_marli : public CreatureScript +// Spawn of Mar'li +struct npc_spawn_of_marli : public ScriptedAI { -public: - npc_spawn_of_marli() : CreatureScript("npc_spawn_of_marli") { } + npc_spawn_of_marli(Creature* creature) : ScriptedAI(creature) { } - struct npc_spawn_of_marliAI : public ScriptedAI + void Reset() override { - npc_spawn_of_marliAI(Creature* creature) : ScriptedAI(creature) { } + _scheduler.CancelAll(); + } - uint32 LevelUp_Timer; - - void Reset() override + void EnterCombat(Unit* /*who*/) override + { + _scheduler.Schedule(4s, [this](TaskContext context) { - LevelUp_Timer = 3000; - } - - void EnterCombat(Unit* /*who*/) override - { - } - - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - //LevelUp_Timer - if (LevelUp_Timer <= diff) + if (context.GetRepeatCounter() < 5) { - DoCast(me, SPELL_LEVELUP); - LevelUp_Timer = 3000; + DoCastSelf(SPELL_GROWTH); + context.Repeat(4s); } - else LevelUp_Timer -= diff; + else + { + Talk(EMOTE_FULL_GROWN); + DoCastSelf(SPELL_FULL_GROWN); + } + }); + } - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetZulGurubAI(creature); + //Return since we have no target + if (!UpdateVictim()) + return; + + _scheduler.Update(diff, [this] + { + DoMeleeAttackIfReady(); + }); + } + +private: + TaskScheduler _scheduler; +}; + +// 24083 - Hatch Eggs +class spell_hatch_eggs : public SpellScript +{ + PrepareSpellScript(spell_hatch_eggs); + + void HandleObjectAreaTargetSelect(std::list& targets) + { + targets.sort(Acore::ObjectDistanceOrderPred(GetCaster())); + targets.resize(GetSpellInfo()->MaxAffectedTargets); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hatch_eggs::HandleObjectAreaTargetSelect, EFFECT_0, TARGET_GAMEOBJECT_DEST_AREA); + } +}; + +// 24110 - Enveloping Webs +class spell_enveloping_webs : public SpellScript +{ + PrepareSpellScript(spell_enveloping_webs); + + void HandleOnHit() + { + Unit* caster = GetCaster(); + Unit* hitUnit = GetHitUnit(); + if (caster && hitUnit && hitUnit->GetTypeId() == TYPEID_PLAYER) + { + caster->GetThreatMgr().modifyThreatPercent(hitUnit, -100); + } + } + + void Register() override + { + OnHit += SpellHitFn(spell_enveloping_webs::HandleOnHit); + } +}; + +// 24084 - Mar'li Transform +class spell_marli_transform : public AuraScript +{ + PrepareAuraScript(spell_marli_transform); + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetCaster() && GetCaster()->ToCreature()) + GetCaster()->ToCreature()->LoadEquipment(0, true); + } + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetCaster() && GetCaster()->ToCreature()) + GetCaster()->ToCreature()->LoadEquipment(1, true); + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_marli_transform::HandleApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_marli_transform::HandleRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); } }; void AddSC_boss_marli() { - new boss_marli(); - new npc_spawn_of_marli(); + RegisterCreatureAI(boss_marli); + RegisterCreatureAI(npc_spawn_of_marli); + RegisterSpellScript(spell_hatch_eggs); + RegisterSpellScript(spell_enveloping_webs); + RegisterSpellScript(spell_marli_transform); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp index 7bcbf01b7..51c05e37c 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp @@ -28,13 +28,20 @@ EndScriptData */ enum Spells { - SPELL_AMBUSH = 34794, - SPELL_THOUSANDBLADES = 34799 + SPELL_VANISH = 24699, + SPELL_AMBUSH = 24337, + SPELL_GOUGE = 24698, + SPELL_THOUSAND_BLADES = 24649, + SPELL_THRASH = 3417, + SPELL_ENRAGE = 8269 }; -enum Misc +enum Events { - EQUIP_ID_MAIN_HAND = 0 //was item display id 31818, but this id does not exist + EVENT_VANISH = 1, + EVENT_AMBUSH = 2, + EVENT_GOUGE = 3, + EVENT_THOUSAND_BLADES = 4 }; class boss_renataki : public CreatureScript @@ -46,36 +53,67 @@ public: { boss_renatakiAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) { } - uint32 Invisible_Timer; - uint32 Ambush_Timer; - uint32 Visible_Timer; - uint32 Aggro_Timer; - uint32 ThousandBlades_Timer; - - bool Invisible; - bool Ambushed; - void Reset() override { _Reset(); - Invisible_Timer = urand(8000, 18000); - Ambush_Timer = 3000; - Visible_Timer = 4000; - Aggro_Timer = urand(15000, 25000); - ThousandBlades_Timer = urand(4000, 8000); - - Invisible = false; - Ambushed = false; - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); + me->SetReactState(REACT_AGGRESSIVE); + _enraged = false; + _thousandBladesCount = urand(2, 5); + _thousandBladesTargets.clear(); + _dynamicFlags = me->GetDynamicFlags(); } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); + events.ScheduleEvent(EVENT_VANISH, 23s, 25s); + events.ScheduleEvent(EVENT_GOUGE, 5s, 10s); + events.ScheduleEvent(EVENT_THOUSAND_BLADES, 15s, 20s); + + DoCastSelf(SPELL_THRASH, true); + } + + void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override + { + if (!_enraged && HealthBelowPct(30)) + { + me->TextEmote("%s becomes enraged", me, false); + DoCast(me, SPELL_ENRAGE); + _enraged = true; + } + } + + bool CanAIAttack(Unit const* target) const override + { + if (me->GetThreatMgr().getThreatList().size() > 1 && me->GetThreatMgr().getOnlineContainer().getMostHated()->getTarget() == target) + return !target->HasAura(SPELL_GOUGE); + + return true; + } + + bool CanBeSeen(Player const* /*player*/) override + { + return me->GetReactState() == REACT_AGGRESSIVE; + } + + bool CanSeeAlways(WorldObject const* obj) override + { + if (me->GetReactState() == REACT_PASSIVE) + { + return obj->ToCreature() && obj->ToCreature()->IsPet(); + } + + return false; + } + + bool CanAlwaysBeDetectable(WorldObject const* seer) override + { + if (me->GetReactState() == REACT_PASSIVE) + { + return seer->ToCreature() && seer->ToCreature()->IsPet(); + } + + return false; } void UpdateAI(uint32 diff) override @@ -83,86 +121,122 @@ public: if (!UpdateVictim()) return; - //Invisible_Timer - if (Invisible_Timer <= diff) + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - me->InterruptSpell(CURRENT_GENERIC_SPELL); - - SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE); - me->SetDisplayId(11686); - - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - Invisible = true; - - Invisible_Timer = urand(15000, 30000); - } - else Invisible_Timer -= diff; - - if (Invisible) - { - if (Ambush_Timer <= diff) + switch (eventId) { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (target) + case EVENT_VANISH: + me->SetReactState(REACT_PASSIVE); + _dynamicFlags = me->GetDynamicFlags(); + me->RemoveDynamicFlag(UNIT_DYNFLAG_TRACK_UNIT); + DoCastSelf(SPELL_VANISH); + events.DelayEvents(5s); + events.ScheduleEvent(EVENT_AMBUSH, 5s); + events.ScheduleEvent(EVENT_VANISH, 38s, 45s); + return; + case EVENT_AMBUSH: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1)) + { + me->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), me->GetOrientation()); + DoCast(target, SPELL_AMBUSH, true); + } + me->SetDynamicFlag(_dynamicFlags); + me->RemoveAurasDueToSpell(SPELL_VANISH); + me->SetReactState(REACT_AGGRESSIVE); + break; + case EVENT_GOUGE: + DoCastAOE(SPELL_GOUGE); + events.ScheduleEvent(EVENT_GOUGE, 10s, 15s); + return; + case EVENT_THOUSAND_BLADES: { - me->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), me->GetOrientation()); - DoCast(target, SPELL_AMBUSH); - } + if (_thousandBladesTargets.empty()) + { + std::vector targetList; + ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().getThreatList(); + for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + { + if (Unit* target = (*itr)->getTarget()) + { + if (target->IsAlive() && target->IsWithinDist2d(me, 100.f)) + { + targetList.push_back(target); + } + } + } - Ambushed = true; - Ambush_Timer = 3000; + if (!targetList.empty()) + { + Acore::Containers::RandomShuffle(targetList); + + // First get ranged targets + for (Unit* target : targetList) + { + if (!target->IsWithinMeleeRange(me)) + { + _thousandBladesTargets.push_back(target); + } + } + + if (_thousandBladesTargets.size() < _thousandBladesCount) + { + // if still not enough, get melee targets + for (Unit* target : targetList) + { + if (target->IsWithinMeleeRange(me)) + { + _thousandBladesTargets.push_back(target); + } + } + } + + if (!_thousandBladesTargets.empty()) + { + Acore::Containers::RandomResize(_thousandBladesTargets, _thousandBladesCount); + } + } + } + + if (!_thousandBladesTargets.empty()) + { + std::vector::iterator itr = _thousandBladesTargets.begin(); + std::advance(itr, urand(0, _thousandBladesTargets.size() - 1)); + + if (Unit* target = *itr) + { + DoCast(target, SPELL_THOUSAND_BLADES, false); + } + + _thousandBladesTargets.erase(itr); + + events.ScheduleEvent(EVENT_THOUSAND_BLADES, 500ms); + } + else + { + _thousandBladesCount = urand(2, 5); + events.ScheduleEvent(EVENT_THOUSAND_BLADES, 15s, 22s); + } + break; + } + default: + break; } - else Ambush_Timer -= diff; } - if (Ambushed) - { - if (Visible_Timer <= diff) - { - me->InterruptSpell(CURRENT_GENERIC_SPELL); - - me->SetDisplayId(15268); - SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE); - - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - Invisible = false; - - Visible_Timer = 4000; - } - else Visible_Timer -= diff; - } - - //Resetting some aggro so he attacks other gamers - if (!Invisible) - { - if (Aggro_Timer <= diff) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 1); - - if (DoGetThreat(me->GetVictim())) - { - DoModifyThreatPercent(me->GetVictim(), -50); - } - - if (target) - { - AttackStart(target); - } - - Aggro_Timer = urand(7000, 20000); - } - else Aggro_Timer -= diff; - - if (ThousandBlades_Timer <= diff) - { - DoCastVictim(SPELL_THOUSANDBLADES); - ThousandBlades_Timer = urand(7000, 12000); - } - else ThousandBlades_Timer -= diff; - } - - DoMeleeAttackIfReady(); + if (me->GetReactState() == REACT_AGGRESSIVE) + DoMeleeAttackIfReady(); } + + private: + bool _enraged; + uint32 _dynamicFlags; + uint8 _thousandBladesCount; + std::vector _thousandBladesTargets; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp index eee9e00cb..2cab32c31 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp @@ -22,6 +22,7 @@ SDComment: Missing reset function after killing a boss for Ohgan, Thekal. SDCategory: Zul'Gurub EndScriptData */ +#include "GameObjectAI.h" #include "InstanceScript.h" #include "ScriptMgr.h" #include "zulgurub.h" @@ -36,7 +37,8 @@ ObjectData const creatureData[] = { { NPC_HIGH_PRIEST_THEKAL, DATA_THEKAL }, { NPC_ZEALOT_LORKHAN, DATA_LORKHAN }, - { NPC_ZEALOT_ZATH, DATA_ZATH } + { NPC_ZEALOT_ZATH, DATA_ZATH }, + { NPC_PRIESTESS_MARLI, DATA_MARLI } }; class instance_zulgurub : public InstanceMapScript @@ -49,6 +51,7 @@ public: instance_zulgurub_InstanceMapScript(Map* map) : InstanceScript(map) { SetBossNumber(EncounterCount); + LoadObjectData(creatureData, nullptr); LoadDoorData(doorData); LoadObjectData(creatureData, nullptr); } @@ -71,7 +74,20 @@ public: case NPC_HAKKAR: _hakkarGUID = creature->GetGUID(); break; + case NPC_SPAWN_OF_MARLI: + if (Creature* marli = GetCreature(DATA_MARLI)) + { + marli->AI()->JustSummoned(creature); + } + break; + case NPC_GAHZRANKA: + _gahzrankaGUID = creature->GetGUID(); + break; + default: + break; } + + InstanceScript::OnCreatureCreate(creature); } void OnGameObjectCreate(GameObject* go) override @@ -109,6 +125,16 @@ public: return ObjectGuid::Empty; } + uint32 GetData(uint32 type) const override + { + if (type == DATA_GAHZRANKA) + { + return _gahzrankaGUID || GetBossState(DATA_GAHZRANKA) == DONE; + } + + return 0; + } + std::string GetSaveData() override { OUT_SAVE_INST_DATA; @@ -160,6 +186,7 @@ public: ObjectGuid _arlokkGUID; ObjectGuid _goGongOfBethekkGUID; ObjectGuid _hakkarGUID; + ObjectGuid _gahzrankaGUID; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override @@ -168,7 +195,56 @@ public: } }; +enum EdgeOfMadnessEnum +{ + EVENT_EDGE_OF_MADNESS_GRILEK = 27, + EVENT_EDGE_OF_MADNESS_HAZZARAH = 28, + EVENT_EDGE_OF_MADNESS_RENATAKI = 29, + EVENT_EDGE_OF_MADNESS_WUSHOOLAY = 30 +}; + +std::vector> BrazierOfMadnessContainer = +{ + { EVENT_EDGE_OF_MADNESS_GRILEK, NPC_GRILEK }, + { EVENT_EDGE_OF_MADNESS_HAZZARAH, NPC_HAZZARAH }, + { EVENT_EDGE_OF_MADNESS_RENATAKI, NPC_RENATAKI }, + { EVENT_EDGE_OF_MADNESS_WUSHOOLAY, NPC_WUSHOOLAY } +}; + +Position const edgeOfMagnessSummonPos = { -11901.229f, -1906.366f, 65.358f, 0.942f }; + +struct go_brazier_of_madness : public GameObjectAI +{ + go_brazier_of_madness(GameObject* go) : GameObjectAI(go) { } + + bool GossipHello(Player* /*player*/, bool reportUse) override + { + if (reportUse) + { + return true; + } + + uint32 bossEntry = 0; + for (uint8 i = 0; i < 4; ++i) + { + if (sGameEventMgr->IsActiveEvent(BrazierOfMadnessContainer[i].first)) + { + bossEntry = BrazierOfMadnessContainer[i].second; + break; + } + } + + if (bossEntry) + { + me->SummonCreature(bossEntry, edgeOfMagnessSummonPos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2 * HOUR * IN_MILLISECONDS); + } + + return false; + } +}; + void AddSC_instance_zulgurub() { new instance_zulgurub(); + RegisterGameObjectAI(go_brazier_of_madness); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h index 79e40b4c4..232328acf 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h +++ b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h @@ -49,6 +49,8 @@ enum CreatureIds NPC_ZULIAN_PROWLER = 15101, // Arlokk Event NPC_ZEALOT_LORKHAN = 11347, NPC_ZEALOT_ZATH = 11348, + NPC_PRIESTESS_MARLI = 14510, + NPC_SPAWN_OF_MARLI = 15041, NPC_HIGH_PRIEST_THEKAL = 14509, NPC_JINDO_THE_HEXXER = 11380, NPC_NIGHTMARE_ILLUSION = 15163, @@ -59,7 +61,13 @@ enum CreatureIds NPC_VILEBRANCH_SPEAKER = 11391, // Mandokir Event NPC_CHAINED_SPIRIT = 15117, // Mandokir Event NPC_HAKKAR = 14834, - NPC_ZULGURUB_TIGER = 11361 + NPC_ZULGURUB_TIGER = 11361, + NPC_BRAIN_WASH_TOTEM = 15112, + NPC_GAHZRANKA = 15114, + NPC_GRILEK = 15082, + NPC_HAZZARAH = 15083, + NPC_RENATAKI = 15084, + NPC_WUSHOOLAY = 15085 }; enum GameobjectIds diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp index 0380ded3e..8001bfd63 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp @@ -25,38 +25,9 @@ EndScriptData */ #include "Player.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "TaskScheduler.h" #include "temple_of_ahnqiraj.h" -/* - * This is a 2 phases events. Here follows an explanation of the main events and transition between phases and sub-phases. - * - * The first phase is the EYE phase: the Eye of C'Thun is active and C'thun is not active. - * During this phase, the "Eye of C'Thun" alternates between 2 sub-phases: - * - PHASE_EYE_GREEN_BEAM: - * 50 sec phase during which the Eye mainly casts its Green Beam every 3 sec. - * - PHASE_EYE_RED_BEAM: - * 35 sec phase during which the Eye casts its red beam every sec. - * This EYE phase ends when the "Eye of C'Thun" is killed. Then starts the CTHUN phase. - * - * The second phase is the CTHUN phase. The Eye of C'Thun is not active and C'Thun is active. - * This phase starts with the transformation of the Eye into C'Thun (PHASE_CTHUN_TRANSITION). - * After the transformation, C'Thun alternates between 2 sub-phases: - * - PHASE_CTHUN_STOMACH: - * - C'Thun is almost insensible to all damage (99% damage reduction). - * - It spawns 2 tentacles in its stomach. - * - C'Thun swallows players. - * - This sub-phase ends when the 2 tentacles are killed. Swallowed players are regurgitate. - * - * - PHASE_CTHUN_WEAK: - * - weakened C'Thun takes normal damage. - * - This sub-phase ends after 45 secs. - * - * This CTHUN phase ends when C'Thun is killed - * - * Note: - * - the current phase is stored in the instance data to be easily shared between the eye and cthun. - */ - enum Phases { PHASE_NOT_STARTED = 0, @@ -99,7 +70,7 @@ enum Spells //SAME AS PHASE1 //Giant Claw Tentacles - SPELL_MASSIVE_GROUND_RUPTURE = 26100, + SPELL_MASSIVE_GROUND_RUPTURE = 26478, //Also casts Hamstring SPELL_THRASH = 3391, @@ -111,6 +82,10 @@ enum Spells SPELL_MOUTH_TENTACLE = 26332, SPELL_EXIT_STOMACH_KNOCKBACK = 25383, SPELL_DIGESTIVE_ACID = 26476, + + // Tentacles + SPELL_SUBMERGE_VISUAL = 26234, + SPELL_BIRTH = 26262 }; enum Actions @@ -142,6 +117,20 @@ const Position FleshTentaclePos[2] = { -8525.0f, 1994.0f, -98.0f, 2.12f}, }; +class NotInStomachSelector +{ +public: + NotInStomachSelector() { } + + bool operator()(Unit* unit) const + { + if (unit->GetTypeId() != TYPEID_PLAYER || unit->HasAura(SPELL_DIGESTIVE_ACID)) + return false; + + return true; + } +}; + //Kick out position const Position KickPos = { -8545.0f, 1984.0f, -96.0f, 0.0f}; @@ -917,32 +906,40 @@ public: { eye_tentacleAI(Creature* creature) : ScriptedAI(creature) { - if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) + if (Creature* portal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) { - pPortal->SetReactState(REACT_PASSIVE); - Portal = pPortal->GetGUID(); + portal->SetReactState(REACT_PASSIVE); + _portalGUID = portal->GetGUID(); } SetCombatMovement(false); } - uint32 MindflayTimer; - uint32 KillSelfTimer; - ObjectGuid Portal; - void JustDied(Unit* /*killer*/) override { - if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) + if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID)) + { Unit::Kill(p, p); + } } void Reset() override { - //Mind flay half a second after we spawn - MindflayTimer = 500; + _scheduler.Schedule(500ms, [this](TaskContext /*task*/) + { + DoCastAOE(SPELL_GROUND_RUPTURE); + }).Schedule(5min, [this](TaskContext /*task*/) + { + me->DespawnOrUnsummon(); + }).Schedule(1s, 5s, [this](TaskContext context) + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [&](Unit* u) { return u && u->GetTypeId() == TYPEID_PLAYER && !u->HasAura(SPELL_DIGESTIVE_ACID) && !u->HasAura(SPELL_MIND_FLAY); })) + { + DoCast(target, SPELL_MIND_FLAY); + } - //This prevents eyes from overlapping - KillSelfTimer = 35000; + context.Repeat(10s, 15s); + }); } void EnterCombat(Unit* /*who*/) override @@ -956,26 +953,12 @@ public: if (!UpdateVictim()) return; - //KillSelfTimer - if (KillSelfTimer <= diff) - { - Unit::Kill(me, me); - return; - } - else KillSelfTimer -= diff; - - //MindflayTimer - if (MindflayTimer <= diff) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (target && !target->HasAura(SPELL_DIGESTIVE_ACID)) - DoCast(target, SPELL_MIND_FLAY); - - //Mindflay every 10 seconds - MindflayTimer = 10000; - } - else MindflayTimer -= diff; + _scheduler.Update(diff); } + + private: + TaskScheduler _scheduler; + ObjectGuid _portalGUID; }; }; @@ -995,35 +978,41 @@ public: { SetCombatMovement(false); - if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) + if (Creature* portal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) { - pPortal->SetReactState(REACT_PASSIVE); - Portal = pPortal->GetGUID(); + portal->SetReactState(REACT_PASSIVE); + _portalGUID = portal->GetGUID(); } } - uint32 GroundRuptureTimer; - uint32 HamstringTimer; - uint32 EvadeTimer; - ObjectGuid Portal; - void JustDied(Unit* /*killer*/) override { - if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) + if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID)) + { Unit::Kill(p, p); + } } void Reset() override { - //First rupture should happen half a second after we spawn - GroundRuptureTimer = 500; - HamstringTimer = 2000; - EvadeTimer = 5000; + _scheduler.Schedule(Milliseconds(500), [this](TaskContext /*task*/) + { + DoCastAOE(SPELL_GROUND_RUPTURE); + }).Schedule(Minutes(5), [this](TaskContext /*task*/) + { + me->DespawnOrUnsummon(); + }); } void EnterCombat(Unit* /*who*/) override { DoZoneInCombat(); + + _scheduler.Schedule(2s, [this](TaskContext context) + { + DoCastVictim(SPELL_HAMSTRING); + context.Repeat(5s); + }); } void UpdateAI(uint32 diff) override @@ -1032,62 +1021,14 @@ public: if (!UpdateVictim()) return; - //EvadeTimer - if (!me->IsWithinMeleeRange(me->GetVictim())) - { - if (EvadeTimer <= diff) - { - if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - Unit::Kill(p, p); - - //Dissapear and reappear at new position - me->SetVisible(false); - - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (!target) - { - Unit::Kill(me, me); - return; - } - - if (!target->HasAura(SPELL_DIGESTIVE_ACID)) - { - me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); - if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) - { - pPortal->SetReactState(REACT_PASSIVE); - Portal = pPortal->GetGUID(); - } - - GroundRuptureTimer = 500; - HamstringTimer = 2000; - EvadeTimer = 5000; - AttackStart(target); - } - - me->SetVisible(true); - } - else EvadeTimer -= diff; - } - - //GroundRuptureTimer - if (GroundRuptureTimer <= diff) - { - DoCastVictim(SPELL_GROUND_RUPTURE); - GroundRuptureTimer = 30000; - } - else GroundRuptureTimer -= diff; - - //HamstringTimer - if (HamstringTimer <= diff) - { - DoCastVictim(SPELL_HAMSTRING); - HamstringTimer = 5000; - } - else HamstringTimer -= diff; + _scheduler.Update(diff); DoMeleeAttackIfReady(); } + + private: + TaskScheduler _scheduler; + ObjectGuid _portalGUID; }; }; @@ -1107,37 +1048,112 @@ public: { SetCombatMovement(false); - if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) + if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) { - pPortal->SetReactState(REACT_PASSIVE); - Portal = pPortal->GetGUID(); + portal->SetReactState(REACT_PASSIVE); + _portalGUID = portal->GetGUID(); } } - uint32 GroundRuptureTimer; - uint32 ThrashTimer; - uint32 HamstringTimer; - uint32 EvadeTimer; - ObjectGuid Portal; - void JustDied(Unit* /*killer*/) override { - if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) + if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID)) + { Unit::Kill(p, p); + } } void Reset() override { - //First rupture should happen half a second after we spawn - GroundRuptureTimer = 500; - HamstringTimer = 2000; - ThrashTimer = 5000; - EvadeTimer = 5000; + _scheduler.Schedule(500ms, [this](TaskContext /*task*/) + { + DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE); + }); } void EnterCombat(Unit* /*who*/) override { DoZoneInCombat(); + + _scheduler.Schedule(2s, [this](TaskContext context) + { + DoCastVictim(SPELL_HAMSTRING); + context.Repeat(10s); + }).Schedule(5s, [this](TaskContext context) { + DoCastSelf(SPELL_THRASH); + context.Repeat(10s); + }); + } + + void ScheduleMeleeCheck() + { + // Check if a target is in melee range + _scheduler.Schedule(10s, [this](TaskContext task) + { + if (Unit* target = me->GetVictim()) + { + if (!target->IsWithinMeleeRange(me)) + { + // Main target not found within melee range, try to select a new one + if (Player* newTarget = me->SelectNearestPlayer(5.0f)) + { + AttackStart(newTarget); + } + else // Main target not found, and failed to acquire a new target... Submerge + { + Submerge(); + } + } + } + + task.Repeat(); + }); + } + + void Submerge() + { + if (me->SelectNearestPlayer(5.0f)) + { + return; + } + + // Despawn portal + if (Creature* p = ObjectAccessor::GetCreature(*me, _portalGUID)) + { + p->DespawnOrUnsummon(); + } + + DoCastSelf(SPELL_SUBMERGE_VISUAL); + me->SetHealth(me->GetMaxHealth()); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + _scheduler.CancelAll(); + + _scheduler.Schedule(5s, [this](TaskContext /*task*/) + { + Emerge(); + }); + } + + void Emerge() + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, NotInStomachSelector())) + { + Position pos = target->GetPosition(); + me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), 0); + if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, pos, TEMPSUMMON_CORPSE_DESPAWN)) + { + portal->SetReactState(REACT_PASSIVE); + _portalGUID = portal->GetGUID(); + } + + me->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL); + DoCastSelf(SPELL_BIRTH); + DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE, true); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + ScheduleMeleeCheck(); + } } void UpdateAI(uint32 diff) override @@ -1146,70 +1162,14 @@ public: if (!UpdateVictim()) return; - //EvadeTimer - if (!me->IsWithinMeleeRange(me->GetVictim())) - { - if (EvadeTimer <= diff) - { - if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) - Unit::Kill(p, p); - - //Dissapear and reappear at new position - me->SetVisible(false); - - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (!target) - { - Unit::Kill(me, me); - return; - } - - if (!target->HasAura(SPELL_DIGESTIVE_ACID)) - { - me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); - if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) - { - pPortal->SetReactState(REACT_PASSIVE); - Portal = pPortal->GetGUID(); - } - - GroundRuptureTimer = 500; - HamstringTimer = 2000; - ThrashTimer = 5000; - EvadeTimer = 5000; - AttackStart(target); - } - me->SetVisible(true); - } - else EvadeTimer -= diff; - } - - //GroundRuptureTimer - if (GroundRuptureTimer <= diff) - { - DoCastVictim(SPELL_GROUND_RUPTURE); - GroundRuptureTimer = 30000; - } - else GroundRuptureTimer -= diff; - - //ThrashTimer - if (ThrashTimer <= diff) - { - DoCastVictim(SPELL_THRASH); - ThrashTimer = 10000; - } - else ThrashTimer -= diff; - - //HamstringTimer - if (HamstringTimer <= diff) - { - DoCastVictim(SPELL_HAMSTRING); - HamstringTimer = 10000; - } - else HamstringTimer -= diff; + _scheduler.Update(diff); DoMeleeAttackIfReady(); } + + private: + TaskScheduler _scheduler; + ObjectGuid _portalGUID; }; }; @@ -1229,26 +1189,34 @@ public: { SetCombatMovement(false); - if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) + if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) { - pPortal->SetReactState(REACT_PASSIVE); - Portal = pPortal->GetGUID(); + portal->SetReactState(REACT_PASSIVE); + _portalGUID = portal->GetGUID(); } } - uint32 BeamTimer; - ObjectGuid Portal; - void JustDied(Unit* /*killer*/) override { - if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) + if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID)) + { Unit::Kill(p, p); + } } void Reset() override { - //Green Beam half a second after we spawn - BeamTimer = 500; + _scheduler.Schedule(500ms, [this](TaskContext /*task*/) + { + DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE); + }).Schedule(1s, 5s, [this](TaskContext context) { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, -SPELL_DIGESTIVE_ACID)) + { + DoCast(target, SPELL_GREEN_BEAM); + } + + context.Repeat(2100ms); + }); } void EnterCombat(Unit* /*who*/) override @@ -1262,18 +1230,12 @@ public: if (!UpdateVictim()) return; - //BeamTimer - if (BeamTimer <= diff) - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (target && !target->HasAura(SPELL_DIGESTIVE_ACID)) - DoCast(target, SPELL_GREEN_BEAM); - - //Beam every 2 seconds - BeamTimer = 2100; - } - else BeamTimer -= diff; + _scheduler.Update(diff); } + + private: + TaskScheduler _scheduler; + ObjectGuid _portalGUID; }; }; diff --git a/src/server/scripts/Kalimdor/boss_azuregos.cpp b/src/server/scripts/Kalimdor/boss_azuregos.cpp index cc8423194..13c6ee445 100644 --- a/src/server/scripts/Kalimdor/boss_azuregos.cpp +++ b/src/server/scripts/Kalimdor/boss_azuregos.cpp @@ -69,8 +69,6 @@ public: { if (p->GetZoneId() == me->GetZoneId()) { - - p->RemoveAurasDueToSpell(SPELL_AURA_OF_FROST); p->RemoveAurasDueToSpell(SPELL_CHILL); p->RemoveAurasDueToSpell(SPELL_FROST_BREATH); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 2a1c06441..7daaa10d4 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -738,48 +738,6 @@ class spell_gen_no_offhand_proc : public AuraScript } }; -/* 71602 - Item - Icecrown 25 Normal Caster Trinket 1 Base - 71645 - Item - Icecrown 25 Heroic Caster Trinket 1 Base - 71845 - Item - Icecrown 25 Normal Caster Weapon Proc - 71846 - Item - Icecrown 25 Heroic Caster Weapon Proc - 72419 - Item - Icecrown Reputation Ring Healer Trigger - 75465 - Item - Chamber of Aspects 25 Nuker Trinket - 75474 - Item - Chamber of Aspects 25 Heroic Nuker Trinket */ -class spell_gen_proc_once_per_cast : public AuraScript -{ - PrepareAuraScript(spell_gen_proc_once_per_cast); - - bool Load() override - { - _spellPointer = nullptr; - return true; - } - - bool CheckProc(ProcEventInfo& eventInfo) - { - if (eventInfo.GetActor()) - { - if (Player* player = eventInfo.GetActor()->ToPlayer()) - { - if (player->m_spellModTakingSpell == _spellPointer) - { - return false; - } - _spellPointer = player->m_spellModTakingSpell; - } - } - return true; - } - - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_gen_proc_once_per_cast::CheckProc); - } - -private: - Spell* _spellPointer; -}; - // 70805 - Item - Rogue T10 2P Bonus class spell_gen_proc_on_self : public AuraScript { @@ -4545,7 +4503,6 @@ void AddSC_generic_spell_scripts() RegisterSpellScript(spell_gen_use_spell_base_level_check); RegisterSpellScript(spell_gen_proc_from_direct_damage); RegisterSpellScript(spell_gen_no_offhand_proc); - RegisterSpellScript(spell_gen_proc_once_per_cast); RegisterSpellScript(spell_gen_proc_on_self); RegisterSpellScript(spell_gen_proc_not_self); RegisterSpellScript(spell_gen_baby_murloc_passive); diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index cd8d9233e..cfa787420 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -1208,6 +1208,27 @@ class spell_warl_drain_soul : public AuraScript } }; +// 29341 - Shadowburn +class spell_warl_shadowburn : public AuraScript +{ + PrepareAuraScript(spell_warl_shadowburn); + + void RemoveEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* caster = GetCaster(); + Unit* target = GetTarget(); + if (!(GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH && caster && target && caster->IsPlayer() && caster->ToPlayer()->isHonorOrXPTarget(target))) + { + PreventDefaultAction(); + } + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_warl_shadowburn::RemoveEffect, EFFECT_0, SPELL_AURA_CHANNEL_DEATH_ITEM, AURA_EFFECT_HANDLE_REAL); + } +}; + void AddSC_warlock_spell_scripts() { RegisterSpellScript(spell_warl_eye_of_kilrogg); @@ -1238,4 +1259,5 @@ void AddSC_warlock_spell_scripts() RegisterSpellScript(spell_warl_soulshatter); RegisterSpellScript(spell_warl_unstable_affliction); RegisterSpellScript(spell_warl_drain_soul); + RegisterSpellScript(spell_warl_shadowburn); } diff --git a/src/server/scripts/World/boss_emerald_dragons.cpp b/src/server/scripts/World/boss_emerald_dragons.cpp index 84a9740f6..b667c468d 100644 --- a/src/server/scripts/World/boss_emerald_dragons.cpp +++ b/src/server/scripts/World/boss_emerald_dragons.cpp @@ -302,9 +302,6 @@ public: * --- * --- Dragonspecific scripts and handling: LETHON * --- - * - * @todo - * - Spell: Shadow bolt whirl casts needs custom handling (spellscript) */ enum LethonTexts @@ -318,6 +315,14 @@ enum LethonSpells SPELL_DRAW_SPIRIT = 24811, SPELL_SHADOW_BOLT_WHIRL = 24834, SPELL_DARK_OFFERING = 24804, + SPELL_SHADOW_BOLT_WHIRL1 = 24820, + SPELL_SHADOW_BOLT_WHIRL2 = 24821, + SPELL_SHADOW_BOLT_WHIRL3 = 24822, + SPELL_SHADOW_BOLT_WHIRL4 = 24823, + SPELL_SHADOW_BOLT_WHIRL5 = 24835, + SPELL_SHADOW_BOLT_WHIRL6 = 24836, + SPELL_SHADOW_BOLT_WHIRL7 = 24837, + SPELL_SHADOW_BOLT_WHIRL8 = 24838, }; enum LethonCreatures @@ -340,13 +345,14 @@ public: { _stage = 1; emerald_dragonAI::Reset(); - events.ScheduleEvent(EVENT_SHADOW_BOLT_WHIRL, 10000); + me->RemoveAurasDueToSpell(SPELL_SHADOW_BOLT_WHIRL); } void EnterCombat(Unit* who) override { Talk(SAY_LETHON_AGGRO); WorldBossAI::EnterCombat(who); + DoCastSelf(SPELL_SHADOW_BOLT_WHIRL, true); } void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override @@ -368,20 +374,6 @@ public: } } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_SHADOW_BOLT_WHIRL: - me->CastSpell((Unit*)nullptr, SPELL_SHADOW_BOLT_WHIRL, false); - events.ScheduleEvent(EVENT_SHADOW_BOLT_WHIRL, urand(15000, 30000)); - break; - default: - emerald_dragonAI::ExecuteEvent(eventId); - break; - } - } - private: uint8 _stage; }; @@ -712,6 +704,41 @@ public: } }; +class spell_shadow_bolt_whirl : public AuraScript +{ + PrepareAuraScript(spell_shadow_bolt_whirl); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SHADOW_BOLT_WHIRL1, SPELL_SHADOW_BOLT_WHIRL2, SPELL_SHADOW_BOLT_WHIRL3, SPELL_SHADOW_BOLT_WHIRL4, SPELL_SHADOW_BOLT_WHIRL5, SPELL_SHADOW_BOLT_WHIRL6, SPELL_SHADOW_BOLT_WHIRL7, SPELL_SHADOW_BOLT_WHIRL8 }); + } + + void HandlePeriodic(AuraEffect const* aurEff) + { + Unit* caster = GetCaster(); + Unit* target = GetTarget(); + + if (!caster || !target) + return; + std::array spellForTick = { SPELL_SHADOW_BOLT_WHIRL1, SPELL_SHADOW_BOLT_WHIRL2, SPELL_SHADOW_BOLT_WHIRL3, SPELL_SHADOW_BOLT_WHIRL4, SPELL_SHADOW_BOLT_WHIRL5, SPELL_SHADOW_BOLT_WHIRL6, SPELL_SHADOW_BOLT_WHIRL7, SPELL_SHADOW_BOLT_WHIRL8 }; + uint32 tick = (aurEff->GetTickNumber() + 7/*-1*/) % 8; + + // casted in left/right (but triggered spell have wide forward cone) + float forward = target->GetOrientation(); + if (tick <= 3) + target->SetOrientation(forward + 0.75f * M_PI - tick * M_PI / 8); // Left + else + target->SetOrientation(forward - 0.75f * M_PI + (8 - tick) * M_PI / 8); // Right + + target->CastSpell(target, spellForTick[tick], true); + target->SetOrientation(forward); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_shadow_bolt_whirl::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; class spell_mark_of_nature : public SpellScriptLoader { public: @@ -765,4 +792,5 @@ void AddSC_emerald_dragons() // dragon spellscripts new spell_dream_fog_sleep(); new spell_mark_of_nature(); + RegisterSpellScript(spell_shadow_bolt_whirl); }; diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h index 9d23adabf..249167c0e 100644 --- a/src/server/shared/DataStores/DBCStructure.h +++ b/src/server/shared/DataStores/DBCStructure.h @@ -1015,6 +1015,13 @@ struct FactionTemplateEntry [[nodiscard]] bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_ATTACK_PVP_ACTIVE_PLAYERS) != 0; } }; +struct GameObjectArtKitEntry +{ + uint32 ID; // 0 + //char* TextureVariation[3] // 1-3 m_textureVariations[3] + //char* AttachModel[4] // 4-8 m_attachModels[4] +}; + struct GameObjectDisplayInfoEntry { uint32 Displayid; // 0 m_ID diff --git a/src/server/shared/DataStores/DBCfmt.h b/src/server/shared/DataStores/DBCfmt.h index 56f87d780..ccdad27d6 100644 --- a/src/server/shared/DataStores/DBCfmt.h +++ b/src/server/shared/DataStores/DBCfmt.h @@ -52,6 +52,7 @@ char constexpr EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx"; char constexpr EmotesTextSoundEntryfmt[] = "niiii"; char constexpr FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx"; char constexpr FactionTemplateEntryfmt[] = "niiiiiiiiiiiii"; +char constexpr GameObjectArtKitfmt[] = "nxxxxxxx"; char constexpr GameObjectDisplayInfofmt[] = "nsxxxxxxxxxxffffffx"; char constexpr GemPropertiesEntryfmt[] = "nixxi"; char constexpr GlyphPropertiesfmt[] = "niii";