diff --git a/apps/installer/includes/functions.sh b/apps/installer/includes/functions.sh index 8b70e4acb..ea901c288 100644 --- a/apps/installer/includes/functions.sh +++ b/apps/installer/includes/functions.sh @@ -223,7 +223,7 @@ function inst_simple_restarter { function inst_download_client_data { # change the following version when needed - local VERSION=v14 + local VERSION=v15 echo "#######################" echo "Client data downloader" diff --git a/data/sql/updates/db_world/2022_05_06_03.sql b/data/sql/updates/db_world/2022_05_06_03.sql new file mode 100644 index 000000000..d7677cf49 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_06_03.sql @@ -0,0 +1,3 @@ +-- DB update 2022_05_06_02 -> 2022_05_06_03 +-- Fix Valkyrion Harpoon Gun SAI to set rooted on passenger exit +UPDATE `smart_scripts` SET `event_type`=28, `event_flags`=0, `comment`='Valkyrion Harpoon Gun - On Passenger exit - Set Rooted On' WHERE `entryorguid`=30066; diff --git a/data/sql/updates/db_world/2022_05_07_00.sql b/data/sql/updates/db_world/2022_05_07_00.sql new file mode 100644 index 000000000..6602d4f9b --- /dev/null +++ b/data/sql/updates/db_world/2022_05_07_00.sql @@ -0,0 +1,3 @@ +-- DB update 2022_05_06_03 -> 2022_05_07_00 +-- +UPDATE `spell_area` SET `quest_start`=12924 WHERE `spell`=55858; diff --git a/data/sql/updates/db_world/2022_05_07_01.sql b/data/sql/updates/db_world/2022_05_07_01.sql new file mode 100644 index 000000000..b0322d0e7 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_07_01.sql @@ -0,0 +1,4 @@ +-- DB update 2022_05_07_00 -> 2022_05_07_01 +-- Remove Orphan Matron Aria duplicate +DELETE FROM `creature` WHERE `id1` = 34365 AND `guid` = 245000; +DELETE FROM `game_event_creature` WHERE `guid` = 245000 AND `eventEntry` = 10; diff --git a/data/sql/updates/db_world/2022_05_08_00.sql b/data/sql/updates/db_world/2022_05_08_00.sql new file mode 100644 index 000000000..fd58cfca0 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_00.sql @@ -0,0 +1,10 @@ +-- DB update 2022_05_07_01 -> 2022_05_08_00 +-- +UPDATE `smart_scripts` SET `action_type`=85 WHERE `entryorguid`=30105 AND `source_type`=0 AND `id`=1; +DELETE FROM `smart_scripts` WHERE `entryorguid`=30331 AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `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 +(30331,0,0,1,27,0,100,512,0,0,0,0,0,53,1,30331,0,0,0,0,1,0,0,0,0,0,0,0,0,'Jokkum - On Passenger boarded - Start WP'), +(30331,0,1,2,61,0,100,512,0,0,0,0,0,1,0,0,0,0,0,0,23,0,0,0,0,0,0,0,0,'Jokkum - On Passenger boarded - Talk1'), +(30331,0,2,0,61,0,100,512,0,0,0,0,0,18,768,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Jokkum - On Passenger boarded - Set ImmuneNPC/PC'), +(30331,0,3,4,40,0,100,512,30,30331,0,0,0,28,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Jokkum - On way pont 22 - Remove all auras'), +(30331,0,4,0,61,0,100,512,0,0,0,0,0,80,3033100,0,0,0,0,0,1,0,0,0,0,0,0,0,0,'Jokkum - On way pont 22 - Actionlist'); diff --git a/data/sql/updates/db_world/2022_05_08_01.sql b/data/sql/updates/db_world/2022_05_08_01.sql new file mode 100644 index 000000000..c05c6df29 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_01.sql @@ -0,0 +1,6 @@ +-- DB update 2022_05_08_00 -> 2022_05_08_01 +-- +DELETE FROM `creature_text` WHERE `CreatureID` = 25740 AND `GroupID` IN (0, 1); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(25740, 0, 0, "Ahune Retreats. His defenses diminish.", 41, 0, 100, 0, 0, 0, 24931, 0, "ahune EMOTE_RETREAT"), +(25740, 1, 0, "Ahune will soon resurface.", 41, 0, 100, 0, 0, 0, 24932, 0, "ahune EMOTE_RESURFACE"); diff --git a/data/sql/updates/db_world/2022_05_08_02.sql b/data/sql/updates/db_world/2022_05_08_02.sql new file mode 100644 index 000000000..d2dd6b77c --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_02.sql @@ -0,0 +1,3 @@ +-- DB update 2022_05_08_01 -> 2022_05_08_02 +-- +UPDATE `areatrigger_teleport` SET `target_map` = 0, `target_position_x` = -7510.59, `target_position_y` = -1041.45, `target_position_z` = 180.911, `target_orientation` = 3.8666 WHERE ID = 2890; diff --git a/data/sql/updates/db_world/2022_05_08_03.sql b/data/sql/updates/db_world/2022_05_08_03.sql new file mode 100644 index 000000000..c3faa802c --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_03.sql @@ -0,0 +1,19 @@ +-- DB update 2022_05_08_02 -> 2022_05_08_03 +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_vem_knockback', 'spell_vem_vengeance'); +DELETE FROM `spell_script_names` WHERE `spell_id` = 18670; -- Delete the current script to prevent stacking. +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(18670, 'spell_vem_knockback'), +(25790, 'spell_vem_vengeance'); + +UPDATE `creature_template` SET `unit_flags` = `unit_flags`|33554432, `flags_extra` = `flags_extra`|128 WHERE `entry` = 15933; + +DELETE FROM `creature_template_addon` WHERE `entry` = 15933; +INSERT INTO `creature_template_addon` (`entry`,`auras`) VALUES +(15933, '25786 26575'); + +DELETE FROM `creature_text` WHERE `CreatureID` IN (15511, 15543, 15544); +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(15511, 0, 0, '%s is devoured!', 16, 0, 100, 0, 0, 0, 11115, 0, 'Lord Kri - EMOTE_DEVOURED'), +(15543, 0, 0, '%s is devoured!', 16, 0, 100, 0, 0, 0, 11115, 0, 'Princess Yauj - EMOTE_DEVOURED'), +(15544, 0, 0, '%s is devoured!', 16, 0, 100, 0, 0, 0, 11115, 0, 'Vem - EMOTE_DEVOURED'); diff --git a/data/sql/updates/db_world/2022_05_08_04.sql b/data/sql/updates/db_world/2022_05_08_04.sql new file mode 100644 index 000000000..4b3796521 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_04.sql @@ -0,0 +1,6 @@ +-- DB update 2022_05_08_03 -> 2022_05_08_04 +-- +DELETE FROM `gossip_menu` WHERE `MenuID` IN (7106,7107); +INSERT INTO `gossip_menu` VALUES (7106, 8363), (7107, 8364); + +UPDATE `gossip_menu_option` SET `ActionMenuID`=7099 WHERE `MenuID`=7107 AND `OptionID`=0; diff --git a/data/sql/updates/db_world/2022_05_08_05.sql b/data/sql/updates/db_world/2022_05_08_05.sql new file mode 100644 index 000000000..1404f2722 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_05.sql @@ -0,0 +1,3 @@ +-- DB update 2022_05_08_04 -> 2022_05_08_05 +-- +UPDATE `page_text` SET `text` = 'Solomon,$B$BThe carrier of this decree has been granted official status as an acting deputy of Stormwind. You may use $ghim:her to find proof of the black dragonflight''s involvement with the Blackrock orcs. Should such proof be found, this deputy shall return said proof to me in Stormwind, at which time I shall release the order to dispense sufficient millitary force to aid Lakeshire.$B$BRegards,$B$B$B$BHighlord Bolvar Fordragon' WHERE `id` = 1471; diff --git a/data/sql/updates/db_world/2022_05_08_06.sql b/data/sql/updates/db_world/2022_05_08_06.sql new file mode 100644 index 000000000..4da71a700 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_06.sql @@ -0,0 +1,5 @@ +-- DB update 2022_05_08_05 -> 2022_05_08_06 +-- +DELETE FROM `spell_dbc` WHERE `id` = 1206; +INSERT INTO `spell_dbc` (`Id`, `Targets`, `CastingTimeIndex`, `ProcChance`, `RangeIndex`, `EquippedItemClass`, `EquippedItemSubclass`, `Effect_1`, `ImplicitTargetA_1`, `EffectTriggerSpell_1`, `Name_Lang_enUS`, `EffectBonusMultiplier_1`, `EffectBonusMultiplier_2`, `EffectBonusMultiplier_3`) VALUES +(1206, 256, 1, 101, 1, -1, -1, 3, 1, 11, 'Dummy Proc', 1, 1, 1); diff --git a/data/sql/updates/db_world/2022_05_08_07.sql b/data/sql/updates/db_world/2022_05_08_07.sql new file mode 100644 index 000000000..404b9451d --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_07.sql @@ -0,0 +1,59 @@ +-- DB update 2022_05_08_06 -> 2022_05_08_07 + +-- Quest You'll Need a Gryphon +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29403; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29403); +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 +(29403, 0, 0, 1, 25, 0, 100, 257, 0, 0, 0, 0, 0, 11, 61646, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Gryphon - On Reset - Cast \'Loaner Vehicle Speed\' (No Repeat)'), +(29403, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Gryphon - On Reset - Set Fly On (No Repeat)'), +(29403, 0, 2, 0, 28, 0, 100, 0, 0, 0, 0, 0, 0, 11, 45472, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Gryphon - On Passenger Removed - Cast \'Parachute\''); + +-- Not necessary +UPDATE `creature_template` SET `npcflag` = 0 WHERE (`entry` = 29403); +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`=29403 AND `spell_id`=49641; + +-- Rename update +UPDATE `spell_script_names` SET `ScriptName`='spell_deliver_gryphon' WHERE `spell_id`=54420 AND `ScriptName`='spell_gen_despawn_self'; + +DELETE FROM `spell_script_names` WHERE `spell_id`=49642 AND `ScriptName`='spell_onslaught_or_call_bone_gryphon'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (49642, 'spell_onslaught_or_call_bone_gryphon'); + +-- An additional + (animation) +-- Is not part of quest! +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29648; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29648); +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 +(29648, 0, 0, 0, 1, 0, 100, 0, 13000, 13000, 13000, 13000, 0, 70, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bone Gryphon - Out of Combat - Respawn Self'), +(29648, 0, 1, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 11, 54476, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bone Gryphon - On Reset - Cast \'Blood Presence\''); + +-- Npc Captured Onslaught Gryphon (animation) +DELETE FROM `creature_template_addon` WHERE (`entry` = 29415); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(29415, 0, 0, 50331648, 1, 0, 0, '57764'); + +-- Observation: TARGET_UNIT_NEARBY_ENTRY (not target) +-- That's why the condition was necessary +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceGroup`=1 AND `SourceEntry`=10727; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,10727,0,0,31,0,3,29415,0,0,0,0,"","Uzo's Ritual of Blood Triggered Transform."); + +-- Continuing the event after the end +SET @NPC_UZO_DEATHCALLER := 29405; + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = @NPC_UZO_DEATHCALLER; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = @NPC_UZO_DEATHCALLER); +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = @NPC_UZO_DEATHCALLER*100); +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 +(@NPC_UZO_DEATHCALLER, 0, 0, 0, 20, 0, 100, 0, 12814, 0, 0, 0, 0, 80, @NPC_UZO_DEATHCALLER*100, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Uzo Deathcaller - On Quest \'You\'ll Need a Gryphon\' Finished - Run Script'), +(@NPC_UZO_DEATHCALLER*100, 9, 0, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 11, 10727, 0, 0, 0, 0, 0, 19, 29415, 0, 0, 0, 0, 0, 0, 0, 'Uzo Deathcaller - Actionlist - Cast \'Uzo`s Ritual of Blood\''), +(@NPC_UZO_DEATHCALLER*100, 9, 1, 0, 0, 0, 100, 0, 8300, 8300, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Uzo Deathcaller - Actionlist - Say Line 0'), +(@NPC_UZO_DEATHCALLER*100, 9, 2, 0, 0, 0, 100, 0, 16000, 16000, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 19, 29415, 0, 0, 0, 0, 0, 0, 0, 'Uzo Deathcaller - Actionlist - Despawn Instant'), +(@NPC_UZO_DEATHCALLER*100, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 70, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Uzo Deathcaller - Actionlist - Respawn Self'); -- in the transformation process there is a visual bug (I mean for magic who use), so it was added + +-- End event +DELETE FROM `creature_text` WHERE `CreatureID`=@NPC_UZO_DEATHCALLER; +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`, `TextRange`, `comment`) VALUES +(@NPC_UZO_DEATHCALLER, 0, 0, 'There you go, mon. Your very own bone gryphon Now let\'s talk about you takin\' it out to fight the Onslaught!', 12 , 0, 100, 0, 0, 0, 30116, 1, ''); diff --git a/data/sql/updates/db_world/2022_05_08_08.sql b/data/sql/updates/db_world/2022_05_08_08.sql new file mode 100644 index 000000000..706cb2d9c --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_08.sql @@ -0,0 +1,34 @@ +-- DB update 2022_05_08_07 -> 2022_05_08_08 +-- Soulflayer +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 11359; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 11359) AND (`source_type` = 0) AND (`id` IN (0, 1, 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 +(11359, 0, 0, 0, 0, 0, 100, 0, 16000, 19000, 2100, 23000, 0, 11, 22678, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 'Soulflayer - In Combat - Cast \'Fear\''), +(11359, 0, 1, 0, 0, 0, 100, 0, 10000, 14000, 20000, 22000, 0, 11, 24619, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Soulflayer - In Combat - Cast \'Soul Tap\''), +(11359, 0, 3, 0, 2, 0, 100, 1, 0, 30, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Soulflayer - Between 0-30% Health - Say Line 0 (No Repeat)'); + +-- Gurubashi Berserker +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 11352; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 11352); +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 +(11352, 0, 0, 0, 0, 0, 100, 0, 15000, 15000, 15000, 15000, 0, 11, 16508, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Berserker - In Combat - Cast \'Intimidating Roar\''), +(11352, 0, 1, 0, 0, 0, 100, 0, 10000, 10000, 15000, 15000, 0, 11, 11130, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Berserker - In Combat - Cast \'Knock Away\''), +(11352, 0, 2, 0, 0, 0, 100, 0, 3000, 3000, 13000, 18000, 0, 11, 15588, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Berserker - In Combat - Cast \'Thunderclap\''), +(11352, 0, 3, 0, 2, 0, 100, 1, 0, 30, 0, 0, 0, 11, 8269, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Berserker - Between 0-30% Health - Cast \'Frenzy\' (No Repeat)'), +(11352, 0, 4, 0, 2, 0, 100, 1, 0, 30, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Berserker - Between 0-30% Health - Say Line 0 (No Repeat)'); + +-- Gurubashi Axe Thrower +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 11350; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 11350) AND (`source_type` = 0) AND (`id` IN (0, 1, 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 +(11350, 0, 0, 0, 4, 0, 30, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Axe Thrower - On Aggro - Say Line 1'), +(11350, 0, 1, 0, 9, 0, 100, 1, 5, 30, 1500, 2000, 0, 11, 22887, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Axe Thrower - Within 5-30 Range - Cast \'Throw\''), +(11350, 0, 3, 0, 2, 0, 100, 1, 0, 30, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Gurubashi Axe Thrower - Between 0-30% Health - Say Line 0 (No Repeat)'); + +-- Razzashi Serpent +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 11371; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 11371); +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 +(11371, 0, 0, 0, 0, 0, 100, 0, 7000, 9000, 12000, 15000, 0, 11, 20539, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Razzashi Serpent - In Combat - Cast \'Fatal Bite\''), +(11371, 0, 1, 0, 0, 0, 100, 0, 9000, 11000, 14000, 17000, 0, 11, 24002, 1, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 'Razzashi Serpent - In Combat - Cast \'Tranquilizing Poison\''), +(11371, 0, 2, 0, 0, 0, 100, 0, 3000, 5000, 11000, 11000, 0, 11, 12097, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Razzashi Serpent - In Combat - Cast \'Pierce Armor\''); diff --git a/data/sql/updates/db_world/2022_05_08_09.sql b/data/sql/updates/db_world/2022_05_08_09.sql new file mode 100644 index 000000000..b16c1aa87 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_08_09.sql @@ -0,0 +1,3 @@ +-- DB update 2022_05_08_08 -> 2022_05_08_09 +-- +UPDATE `quest_template` SET `AllowableRaces`=1024 WHERE `id`=10366; diff --git a/data/sql/updates/db_world/2022_05_09_00.sql b/data/sql/updates/db_world/2022_05_09_00.sql new file mode 100644 index 000000000..3381acc92 --- /dev/null +++ b/data/sql/updates/db_world/2022_05_09_00.sql @@ -0,0 +1,13 @@ +-- DB update 2022_05_08_09 -> 2022_05_09_00 +-- Clear Auriaya's creature_text (duplicate and EMOTE_DEATH missing), CN locale is also wrong +DELETE FROM `creature_text` WHERE `CreatureID`=33515; + +-- EMOTE_DEATH has no BroadcastTextID +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(33515, 0, 0, 'Some things are better left alone!', 14, 0, 100, 0, 0, 15473, 34341, 0, 'Auriaya SAY_AGGRO'), +(33515, 1, 0, 'The secret dies with you.', 14, 0, 100, 0, 0, 15474, 34354, 0, 'Auriaya SAY_SLAY_1'), +(33515, 1, 1, 'There is no escape!', 14, 0, 100, 0, 0, 15475, 37177, 0, 'Auriaya SAY_SLAY_2'), +(33515, 2, 0, 'You waste my time!', 14, 0, 100, 0, 0, 15477, 34358, 0, 'Auriaya SAY_BERSERK'), +(33515, 3, 0, '%s screams in agony.', 16, 0, 100, 0, 0, 15476, 0, 0, 'Auriaya EMOTE_DEATH'), +(33515, 4, 0, '%s begins to cast Terrifying Screech.', 41, 0, 100, 0, 0, 0, 34450, 0, 'Auriaya EMOTE_FEAR'), +(33515, 5, 0, '%s begins to activate the Feral Defender!', 41, 0, 100, 0, 0, 0, 34162, 0, 'Auriaya EMOTE_DEFENDER'); diff --git a/src/common/Collision/Maps/MapDefines.h b/src/common/Collision/Maps/MapDefines.h index 203d62a55..91d27480c 100644 --- a/src/common/Collision/Maps/MapDefines.h +++ b/src/common/Collision/Maps/MapDefines.h @@ -13,7 +13,7 @@ #define SIZE_OF_GRIDS 533.3333f #define MMAP_MAGIC 0x4d4d4150 // 'MMAP' -#define MMAP_VERSION 13 +#define MMAP_VERSION 14 struct MmapTileHeader { @@ -41,11 +41,11 @@ enum NavArea NAV_AREA_EMPTY = 0, // areas 1-60 will be used for destructible areas (currently skipped in vmaps, WMO with flag 1) // ground is the highest value to make recast choose ground over water when merging surfaces very close to each other (shallow water would be walkable) - NAV_AREA_GROUND_STEEP = 11, - NAV_AREA_GROUND = 10, + NAV_AREA_GROUND = 11, + NAV_AREA_GROUND_STEEP = 10, NAV_AREA_WATER = 9, NAV_AREA_MAGMA_SLIME = 8, // don't need to differentiate between them - NAV_AREA_MAX_VALUE = NAV_AREA_GROUND_STEEP, + NAV_AREA_MAX_VALUE = NAV_AREA_GROUND, NAV_AREA_MIN_VALUE = NAV_AREA_MAGMA_SLIME, NAV_AREA_ALL_MASK = 0x3F // max allowed value }; @@ -53,10 +53,10 @@ enum NavArea enum NavTerrainFlag { NAV_EMPTY = 0x00, - NAV_GROUND_STEEP = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_GROUND_STEEP), NAV_GROUND = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_GROUND), + NAV_GROUND_STEEP = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_GROUND_STEEP), NAV_WATER = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_WATER), NAV_MAGMA_SLIME = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_MAGMA_SLIME) }; -#endif +#endif /* _MAPDEFINES_H */ diff --git a/src/common/Collision/Maps/TileAssembler.cpp b/src/common/Collision/Maps/TileAssembler.cpp index 806f4e202..955c3959c 100644 --- a/src/common/Collision/Maps/TileAssembler.cpp +++ b/src/common/Collision/Maps/TileAssembler.cpp @@ -327,7 +327,7 @@ namespace VMAP float pos_x; float pos_y; float pos_z; - short type; + short material; }; #pragma pack(pop) //================================================================= @@ -532,19 +532,29 @@ namespace VMAP delete[] vectorarray; } // ----- liquid - liquid = 0; - if (liquidflags & 1) + liquid = nullptr; + if (liquidflags & 3) { - WMOLiquidHeader hlq; READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "LIQU"); READ_OR_RETURN(&blocksize, sizeof(int)); - READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader)); - liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), hlq.type); - uint32 size = hlq.xverts * hlq.yverts; - READ_OR_RETURN(liquid->GetHeightStorage(), size * sizeof(float)); - size = hlq.xtiles * hlq.ytiles; - READ_OR_RETURN(liquid->GetFlagsStorage(), size); + uint32 liquidType; + READ_OR_RETURN(&liquidType, sizeof(uint32)); + if (liquidflags & 1) + { + WMOLiquidHeader hlq; + READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader)); + liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), liquidType); + uint32 size = hlq.xverts * hlq.yverts; + READ_OR_RETURN(liquid->GetHeightStorage(), size * sizeof(float)); + size = hlq.xtiles * hlq.ytiles; + READ_OR_RETURN(liquid->GetFlagsStorage(), size); + } + else + { + liquid = new WmoLiquid(0, 0, Vector3::zero(), liquidType); + liquid->GetHeightStorage()[0] = bounds.high().z; + } } return true; diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp index 985035415..99375e294 100644 --- a/src/common/Collision/Models/WorldModel.cpp +++ b/src/common/Collision/Models/WorldModel.cpp @@ -107,8 +107,16 @@ namespace VMAP WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3& corner, uint32 type): iTilesX(width), iTilesY(height), iCorner(corner), iType(type) { - iHeight = new float[(width + 1) * (height + 1)]; - iFlags = new uint8[width * height]; + if (width && height) + { + iHeight = new float[(width + 1) * (height + 1)]; + iFlags = new uint8[width * height]; + } + else + { + iHeight = new float[1]; + iFlags = nullptr; + } } WmoLiquid::WmoLiquid(const WmoLiquid& other): iHeight(0), iFlags(0) @@ -157,6 +165,13 @@ namespace VMAP bool WmoLiquid::GetLiquidHeight(const Vector3& pos, float& liqHeight) const { + // simple case + if (!iFlags) + { + liqHeight = iHeight[0]; + return true; + } + float tx_f = (pos.x - iCorner.x) / LIQUID_TILE_SIZE; uint32 tx = uint32(tx_f); if (tx_f < 0.0f || tx >= iTilesX) @@ -219,8 +234,8 @@ namespace VMAP { return 2 * sizeof(uint32) + sizeof(Vector3) + - (iTilesX + 1) * (iTilesY + 1) * sizeof(float) + - iTilesX * iTilesY; + sizeof(uint32) + + (iFlags ? ((iTilesX + 1) * (iTilesY + 1) * sizeof(float) + iTilesX * iTilesY) : sizeof(float)); } bool WmoLiquid::writeToFile(FILE* wf) @@ -231,12 +246,17 @@ namespace VMAP fwrite(&iCorner, sizeof(Vector3), 1, wf) == 1 && fwrite(&iType, sizeof(uint32), 1, wf) == 1) { - uint32 size = (iTilesX + 1) * (iTilesY + 1); - if (fwrite(iHeight, sizeof(float), size, wf) == size) + if (iTilesX && iTilesY) { - size = iTilesX * iTilesY; - result = fwrite(iFlags, sizeof(uint8), size, wf) == size; + uint32 size = (iTilesX + 1) * (iTilesY + 1); + if (fwrite(iHeight, sizeof(float), size, wf) == size) + { + size = iTilesX * iTilesY; + result = fwrite(iFlags, sizeof(uint8), size, wf) == size; + } } + else + result = fwrite(iHeight, sizeof(float), 1, wf) == 1; } return result; @@ -252,13 +272,21 @@ namespace VMAP fread(&liquid->iCorner, sizeof(Vector3), 1, rf) == 1 && fread(&liquid->iType, sizeof(uint32), 1, rf) == 1) { - uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1); - liquid->iHeight = new float[size]; - if (fread(liquid->iHeight, sizeof(float), size, rf) == size) + if (liquid->iTilesX && liquid->iTilesY) { - size = liquid->iTilesX * liquid->iTilesY; - liquid->iFlags = new uint8[size]; - result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size; + uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1); + liquid->iHeight = new float[size]; + if (fread(liquid->iHeight, sizeof(float), size, rf) == size) + { + size = liquid->iTilesX * liquid->iTilesY; + liquid->iFlags = new uint8[size]; + result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size; + } + } + else + { + liquid->iHeight = new float[1]; + result = fread(liquid->iHeight, sizeof(float), 1, rf) == 1; } } diff --git a/src/common/Collision/VMapDefinitions.h b/src/common/Collision/VMapDefinitions.h index f83790da0..c2094a722 100644 --- a/src/common/Collision/VMapDefinitions.h +++ b/src/common/Collision/VMapDefinitions.h @@ -23,8 +23,8 @@ namespace VMAP { - const char VMAP_MAGIC[] = "VMAP_4.6"; - const char RAW_VMAP_MAGIC[] = "VMAP046"; // used in extracted vmap files with raw data + const char VMAP_MAGIC[] = "VMAP_4.7"; + const char RAW_VMAP_MAGIC[] = "VMAP047"; // used in extracted vmap files with raw data const char GAMEOBJECT_MODELS[] = "GameObjectModels.dtree"; // defined in TileAssembler.cpp currently... diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 1f2997681..44883f1fb 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -392,11 +392,13 @@ void Guardian::InitStats(uint32 duration) { Minion::InitStats(duration); - Unit* m_owner = GetOwner(); - InitStatsForLevel(m_owner->getLevel()); + if (Unit* m_owner = GetOwner()) + { + InitStatsForLevel(m_owner->getLevel()); - if (m_owner->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - m_charmInfo->InitCharmCreateSpells(); + if (m_owner->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) + m_charmInfo->InitCharmCreateSpells(); + } SetReactState(REACT_AGGRESSIVE); } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index b36eabc93..306a30db7 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -349,14 +349,14 @@ public: m_flags = 0; } - [[nodiscard]] T_FLAGS GetFlags() const { return m_flags; } - [[nodiscard]] bool HasFlag(FLAG_TYPE flag) const { return m_flags & (1 << flag); } - void AddFlag(FLAG_TYPE flag) { m_flags |= (1 << flag); } - void DelFlag(FLAG_TYPE flag) { m_flags &= ~(1 << flag); } + [[nodiscard]] T_FLAGS GetFlags() const { return m_flags; } + [[nodiscard]] bool HasFlag(FLAG_TYPE flag) const { return m_flags & (1 << flag); } + void AddFlag(FLAG_TYPE flag) { m_flags |= (1 << flag); } + void DelFlag(FLAG_TYPE flag) { m_flags &= ~(1 << flag); } [[nodiscard]] T_VALUES GetValue(FLAG_TYPE flag) const { return m_values[flag]; } - void SetValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] = value; } - void AddValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] += value; } + void SetValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] = value; } + void AddValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] += value; } private: T_VALUES m_values[ARRAY_SIZE]; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 93812dfac..e0240d1a0 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2947,7 +2947,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo // Check for attack from behind // xinef: if from behind or spell requires cast from behind - if (!victim->HasInArc(M_PI, this) || spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET)) + if (!victim->HasInArc(M_PI, this)) { if (!victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) { @@ -2958,6 +2958,11 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canParry = false; canBlock = false; } + else // Only deterrence as of 3.3.5 + { + if (spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET)) + canParry = false; + } } // Check creatures flags_extra for disable parry diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index e22d4f5b2..19bcc766c 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -807,6 +807,14 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) { if (pProto->SellPrice > 0) { + uint32 money = pProto->SellPrice * count; + if (_player->GetMoney() >= MAX_MONEY_AMOUNT - money) // prevent exceeding gold limit + { + _player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, nullptr, nullptr); + _player->SendSellError(SELL_ERR_UNK, creature, itemguid, 0); + return; + } + if (sWorld->getBoolConfig(CONFIG_ITEMDELETE_VENDOR)) recoveryItem(pItem); @@ -839,7 +847,6 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) _player->UpdateTitansGrip(); } - uint32 money = pProto->SellPrice * count; _player->ModifyMoney(money); _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money); } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index a9856ddaf..eb5d57836 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1336,7 +1336,8 @@ GridMap::GridMap() _maxHeight = nullptr; _minHeight = nullptr; // Liquid data - _liquidType = 0; + _liquidGlobalEntry = 0; + _liquidGlobalFlags = 0; _liquidOffX = 0; _liquidOffY = 0; _liquidWidth = 0; @@ -1513,7 +1514,8 @@ bool GridMap::loadLiquidData(FILE* in, uint32 offset, uint32 /*size*/) if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapLiquidMagic.asUInt) return false; - _liquidType = header.liquidType; + _liquidGlobalEntry = header.liquidType; + _liquidGlobalFlags = header.liquidFlags; _liquidOffX = header.offsetX; _liquidOffY = header.offsetY; _liquidWidth = header.width; @@ -1893,7 +1895,7 @@ inline LiquidData const GridMap::GetLiquidData(float x, float y, float z, float LiquidData liquidData; // Check water type (if no water return) - if (_liquidType || _liquidFlags) + if (_liquidGlobalFlags || _liquidFlags) { // Get cell float cx = MAP_RESOLUTION * (32 - x / SIZE_OF_GRIDS); @@ -1903,38 +1905,34 @@ inline LiquidData const GridMap::GetLiquidData(float x, float y, float z, float int y_int = (int) cy & (MAP_RESOLUTION - 1); // Check water type in cell - int idx = (x_int >> 3) * 16 + (y_int >> 3); - uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType; - uint32 entry = 0; - if (_liquidEntry) + int idx=(x_int>>3)*16 + (y_int>>3); + uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidGlobalFlags; + uint32 entry = _liquidEntry ? _liquidEntry[idx] : _liquidGlobalEntry; + if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(entry)) { - if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx])) + type &= MAP_LIQUID_TYPE_DARK_WATER; + uint32 liqTypeIdx = liquidEntry->Type; + if (entry < 21) { - entry = liquidEntry->Id; - type &= MAP_LIQUID_TYPE_DARK_WATER; - uint32 liqTypeIdx = liquidEntry->Type; - if (entry < 21) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) { - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) + uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; + if (!overrideLiquid && area->zone) { - uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; - if (!overrideLiquid && area->zone) - { - area = sAreaTableStore.LookupEntry(area->zone); - if (area) - overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; - } + area = sAreaTableStore.LookupEntry(area->zone); + if (area) + overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; + } - if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) - { - entry = overrideLiquid; - liqTypeIdx = liq->Type; - } + if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) + { + entry = overrideLiquid; + liqTypeIdx = liq->Type; } } - - type |= 1 << liqTypeIdx; } + + type |= 1 << liqTypeIdx; } // Check req liquid type mask diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index b957baa35..cfa5fba90 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -128,7 +128,8 @@ struct map_heightHeader struct map_liquidHeader { uint32 fourcc; - uint16 flags; + uint8 flags; + uint8 liquidFlags; uint16 liquidType; uint8 offsetX; uint8 offsetY; @@ -158,7 +159,6 @@ enum LiquidStatus #define MAP_ALL_LIQUIDS (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME) #define MAP_LIQUID_TYPE_DARK_WATER 0x10 -#define MAP_LIQUID_TYPE_WMO_WATER 0x20 #define MAX_HEIGHT 100000.0f // can be use for find ground height at surface #define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE @@ -227,7 +227,8 @@ class GridMap uint8* _liquidFlags; float* _liquidMap; uint16 _gridArea; - uint16 _liquidType; + uint16 _liquidGlobalEntry; + uint8 _liquidGlobalFlags; uint8 _liquidOffX; uint8 _liquidOffY; uint8 _liquidWidth; diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 6dd0d8f8a..6d2cdcb95 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -608,11 +608,11 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, } } -void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/) +void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/, ObjectGuid targetGUID /*= ObjectGuid::Empty*/) { G3D::Vector3 dest = path.GetActualEndPosition(); - MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH); + MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH, nullptr, false, 0.0f, targetGUID); // Charge movement is not started when using EVENT_CHARGE_PREPATH Movement::MoveSplineInit init(_owner); diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 718177828..73d8144c6 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -219,7 +219,7 @@ public: void MoveTakeoff(uint32 id, float x, float y, float z, float speed = 0.0f); // pussywizard: added for easy calling by passing 3 floats x, y, z void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, const Movement::PointsArray* path = nullptr, bool generatePath = false, float orientation = 0.0f, ObjectGuid targetGUID = ObjectGuid::Empty); - void MoveCharge(PathGenerator const& path, float speed = SPEED_CHARGE); + void MoveCharge(PathGenerator const& path, float speed = SPEED_CHARGE, ObjectGuid targetGUID = ObjectGuid::Empty); void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ); void MoveJumpTo(float angle, float speedXY, float speedZ); void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = 0) diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp index 0d5b1919b..5f62e542c 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp @@ -27,304 +27,31 @@ #define MIN_QUIET_DISTANCE 28.0f #define MAX_QUIET_DISTANCE 43.0f -template -void FleeingMovementGenerator::_setTargetLocation(T* owner) -{ - if (!owner) - return; - - if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) - { - return; - } - - if (!_setMoveData(owner)) - return; - - float x, y, z; - if (!_getPoint(owner, x, y, z)) { - i_nextCheckTime.Reset(100); - return; - } - - owner->AddUnitState(UNIT_STATE_FLEEING_MOVE); - - Movement::MoveSplineInit init(owner); - init.MoveTo(x, y, z, true); - init.SetWalk(false); - init.Launch(); -} - -template -bool FleeingMovementGenerator::_getPoint(T* owner, float& x, float& y, float& z) -{ - if (!owner) - return false; - - const Map* _map = owner->GetMap(); - - x = owner->GetPositionX(); - y = owner->GetPositionY(); - z = owner->GetPositionZ(); - - float temp_x, temp_y, angle; - // primitive path-finding - for (uint8 i = 0; i < 18; ++i) - { - if (i_only_forward && i > 2) - break; - - float distance = 5.0f; - - switch (i) - { - case 0: - angle = i_cur_angle; - break; - case 1: - angle = i_cur_angle; - distance /= 2; - break; - case 2: - angle = i_cur_angle; - distance /= 4; - break; - case 3: - angle = i_cur_angle + static_cast(M_PI / 4); - break; - case 4: - angle = i_cur_angle - static_cast(M_PI / 4); - break; - case 5: - angle = i_cur_angle + static_cast(M_PI / 4); - distance /= 2; - break; - case 6: - angle = i_cur_angle - static_cast(M_PI / 4); - distance /= 2; - break; - case 7: - angle = i_cur_angle + static_cast(M_PI / 2); - break; - case 8: - angle = i_cur_angle - static_cast(M_PI / 2); - break; - case 9: - angle = i_cur_angle + static_cast(M_PI / 2); - distance /= 2; - break; - case 10: - angle = i_cur_angle - static_cast(M_PI / 2); - distance /= 2; - break; - case 11: - angle = i_cur_angle + static_cast(M_PI / 4); - distance /= 4; - break; - case 12: - angle = i_cur_angle - static_cast(M_PI / 4); - distance /= 4; - break; - case 13: - angle = i_cur_angle + static_cast(M_PI / 2); - distance /= 4; - break; - case 14: - angle = i_cur_angle - static_cast(M_PI / 2); - distance /= 4; - break; - case 15: - angle = i_cur_angle + static_cast(3 * M_PI / 4); - distance /= 2; - break; - case 16: - angle = i_cur_angle - static_cast(3 * M_PI / 4); - distance /= 2; - break; - case 17: - angle = i_cur_angle + static_cast(M_PI); - distance /= 2; - break; - default: - angle = 0.0f; - distance = 0.0f; - break; - } - - temp_x = x + distance * cos(angle); - temp_y = y + distance * std::sin(angle); - float temp_z = z; - - if (!_map->CanReachPositionAndGetValidCoords(owner, temp_x, temp_y, temp_z, true, true)) - { - break; - } - - if (!(temp_z - z) || distance / std::fabs(temp_z - z) > 1.0f) - { - float temp_z_left = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle + static_cast(M_PI / 2)), temp_y + 1.0f * std::sin(angle + static_cast(M_PI / 2)), z, true); - float temp_z_right = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle - static_cast(M_PI / 2)), temp_y + 1.0f * std::sin(angle - static_cast(M_PI / 2)), z, true); - if (std::fabs(temp_z_left - temp_z) < 1.2f && std::fabs(temp_z_right - temp_z) < 1.2f) - { - // use new values - x = temp_x; - y = temp_y; - z = temp_z; - return true; - } - } - } - i_to_distance_from_caster = 0.0f; - i_nextCheckTime.Reset(urand(500, 1000)); - return false; -} - -template -bool FleeingMovementGenerator::_setMoveData(T* owner) -{ - float cur_dist_xyz = owner->GetDistance(i_caster_x, i_caster_y, i_caster_z); - - if (i_to_distance_from_caster > 0.0f) - { - if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) || - // if we reach lower distance - (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) || - // if we can't be close - (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) || - // if we reach bigger distance - (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far - (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE)) - // if we leave 'quiet zone' - { - // we are very far or too close, stopping - i_to_distance_from_caster = 0.0f; - i_nextCheckTime.Reset(urand(500, 1000)); - return false; - } - else - { - // now we are running, continue - i_last_distance_from_caster = cur_dist_xyz; - return true; - } - } - - float cur_dist; - float angle_to_caster; - - if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID)) - { - cur_dist = fright->GetDistance(owner); - if (cur_dist < cur_dist_xyz) - { - i_caster_x = fright->GetPositionX(); - i_caster_y = fright->GetPositionY(); - i_caster_z = fright->GetPositionZ(); - angle_to_caster = fright->GetAngle(owner); - } - else - { - cur_dist = cur_dist_xyz; - angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast(M_PI); - } - } - else - { - cur_dist = cur_dist_xyz; - angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast(M_PI); - } - - // if we too close may use 'path-finding' else just stop - i_only_forward = cur_dist >= MIN_QUIET_DISTANCE / 3; - - //get angle and 'distance from caster' to run - float angle; - - if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time - { - angle = (float)rand_norm() * (1.0f - cur_dist / MIN_QUIET_DISTANCE) * static_cast(M_PI / 3) + (float)rand_norm() * static_cast(M_PI * 2 / 3); - i_to_distance_from_caster = MIN_QUIET_DISTANCE; - i_only_forward = true; - } - else if (cur_dist < MIN_QUIET_DISTANCE) - { - angle = static_cast(M_PI / 6) + (float)rand_norm() * static_cast(M_PI * 2 / 3); - i_to_distance_from_caster = cur_dist * 2 / 3 + (float)rand_norm() * (MIN_QUIET_DISTANCE - cur_dist * 2 / 3); - } - else if (cur_dist > MAX_QUIET_DISTANCE) - { - angle = (float)rand_norm() * static_cast(M_PI / 3) + static_cast(M_PI * 2 / 3); - i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm() * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); - } - else - { - angle = (float)rand_norm() * static_cast(M_PI); - i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm() * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f); - } - - int8 sign = (float)rand_norm() > 0.5f ? 1 : -1; - i_cur_angle = sign * angle + angle_to_caster; - - // current distance - i_last_distance_from_caster = cur_dist; - - return true; -} - template void FleeingMovementGenerator::DoInitialize(T* owner) { if (!owner) + { return; + } + _path = nullptr; owner->SetUnitFlag(UNIT_FLAG_FLEEING); - owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); - - _Init(owner); - - if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID)) - { - i_caster_x = fright->GetPositionX(); - i_caster_y = fright->GetPositionY(); - i_caster_z = fright->GetPositionZ(); - } - else - { - i_caster_x = owner->GetPositionX(); - i_caster_y = owner->GetPositionY(); - i_caster_z = owner->GetPositionZ(); - } - - i_only_forward = true; - i_cur_angle = 0.0f; - i_last_distance_from_caster = 0.0f; - i_to_distance_from_caster = 0.0f; - _setTargetLocation(owner); + owner->AddUnitState(UNIT_STATE_FLEEING); + SetTargetLocation(owner); } -template<> -void FleeingMovementGenerator::_Init(Creature* owner) +template +void FleeingMovementGenerator::DoFinalize(T*) { - if (!owner) - return; - - //owner->SetTargetGuid(ObjectGuid()); - is_water_ok = owner->CanEnterWater(); - is_land_ok = owner->CanWalk(); -} - -template<> -void FleeingMovementGenerator::_Init(Player* ) -{ - is_water_ok = true; - is_land_ok = true; } template<> void FleeingMovementGenerator::DoFinalize(Player* owner) { owner->RemoveUnitFlag(UNIT_FLAG_FLEEING); - owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); + owner->ClearUnitState(UNIT_STATE_FLEEING); + owner->StopMoving(); } template<> @@ -332,8 +59,11 @@ void FleeingMovementGenerator::DoFinalize(Creature* owner) { owner->RemoveUnitFlag(UNIT_FLAG_FLEEING); owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); - if (owner->GetVictim()) - owner->SetTarget(owner->GetVictim()->GetGUID()); + + if (Unit* victim = owner->GetVictim()) + { + owner->SetTarget(victim->GetGUID()); + } } template @@ -343,36 +73,140 @@ void FleeingMovementGenerator::DoReset(T* owner) } template -bool FleeingMovementGenerator::DoUpdate(T* owner, uint32 time_diff) +bool FleeingMovementGenerator::DoUpdate(T* owner, uint32 diff) { if (!owner || !owner->IsAlive()) + { return false; + } if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { + _path = nullptr; + _interrupt = true; owner->StopMoving(); return true; } + else + _interrupt = false; - i_nextCheckTime.Update(time_diff); - if (i_nextCheckTime.Passed() && owner->movespline->Finalized()) - _setTargetLocation(owner); + _timer.Update(diff); + if (!_interrupt && _timer.Passed() && owner->movespline->Finalized()) + { + SetTargetLocation(owner); + } return true; } +template +void FleeingMovementGenerator::SetTargetLocation(T* owner) +{ + if (!owner) + { + return; + } + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) + { + _path = nullptr; + _interrupt = true; + owner->StopMoving(); + return; + } + + owner->AddUnitState(UNIT_STATE_FLEEING_MOVE); + + Position destination = owner->GetPosition(); + GetPoint(owner, destination); + + // Add LOS check for target point + if (!owner->IsWithinLOS(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ())) + { + _timer.Reset(200); + return; + } + + if (!_path) + { + _path = std::make_unique(owner); + } + else + { + _path->Clear(); + } + + _path->SetPathLengthLimit(30.0f); + bool result = _path->CalculatePath(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ()); + if (!result || (_path->GetPathType() & PathType(PATHFIND_NOPATH | PATHFIND_SHORTCUT | PATHFIND_FARFROMPOLY))) + { + _timer.Reset(100); + return; + } + + Movement::MoveSplineInit init(owner); + init.MovebyPath(_path->GetPath()); + init.SetWalk(false); + int32 traveltime = init.Launch(); + _timer.Reset(traveltime + urand(800, 1500)); +} + +template +void FleeingMovementGenerator::GetPoint(T* owner, Position& position) +{ + float casterDistance, casterAngle; + if (Unit* fleeTarget = ObjectAccessor::GetUnit(*owner, _fleeTargetGUID)) + { + casterDistance = fleeTarget->GetDistance(owner); + if (casterDistance > 0.2f) + { + casterAngle = fleeTarget->GetAngle(owner); + } + else + { + casterAngle = frand(0.0f, 2.0f * float(M_PI)); + } + } + else + { + casterDistance = 0.0f; + casterAngle = frand(0.0f, 2.0f * float(M_PI)); + } + + float distance, angle; + if (casterDistance < MIN_QUIET_DISTANCE) + { + distance = frand(0.4f, 1.3f) * (MIN_QUIET_DISTANCE - casterDistance); + angle = casterAngle + frand(-float(M_PI) / 8.0f, float(M_PI) / 8.0f); + } + else if (casterDistance > MAX_QUIET_DISTANCE) + { + distance = frand(0.4f, 1.0f) * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE); + angle = -casterAngle + frand(-float(M_PI) / 4.0f, float(M_PI) / 4.0f); + } + else // we are inside quiet range + { + distance = frand(0.6f, 1.2f) * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE); + angle = frand(0.0f, 2.0f * float(M_PI)); + } + + // In MovePositionToFirstCollision we have added owner's orientation + // so now let's subtract it + angle -= owner->GetOrientation(); + + owner->MovePositionToFirstCollision(position, distance, angle); +} + template void FleeingMovementGenerator::DoInitialize(Player*); template void FleeingMovementGenerator::DoInitialize(Creature*); -template bool FleeingMovementGenerator::_setMoveData(Player*); -template bool FleeingMovementGenerator::_setMoveData(Creature*); -template bool FleeingMovementGenerator::_getPoint(Player*, float&, float&, float&); -template bool FleeingMovementGenerator::_getPoint(Creature*, float&, float&, float&); -template void FleeingMovementGenerator::_setTargetLocation(Player*); -template void FleeingMovementGenerator::_setTargetLocation(Creature*); template void FleeingMovementGenerator::DoReset(Player*); template void FleeingMovementGenerator::DoReset(Creature*); template bool FleeingMovementGenerator::DoUpdate(Player*, uint32); template bool FleeingMovementGenerator::DoUpdate(Creature*, uint32); +template void FleeingMovementGenerator::SetTargetLocation(Player*); +template void FleeingMovementGenerator::SetTargetLocation(Creature*); +template void FleeingMovementGenerator::GetPoint(Player*, Position&); +template void FleeingMovementGenerator::GetPoint(Creature*, Position&); void TimedFleeingMovementGenerator::Finalize(Unit* owner) { diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h index 734486026..dfd59a255 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h @@ -23,34 +23,24 @@ template class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovementGenerator > { -public: - FleeingMovementGenerator(ObjectGuid fright) : i_frightGUID(fright), i_nextCheckTime(0) {} + public: + explicit FleeingMovementGenerator(ObjectGuid fleeTargetGUID) : _path(nullptr), _fleeTargetGUID(fleeTargetGUID), _timer(0), _interrupt(false) {} - void DoInitialize(T*); - void DoFinalize(T*); - void DoReset(T*); - bool DoUpdate(T*, uint32); + MovementGeneratorType GetMovementGeneratorType() override { return FLEEING_MOTION_TYPE; } - MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; } + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(T*); + bool DoUpdate(T*, uint32); -private: - void _setTargetLocation(T*); - bool _getPoint(T*, float& x, float& y, float& z); - bool _setMoveData(T* owner); - void _Init(T* ); + private: + void SetTargetLocation(T*); + void GetPoint(T*, Position& position); - bool is_water_ok : 1; - bool is_land_ok : 1; - bool i_only_forward: 1; - - float i_caster_x; - float i_caster_y; - float i_caster_z; - float i_last_distance_from_caster; - float i_to_distance_from_caster; - float i_cur_angle; - ObjectGuid i_frightGUID; - TimeTracker i_nextCheckTime; + std::unique_ptr _path; + ObjectGuid _fleeTargetGUID; + TimeTracker _timer; + bool _interrupt; }; class TimedFleeingMovementGenerator : public FleeingMovementGenerator diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 1e5187024..4d9e89169 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1537,7 +1537,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode if (apply) { // apply glow vision - if (target->GetTypeId() == TYPEID_PLAYER && type == INVISIBILITY_GENERAL) + if (target->GetTypeId() == TYPEID_PLAYER && (type == INVISIBILITY_GENERAL || type == INVISIBILITY_UNK10)) target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); target->m_invisibility.AddFlag(type); @@ -1568,14 +1568,14 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode } if (!found) { + target->m_invisibility.DelFlag(type); + // if not have invisibility auras of type INVISIBILITY_GENERAL // remove glow vision - if (target->GetTypeId() == TYPEID_PLAYER && type == INVISIBILITY_GENERAL) + if (target->GetTypeId() == TYPEID_PLAYER && !target->m_invisibility.HasFlag(INVISIBILITY_GENERAL) && !target->m_invisibility.HasFlag(INVISIBILITY_UNK10)) { target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); } - - target->m_invisibility.DelFlag(type); } } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 9465bb82f..0150cf77a 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4875,7 +4875,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) if (!m_preGeneratedPath) { Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetCombatReach(), unitTarget->GetRelativeAngle(m_caster)); - m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed); + m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed, EVENT_CHARGE, nullptr, false, 0.0f, targetGUID); if (m_caster->GetTypeId() == TYPEID_PLAYER) { @@ -4884,7 +4884,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) } else { - m_caster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed); + m_caster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed, targetGUID); if (m_caster->GetTypeId() == TYPEID_PLAYER) { diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index e7e55bc2a..6f5257301 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1360,14 +1360,6 @@ bool SpellInfo::IsSingleTarget() const if (AttributesEx5 & SPELL_ATTR5_LIMIT_N) return true; - switch (GetSpellSpecific()) - { - case SPELL_SPECIFIC_JUDGEMENT: - return true; - default: - break; - } - return false; } diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 5a68ba402..cf630937c 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -3672,8 +3672,8 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_0].BasePoints = 0; }); - // Krolmir, Hammer of Storms (13010) - ApplySpellFix({ 56606, 56541 }, [](SpellInfo* spellInfo) + // Riding Jokkum + ApplySpellFix({ 56606 }, [](SpellInfo* spellInfo) { spellInfo->Effects[EFFECT_0].BasePoints = 1; }); diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp index 2a72f7628..ff51e0f48 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp @@ -15,330 +15,446 @@ * with this program. If not, see . */ -/* ScriptData -SDName: boss_kri, boss_yauj, boss_vem : The Bug Trio -SD%Complete: 100 -SDComment: -SDCategory: Temple of Ahn'Qiraj -EndScriptData */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "temple_of_ahnqiraj.h" +#include "TaskScheduler.h" enum Spells { - SPELL_CLEAVE = 26350, - SPELL_TOXIC_VOLLEY = 25812, - SPELL_POISON_CLOUD = 38718, //Only Spell with right dmg. - SPELL_ENRAGE = 34624, //Changed cause 25790 is cast on gamers too. Same prob with old explosion of twin emperors. + SPELL_BLOODY_DEATH = 25770, + SPELL_FULL_HEAL = 17683, - SPELL_CHARGE = 26561, - SPELL_KNOCKBACK = 26027, + // Kri + SPELL_CLEAVE = 26350, + SPELL_THRASH = 3391, + SPELL_TOXIC_VOLLEY = 25812, + SPELL_POISON_CLOUD = 26590, - SPELL_HEAL = 25807, - SPELL_FEAR = 19408 + // Vem + SPELL_CHARGE = 26561, + SPELL_KNOCKBACK = 18670, + SPELL_KNOCKDOWN = 19128, + SPELL_VENGEANCE = 25790, + + // Yauj + SPELL_HEAL = 25807, + SPELL_FEAR = 26580, + SPELL_RAVAGE = 3242, + SPELL_DISPEL = 25808, + + NPC_YAUJ_BROOD = 15621 }; -class boss_kri : public CreatureScript +enum Misc { -public: - boss_kri() : CreatureScript("boss_kri") { } + ACTION_CONSUME = 0, - CreatureAI* GetAI(Creature* creature) const override - { - return GetTempleOfAhnQirajAI(creature); - } + EMOTE_DEVOURED = 0, - struct boss_kriAI : public ScriptedAI - { - boss_kriAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } + POINT_CONSUME = 0, - InstanceScript* instance; - - uint32 Cleave_Timer; - uint32 ToxicVolley_Timer; - uint32 Check_Timer; - - bool VemDead; - bool Death; - - void Reset() override - { - Cleave_Timer = urand(4000, 8000); - ToxicVolley_Timer = urand(6000, 12000); - Check_Timer = 2000; - - VemDead = false; - Death = false; - } - - void EnterCombat(Unit* /*who*/) override - { - } - - void JustDied(Unit* /*killer*/) override - { - if (instance->GetData(DATA_BUG_TRIO_DEATH) < 2)// Unlootable if death - me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); - - instance->SetData(DATA_BUG_TRIO_DEATH, 1); - } - void UpdateAI(uint32 diff) override - { - //Return since we have no target - if (!UpdateVictim()) - return; - - //Cleave_Timer - if (Cleave_Timer <= diff) - { - DoCastVictim(SPELL_CLEAVE); - Cleave_Timer = urand(5000, 12000); - } - else Cleave_Timer -= diff; - - //ToxicVolley_Timer - if (ToxicVolley_Timer <= diff) - { - DoCastVictim(SPELL_TOXIC_VOLLEY); - ToxicVolley_Timer = urand(10000, 15000); - } - else ToxicVolley_Timer -= diff; - - if (!HealthAbovePct(5) && !Death) - { - DoCastVictim(SPELL_POISON_CLOUD); - Death = true; - } - - if (!VemDead) - { - //Checking if Vem is dead. If yes we will enrage. - if (Check_Timer <= diff) - { - if (instance->GetData(DATA_VEMISDEAD)) - { - DoCast(me, SPELL_ENRAGE); - VemDead = true; - } - Check_Timer = 2000; - } - else Check_Timer -= diff; - } - - DoMeleeAttackIfReady(); - } - }; + VEM_WAYPOINT_PATH = 876030 }; -class boss_vem : public CreatureScript +const Position resetPoint = { -8582.0f, 2047.0f, -1.62f }; // Taken from CMangos + +struct boss_bug_trio : public BossAI { public: - boss_vem() : CreatureScript("boss_vem") { } + boss_bug_trio(Creature* creature) : BossAI(creature, DATA_BUG_TRIO) { Reset(); } - CreatureAI* GetAI(Creature* creature) const override + bool CheckInRoom() override { - return GetTempleOfAhnQirajAI(creature); + if (me->GetExactDist2d(resetPoint) <= 10.f) + { + EnterEvadeMode(EVADE_REASON_BOUNDARY); + return false; + } + + return true; } - struct boss_vemAI : public ScriptedAI + void EnterCombatWithTrio(Unit* who) { - boss_vemAI(Creature* creature) : ScriptedAI(creature) + if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM))) + if (vem->GetGUID() != me->GetGUID()) + vem->GetAI()->AttackStart(who); + if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI))) + if (kri->GetGUID() != me->GetGUID()) + kri->GetAI()->AttackStart(who); + if (Creature* yauj = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_YAUJ))) + if (yauj->GetGUID() != me->GetGUID()) + yauj->GetAI()->AttackStart(who); + } + + void EvadeAllBosses(EvadeReason why) + { + if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM))) { - instance = creature->GetInstanceScript(); + if (vem->GetGUID() != me->GetGUID()) + { + if (vem->IsAlive() && !vem->IsInEvadeMode()) + vem->AI()->EnterEvadeMode(why); + else + vem->Respawn(); + } } - InstanceScript* instance; - - uint32 Charge_Timer; - uint32 KnockBack_Timer; - uint32 Enrage_Timer; - - bool Enraged; - - void Reset() override + if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI))) { - Charge_Timer = urand(15000, 27000); - KnockBack_Timer = urand(8000, 20000); - Enrage_Timer = 120000; - - Enraged = false; + if (kri->GetGUID() != me->GetGUID()) + { + if (kri->IsAlive() && !kri->IsInEvadeMode()) + kri->AI()->EnterEvadeMode(why); + else + kri->Respawn(); + } } - void JustDied(Unit* /*killer*/) override + if (Creature* yauj = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_YAUJ))) { - instance->SetData(DATA_VEM_DEATH, 0); - if (instance->GetData(DATA_BUG_TRIO_DEATH) < 2)// Unlootable if death - me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); - instance->SetData(DATA_BUG_TRIO_DEATH, 1); + if (yauj->GetGUID() != me->GetGUID()) + { + if (yauj->IsAlive() && !yauj->IsInEvadeMode()) + yauj->AI()->EnterEvadeMode(why); + else + yauj->Respawn(); + } + } + } + + void DoAction(int32 action) override + { + if (action != ACTION_CONSUME || dying) + { + return; } - void EnterCombat(Unit* /*who*/) override - { - } + isEating = true; + me->SetSpeed(MOVE_RUN, 45.f/7.f); // From sniffs + me->SetReactState(REACT_PASSIVE); + } - void UpdateAI(uint32 diff) override + void MovementInform(uint32 type, uint32 /*id*/) override + { + if (type != POINT_MOTION_TYPE) + return; + + me->GetMotionMaster()->MoveIdle(); + me->SetSpeed(MOVE_RUN, 15.f/7.f); // From sniffs + DoCastSelf(SPELL_FULL_HEAL, true); + DoResetThreat(); + isEating = false; + + _scheduler.Schedule(4s, [this](TaskContext /*context*/) { - //Return since we have no target - if (!UpdateVictim()) + me->SetReactState(REACT_AGGRESSIVE); + if (Unit* target = me->GetVictim()) + { + me->GetMotionMaster()->MoveChase(target); + AttackStart(target); + } + }); + } + + void EnterEvadeMode(EvadeReason why) override + { + BossAI::EnterEvadeMode(why); + EvadeAllBosses(why); + } + + void Reset() override + { + BossAI::Reset(); + _scheduler.CancelAll(); + dying = false; + isEating = false; + instance->SetData(DATA_BUG_TRIO_DEATH, 0); + me->SetSpeed(MOVE_RUN, 15.f / 7.f); // From sniffs + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetControlled(false, UNIT_STATE_ROOT); + + if (me->GetEntry() == NPC_VEM) + { + me->GetMotionMaster()->MovePath(VEM_WAYPOINT_PATH, true); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || isEating || !CheckInRoom()) + return; + + _scheduler.Update(diff, [this] + { + DoMeleeAttackIfReady(); + }); + } + + void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (me->HealthBelowPctDamaged(1, damage) && instance->GetData(DATA_BUG_TRIO_DEATH) < 2 && who->GetGUID() != me->GetGUID()) + { + damage = 0; + if (isEating) return; - //Charge_Timer - if (Charge_Timer <= diff) + _scheduler.CancelAll(); + me->SetStandState(UNIT_STAND_STATE_DEAD); + me->SetReactState(REACT_PASSIVE); + me->SetControlled(true, UNIT_STATE_ROOT); + + DoFinalSpell(); + + // Move the other bugs to this bug position + if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM))) { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0); - if (target) + if (vem->GetGUID() != me->GetGUID()) { - DoCast(target, SPELL_CHARGE); - //me->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true, 1); - AttackStart(target); + vem->AI()->DoAction(ACTION_CONSUME); + vem->GetMotionMaster()->MovePoint(POINT_CONSUME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); } - - Charge_Timer = urand(8000, 16000); } - else Charge_Timer -= diff; - - //KnockBack_Timer - if (KnockBack_Timer <= diff) + if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI))) { - DoCastVictim(SPELL_KNOCKBACK); - if (DoGetThreat(me->GetVictim())) - DoModifyThreatPercent(me->GetVictim(), -80); - KnockBack_Timer = urand(15000, 25000); + if (kri->GetGUID() != me->GetGUID()) + { + kri->AI()->DoAction(ACTION_CONSUME); + kri->GetMotionMaster()->MovePoint(POINT_CONSUME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); + } } - else KnockBack_Timer -= diff; - - //Enrage_Timer - if (!Enraged && Enrage_Timer <= diff) + if (Creature* yauj = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_YAUJ))) { - DoCast(me, SPELL_ENRAGE); - Enraged = true; + if (yauj->GetGUID() != me->GetGUID()) + { + yauj->AI()->DoAction(ACTION_CONSUME); + yauj->GetMotionMaster()->MovePoint(POINT_CONSUME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); + } } - else Charge_Timer -= diff; - DoMeleeAttackIfReady(); + _scheduler.Schedule(4s, [this](TaskContext /*context*/) + { + if (!me->IsInEvadeMode()) + { + DoCastSelf(SPELL_BLOODY_DEATH, true); + Talk(EMOTE_DEVOURED); + me->DespawnOrUnsummon(1000); + } + }); } - }; + } + + void DoFinalSpell() + { + switch (me->GetEntry()) + { + case NPC_KRI: + DoCastSelf(SPELL_POISON_CLOUD, true); + break; + case NPC_VEM: + DoCastSelf(SPELL_VENGEANCE, true); + break; + case NPC_YAUJ: + for (uint8 i = 0; i < 10; ++i) + { + Position randomPos; + me->GetRandomContactPoint(me, randomPos.m_positionX, randomPos.m_positionY, randomPos.m_positionZ); + if (Creature* summon = me->SummonCreature(NPC_YAUJ_BROOD, randomPos)) + DoZoneInCombat(summon); + } + break; + default: + break; + } + } + + void JustDied(Unit* killer) override + { + if (killer->GetGUID() == me->GetGUID()) + { + instance->SetData(DATA_BUG_TRIO_DEATH, 1); + me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); + return; + } + + BossAI::JustDied(killer); + } + + TaskScheduler _scheduler; + bool dying; + bool isEating; }; -class boss_yauj : public CreatureScript +struct boss_kri : public boss_bug_trio { -public: - boss_yauj() : CreatureScript("boss_yauj") { } - - CreatureAI* GetAI(Creature* creature) const override + boss_kri(Creature* creature) : boss_bug_trio(creature) { - return GetTempleOfAhnQirajAI(creature); } - struct boss_yaujAI : public ScriptedAI + void EnterCombat(Unit* who) override { - boss_yaujAI(Creature* creature) : ScriptedAI(creature) + EnterCombatWithTrio(who); + + _scheduler.Schedule(4s, 8s, [this](TaskContext context) { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - - uint32 Heal_Timer; - uint32 Fear_Timer; - uint32 Check_Timer; - - bool VemDead; - - void Reset() override + DoCastVictim(SPELL_CLEAVE); + context.Repeat(5s, 12s); + }) + .Schedule(6s, 30s, [this](TaskContext context) { - Heal_Timer = urand(25000, 40000); - Fear_Timer = urand(12000, 24000); - Check_Timer = 2000; - - VemDead = false; - } - - void JustDied(Unit* /*killer*/) override + DoCastVictim(SPELL_TOXIC_VOLLEY); + context.Repeat(10s, 25s); + }) + .Schedule(6s, [this](TaskContext context) { - if (instance->GetData(DATA_BUG_TRIO_DEATH) < 2)// Unlootable if death - me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE); - instance->SetData(DATA_BUG_TRIO_DEATH, 1); + DoCastVictim(SPELL_THRASH); + context.Repeat(2s, 6s); + }); + } +}; - for (uint8 i = 0; i < 10; ++i) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) +struct boss_vem : public boss_bug_trio +{ + boss_vem(Creature* creature) : boss_bug_trio(creature) + { + } + + void EnterCombat(Unit* who) override + { + EnterCombatWithTrio(who); + + _scheduler.Schedule(15s, 27s, [this](TaskContext context) + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [this](Unit* target) -> bool { - if (Creature* Summoned = me->SummonCreature(15621, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000)) - Summoned->AI()->AttackStart(target); - } - } - } + if (target->GetTypeId() != TYPEID_PLAYER) + return false; + if (me->IsWithinMeleeRange(target) || target == me->GetVictim()) + return false; + if (!me->IsWithinLOS(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ())) + return false; - void EnterCombat(Unit* /*who*/) override + return true; + })) + { + DoCast(target, SPELL_CHARGE); + } + context.Repeat(8s, 16s); + }) + .Schedule(10s, 20s, [this](TaskContext context) { - } - - void UpdateAI(uint32 diff) override + DoCastVictim(SPELL_KNOCKBACK); + context.Repeat(10s, 20s); + }) + .Schedule(5s, 8s, [this](TaskContext context) { - //Return since we have no target - if (!UpdateVictim()) - return; - - //Fear_Timer - if (Fear_Timer <= diff) + DoCastVictim(SPELL_KNOCKDOWN); + context.Repeat(15s, 20s); + }) + .Schedule(1s, [this](TaskContext context) + { + if (instance->GetData(DATA_BUG_TRIO_DEATH) == 2 && !me->HasAura(SPELL_VENGEANCE)) // Vem is the only one left. { - DoCastVictim(SPELL_FEAR); - DoResetThreat(); - Fear_Timer = 20000; + DoCastSelf(SPELL_VENGEANCE, true); } - else Fear_Timer -= diff; + context.Repeat(1s); + }); + } +}; - //Casting Heal to other twins or herself. - if (Heal_Timer <= diff) +struct boss_yauj : public boss_bug_trio +{ + boss_yauj(Creature* creature) : boss_bug_trio(creature) + { + } + + void EnterCombat(Unit* who) override + { + EnterCombatWithTrio(who); + + _scheduler.Schedule(20s, 30s, [this](TaskContext context) + { + if (me->GetHealthPct() <= 93.f) { - switch (urand(0, 2)) - { - case 0: - if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI))) - DoCast(kri, SPELL_HEAL); - break; - case 1: - if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM))) - DoCast(vem, SPELL_HEAL); - break; - case 2: - DoCast(me, SPELL_HEAL); - break; - } - - Heal_Timer = 15000 + rand() % 15000; + DoCastSelf(SPELL_HEAL); } - else Heal_Timer -= diff; - - //Checking if Vem is dead. If yes we will enrage. - if (Check_Timer <= diff) + else if (Unit* friendly = DoSelectLowestHpFriendly(100.f)) { - if (!VemDead) - { - if (instance->GetData(DATA_VEMISDEAD)) - { - DoCast(me, SPELL_ENRAGE); - VemDead = true; - } - } - Check_Timer = 2000; + DoCast(friendly, SPELL_HEAL); } - else Check_Timer -= diff; + context.Repeat(10s, 30s); + }) + .Schedule(12s, 24s, [this](TaskContext context) + { + DoCastAOE(SPELL_FEAR); + DoResetThreat(); + context.Repeat(20s); + }) + .Schedule(12s, [this](TaskContext context) + { + DoCastVictim(SPELL_RAVAGE); + context.Repeat(10s, 15s); + }) + .Schedule(10s, 30s, [this](TaskContext context) + { + std::list targets = DoFindFriendlyCC(50.f); + if (!targets.empty()) + { + if (Creature* target = *(targets.begin())) + me->CastSpell(target, SPELL_DISPEL); + } + context.Repeat(10s, 15s); + }); + } +}; - DoMeleeAttackIfReady(); +class spell_vem_knockback : public SpellScript +{ + PrepareSpellScript(spell_vem_knockback); + + void HandleScriptEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + if (Unit* target = GetHitUnit()) + { + if (Creature* cCaster = GetCaster()->ToCreature()) + { + cCaster->getThreatMgr().modifyThreatPercent(target, -80); + } } - }; + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_vem_knockback::HandleScriptEffect, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +class spell_vem_vengeance : public SpellScript +{ + PrepareSpellScript(spell_vem_vengeance); + + void FilterTargets(std::list& targets) + { + targets.remove_if([&](WorldObject const* target) -> bool + { + return target->GetEntry() != NPC_YAUJ && target->GetEntry() != NPC_VEM && target->GetEntry() != NPC_KRI; + }); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_vem_vengeance::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENTRY); + } }; void AddSC_bug_trio() { - new boss_kri(); - new boss_vem(); - new boss_yauj(); + RegisterCreatureAI(boss_kri); + RegisterCreatureAI(boss_vem); + RegisterCreatureAI(boss_yauj); + RegisterSpellScript(spell_vem_knockback); + RegisterSpellScript(spell_vem_vengeance); } diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp index ebbe586a4..d05e3687d 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp @@ -56,6 +56,7 @@ public: ObjectGuid SkeramGUID; ObjectGuid VemGUID; ObjectGuid KriGUID; + ObjectGuid YaujGUID; ObjectGuid VeklorGUID; ObjectGuid VeknilashGUID; ObjectGuid ViscidusGUID; @@ -88,6 +89,9 @@ public: case NPC_KRI: KriGUID = creature->GetGUID(); break; + case NPC_YAUJ: + YaujGUID = creature->GetGUID(); + break; case NPC_VEKLOR: VeklorGUID = creature->GetGUID(); break; @@ -106,11 +110,6 @@ public: { switch (type) { - case DATA_VEMISDEAD: - if (IsBossDied[0]) - return 1; - break; - case DATA_VEKLORISDEAD: if (IsBossDied[1]) return 1; @@ -140,6 +139,8 @@ public: return VemGUID; case DATA_KRI: return KriGUID; + case DATA_YAUJ: + return YaujGUID; case DATA_VEKLOR: return VeklorGUID; case DATA_VEKNILASH: @@ -155,12 +156,11 @@ public: { switch (type) { - case DATA_VEM_DEATH: - IsBossDied[0] = true; - break; - case DATA_BUG_TRIO_DEATH: - ++BugTrioDeathCount; + if (data != 0) + ++BugTrioDeathCount; + else + BugTrioDeathCount = 0; break; case DATA_VEKLOR_DEATH: diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/temple_of_ahnqiraj.h b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/temple_of_ahnqiraj.h index 8b0a1d524..a41d38da0 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/temple_of_ahnqiraj.h +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/temple_of_ahnqiraj.h @@ -27,8 +27,8 @@ enum DataTypes DATA_SKERAM = 1, DATA_KRI = 2, DATA_VEM = 3, - DATA_VEMISDEAD = 4, - DATA_VEM_DEATH = 5, + DATA_YAUJ = 4, + DATA_BUG_TRIO = 5, DATA_VEKLOR = 6, DATA_VEKLORISDEAD = 7, DATA_VEKLOR_DEATH = 8, @@ -60,6 +60,7 @@ enum Creatures NPC_SKERAM = 15263, NPC_VEM = 15544, NPC_KRI = 15511, + NPC_YAUJ = 15543, NPC_VEKLOR = 15276, NPC_VEKNILASH = 15275, NPC_OURO = 15517, diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp index ae3a28aa8..17001fc61 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp @@ -80,14 +80,14 @@ enum AuriayaEvents EVENT_ENRAGE = 10, }; -enum AuriayaSounds +enum Texts { - SOUND_AGGRO = 15473, - SOUND_SLAY1 = 15474, - SOUND_SLAY2 = 15475, - SOUND_DEATH = 15476, - SOUND_BERSERK = 15477, - SOUND_WOUND = 15478, + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_BERSERK = 2, + EMOTE_DEATH = 3, + EMOTE_FEAR = 4, + EMOTE_DEFFENDER = 5, }; enum Misc @@ -186,26 +186,16 @@ public: summons.DoZoneInCombat(NPC_SANCTUM_SENTRY); - me->Yell("Some things are better left alone!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_AGGRO); + Talk(SAY_AGGRO); me->setActive(true); } - void KilledUnit(Unit* /*victim*/) override + void KilledUnit(Unit* victim) override { - if (urand(0, 2)) + if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2)) return; - if (urand(0, 1)) - { - me->Yell("The secret dies with you!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SLAY1); - } - else - { - me->Yell("There is no escape!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SLAY2); - } + Talk(SAY_SLAY); } void JustDied(Unit* /*killer*/) override @@ -216,8 +206,7 @@ public: EntryCheckPredicate pred(NPC_FERAL_DEFENDER); summons.DoAction(ACTION_DESPAWN_ADDS, pred); summons.DespawnAll(); - me->TextEmote("Auriaya screams in agony.", nullptr, true); - me->PlayDirectSound(SOUND_DEATH); + Talk(EMOTE_DEATH); } void DoAction(int32 param) override @@ -240,7 +229,7 @@ public: switch (events.ExecuteEvent()) { case EVENT_SUMMON_FERAL_DEFENDER: - me->TextEmote("Auriaya begins to activate Feral Defender.", nullptr, true); + Talk(EMOTE_DEFFENDER); me->CastSpell(me, SPELL_ACTIVATE_FERAL_DEFENDER, true); me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, true); events.ScheduleEvent(EVENT_REMOVE_IMMUNE, 3000); @@ -249,7 +238,7 @@ public: me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, false); break; case EVENT_TERRIFYING_SCREECH: - me->TextEmote("Auriaya begins to cast Terrifying Screech.", nullptr, true); + Talk(EMOTE_FEAR); me->CastSpell(me, SPELL_TERRIFYING_SCREECH, false); events.RepeatEvent(35000); break; @@ -273,8 +262,7 @@ public: break; } case EVENT_ENRAGE: - me->TextEmote("You waste my time!", nullptr, true); - me->PlayDirectSound(SOUND_BERSERK); + Talk(SAY_BERSERK); me->CastSpell(me, SPELL_ENRAGE, true); break; } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index 60e1b55a6..c6ea86ff9 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -183,25 +183,26 @@ enum FreyaEvents EVENT_DETONATING_LASHER_FLAME_LASH = 55, }; -enum FreyaSounds +enum Texts { - // STONEBARK - SOUND_STONEBARK_AGGRO = 15500, - SOUND_STONEBARK_SLAY1 = 15501, - SOUND_STONEBARK_SLAY2 = 15502, - SOUND_STONEBARK_DEATH = 15503, + // Elder Brightleaf / Elder Ironbranch / Elder Stonebark + SAY_ELDER_AGGRO = 0, + SAY_ELDER_SLAY = 1, + SAY_ELDER_DEATH = 2, - // IRONBRANCH - SOUND_IRONBRANCH_AGGRO = 15493, - SOUND_IRONBRANCH_SLAY1 = 15494, - SOUND_IRONBRANCH_SLAY2 = 15495, - SOUND_IRONBRANCH_DEATH = 15496, - - // BRIGHTLEAF - SOUND_BRIGHTLEAF_AGGRO = 15483, - SOUND_BRIGHTLEAF_SLAY1 = 15485, - SOUND_BRIGHTLEAF_SLAY2 = 15486, - SOUND_BRIGHTLEAF_DEATH = 15487, + // Freya + SAY_AGGRO = 0, + SAY_AGGRO_WITH_ELDER = 1, + SAY_SLAY = 2, + SAY_DEATH = 3, + SAY_BERSERK = 4, + SAY_SUMMON_CONSERVATOR = 5, + SAY_SUMMON_TRIO = 6, + SAY_SUMMON_LASHERS = 7, + EMOTE_LIFEBINDERS_GIFT = 8, + EMOTE_ALLIES_OF_NATURE = 9, + EMOTE_GROUND_TREMOR = 10, + EMOTE_IRON_ROOTS = 11, }; enum FreyaNPCs @@ -224,20 +225,6 @@ enum FreyaNPCs NPC_DETONATING_LASHER = 32918, }; -enum FreyaSouns -{ - SOUND_AGGRO = 15526, - SOUND_ELDERS = 15527, - SOUND_CONSERVATOR = 15528, - SOUND_SLAY1 = 15529, - SOUND_SLAY2 = 15530, - SOUND_DEATH = 15531, - SOUND_BERSERK = 15532, - SOUND_TRIO = 15533, - SOUND_DETONATING = 15534, - SOUND_ASKHELP = 15535, -}; - enum Misc { ACTION_REMOVE_10_STACK = 10, @@ -322,32 +309,21 @@ public: if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2)) return; - if (urand(0, 1)) - { - me->Yell("Forgive me.", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SLAY1); - } - else - { - me->Yell("From your death springs life anew!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SLAY2); - } + Talk(SAY_SLAY); } void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override { - // kaboom! if (damage >= me->GetHealth()) { - me->Yell("His hold on me dissipates. I can see clearly once more. Thank you, heroes.", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_DEATH); + Talk(SAY_DEATH); damage = 0; + me->SetReactState(REACT_PASSIVE); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); me->SetFaction(FACTION_FRIENDLY); - me->SetHealth(me->GetMaxHealth()); - me->CombatStop(); me->RemoveAllAuras(); + me->AttackStop(); events.Reset(); summons.DespawnAll(); @@ -360,7 +336,7 @@ public: continue; if (Creature* e = ObjectAccessor::GetCreature(*me, _elderGUID[i])) - Unit::Kill(e, e); + e->DespawnOrUnsummon(); ++_elderCount; } @@ -397,12 +373,12 @@ public: void SpawnWave() { _waveNumber = _waveNumber == 1 ? 3 : _waveNumber - 1; + Talk(EMOTE_ALLIES_OF_NATURE); // Wave of three if (_waveNumber == 1) { - me->Yell("Children, assist me!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_TRIO); + Talk(SAY_SUMMON_TRIO); me->SummonCreature(NPC_ANCIENT_WATER_SPIRIT, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); me->SummonCreature(NPC_STORM_LASHER, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); me->SummonCreature(NPC_SNAPLASHER, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); @@ -410,15 +386,13 @@ public: // Ancient Conservator else if (_waveNumber == 2) { - me->Yell("Eonar, your servant requires aid!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_CONSERVATOR); + Talk(SAY_SUMMON_CONSERVATOR); me->SummonCreature(NPC_ANCIENT_CONSERVATOR, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()), 0, TEMPSUMMON_CORPSE_DESPAWN); } // Detonating Lashers else if (_waveNumber == 3) { - me->Yell("The swarm of the elements shall overtake you!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_DETONATING); + Talk(SAY_SUMMON_LASHERS); for (uint8 i = 0; i < 10; ++i) me->SummonCreature(NPC_DETONATING_LASHER, me->GetPositionX() + urand(5, 20), me->GetPositionY() + urand(5, 20), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()), 0, TEMPSUMMON_CORPSE_DESPAWN); } @@ -553,13 +527,11 @@ public: if (_elderGUID[0] || _elderGUID[1] || _elderGUID[2]) { - me->Yell("Elders, grant me your strength!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_ELDERS); + Talk(SAY_AGGRO_WITH_ELDER); } else { - me->Yell("The Conservatory must be protected!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_AGGRO); + Talk(SAY_AGGRO); } } @@ -595,6 +567,7 @@ public: break; case EVENT_FREYA_LIFEBINDER: { + Talk(EMOTE_LIFEBINDERS_GIFT); events.RepeatEvent(45000); float x, y, z; for (uint8 i = 0; i < 10; ++i) @@ -643,15 +616,16 @@ public: break; } case EVENT_FREYA_BERSERK: - me->Yell("You have strayed too far, wasted too much time!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_BERSERK); + Talk(SAY_BERSERK); me->CastSpell(me, SPELL_BERSERK, true); break; case EVENT_FREYA_GROUND_TREMOR: + Talk(EMOTE_GROUND_TREMOR); me->CastSpell(me, SPELL_GROUND_TREMOR_FREYA, false); events.RepeatEvent(25000 + urand(0, 10000)); break; case EVENT_FREYA_IRON_ROOT: + Talk(EMOTE_IRON_ROOTS); me->CastCustomSpell(SPELL_IRON_ROOTS_FREYA, SPELLVALUE_MAX_TARGETS, 1, me, false); events.RepeatEvent(45000 + urand(0, 10000)); break; @@ -706,24 +680,14 @@ public: if (urand(0, 1)) return; - if (urand(0, 1)) - { - me->TextEmote("Angry roar"); - me->PlayDirectSound(SOUND_STONEBARK_SLAY1); - } - else - { - me->Yell("Such a waste.", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_STONEBARK_SLAY2); - } + Talk(SAY_ELDER_SLAY); } void JustDied(Unit* killer) override { if (killer && me->GetEntry() == killer->GetEntry()) return; - me->Yell("Matron, flee! They are ruthless....", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_STONEBARK_DEATH); + Talk(SAY_ELDER_DEATH); // Lumberjacked if (me->GetInstanceScript()) @@ -737,8 +701,8 @@ public: events.ScheduleEvent(EVENT_STONEBARK_GROUND_TREMOR, 5000); events.ScheduleEvent(EVENT_STONEBARK_PETRIFIED_BARK, 20000); - me->Yell("This place will serve as your graveyard.", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_STONEBARK_AGGRO); + if (!me->HasAura(SPELL_DRAINED_OF_POWER)) // Prevents speech if combat is initiated by hardmode activation + Talk(SAY_ELDER_AGGRO); } void DamageTaken(Unit*, uint32& damage, DamageEffectType damageType, SpellSchoolMask damageSchoolMask) override @@ -812,24 +776,14 @@ public: if (urand(0, 1)) return; - if (urand(0, 1)) - { - me->Yell("Fertilizer.", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_BRIGHTLEAF_SLAY1); - } - else - { - me->Yell("Your corpse will nourish the soil!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_BRIGHTLEAF_SLAY2); - } + Talk(SAY_ELDER_SLAY); } void JustDied(Unit* killer) override { if (killer && me->GetEntry() == killer->GetEntry()) return; - me->Yell("Matron, one has fallen!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_BRIGHTLEAF_DEATH); + Talk(SAY_ELDER_DEATH); // Lumberjacked if (me->GetInstanceScript()) @@ -843,8 +797,8 @@ public: events.ScheduleEvent(EVENT_BRIGHTLEAF_SOLAR_FLARE, 5000); events.ScheduleEvent(EVENT_BRIGHTLEAF_UNSTABLE_SUN_BEAM, 8000); - me->Yell("Matron, the Conservatory has been breached!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_BRIGHTLEAF_AGGRO); + if (!me->HasAura(SPELL_DRAINED_OF_POWER)) // Prevents speech if combat is initiated by hardmode activation + Talk(SAY_ELDER_AGGRO); } void UpdateAI(uint32 diff) override @@ -933,24 +887,14 @@ public: if (urand(0, 1)) return; - if (urand(0, 1)) - { - me->Yell("I return you whence you came!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_IRONBRANCH_SLAY1); - } - else - { - me->Yell("BEGONE!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_IRONBRANCH_SLAY2); - } + Talk(SAY_ELDER_SLAY); } void JustDied(Unit* killer) override { if (killer && me->GetEntry() == killer->GetEntry()) return; - me->Yell("Freya! They come for you.", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_IRONBRANCH_DEATH); + Talk(SAY_ELDER_DEATH); // Lumberjacked if (me->GetInstanceScript()) @@ -964,8 +908,8 @@ public: events.ScheduleEvent(EVENT_IRONBRANCH_IRON_ROOT, 15000); events.ScheduleEvent(EVENT_IRONBRANCH_THORN_SWARM, 3000); - me->Yell("Mortals have no place here!", LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_IRONBRANCH_AGGRO); + if (!me->HasAura(SPELL_DRAINED_OF_POWER)) // Prevents speech if combat is initiated by hardmode activation + Talk(SAY_ELDER_AGGRO); } void UpdateAI(uint32 diff) override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp index 59d13140c..8e98b0c8c 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp @@ -57,26 +57,17 @@ #define NPC_SCORCHED_GROUND 33123 #define NPC_WATER_TRIGGER 22515 -#define TEXT_AGGRO "Insolent whelps! Your blood will temper the weapons used to reclaim this world!" -#define TEXT_ACTIVATE_CONSTRUCT "Arise, soldiers of the Iron Crucible! The Makers' will be done!" -#define TEXT_SCORCH_1 "Let the inferno consume you!" -#define TEXT_SCORCH_2 "BURN! Burn in the makers fire!" -#define TEXT_SLAG_POT "I will burn away your impurities!" -#define TEXT_SLAY_1 "More scraps for the scrapheap!" -#define TEXT_SLAY_2 "Your bones will serve as kindling!" -#define TEXT_BERSERK "Let it be finished!" -#define TEXT_DEATH "I. Have. Failed." -#define TEXT_FLAME_JETS "Ignis The Furnace Master begins to cast Flame Jets!" - -#define SOUND_AGGRO 15564 -#define SOUND_ACTIVATE_CONSTRUCT 15565 -#define SOUND_SLAG_POT 15566 -#define SOUND_SCORCH_1 15567 -#define SOUND_SCORCH_2 15568 -#define SOUND_SLAY_1 15569 -#define SOUND_SLAY_2 15570 -#define SOUND_BERSERK 15571 -#define SOUND_DEATH 15572 +enum Texts +{ + SAY_AGGRO = 0, + SAY_SUMMON = 1, + SAY_SLAG_POT = 2, + SAY_SCORCH = 3, + SAY_SLAY = 4, + SAY_BERSERK = 5, + SAY_DEATH = 6, + EMOTE_JETS = 7, +}; #define ACHIEV_STOKIN_THE_FURNACE_EVENT 20951 @@ -260,8 +251,7 @@ public: events.ScheduleEvent(EVENT_SPELL_FLAME_JETS, 32000); events.ScheduleEvent(EVENT_GRAB, 25000); - me->Yell(TEXT_AGGRO, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_AGGRO); + Talk(SAY_AGGRO); DoZoneInCombat(); if( InstanceScript* m_pInstance = me->GetInstanceScript() ) @@ -296,24 +286,15 @@ public: me->setActive(false); } - void KilledUnit(Unit* /*victim*/) override + void KilledUnit(Unit* victim) override { - if( rand() % 2 ) - { - me->Yell(TEXT_SLAY_1, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SLAY_1); - } - else - { - me->Yell(TEXT_SLAY_2, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SLAY_2); - } + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); } void JustDied(Unit* /*killer*/) override { - me->Yell(TEXT_DEATH, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_DEATH); + Talk(SAY_DEATH); if( me->GetInstanceScript() ) me->GetInstanceScript()->SetData(TYPE_IGNIS, DONE); @@ -357,27 +338,18 @@ public: case 0: break; case EVENT_ACTIVATE_CONSTRUCT: + Talk(SAY_SUMMON); me->CastCustomSpell(SPELL_ACTIVATE_CONSTRUCT, SPELLVALUE_MAX_TARGETS, 1, (Unit*)nullptr, false); if (++counter >= 20) { - me->Yell(TEXT_BERSERK, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_BERSERK); + Talk(SAY_BERSERK); me->CastSpell(me, SPELL_BERSERK, true); break; } events.RepeatEvent(RAID_MODE(40000, 30000)); break; case EVENT_SPELL_SCORCH: - if( rand() % 2 ) - { - me->Yell(TEXT_SCORCH_1, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SCORCH_1); - } - else - { - me->Yell(TEXT_SCORCH_2, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SCORCH_2); - } + Talk(SAY_SCORCH); me->SetControlled(true, UNIT_STATE_ROOT); me->DisableRotate(true); me->SendMovementFlagUpdate(); @@ -390,7 +362,7 @@ public: me->DisableRotate(false); break; case EVENT_SPELL_FLAME_JETS: - me->TextEmote(TEXT_FLAME_JETS, nullptr, true); + Talk(EMOTE_JETS); me->CastSpell(me->GetVictim(), S_FLAME_JETS, false); events.RepeatEvent(25000); break; @@ -429,8 +401,7 @@ public: int8 pos = urand(0, playerGUIDs.size() - 1); if( Player* pTarget = ObjectAccessor::GetPlayer(*me, playerGUIDs.at(pos)) ) { - me->Yell(TEXT_SLAG_POT, LANG_UNIVERSAL); - me->PlayDirectSound(SOUND_SLAG_POT); + Talk(SAY_SLAG_POT); me->CastSpell(pTarget, SPELL_GRAB, false); } } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp index 36792aa52..cf14371f9 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp @@ -25,72 +25,68 @@ #include "WaypointMgr.h" #include "ulduar.h" -#define SPELL_FLAMEBUFFET_10 64016 -#define SPELL_FLAMEBUFFET_25 64023 -#define S_FLAMEBUFFET RAID_MODE(SPELL_FLAMEBUFFET_10, SPELL_FLAMEBUFFET_25) -#define SPELL_FIREBALL 63815 -#define SPELL_WINGBUFFET 62666 -#define SPELL_FLAMEBREATH_10 63317 -#define SPELL_FLAMEBREATH_25 64021 -#define S_FLAMEBREATH RAID_MODE(SPELL_FLAMEBREATH_10, SPELL_FLAMEBREATH_25) -#define SPELL_FUSEARMOR 64771 -#define SPELL_DEVOURINGFLAME 63236 -#define SPELL_BERSERK 47008 +enum Spells +{ + // Razorscale + SPELL_FLAMEBUFFET_10 = 64016, + SPELL_FLAMEBUFFET_25 = 64023, + SPELL_FIREBALL = 63815, + SPELL_WINGBUFFET = 62666, + SPELL_FLAMEBREATH_10 = 63317, + SPELL_FLAMEBREATH_25 = 64021, + SPELL_FUSEARMOR = 64771, + SPELL_FUSED_ARMOR = 64774, // Applied on 5th stack of SPELL_FUSEARMOR + SPELL_DEVOURINGFLAME = 63236, + SPELL_BERSERK = 47008, -#define SPELL_CHAIN_1 49679 -#define SPELL_CHAIN_2 49682 -#define SPELL_CHAIN_3 49683 -#define SPELL_CHAIN_4 49684 -#define SPELL_LAUNCH_CHAIN 62505 -//#define SPELL_HARPOON_SHOT_BUFF 62509 -//#define SPELL_HARPOON_FIRE_STATE 62696 + // Haproons + SPELL_CHAIN_1 = 49679, + SPELL_CHAIN_2 = 49682, + SPELL_CHAIN_3 = 49683, + SPELL_CHAIN_4 = 49684, + SPELL_LAUNCH_CHAIN = 62505, + + // Dark Rune Sentinel + SPELL_WHIRLWIND = 63808, + SPELL_BATTLE_SHOUT_10 = 46763, + SPELL_BATTLE_SHOUT_25 = 64062, + + // Dark Rune Guardian + SPELL_STORMSTRIKE_DMG = 65971, + SPELL_STORMSTRIKE_DEBUFF = 64757, + + // Dark Rune Watcher + SPELL_LIGHTINGBOLT_10 = 63809, + SPELL_LIGHTINGBOLT_25 = 64696, + SPELL_CHAINLIGHTNING_10 = 64758, + SPELL_CHAINLIGHTNING_25 = 64759, +}; + +#define SPELL_FLAMEBUFFET RAID_MODE(SPELL_FLAMEBUFFET_10, SPELL_FLAMEBUFFET_25) +#define SPELL_FLAMEBREATH RAID_MODE(SPELL_FLAMEBREATH_10, SPELL_FLAMEBREATH_25) +#define SPELL_BATTLE_SHOUT RAID_MODE(SPELL_BATTLE_SHOUT_10, SPELL_BATTLE_SHOUT_25) +#define SPELL_LIGHTINGBOLT RAID_MODE(SPELL_LIGHTINGBOLT_10, SPELL_LIGHTINGBOLT_25) +#define SPELL_CHAINLIGHTNING RAID_MODE(SPELL_CHAINLIGHTNING_10, SPELL_CHAINLIGHTNING_25) #define REQ_CHAIN_COUNT RAID_MODE(2, 4) -#define SPELL_DEVOURINGFLAME_SUMMON 63308 -//#define SPELL_DEVOURINGFLAME_GROUNDAURA_10 64709 -//#define SPELL_DEVOURINGFLAME_GROUNDAURA_25 64734 -//#define S_DEVOURINGFLAME_GROUNDAURA RAID_MODE(SPELL_DEVOURINGFLAME_GROUNDAURA_10, SPELL_DEVOURINGFLAME_GROUNDAURA_25) -//#define NPC_DEVOURINGFLAME 34188 -#define SPELL_STORMSTRIKE 51876 -#define SPELL_WHIRLWIND 63808 -#define SPELL_LIGHTINGBOLT 63809 -#define SPELL_CHAINLIGHTNING 64758 - -#define NPC_DARK_RUNE_SENTINEL 33846 -#define NPC_DARK_RUNE_GUARDIAN 33388 -#define NPC_DARK_RUNE_WATCHER 33453 -#define NPC_EXPEDITION_ENGINEER 33287 -#define NPC_EXPEDITION_COMMANDER 33210 -//#define NPC_EXPEDITION_DEFENDER 33816 -//#define NPC_EXPEDITION_TRAPPER 33259 -#define NPC_RAZORSCALE 33186 -//#define NPC_HARPOON_FIRE_STATE 33282 - -#define GO_DRILL 195305 -#define GO_HARPOON_GUN_1 194519 -#define GO_HARPOON_GUN_2 194541 -#define GO_HARPOON_GUN_3 194542 -#define GO_HARPOON_GUN_4 194543 -#define GO_BROKEN_HARPOON 194565 - -#define TEXT_GOSSIP_ACTION "We are ready to help!" -#define TEXT_EE_AGGRO "Give us a moment to prepare to build the turrets." -#define TEXT_EE_MOVE_OUT "Ready to move out, keep those dwarves off of our backs!" -#define TEXT_EE_FIRES_OUT "Fires out! Let's rebuild those turrets!" - -#define TEXT_TURRET_READY "Harpoon Turret is ready for use!" -#define TEXT_DEEP_BREATH "Razorscale takes a deep breath..." -#define TEXT_GROUNDED_PERMANENTLY "Razorscale grounded permanently!" - -#define CORDS_GROUND 588.0f, -166.0f, 391.1f -#define CORDS_AIR 588.0f, -178.0f, 490.0f -#define REPAIR_POINTS 25 - -enum eSay +enum NPCs { - SAY_COMMANDER_INTRO = 0, - SAY_COMMANDER_GROUND = 1, - SAY_COMMANDER_AGGRO = 2 + NPC_DARK_RUNE_SENTINEL = 33846, + NPC_DARK_RUNE_GUARDIAN = 33388, + NPC_DARK_RUNE_WATCHER = 33453, + NPC_EXPEDITION_ENGINEER = 33287, + NPC_EXPEDITION_COMMANDER = 33210, + NPC_RAZORSCALE_CONTROLLER = 33233, // Trigger Creature +}; + +enum GOs +{ + GO_DRILL = 195305, + GO_HARPOON_GUN_1 = 194519, + GO_HARPOON_GUN_2 = 194541, + GO_HARPOON_GUN_3 = 194542, + GO_HARPOON_GUN_4 = 194543, + GO_BROKEN_HARPOON = 194565, }; enum eEvents @@ -113,11 +109,40 @@ enum eEvents EVENT_SPELL_FLAME_BUFFET, }; -enum eMisc +enum Texts { - POINT_RAZORSCALE_INIT = 1 + // Razorscale + EMOTE_PERMA_GROUND = 0, + EMOTE_BREATH = 1, + EMOTE_BERSERK = 2, + + // Expedition Commander + SAY_COMMANDER_AGGRO = 0, + SAY_COMMANDER_GROUND_PHASE = 1, + SAY_COMMANDER_ENGINEERS_DEAD = 2, // Should be called when all engineers are dead, currently unused + + // Expedition Engineer + SAY_EE_AGGRO = 0, + SAY_EE_START_REPAIR = 1, + SAY_EE_REBUILD_TURRETS = 2, + + // Harpoon + EMOTE_HARPOON = 0, }; +enum Misc +{ + POINT_RAZORSCALE_INIT = 1, + REPAIR_POINTS = 25, + + // Expedition Commander Gossip + GOSSIP_MENU_START_ENCOUNTER = 10314, + NPC_TEXT_COMMANDER = 40100, +}; + +const Position CORDS_GROUND = {588.0f, -166.0f, 391.1f}; +const Position CORDS_AIR = {588.0f, -178.0f, 490.0f}; + class boss_razorscale : public CreatureScript { public: @@ -161,6 +186,11 @@ public: for (uint8 i = 0; i < 3; ++i) ExpeditionEngineerGUIDs[i].Clear(); + // Show gossip icon if previously hidden + if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID)) + if (!commander->HasNpcFlag(UNIT_NPC_FLAG_GOSSIP)) + commander->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + CommanderGUID.Clear(); bGroundPhase = false; flyTimes = 0; @@ -195,7 +225,7 @@ public: break; ExpeditionEngineerGUIDs[i] = (*itr)->GetGUID(); if (!i) - (*itr)->Yell(TEXT_EE_AGGRO, LANG_UNIVERSAL); + (*itr)->AI()->Talk(SAY_EE_AGGRO); ++i; } if (Creature* c = me->FindNearestCreature(NPC_EXPEDITION_COMMANDER, 300.0f, true)) @@ -260,7 +290,7 @@ public: if( count >= REQ_CHAIN_COUNT ) { if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID)) - commander->AI()->Talk(SAY_COMMANDER_GROUND); + commander->AI()->Talk(SAY_COMMANDER_GROUND_PHASE); me->InterruptNonMeleeSpells(true); events.CancelEvent(EVENT_SPELL_FIREBALL); @@ -348,8 +378,8 @@ public: case 0: break; case EVENT_ENRAGE: + Talk(EMOTE_BERSERK); me->CastSpell(me, SPELL_BERSERK, true); - events.RepeatEvent(600000); break; case EVENT_COMMANDER_SAY_AGGRO: if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID)) @@ -360,7 +390,7 @@ public: if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i])) { if (!i) - c->Yell(TEXT_EE_MOVE_OUT, LANG_UNIVERSAL); + c->AI()->Talk(SAY_EE_START_REPAIR); c->AI()->SetData(1, 0); // start repairing } break; @@ -454,12 +484,12 @@ public: } break; case EVENT_WARN_DEEP_BREATH: - me->TextEmote(TEXT_DEEP_BREATH, nullptr, true); + Talk(EMOTE_BREATH); me->RemoveAura(62794); events.ScheduleEvent(EVENT_PHASE2_FLAME_BREATH, 2500); break; case EVENT_PHASE2_FLAME_BREATH: - me->CastSpell(me, S_FLAMEBREATH, true); + me->CastSpell(me, SPELL_FLAMEBREATH, true); events.ScheduleEvent(EVENT_FLY_UP, 2000); break; case EVENT_FLY_UP: @@ -485,6 +515,7 @@ public: if( (me->GetHealth() * 100) / me->GetMaxHealth() < 50 ) // start phase 3 { + Talk(EMOTE_PERMA_GROUND); me->SetControlled(false, UNIT_STATE_ROOT); me->DisableRotate(false); DoResetThreat(); @@ -527,12 +558,12 @@ public: if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i])) { if (!i) - c->Yell(TEXT_EE_FIRES_OUT, LANG_UNIVERSAL); + c->AI()->Talk(SAY_EE_REBUILD_TURRETS); c->AI()->SetData(1, 0); // start repairing } break; case EVENT_SPELL_FLAME_BREATH: - me->CastSpell(me->GetVictim(), S_FLAMEBREATH, false); + me->CastSpell(me->GetVictim(), SPELL_FLAMEBREATH, false); events.RepeatEvent(20000); break; case EVENT_SPELL_DEVOURING_FLAME_GROUND: @@ -546,14 +577,14 @@ public: me->CastSpell(victim, SPELL_FUSEARMOR, false); if (Aura* aur = victim->GetAura(SPELL_FUSEARMOR)) if (aur->GetStackAmount() == 5) - victim->CastSpell(victim, 64774, true); + victim->CastSpell(victim, SPELL_FUSED_ARMOR, true); events.RepeatEvent(10000); break; } events.RepeatEvent(2000); break; case EVENT_SPELL_FLAME_BUFFET: - me->CastSpell(me->GetVictim(), S_FLAMEBUFFET, false); + me->CastSpell(me->GetVictim(), SPELL_FLAMEBUFFET, false); events.RepeatEvent(7000); break; } @@ -619,8 +650,8 @@ public: if (!razorscale || razorscale->IsInCombat()) return true; - AddGossipItemFor(player, GOSSIP_ICON_CHAT, TEXT_GOSSIP_ACTION, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, 40100, creature); + AddGossipItemFor(player, GOSSIP_MENU_START_ENCOUNTER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + SendGossipMenuFor(player, NPC_TEXT_COMMANDER, creature); return true; } @@ -638,6 +669,9 @@ public: Creature* razorscale = ObjectAccessor::GetCreature(*creature, instance->GetGuidData(TYPE_RAZORSCALE)); if (razorscale && !razorscale->IsInCombat()) { + // Do not show gossip icon if encounter is in progress + creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + // reset npcs NPC_HARPOON_FIRE_STATE for (uint8 i = 0; i < 4; ++i) if (Creature* hfs = ObjectAccessor::GetCreature(*creature, instance->GetGuidData(DATA_HARPOON_FIRE_STATE_1 + i))) @@ -679,7 +713,7 @@ public: return; _introSpoken = true; - Talk(SAY_COMMANDER_INTRO); + //Talk(SAY_COMMANDER_INTRO); // No source leads to showing any text messages, perhaps only SOUND ID 15647 is played? } private: @@ -758,7 +792,8 @@ public: if( GameObject* wh = me->SummonGameObject(GetHarpoonGunIdForThisHFS(), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 3 * M_PI / 2, 0.0f, 0.0f, 0.0f, 0.0f, 0) ) { me->RemoveGameObject(wh, false); - me->TextEmote(TEXT_TURRET_READY, nullptr, true); + if (Creature* cr = me->SummonCreature(NPC_RAZORSCALE_CONTROLLER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 5000)) + cr->AI()->Talk(EMOTE_HARPOON); } } } @@ -987,9 +1022,9 @@ public: else timer2 -= diff; if (timer2 == 0 && me->GetVictim() && me->IsWithinMeleeRange(me->GetVictim())) { - me->CastSpell(me->GetVictim(), 65971, true); - me->CastSpell(me->GetVictim(), 65971, true); // me->CastSpell(me->GetVictim(), 65972, true); // cast the same twice cus second one requires setting offhand damage - me->CastSpell(me->GetVictim(), 64757, true); + me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE_DMG, true); + me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE_DMG, true); // cast the same twice cus second one requires setting offhand damage + me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE_DEBUFF, true); timer2 = urand(8000, 10000); return; } @@ -1034,7 +1069,7 @@ public: if( timer1 <= diff ) { - me->CastSpell(me->GetVictim(), RAID_MODE(64758, 64759), false); + me->CastSpell(me->GetVictim(), SPELL_CHAINLIGHTNING, false); timer1 = urand(10000, 12000); return; } @@ -1043,7 +1078,7 @@ public: if (timer2 <= diff) { - me->CastSpell(me->GetVictim(), RAID_MODE(63809, 64696), false); + me->CastSpell(me->GetVictim(), SPELL_LIGHTINGBOLT, false); timer2 = 4000; return; } @@ -1090,7 +1125,7 @@ public: if( timer1 <= diff ) { - me->CastSpell(me, RAID_MODE(46763, 64062), false); + me->CastSpell(me, SPELL_BATTLE_SHOUT, false); timer1 = urand(15000, 20000); } else @@ -1100,7 +1135,7 @@ public: else timer2 -= diff; if (timer2 == 0 && me->GetVictim() && me->IsWithinMeleeRange(me->GetVictim())) { - me->CastSpell(me, 63808, false); + me->CastSpell(me, SPELL_WHIRLWIND, false); timer2 = urand(10000, 12000); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp index 70a3432f4..fae50e043 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_xt002.cpp @@ -91,17 +91,20 @@ enum NPCs NPC_PILE_TRIGGER = 33337, }; -enum Sounds +enum Texts { - XT_SOUND_AGGRO = 15724, - XT_SOUND_HEART_OPEN = 15725, - XT_SOUND_HEART_CLOSED = 15726, - XT_SOUND_TANTARUM = 15727, - XT_SOUND_SLAY1 = 15728, - XT_SOUND_SLAY2 = 15729, - XT_SOUND_ENRAGE = 15730, - XT_SOUND_DEATH = 15731, - XT_SOUND_SUMMON = 15732, + SAY_AGGRO = 0, + SAY_HEART_OPENED = 1, + SAY_HEART_CLOSED = 2, + SAY_TYMPANIC_TANTRUM = 3, + SAY_SLAY = 4, + SAY_BERSERK = 5, + SAY_DEATH = 6, + SAY_SUMMON = 7, + EMOTE_HEART_OPENED = 8, + EMOTE_HEART_CLOSED = 9, + EMOTE_TYMPANIC_TANTRUM = 10, + EMOTE_SCRAPBOT = 11, }; enum Misc @@ -203,8 +206,7 @@ public: RescheduleEvents(); // Other events are scheduled here me->setActive(true); - me->Yell("New toys? For me? I promise I won't break them this time!", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_AGGRO); + Talk(SAY_AGGRO); if (m_pInstance) { @@ -223,23 +225,13 @@ public: { if (victim->GetTypeId() == TYPEID_PLAYER && !urand(0, 2)) { - if (urand(0, 1)) - { - me->Yell("I... I think I broke it.", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_SLAY1); - } - else - { - me->Yell("I guess it doesn't bend that way.", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_SLAY2); - } + Talk(SAY_SLAY); } } void JustDied(Unit* /*killer*/) override { - me->Yell("You are bad... Toys... Very... Baaaaad!", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_DEATH); + Talk(SAY_DEATH); if (m_pInstance) { @@ -279,7 +271,7 @@ public: me->CastSpell(me, SPELL_HEARTBREAK, true); - me->TextEmote("XT-002 Deconstructor's heart is severed from his body.", nullptr, true); + Talk(EMOTE_HEART_CLOSED); events.ScheduleEvent(EVENT_REMOVE_EMOTE, 4000); return; } @@ -329,8 +321,7 @@ public: me->SetControlled(true, UNIT_STATE_STUNNED); me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_SUBMERGED); // submerge with animation - me->Yell("So tired. I will rest for just a moment!", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_HEART_OPEN); + Talk(SAY_HEART_OPENED); events.CancelEventGroup(1); events.ScheduleEvent(EVENT_START_SECOND_PHASE, 5000); @@ -355,21 +346,19 @@ public: events.ScheduleEvent(EVENT_GRAVITY_BOMB, 10000, 1); break; case EVENT_TYMPANIC_TANTARUM: - me->TextEmote("XT-002 Deconstructor begins to cause the earth to quake.", nullptr, true); - me->Yell("NO! NO! NO! NO! NO!", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_TANTARUM); + Talk(EMOTE_TYMPANIC_TANTRUM); + Talk(SAY_TYMPANIC_TANTRUM); me->CastSpell(me, SPELL_TYMPANIC_TANTARUM, true); events.RepeatEvent(60000); return; case EVENT_ENRAGE: - me->Yell("I'm tired of these toys. I don't want to play anymore!", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_ENRAGE); + Talk(SAY_BERSERK); me->CastSpell(me, SPELL_XT002_ENRAGE, true); break; // Animation events case EVENT_START_SECOND_PHASE: - me->TextEmote("XT-002 Deconstructor's heart is exposed and leaking energy.", nullptr, true); + Talk(EMOTE_HEART_OPENED); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); if (Unit* heart = me->GetVehicleKit() ? me->GetVehicleKit()->GetPassenger(HEART_VEHICLE_SEAT) : nullptr) heart->GetAI()->DoAction(ACTION_AWAKEN_HEART); @@ -383,8 +372,7 @@ public: return; } - me->Yell("I'm ready to play!", LANG_UNIVERSAL); - me->PlayDirectSound(XT_SOUND_HEART_CLOSED); + Talk(SAY_HEART_CLOSED); me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_STAND); // emerge // Hide heart @@ -619,7 +607,7 @@ public: } if (!urand(0, 2)) - me->TextEmote("XT-002 Deconstructor consumes scrap bot to repair himself.", nullptr, true); + pXT002->AI()->Talk(EMOTE_SCRAPBOT); me->DespawnOrUnsummon(1); } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp index 4fe68716c..72c739bf2 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp @@ -28,11 +28,14 @@ enum Misc // TEXTS SAY_AGGRO = 0, SAY_KILL = 1, - EMOTE_RANGE = 2, SAY_DEATH = 3, SAY_DRAKE_DEATH = 5, SAY_DRAKE_BREATH = 6, + // EMOTES + EMOTE_DEEP_BREATH = 0, + EMOTE_RANGE = 1, + // SPELLS SPELL_CRUSH_N = 50234, SPELL_CRUSH_H = 59330, @@ -41,9 +44,9 @@ enum Misc SPELL_WHIRLWIND_N = 50228, SPELL_WHIRLWIND_H = 50228, - SPELL_FLAME_VISUAL = 47592, - SPELL_FLAME_BREATH_N = 47579, - SPELL_FLAME_BREATH_H = 60020, + SPELL_FREEZING_CLOUD_VISUAL = 47592, + SPELL_FREEZING_CLOUD_N = 47579, + SPELL_FREEZING_CLOUD_H = 60020, SPELL_LAUNCH_HARPOON = 48642, @@ -95,8 +98,6 @@ static Position SkadiPosition[] = {490.76f, -517.389f, 123.368f, 0.0f} }; -#define EMOTE_IN_RANGE "Skadi the Ruthless is within range of the harpoon launchers" - enum phase { PHASE_NONE, @@ -323,8 +324,8 @@ public: void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override { - if (spellInfo->Id == 47593) // SPELL_FLAME_VISUAL trigger - target->CastSpell(target, me->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true); + if (spellInfo->Id == 47593) // SPELL_FREEZING_CLOUD_VISUAL trigger + target->CastSpell(target, me->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true); } void SpawnFlameTriggers(uint8 point) @@ -341,13 +342,13 @@ public: { Creature* cr; if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 483, -484.9f, 105, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000))) - cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true); + cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true); if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 471.0f, -484.7f, 105, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000))) - cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true); + cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true); for (uint8 j = 0; j < 7; j++) if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 477.0f, -507.0f + (j * 3), 105.0f, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000))) - cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true); + cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true); } } @@ -357,15 +358,14 @@ public: { case 0: case 1: - me->RemoveAurasDueToSpell(SPELL_FLAME_VISUAL); + me->RemoveAurasDueToSpell(SPELL_FREEZING_CLOUD_VISUAL); me->SetFacingTo(M_PI * 2); break; case 2: case 3: if (m_pInstance) m_pInstance->SetData(SKADI_IN_RANGE, 1); - - me->TextEmote(EMOTE_IN_RANGE, nullptr, true); + Talk(EMOTE_RANGE); me->SetFacingTo(M_PI); break; } @@ -466,8 +466,9 @@ public: me->GetMotionMaster()->MovePoint(targetPoint, SkadiPosition[targetPoint].GetPositionX(), SkadiPosition[targetPoint].GetPositionY(), SkadiPosition[targetPoint].GetPositionZ()); if (targetPoint <= 1) { + Talk(EMOTE_DEEP_BREATH); SpawnFlameTriggers(targetPoint); - me->CastSpell(me, SPELL_FLAME_VISUAL, false); + me->CastSpell(me, SPELL_FREEZING_CLOUD_VISUAL, false); } if (m_pInstance) diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp index ccc603448..5635b24da 100644 --- a/src/server/scripts/Northrend/zone_icecrown.cpp +++ b/src/server/scripts/Northrend/zone_icecrown.cpp @@ -1472,6 +1472,48 @@ public: } }; +enum OnslaughtGryphon +{ + SPELL_DELIVER_GRYPHON = 54420, + SPELL_ONSLAUGHT_GRYPHON = 49641, + + NPC_CAPTURED_ONSLAUGHT_GRYPHON = 29415, + + SEAT_PLAYER = 0 +}; + +class spell_deliver_gryphon : public SpellScript +{ + PrepareSpellScript(spell_deliver_gryphon); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DELIVER_GRYPHON, SPELL_ONSLAUGHT_GRYPHON }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + { + if (Vehicle* gryphon = caster->GetVehicleKit()) + { + if (Unit* player = gryphon->GetPassenger(SEAT_PLAYER)) + { + player->ExitVehicle(); + player->RemoveAurasDueToSpell(VEHICLE_SPELL_PARACHUTE); + player->RemoveAurasDueToSpell(SPELL_ONSLAUGHT_GRYPHON); + player->SummonCreature(NPC_CAPTURED_ONSLAUGHT_GRYPHON, 7434.7f, 4213.3f, 316.52f, 3.88f, TEMPSUMMON_TIMED_DESPAWN, 1 * MINUTE * IN_MILLISECONDS); + } + } + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_deliver_gryphon::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + // Theirs /*###### ## npc_guardian_pavilion @@ -2159,6 +2201,7 @@ void AddSC_icecrown() new spell_anti_air_rocket_bomber(); new npc_infra_green_bomber_generic(); new spell_onslaught_or_call_bone_gryphon(); + RegisterSpellScript(spell_deliver_gryphon); // Theirs new npc_guardian_pavilion(); diff --git a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_ahune.cpp b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_ahune.cpp index 8c7aad8ef..51866c34f 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_ahune.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SlavePens/boss_ahune.cpp @@ -28,13 +28,17 @@ #define QUEST_SUMMON_AHUNE 11691 #define ITEM_MAGMA_TOTEM 34953 #define AHUNE_DEFAULT_MODEL 23344 -#define TEXT_RETREAT "Ahune Retreats. His defenses diminish." -#define TEXT_RESURFACE "Ahune will soon resurface." const Position AhuneSummonPos = {-97.3473f, -233.139f, -1.27587f, M_PI / 2}; const Position TotemPos[3] = { {-115.141f, -143.317f, -2.09467f, 4.92772f}, {-120.178f, -144.398f, -2.23786f, 4.92379f}, {-125.277f, -145.463f, -1.95209f, 4.97877f} }; const Position MinionSummonPos = {-97.154404f, -204.382675f, -1.19f, M_PI / 2}; +enum Text +{ + EMOTE_RETREAT = 0, + EMOTE_RESURFACE = 1, +}; + enum EventSpells { SPELL_STARTING_BEAM = 46593, @@ -192,7 +196,7 @@ public: events.RescheduleEvent(EVENT_SUBMERGE, 10000); break; case EVENT_SUBMERGE: - me->TextEmote(TEXT_RETREAT, nullptr, true); + Talk(EMOTE_RETREAT); me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); me->CastSpell(me, SPELL_SUBMERGE_0, true); me->CastSpell(me, SPELL_SELF_STUN, true); @@ -205,7 +209,7 @@ public: events.RescheduleEvent(EVENT_EMERGE_WARNING, 20000); break; case EVENT_EMERGE_WARNING: - me->TextEmote(TEXT_RESURFACE, nullptr, true); + Talk(EMOTE_RESURFACE); break; case EVENT_COMBAT_EMERGE: me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp index 62ac294ac..6e62cfa15 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef _WIN32 #include "direct.h" @@ -62,8 +63,13 @@ typedef struct uint32 id; } map_id; -map_id* map_ids; -uint16* LiqType; +struct LiquidTypeEntry +{ + uint8 SoundBank; +}; + +std::vector map_ids; +std::unordered_map LiquidTypes; #define MAX_PATH_LENGTH 128 char output_path[MAX_PATH_LENGTH] = "."; char input_path[MAX_PATH_LENGTH] = "."; @@ -271,7 +277,7 @@ uint32 ReadMapDBC() } size_t map_count = dbc.getRecordCount(); - map_ids = new map_id[map_count]; + map_ids.resize(map_count); for (uint32 x = 0; x < map_count; ++x) { map_ids[x].id = dbc.getRecord(x).getUInt(0); @@ -291,15 +297,13 @@ void ReadLiquidTypeTableDBC() exit(1); } - size_t liqTypeCount = dbc.getRecordCount(); - size_t liqTypeMaxId = dbc.getMaxId(); - LiqType = new uint16[liqTypeMaxId + 1]; - memset(LiqType, 0xff, (liqTypeMaxId + 1) * sizeof(uint16)); + for (uint32 x = 0; x < dbc.getRecordCount(); ++x) + { + LiquidTypeEntry& liquidType = LiquidTypes[dbc.getRecord(x).getUInt(0)]; + liquidType.SoundBank = dbc.getRecord(x).getUInt(3); + } - for (uint32 x = 0; x < liqTypeCount; ++x) - LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3); - - printf("Done! (%u LiqTypes loaded)\n", (uint32)liqTypeCount); + printf("Done! (%lu LiquidTypes loaded)\n", LiquidTypes.size()); } // @@ -357,7 +361,6 @@ struct map_heightHeader #define MAP_LIQUID_TYPE_SLIME 0x08 #define MAP_LIQUID_TYPE_DARK_WATER 0x10 -#define MAP_LIQUID_TYPE_WMO_WATER 0x20 #define MAP_LIQUID_NO_TYPE 0x0001 #define MAP_LIQUID_NO_HEIGHT 0x0002 @@ -365,7 +368,8 @@ struct map_heightHeader struct map_liquidHeader { uint32 fourcc; - uint16 flags; + uint8 flags; + uint8 liquidFlags; uint16 liquidType; uint8 offsetX; uint8 offsetY; @@ -720,73 +724,57 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int adt_MH2O* h2o = adt.a_grid->getMH2O(); if (h2o) { - for (int i = 0; i < ADT_CELLS_PER_GRID; i++) + for (int32 i = 0; i < ADT_CELLS_PER_GRID; i++) { - for (int j = 0; j < ADT_CELLS_PER_GRID; j++) + for (int32 j = 0; j < ADT_CELLS_PER_GRID; j++) { - adt_liquid_header* h = h2o->getLiquidData(i, j); + adt_liquid_instance const* h = h2o->GetLiquidInstance(i,j); if (!h) continue; - int count = 0; - uint64 show = h2o->getLiquidShowMap(h); - for (int y = 0; y < h->height; y++) + adt_liquid_attributes attrs = h2o->GetLiquidAttributes(i, j); + + int32 count = 0; + uint64 existsMask = h2o->GetLiquidExistsBitmap(h); + for (int32 y = 0; y < h->GetHeight(); y++) { - int cy = i * ADT_CELL_SIZE + y + h->yOffset; - for (int x = 0; x < h->width; x++) + int32 cy = i * ADT_CELL_SIZE + y + h->GetOffsetY(); + for (int32 x = 0; x < h->GetWidth(); x++) { - int cx = j * ADT_CELL_SIZE + x + h->xOffset; - if (show & 1) + int32 cx = j * ADT_CELL_SIZE + x + h->GetOffsetX(); + if (existsMask & 1) { liquid_show[cy][cx] = true; ++count; } - show >>= 1; + existsMask >>= 1; } } - liquid_entry[i][j] = h->liquidType; - switch (LiqType[h->liquidType]) + liquid_entry[i][j] = h->LiquidType; + switch (LiquidTypes.at(h->LiquidType).SoundBank) { - case LIQUID_TYPE_WATER: - liquid_flags[i][j] |= MAP_LIQUID_TYPE_WATER; - break; - case LIQUID_TYPE_OCEAN: - liquid_flags[i][j] |= MAP_LIQUID_TYPE_OCEAN; - break; - case LIQUID_TYPE_MAGMA: - liquid_flags[i][j] |= MAP_LIQUID_TYPE_MAGMA; - break; - case LIQUID_TYPE_SLIME: - liquid_flags[i][j] |= MAP_LIQUID_TYPE_SLIME; - break; + case LIQUID_TYPE_WATER: liquid_flags[i][j] |= MAP_LIQUID_TYPE_WATER; break; + case LIQUID_TYPE_OCEAN: liquid_flags[i][j] |= MAP_LIQUID_TYPE_OCEAN; if (attrs.Deep) liquid_flags[i][j] |= MAP_LIQUID_TYPE_DARK_WATER; break; + case LIQUID_TYPE_MAGMA: liquid_flags[i][j] |= MAP_LIQUID_TYPE_MAGMA; break; + case LIQUID_TYPE_SLIME: liquid_flags[i][j] |= MAP_LIQUID_TYPE_SLIME; break; default: - printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->liquidType, inputPath.c_str(), i, j); + printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->LiquidType, inputPath.c_str(), i, j); break; } - // Dark water detect - if (LiqType[h->liquidType] == LIQUID_TYPE_OCEAN) - { - uint8* lm = h2o->getLiquidLightMap(h); - if (!lm) - liquid_flags[i][j] |= MAP_LIQUID_TYPE_DARK_WATER; - } if (!count && liquid_flags[i][j]) printf("Wrong liquid detect in MH2O chunk"); - float* height = h2o->getLiquidHeightMap(h); - int pos = 0; - for (int y = 0; y <= h->height; y++) + int32 pos = 0; + for (int32 y = 0; y <= h->GetHeight(); y++) { - int cy = i * ADT_CELL_SIZE + y + h->yOffset; - for (int x = 0; x <= h->width; x++) + int cy = i * ADT_CELL_SIZE + y + h->GetOffsetY(); + for (int32 x = 0; x <= h->GetWidth(); x++) { - int cx = j * ADT_CELL_SIZE + x + h->xOffset; - if (height) - liquid_height[cy][cx] = height[pos]; - else - liquid_height[cy][cx] = h->heightLevel1; + int32 cx = j * ADT_CELL_SIZE + x + h->GetOffsetX(); + liquid_height[cy][cx] = h2o->GetLiquidHeight(h, pos); + pos++; } } @@ -796,13 +784,14 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int //============================================ // Pack liquid data //============================================ - uint8 type = liquid_flags[0][0]; + uint16 firstLiquidType = liquid_entry[0][0]; + uint8 firstLiquidFlag = liquid_flags[0][0]; bool fullType = false; for (int y = 0; y < ADT_CELLS_PER_GRID; y++) { for (int x = 0; x < ADT_CELLS_PER_GRID; x++) { - if (liquid_flags[y][x] != type) + if (liquid_entry[y][x] != firstLiquidType || liquid_flags[y][x] != firstLiquidFlag) { fullType = true; y = ADT_CELLS_PER_GRID; @@ -814,7 +803,7 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int map_liquidHeader liquidHeader; // no water data (if all grid have 0 liquid type) - if (type == 0 && !fullType) + if (firstLiquidFlag == 0 && !fullType) { // No liquid data map.liquidMapOffset = 0; @@ -873,7 +862,10 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int liquidHeader.flags |= MAP_LIQUID_NO_TYPE; if (liquidHeader.flags & MAP_LIQUID_NO_TYPE) - liquidHeader.liquidType = type; + { + liquidHeader.liquidFlags = firstLiquidFlag; + liquidHeader.liquidType = firstLiquidType; + } else map.liquidMapSize += sizeof(liquid_entry) + sizeof(liquid_flags); @@ -1020,7 +1012,6 @@ void ExtractMapsFromMpq(uint32 build) } } printf("\n"); - delete[] map_ids; } bool ExtractFile( char const* mpq_name, std::string const& filename ) diff --git a/src/tools/map_extractor/adt.h b/src/tools/map_extractor/adt.h index e6f94a882..01917e66f 100644 --- a/src/tools/map_extractor/adt.h +++ b/src/tools/map_extractor/adt.h @@ -177,21 +177,36 @@ public: } }; -#define ADT_LIQUID_HEADER_FULL_LIGHT 0x01 -#define ADT_LIQUID_HEADER_NO_HIGHT 0x02 - -struct adt_liquid_header +enum class LiquidVertexFormatType : uint16 { - uint16 liquidType; // Index from LiquidType.dbc - uint16 formatFlags; - float heightLevel1; - float heightLevel2; - uint8 xOffset; - uint8 yOffset; - uint8 width; - uint8 height; - uint32 offsData2a; - uint32 offsData2b; + HeightDepth = 0, + HeightTextureCoord = 1, + Depth = 2, +}; + +struct adt_liquid_instance +{ + uint16 LiquidType; // Index from LiquidType.db2 + LiquidVertexFormatType LiquidVertexFormat; + float MinHeightLevel; + float MaxHeightLevel; + uint8 OffsetX; + uint8 OffsetY; + uint8 Width; + uint8 Height; + uint32 OffsetExistsBitmap; + uint32 OffsetVertexData; + + uint8 GetOffsetX() const { return OffsetX; } + uint8 GetOffsetY() const { return OffsetY; } + uint8 GetWidth() const { return Width; } + uint8 GetHeight() const { return Height; } +}; + +struct adt_liquid_attributes +{ + uint64 Fishable; + uint64 Deep; }; // @@ -209,59 +224,99 @@ public: struct adt_LIQUID { - uint32 offsData1; + uint32 OffsetInstances; uint32 used; - uint32 offsData2; + uint32 OffsetAttributes; } liquid[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]; bool prepareLoadedData(); - adt_liquid_header* getLiquidData(int x, int y) + adt_liquid_instance const* GetLiquidInstance(int32 x, int32 y) const { - if (liquid[x][y].used && liquid[x][y].offsData1) - return (adt_liquid_header*)((uint8*)this + 8 + liquid[x][y].offsData1); + if (liquid[x][y].used && liquid[x][y].OffsetInstances) + return (adt_liquid_instance *)((uint8*)this + 8 + liquid[x][y].OffsetInstances); return nullptr; } - float* getLiquidHeightMap(adt_liquid_header* h) + adt_liquid_attributes GetLiquidAttributes(int32 x, int32 y) const { - if (h->formatFlags & ADT_LIQUID_HEADER_NO_HIGHT) - return nullptr; - if (h->offsData2b) - return (float*)((uint8*)this + 8 + h->offsData2b); - return nullptr; - } - - uint8* getLiquidLightMap(adt_liquid_header* h) - { - if (h->formatFlags & ADT_LIQUID_HEADER_FULL_LIGHT) - return nullptr; - if (h->offsData2b) + if (liquid[x][y].used) { - if (h->formatFlags & ADT_LIQUID_HEADER_NO_HIGHT) - return (uint8*)((uint8*)this + 8 + h->offsData2b); - return (uint8*)((uint8*)this + 8 + h->offsData2b + (h->width + 1) * (h->height + 1) * 4); + if (liquid[x][y].OffsetAttributes) + return *((adt_liquid_attributes *)((uint8*)this + 8 + liquid[x][y].OffsetAttributes)); + return { 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF }; + } + return { 0, 0 }; + } + + uint16 GetLiquidType(adt_liquid_instance const* h) const + { + if (h->LiquidVertexFormat == LiquidVertexFormatType::Depth) + return 2; + + return h->LiquidType; + } + + float GetLiquidHeight(adt_liquid_instance const* h, int32 pos) const + { + if (!h->OffsetVertexData) + return 0.0f; + + switch (h->LiquidVertexFormat) + { + case LiquidVertexFormatType::HeightDepth: + case LiquidVertexFormatType::HeightTextureCoord: + return ((float const*)((uint8*)this + 8 + h->OffsetVertexData))[pos]; + case LiquidVertexFormatType::Depth: + return 0.0f; + default: + break; + } + + return 0.0f; + } + + int8 GetLiquidDepth(adt_liquid_instance const* h, int32 pos) const + { + if (!h->OffsetVertexData) + return -1; + + switch (h->LiquidVertexFormat) + { + case LiquidVertexFormatType::HeightDepth: + return ((int8 const*)((int8 const*)this + 8 + h->OffsetVertexData + (h->GetWidth() + 1) * (h->GetHeight() + 1) * 4))[pos]; + case LiquidVertexFormatType::HeightTextureCoord: + return 0; + case LiquidVertexFormatType::Depth: + return ((int8 const*)((uint8*)this + 8 + h->OffsetVertexData))[pos]; + default: + break; + } + return 0; + } + + uint16 const* GetLiquidTextureCoordMap(adt_liquid_instance const* h, int32 pos) const + { + if (!h->OffsetVertexData) + return nullptr; + + switch (h->LiquidVertexFormat) + { + case LiquidVertexFormatType::HeightDepth: + case LiquidVertexFormatType::Depth: + return nullptr; + case LiquidVertexFormatType::HeightTextureCoord: + return (uint16 const*)((uint8 const*)this + 8 + h->OffsetVertexData + 4 * ((h->GetWidth() + 1) * (h->GetHeight() + 1) + pos)); + default: + break; } return nullptr; } - uint32* getLiquidFullLightMap(adt_liquid_header* h) + uint64 GetLiquidExistsBitmap(adt_liquid_instance const* h) const { - if (!(h->formatFlags & ADT_LIQUID_HEADER_FULL_LIGHT)) - return nullptr; - if (h->offsData2b) - { - if (h->formatFlags & ADT_LIQUID_HEADER_NO_HIGHT) - return (uint32*)((uint8*)this + 8 + h->offsData2b); - return (uint32*)((uint8*)this + 8 + h->offsData2b + (h->width + 1) * (h->height + 1) * 4); - } - return nullptr; - } - - uint64 getLiquidShowMap(adt_liquid_header* h) - { - if (h->offsData2a) - return *((uint64*)((uint8*)this + 8 + h->offsData2a)); + if (h->OffsetExistsBitmap) + return *((uint64 *)((uint8*)this + 8 + h->OffsetExistsBitmap)); else return 0xFFFFFFFFFFFFFFFFuLL; } diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp index 2b7fdade4..16e57a6e9 100644 --- a/src/tools/mmaps_generator/MapBuilder.cpp +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -634,10 +634,15 @@ namespace MMAP Tile& tile = tiles[x + y * TILES_PER_MAP]; // Calculate the per tile bounding box. - tileCfg.bmin[0] = config.bmin[0] + float(x * config.tileSize - config.borderSize) * config.cs; - tileCfg.bmin[2] = config.bmin[2] + float(y * config.tileSize - config.borderSize) * config.cs; - tileCfg.bmax[0] = config.bmin[0] + float((x + 1) * config.tileSize + config.borderSize) * config.cs; - tileCfg.bmax[2] = config.bmin[2] + float((y + 1) * config.tileSize + config.borderSize) * config.cs; + tileCfg.bmin[0] = config.bmin[0] + x * float(config.tileSize * config.cs); + tileCfg.bmin[2] = config.bmin[2] + y * float(config.tileSize * config.cs); + tileCfg.bmax[0] = config.bmin[0] + (x + 1) * float(config.tileSize * config.cs); + tileCfg.bmax[2] = config.bmin[2] + (y + 1) * float(config.tileSize * config.cs); + + tileCfg.bmin[0] -= tileCfg.borderSize * tileCfg.cs; + tileCfg.bmin[2] -= tileCfg.borderSize * tileCfg.cs; + tileCfg.bmax[0] += tileCfg.borderSize * tileCfg.cs; + tileCfg.bmax[2] += tileCfg.borderSize * tileCfg.cs; // build heightfield tile.solid = rcAllocHeightfield(); diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp index 469ff7984..5407d99c0 100644 --- a/src/tools/mmaps_generator/PathGenerator.cpp +++ b/src/tools/mmaps_generator/PathGenerator.cpp @@ -20,6 +20,7 @@ #include "Timer.h" #include "DBCFileLoader.h" #include "PathCommon.h" +#include "Util.h" #include #include @@ -341,6 +342,6 @@ int main(int argc, char** argv) builder.buildMaps({}); if (!silent) - printf("Finished. MMAPS were built in %u ms!\n", GetMSTimeDiffToNow(start)); + printf("Finished. MMAPS were built in %s\n", secsToTimeString(GetMSTimeDiffToNow(start) / 1000).c_str()); return 0; } diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp index fa0581f98..04e4ac76c 100644 --- a/src/tools/mmaps_generator/TerrainBuilder.cpp +++ b/src/tools/mmaps_generator/TerrainBuilder.cpp @@ -60,7 +60,8 @@ struct map_heightHeader struct map_liquidHeader { uint32 fourcc; - uint16 flags; + uint8 flags; + uint8 liquidFlags; uint16 liquidType; uint8 offsetX; uint8 offsetY; @@ -75,7 +76,6 @@ struct map_liquidHeader #define MAP_LIQUID_TYPE_MAGMA 0x04 #define MAP_LIQUID_TYPE_SLIME 0x08 #define MAP_LIQUID_TYPE_DARK_WATER 0x10 -#define MAP_LIQUID_TYPE_WMO_WATER 0x20 uint32 GetLiquidFlags(uint32 liquidId); @@ -172,8 +172,10 @@ namespace MMAP // data used later uint16 holes[16][16]; memset(holes, 0, sizeof(holes)); - uint8 liquid_type[16][16]; - memset(liquid_type, 0, sizeof(liquid_type)); + uint16 liquid_entry[16][16]; + memset(liquid_entry, 0, sizeof(liquid_entry)); + uint8 liquid_flags[16][16]; + memset(liquid_flags, 0, sizeof(liquid_flags)); G3D::Array ltriangles; G3D::Array ttriangles; @@ -284,75 +286,87 @@ namespace MMAP float* liquid_map = nullptr; if (!(lheader.flags & MAP_LIQUID_NO_TYPE)) - if (fread(liquid_type, sizeof(liquid_type), 1, mapFile) != 1) + { + if (fread(liquid_entry, sizeof(liquid_entry), 1, mapFile) != 1) printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n"); + if (fread(liquid_flags, sizeof(liquid_flags), 1, mapFile) != 1) + printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n"); + } + else + { + std::fill_n(&liquid_entry[0][0], 16 * 16, lheader.liquidType); + std::fill_n(&liquid_flags[0][0], 16 * 16, lheader.liquidFlags); + } if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) { uint32 toRead = lheader.width * lheader.height; liquid_map = new float [toRead]; if (fread(liquid_map, sizeof(float), toRead, mapFile) != toRead) + { printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n"); + delete[] liquid_map; + liquid_map = nullptr; + } } - if (liquid_map) + int count = meshData.liquidVerts.size() / 3; + float xoffset = (float(tileX)-32)*GRID_SIZE; + float yoffset = (float(tileY)-32)*GRID_SIZE; + + float coord[3]; + int row, col; + + // generate coordinates + if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) { - int count = meshData.liquidVerts.size() / 3; - float xoffset = (float(tileX) - 32) * GRID_SIZE; - float yoffset = (float(tileY) - 32) * GRID_SIZE; - - float coord[3]; - int row, col; - - // generate coordinates - if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) + int j = 0; + for (int i = 0; i < V9_SIZE_SQ; ++i) { - int j = 0; - for (int i = 0; i < V9_SIZE_SQ; ++i) + row = i / V9_SIZE; + col = i % V9_SIZE; + + if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height || + col < lheader.offsetX || col >= lheader.offsetX + lheader.width) { - row = i / V9_SIZE; - col = i % V9_SIZE; - - if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height || - col < lheader.offsetX || col >= lheader.offsetX + lheader.width) - { - // dummy vert using invalid height - meshData.liquidVerts.append((xoffset + col * GRID_PART_SIZE) * -1, INVALID_MAP_LIQ_HEIGHT, (yoffset + row * GRID_PART_SIZE) * -1); - continue; - } - - getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map); - meshData.liquidVerts.append(coord[0]); - meshData.liquidVerts.append(coord[2]); - meshData.liquidVerts.append(coord[1]); - j++; + // dummy vert using invalid height + meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1); + continue; } + + getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map); + meshData.liquidVerts.append(coord[0]); + meshData.liquidVerts.append(coord[2]); + meshData.liquidVerts.append(coord[1]); + j++; } - else + } + else + { + for (int i = 0; i < V9_SIZE_SQ; ++i) { - for (int i = 0; i < V9_SIZE_SQ; ++i) - { - row = i / V9_SIZE; - col = i % V9_SIZE; - meshData.liquidVerts.append((xoffset + col * GRID_PART_SIZE) * -1, lheader.liquidLevel, (yoffset + row * GRID_PART_SIZE) * -1); - } + row = i / V9_SIZE; + col = i % V9_SIZE; + meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1); } + } - delete [] liquid_map; + delete[] liquid_map; - int indices[] = { 0, 0, 0 }; - int loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM - TOP; - getLoopVars(portion, loopStart, loopEnd, loopInc); + int indices[] = { 0, 0, 0 }; + int loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM-TOP; + getLoopVars(portion, loopStart, loopEnd, loopInc); - // generate triangles - for (int i = loopStart; i < loopEnd; i += loopInc) - for (int j = TOP; j <= BOTTOM; j += triInc) - { - getHeightTriangle(i, Spot(j), indices, true); - ltriangles.append(indices[2] + count); - ltriangles.append(indices[1] + count); - ltriangles.append(indices[0] + count); - } + // generate triangles + for (int i = loopStart; i < loopEnd; i += loopInc) + { + for (int j = TOP; j <= BOTTOM; j += triInc) + { + getHeightTriangle(i, Spot(j), indices, true); + ltriangles.append(indices[2] + count); + ltriangles.append(indices[1] + count); + ltriangles.append(indices[0] + count); + } } } @@ -399,7 +413,7 @@ namespace MMAP } else { - liquidType = getLiquidType(i, liquid_type); + liquidType = getLiquidType(i, liquid_flags); if (liquidType & MAP_LIQUID_TYPE_DARK_WATER) { // players should not be here, so logically neither should creatures @@ -708,7 +722,7 @@ namespace MMAP copyIndices(tempTriangles, meshData.solidTris, offset, isM2); // now handle liquid data - if (liquid) + if (liquid && liquid->GetFlagsStorage()) { std::vector liqVerts; std::vector liqTris; diff --git a/src/tools/vmap4_extractor/adtfile.h b/src/tools/vmap4_extractor/adtfile.h index ce6182cc2..f91a215dd 100644 --- a/src/tools/vmap4_extractor/adtfile.h +++ b/src/tools/vmap4_extractor/adtfile.h @@ -22,91 +22,7 @@ #include "mpq_libmpq04.h" #include "wmo.h" -#define TILESIZE (533.33333f) -#define CHUNKSIZE ((TILESIZE) / 16.0f) -#define UNITSIZE (CHUNKSIZE / 8.0f) - -class Liquid; - -typedef struct -{ - float x; - float y; - float z; -} svec; - -struct vec -{ - double x; - double y; - double z; -}; - -struct triangle -{ - vec v[3]; -}; - -typedef struct -{ - float v9[16 * 8 + 1][16 * 8 + 1]; - float v8[16 * 8][16 * 8]; -} Cell; - -typedef struct -{ - double v9[9][9]; - double v8[8][8]; - uint16 area_id; - //Liquid *lq; - float waterlevel[9][9]; - uint8 flag; -} chunk; - -typedef struct -{ - chunk ch[16][16]; -} mcell; - -struct MapChunkHeader -{ - uint32 flags; - uint32 ix; - uint32 iy; - uint32 nLayers; - uint32 nDoodadRefs; - uint32 ofsHeight; - uint32 ofsNormal; - uint32 ofsLayer; - uint32 ofsRefs; - uint32 ofsAlpha; - uint32 sizeAlpha; - uint32 ofsShadow; - uint32 sizeShadow; - uint32 areaid; - uint32 nMapObjRefs; - uint32 holes; - uint16 s1; - uint16 s2; - uint32 d1; - uint32 d2; - uint32 d3; - uint32 predTex; - uint32 nEffectDoodad; - uint32 ofsSndEmitters; - uint32 nSndEmitters; - uint32 ofsLiquid; - uint32 sizeLiquid; - float zpos; - float xpos; - float ypos; - uint32 textureId; - uint32 props; - uint32 effectId; -}; - #pragma pack(push, 1) - namespace ADT { struct MDDF @@ -127,7 +43,7 @@ namespace ADT Vec3D Rotation; AaBox3D Bounds; uint16 Flags; - uint16 DoodadSet; // can be larger than number of doodad sets in WMO + uint16 DoodadSet; uint16 NameSet; uint16 Scale; }; diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp index 1b334d432..38ae3b935 100644 --- a/src/tools/vmap4_extractor/vmapexport.cpp +++ b/src/tools/vmap4_extractor/vmapexport.cpp @@ -163,7 +163,7 @@ bool ExtractSingleWmo(std::string& fname) string s = groupFileName; WMOGroup fgroup(s); - if (!fgroup.open()) + if (!fgroup.open(&froot)) { printf("Could not open all Group file for: %s\n", plain_name); file_ok = false; diff --git a/src/tools/vmap4_extractor/vmapexport.h b/src/tools/vmap4_extractor/vmapexport.h index 096f84b7f..4f51ae165 100644 --- a/src/tools/vmap4_extractor/vmapexport.h +++ b/src/tools/vmap4_extractor/vmapexport.h @@ -24,8 +24,8 @@ namespace VMAP { - const char VMAP_MAGIC[] = "VMAP_4.6"; - const char RAW_VMAP_MAGIC[] = "VMAP046"; // used in extracted vmap files with raw data + const char VMAP_MAGIC[] = "VMAP_4.7"; + const char RAW_VMAP_MAGIC[] = "VMAP047"; // used in extracted vmap files with raw data } enum ModelFlags diff --git a/src/tools/vmap4_extractor/wmo.cpp b/src/tools/vmap4_extractor/wmo.cpp index 885a8f4bd..a7b94a24f 100644 --- a/src/tools/vmap4_extractor/wmo.cpp +++ b/src/tools/vmap4_extractor/wmo.cpp @@ -30,7 +30,7 @@ WMORoot::WMORoot(std::string const& filename) : filename(filename), color(0), nTextures(0), nGroups(0), nPortals(0), nLights(0), - nDoodadNames(0), nDoodadDefs(0), nDoodadSets(0), RootWMOID(0), liquidType(0) + nDoodadNames(0), nDoodadDefs(0), nDoodadSets(0), RootWMOID(0), flags(0) { memset(bbcorn1, 0, sizeof(bbcorn1)); memset(bbcorn2, 0, sizeof(bbcorn2)); @@ -71,7 +71,7 @@ bool WMORoot::open() f.read(&RootWMOID, 4); f.read(bbcorn1, 12); f.read(bbcorn2, 12); - f.read(&liquidType, 4); + f.read(&flags, 4); } else if (!strcmp(fourcc, "MODS")) { @@ -158,14 +158,14 @@ WMOGroup::WMOGroup(std::string const& filename) : filename(std::move(filename)), MOPY(nullptr), MOVI(nullptr), MoviEx(nullptr), MOVT(nullptr), MOBA(nullptr), MobaEx(nullptr), hlq(nullptr), LiquEx(nullptr), LiquBytes(nullptr), groupName(0), descGroupName(0), mogpFlags(0), moprIdx(0), moprNItems(0), nBatchA(0), nBatchB(0), nBatchC(0), fogIdx(0), - liquidType(0), groupWMOID(0), mopy_size(0), moba_size(0), LiquEx_size(0), + groupLiquid(0), groupWMOID(0), mopy_size(0), moba_size(0), LiquEx_size(0), nVertices(0), nTriangles(0), liquflags(0) { memset(bbcorn1, 0, sizeof(bbcorn1)); memset(bbcorn2, 0, sizeof(bbcorn2)); } -bool WMOGroup::open() +bool WMOGroup::open(WMORoot* rootWMO) { MPQFile f(filename.c_str()); if (f.isEof ()) @@ -202,8 +202,19 @@ bool WMOGroup::open() f.read(&nBatchB, 2); f.read(&nBatchC, 4); f.read(&fogIdx, 4); - f.read(&liquidType, 4); + f.read(&groupLiquid, 4); f.read(&groupWMOID, 4); + + // according to WoW.Dev Wiki: + if (rootWMO->flags & 4) + groupLiquid = GetLiquidTypeId(groupLiquid); + else if (groupLiquid == 15) + groupLiquid = 0; + else + groupLiquid = GetLiquidTypeId(groupLiquid + 1); + + if (groupLiquid) + liquflags |= 2; } else if (!strcmp(fourcc, "MOPY")) { @@ -252,6 +263,19 @@ bool WMOGroup::open() LiquBytes = new char[nLiquBytes]; f.read(LiquBytes, nLiquBytes); + // Determine legacy liquid type + if (!groupLiquid) + { + for (int i = 0; i < hlq->xtiles * hlq->ytiles; ++i) + { + if ((LiquBytes[i] & 0xF) != 15) + { + groupLiquid = GetLiquidTypeId((LiquBytes[i] & 0xF) + 1); + break; + } + } + } + /* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app); llog << filename; llog << "\nbbox: " << bbcorn1[0] << ", " << bbcorn1[1] << ", " << bbcorn1[2] << " | " << bbcorn2[0] << ", " << bbcorn2[1] << ", " << bbcorn2[2]; @@ -423,78 +447,55 @@ int WMOGroup::ConvertToVMAPGroupWmo(FILE* output, WMORoot* rootWMO, bool precise } //------LIQU------------------------ - if (LiquEx_size != 0) + if (liquflags & 3) { - int LIQU_h[] = { 0x5551494C, static_cast(sizeof(WMOLiquidHeader) + LiquEx_size) + hlq->xtiles * hlq->ytiles }; // "LIQU" + int LIQU_totalSize = sizeof(uint32); + if (liquflags & 1) + { + LIQU_totalSize += sizeof(WMOLiquidHeader); + LIQU_totalSize += LiquEx_size / sizeof(WMOLiquidVert) * sizeof(float); + LIQU_totalSize += hlq->xtiles * hlq->ytiles; + } + + int LIQU_h[] = { 0x5551494C, LIQU_totalSize };// "LIQU" fwrite(LIQU_h, 4, 2, output); - // according to WoW.Dev Wiki: - uint32 liquidEntry; - if (rootWMO->liquidType & 4) - liquidEntry = liquidType; - else if (liquidType == 15) - liquidEntry = 0; - else - liquidEntry = liquidType + 1; - - if (!liquidEntry) - { - int v1; // edx@1 - int v2; // eax@1 - - v1 = hlq->xtiles * hlq->ytiles; - v2 = 0; - if (v1 > 0) - { - while ((LiquBytes[v2] & 0xF) == 15) - { - ++v2; - if (v2 >= v1) - break; - } - - if (v2 < v1 && (LiquBytes[v2] & 0xF) != 15) - liquidEntry = (LiquBytes[v2] & 0xF) + 1; - } - } - - if (liquidEntry && liquidEntry < 21) - { - switch ((liquidEntry - 1) & 3) - { - case 0: - liquidEntry = ((mogpFlags & 0x80000) != 0) + 13; - break; - case 1: - liquidEntry = 14; - break; - case 2: - liquidEntry = 19; - break; - case 3: - liquidEntry = 20; - break; - } - } - - hlq->type = liquidEntry; - /* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app); llog << filename; llog << ":\nliquidEntry: " << liquidEntry << " type: " << hlq->type << " (root:" << rootWMO->liquidType << " group:" << liquidType << ")\n"; llog.close(); */ - fwrite(hlq, sizeof(WMOLiquidHeader), 1, output); - // only need height values, the other values are unknown anyway - for (uint32 i = 0; i < LiquEx_size / sizeof(WMOLiquidVert); ++i) - fwrite(&LiquEx[i].height, sizeof(float), 1, output); - // todo: compress to bit field - fwrite(LiquBytes, 1, hlq->xtiles * hlq->ytiles, output); + fwrite(&groupLiquid, sizeof(uint32), 1, output); + if (liquflags & 1) + { + fwrite(hlq, sizeof(WMOLiquidHeader), 1, output); + // only need height values, the other values are unknown anyway + for (uint32 i = 0; i < LiquEx_size / sizeof(WMOLiquidVert); ++i) + fwrite(&LiquEx[i].height, sizeof(float), 1, output); + // todo: compress to bit field + fwrite(LiquBytes, 1, hlq->xtiles * hlq->ytiles, output); + } } return nColTriangles; } +uint32 WMOGroup::GetLiquidTypeId(uint32 liquidTypeId) +{ + if (liquidTypeId < 21 && liquidTypeId) + { + switch (((static_cast(liquidTypeId) - 1) & 3)) + { + case 0: return ((mogpFlags & 0x80000) != 0) + 13; + case 1: return 14; + case 2: return 19; + case 3: return 20; + default: break; + } + } + return liquidTypeId; +} + WMOGroup::~WMOGroup() { delete [] MOPY; diff --git a/src/tools/vmap4_extractor/wmo.h b/src/tools/vmap4_extractor/wmo.h index c7437247e..d87edf3c8 100644 --- a/src/tools/vmap4_extractor/wmo.h +++ b/src/tools/vmap4_extractor/wmo.h @@ -26,9 +26,6 @@ #include "vec3d.h" #include "loadlib/loadlib.h" -#define TILESIZE (533.33333f) -#define CHUNKSIZE ((TILESIZE) / 16.0f) - // MOPY flags enum MopyFlags { @@ -84,7 +81,7 @@ private: std::string filename; public: unsigned int color; - uint32 nTextures, nGroups, nPortals, nLights, nDoodadNames, nDoodadDefs, nDoodadSets, RootWMOID, liquidType; + uint32 nTextures, nGroups, nPortals, nLights, nDoodadNames, nDoodadDefs, nDoodadSets, RootWMOID, flags; float bbcorn1[3]; float bbcorn2[3]; @@ -105,7 +102,7 @@ struct WMOLiquidHeader float pos_x; float pos_y; float pos_z; - short type; + short material; }; struct WMOLiquidVert @@ -141,7 +138,7 @@ public: uint16 moprNItems; uint16 nBatchA; uint16 nBatchB; - uint32 nBatchC, fogIdx, liquidType, groupWMOID; + uint32 nBatchC, fogIdx, groupLiquid, groupWMOID; int mopy_size, moba_size; int LiquEx_size; @@ -154,8 +151,9 @@ public: WMOGroup(std::string const& filename); ~WMOGroup(); - bool open(); + bool open(WMORoot* rootWMO); int ConvertToVMAPGroupWmo(FILE* output, WMORoot* rootWMO, bool preciseVectorData); + uint32 GetLiquidTypeId(uint32 liquidTypeId); }; namespace MapObject