Compare commits

...

40 Commits

Author SHA1 Message Date
VG-prog
ed3e16896e
Merge bab53774d2d60535c4616c5dfdc86fd2c34dcea3 into 5bef92d5eaca3e2ecc317f9d599312bc23eb71aa 2025-11-10 10:09:27 +08:00
github-actions[bot]
5bef92d5ea chore(DB): import pending files
Referenced commit(s): 723aae903999e84f8cbfc9de263462d64f3aadf0
2025-11-09 22:15:10 +00:00
sogladev
723aae9039
fix(Scripts/Northrend): Zul'Drak Betrayal quest (#23562) 2025-11-09 23:14:07 +01:00
Andrew
283f03bdcd
fix(Scripts/HoL): Killing Volkhan should despawn all Slags (#23581) 2025-11-09 23:06:47 +01:00
github-actions[bot]
57daeed03a chore(DB): import pending files
Referenced commit(s): 36d739ee42006ba7018f595b36a7cbcc54d06630
2025-11-09 22:06:42 +00:00
Andrew
36d739ee42
fix(DB/Spells): Ionar spark Arcing Burn should stack from different c… (#23588) 2025-11-09 23:05:35 +01:00
github-actions[bot]
3ad79541f6 chore(DB): import pending files
Referenced commit(s): be58898d061b940c13f2039695b270a242591de0
2025-11-09 18:05:21 +00:00
sogladev
be58898d06
fix(DB/SmartAI): Howling Fjord quest vehicle Iron Rune Construct (#23063)
Co-authored-by: Killyana <morphone1@gmail.com>
2025-11-09 15:04:17 -03:00
VG-prog
bab53774d2
Merge branch 'master' into gridmap-height 2025-10-27 10:43:50 +01:00
VG-prog
8f89a77592
Merge branch 'master' into gridmap-height 2025-10-21 14:23:12 +02:00
VG-prog
4e6ccb828b
Merge branch 'master' into gridmap-height 2025-10-14 19:49:27 +02:00
VG-prog
a5549c7fa2
Merge branch 'master' into gridmap-height 2025-09-26 12:30:36 +02:00
VG-prog
49ebc55bda . 2025-09-25 18:23:04 +02:00
VG-prog
19f31152f3 . 2025-09-25 17:29:27 +02:00
VG-prog
e07481c099 vmap and dyn + improvements 2025-09-25 17:26:54 +02:00
VG-prog
a31b747bfb . 2025-09-25 09:02:36 +02:00
VG-prog
ad8cd929fd . 2025-09-24 22:00:57 +02:00
VG-prog
3b98fbeb94 . 2025-09-24 21:58:54 +02:00
VG-prog
18ca9b9dd7 . 2025-09-24 21:50:17 +02:00
VG-prog
f5ce362876 . 2025-09-24 21:45:08 +02:00
VG-prog
fdd27f12c6 . 2025-09-24 21:27:37 +02:00
VG-prog
7c58590b02 . 2025-09-24 19:38:20 +02:00
VG-prog
74391d0d96 Merge branch 'master' of https://github.com/azerothcore/azerothcore-wotlk into gridmap-height 2025-09-24 14:14:37 +02:00
VG-prog
f4b436f67e . 2025-09-24 14:13:29 +02:00
VG-prog
48f966df04 Suggestions, fixes and improvements 2025-09-24 14:07:22 +02:00
VG-prog
2a77ab785c Apply logical suggestions 2025-09-24 13:24:38 +02:00
VG-prog
7b3546753c . 2025-09-24 12:02:21 +02:00
VG-prog
c817ba903c vs 2025-09-24 11:55:35 +02:00
VG-prog
9f86676a2c Default to cylinder 2025-09-24 11:45:25 +02:00
VG-prog
0b9b84389c . 2025-09-24 11:02:19 +02:00
VG-prog
c4afc5534e . 2025-09-24 10:45:33 +02:00
VG-prog
5684cd63d4 . 2025-09-24 10:38:18 +02:00
VG-prog
66c87d231a . 2025-09-24 10:32:48 +02:00
VG-prog
4f1fcb10fd Configs 2025-09-24 10:29:47 +02:00
VG-prog
795570ba8f Square vs Cylinder 2025-09-24 10:17:16 +02:00
VG-prog
f3bd5adf8e . 2025-09-23 16:28:16 +02:00
VG-prog
f0e28838dc Manager to Mgr 2025-09-23 16:21:44 +02:00
VG-prog
bb18ac07df English comments 2025-09-23 16:18:49 +02:00
VG-prog
8cf85f6b8d fix Trailing whitespaces 2025-09-23 16:10:11 +02:00
VG-prog
918b0b46b1 fix(Core/Map/Grids): Sphere–Plane intersection implementation (accurate ground height calculation) 2025-09-23 15:45:34 +02:00
18 changed files with 1164 additions and 273 deletions

View File

@ -0,0 +1,67 @@
-- DB update 2025_11_09_02 -> 2025_11_09_03
--
-- Fixes "Bluff", Set `allowOverride` of action list
UPDATE `smart_scripts` SET `action_param3` = 1 WHERE (`entryorguid` IN (23672, 23673, 23675, 24271)) AND (`source_type` = 0) AND (`event_type` = 8) AND (`event_param1` = 44609);
-- Removes double spawns
DELETE FROM `gameobject` WHERE `id` = 186959 AND `guid` IN (264459, 264460, 264461, 264462, 264463, 264464, 264465);
-- Add missing aura. Usage is unknown
DELETE FROM `creature_template_addon` WHERE (`entry` = 24825);
INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
(24825, 0, 0, 0, 0, 0, 0, '44652');
-- Disable flying vehicle, but causes camera stuttering on rocket jump
UPDATE `creature_template_movement` SET `Flight` = 0 WHERE (`CreatureId` = 24825);
DELETE FROM `creature_text` WHERE (`CreatureID` = 24825) AND (`GroupID` = 1);
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(24825, 1, 0, 'Launching.', 12, 0, 100, 0, 0, 0, 23860, 0, 'Iron Rune Construct');
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 24825) AND (`source_type` = 0) AND (`id` IN (15, 16));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(24825, 0, 15, 0, 31, 0, 100, 512, 44609, 0, 3000, 3000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Spellhit \'Bluff\' - Say Line 0'),
(24825, 0, 16, 0, 8, 0, 100, 512, 44626, 0, 5000, 5000, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Spellhit \'Rocket Jump\' - Say Line 1');
-- Remove unused 'Say Line 0' in actionscripts
DELETE FROM `smart_scripts` WHERE (`entryorguid` IN (2367201, 2367301, 2367501, 2427101)) AND `source_type` = 9 AND `id` = 1 AND `target_type` = 19 AND `target_param1` = 24825 AND `action_type` = 1 AND `target_param2` = 20;
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` IN (2, 4)) AND (`SourceEntry` = 44608) AND (`SourceId` = 0) AND (`ConditionTypeOrReference` = 31);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(13, 4, 44608, 0, 0, 31, 0, 3, 24826, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 4, 44608, 0, 1, 31, 0, 3, 24827, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 4, 44608, 0, 2, 31, 0, 3, 24828, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 4, 44608, 0, 3, 31, 0, 3, 24829, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 4, 44608, 0, 4, 31, 0, 3, 24831, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 4, 44608, 0, 5, 31, 0, 3, 24832, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 2, 44608, 0, 0, 31, 0, 5, 186953, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 2, 44608, 0, 1, 31, 0, 5, 186960, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 2, 44608, 0, 2, 31, 0, 5, 186961, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 2, 44608, 0, 3, 31, 0, 5, 186963, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 2, 44608, 0, 4, 31, 0, 5, 186962, 0, 0, 0, 0, '', 'Rocket Jump'),
(13, 2, 44608, 0, 5, 31, 0, 5, 186964, 0, 0, 0, 0, '', 'Rocket Jump');
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24825);
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(24825, 0, 0, 0, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 75, 44643, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Just Summoned - Add Aura \'Reputation and Language\''),
(24825, 0, 1, 0, 28, 0, 100, 512, 0, 0, 0, 0, 0, 0, 28, 44643, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Passenger Removed - Remove Aura \'Reputation and Language\''),
(24825, 0, 2, 0, 38, 0, 100, 512, 0, 1, 0, 0, 0, 0, 53, 2, 24826, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Data Set 0 1 - Start Waypoint Path 24826'),
(24825, 0, 3, 0, 38, 0, 100, 512, 0, 2, 0, 0, 0, 0, 53, 2, 24827, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Data Set 0 2 - Start Waypoint Path 24827'),
(24825, 0, 4, 0, 38, 0, 100, 512, 0, 3, 0, 0, 0, 0, 53, 2, 24828, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Data Set 0 3 - Start Waypoint Path 24828'),
(24825, 0, 5, 0, 38, 0, 100, 512, 0, 4, 0, 0, 0, 0, 53, 2, 24831, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Data Set 0 4 - Start Waypoint Path 24831'),
(24825, 0, 6, 0, 38, 0, 100, 512, 0, 5, 0, 0, 0, 0, 53, 2, 24829, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Data Set 0 5 - Start Waypoint Path 24829'),
(24825, 0, 7, 0, 38, 0, 100, 512, 0, 6, 0, 0, 0, 0, 53, 2, 24832, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Data Set 0 6 - Start Waypoint Path 24832'),
(24825, 0, 8, 0, 58, 0, 100, 512, 0, 0, 0, 0, 0, 0, 28, 44626, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Path 0 Finished - Remove Aura \'Rocket Jump\''),
(24825, 0, 9, 0, 31, 0, 100, 512, 44609, 0, 3000, 3000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Target Spellhit \'Bluff\' - Say Line 0'),
(24825, 0, 10, 0, 8, 0, 100, 512, 44626, 0, 5000, 5000, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Iron Rune Construct - On Spellhit \'Rocket Jump\' - Say Line 1');
--
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (24826, 24827, 24828, 24829, 24831, 24832));
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` IN (24826, 24827, 24828, 24829, 24831, 24832));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(24826, 0, 0, 0, 8, 0, 100, 0, 44608, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'On Spellhit \'Rocket Jump\' - Set Data 0 1'),
(24827, 0, 0, 0, 8, 0, 100, 0, 44608, 0, 0, 0, 0, 0, 45, 0, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'On Spellhit \'Rocket Jump\' - Set Data 0 2'),
(24828, 0, 0, 0, 8, 0, 100, 0, 44608, 0, 0, 0, 0, 0, 45, 0, 3, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'On Spellhit \'Rocket Jump\' - Set Data 0 3'),
(24831, 0, 0, 0, 8, 0, 100, 0, 44608, 0, 0, 0, 0, 0, 45, 0, 4, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'On Spellhit \'Rocket Jump\' - Set Data 0 4'),
(24829, 0, 0, 0, 8, 0, 100, 0, 44608, 0, 0, 0, 0, 0, 45, 0, 5, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'On Spellhit \'Rocket Jump\' - Set Data 0 5'),
(24832, 0, 0, 0, 8, 0, 100, 0, 44608, 0, 0, 0, 0, 0, 45, 0, 6, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'On Spellhit \'Rocket Jump\' - Set Data 0 6');
DELETE FROM `smart_scripts` WHERE `source_type` = 9 AND `entryorguid` IN (2482600, 2482700, 2482800, 2482900, 2483100, 2483200);

View File

@ -0,0 +1,6 @@
-- DB update 2025_11_09_03 -> 2025_11_09_04
--
DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (52671, 59834);
INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES
(52671, 0x00400000),
(59834, 0x00400000);

View File

@ -0,0 +1,79 @@
-- DB update 2025_11_09_04 -> 2025_11_09_05
--
-- v11_2_5_63906
SET @VBUILD := 63906;
DELETE FROM `creature_template_addon` WHERE (`entry` = 28503);
INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
(28503, 0, 0, 0, 0, 0, 0, '58837');
DELETE FROM `creature_template_addon` WHERE (`entry` = 28998);
INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
(28998, 0, 0, 0, 0, 0, 0, '58837');
DELETE FROM `creature` WHERE (`id1` = 28998) AND (`guid` IN (1974609));
INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `Comment`, `VerifiedBuild`) VALUES
(1974609, 28998, 0, 0, 571, 0, 0, 1, 1, 0, 6175.2456, -2017.6545, 590.9613, 3.0019662, 300, 0, 0, 550001, 0, 0, 0, 0, 0, '', NULL, @VBUILD);
DELETE FROM `creature_template_addon` WHERE (`entry` = 28998);
INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
(28998, 0, 0, 0, 1, 0, 0, '');
UPDATE `spell_target_position` SET `PositionX`=6161.15, `PositionY`=-2015.36, `PositionZ`=590.878, `Orientation`=6.283189773559570312, `VerifiedBuild`=@VBUILD WHERE `ID`=52863 AND `EffectIndex`=0;
UPDATE `creature_template_addon` SET `bytes2` = 1 WHERE (`entry` = 28717);
-- Update comments
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28498) AND (`source_type` = 0) AND (`id` IN (0, 1, 2, 3, 4));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(28498, 0, 0, 0, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 1, 28498, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'The Lich King - On Just Summoned - Start Waypoint Path 28498'),
(28498, 0, 1, 2, 40, 0, 100, 512, 2, 0, 0, 0, 0, 0, 54, 83000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'The Lich King - On Point 2 of Path Any Reached - Pause Waypoint'),
(28498, 0, 2, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2849800, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'The Lich King - On Point 2 of Path Any Reached - Run Script'),
(28498, 0, 3, 4, 40, 0, 100, 512, 3, 0, 0, 0, 0, 0, 45, 0, 2, 0, 0, 0, 0, 10, 127495, 0, 0, 0, 0, 0, 0, 0, 'The Lich King - On Point 3 of Path Any Reached - Set Data 0 2'),
(28498, 0, 4, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'The Lich King - On Point 3 of Path Any Reached - Despawn Instant');
-- Disable gravity
DELETE FROM `creature_template_movement` WHERE (`CreatureId` = 29100);
INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES
(29100, 0, 0, 1, 0, 0, 0, 0);
-- Idle
UPDATE `creature` SET `MovementType` = 0, `wander_distance` = 0 WHERE `id1` = 29100 AND `guid` IN (112307, 112308, 112309, 112310);
UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 202357;
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 202357) AND (`source_type` = 1) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(202357, 1, 0, 0, 62, 0, 100, 0, 11091, 0, 0, 0, 0, 0, 11, 57553, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Drakuru\'s Last Wish - On Gossip Option 0 Selected - Cast \'Escape Voltarus\'');
-- Drakuru's Last Wish
UPDATE `gameobject_template_addon` SET `flags` = 32 WHERE (`entry` = 202357);
-- Skull and Portal spells target 'Totally Generic Bunny (JSB)'
DELETE FROM `creature` WHERE (`id1` = 28960) and `guid` IN (98914, 98920);
INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `Comment`, `VerifiedBuild`) VALUES
(98914, 28960, 0, 0, 571, 0, 0, 1, 1, 0, 6144.01, -2011.8, 590.963, 6.16101, 300, 0, 0, 4979, 0, 0, 0, 0, 0, '', '\'Throw Portal Crystal\' guid target', @VBUILD),
(98920, 28960, 0, 0, 571, 0, 0, 1, 1, 0, 6181.5137, -2032.4258, 590.96124, 1.01229, 300, 0, 0, 4979, 0, 0, 0, 0, 0, '', '\'Drakuru\'s Skull Missile\' guid target', @VBUILD);
UPDATE `conditions` SET `ConditionValue3` = 98914, `Comment` = 'target Totally Generic Bunny (JSB)' WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 54209) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 28960) AND (`ConditionValue3` = 0);
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 54250) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 28960);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(13, 1, 54250, 0, 0, 31, 0, 3, 28960, 98920, 0, 0, 0, '', 'target Totally Generic Bunny (JSB)');
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 54089) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 1) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 51966) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(13, 1, 54089, 0, 0, 1, 0, 51966, 0, 0, 0, 0, 0, '', 'Has Aura \'Scourge Disguise\'');
-- 54104 Blight Fog
UPDATE `creature_template_addon` SET `auras` = '54104' WHERE (`entry` = 28998);
DELETE FROM `creature_summon_groups` WHERE `summonerId` = 28998 and `summonerType` = 0 AND `groupId` = 1;
INSERT INTO `creature_summon_groups` (`summonerId`, `summonerType`, `groupId`, `entry`, `position_x`, `position_y`, `position_z`, `orientation`, `summonType`, `summonTime`, `Comment`) VALUES
(28998, 0, 1, 28931, 6184.1455, -1970.1699, 586.84186, 4.5902, 8, 0, 'Overlord Drakuru - Group 1 - Blightblood Troll'),
(28998, 0, 1, 28931, 6222.855, -2026.6315, 586.84186, 3.00197, 8, 0, 'Overlord Drakuru - Group 1 - Blightblood Troll'),
(28998, 0, 1, 28931, 6166.278, -2065.3123, 586.84186, 1.44862, 8, 0, 'Overlord Drakuru - Group 1 - Blightblood Troll'),
(28998, 0, 1, 28931, 6127.5117, -2008.6506, 586.84186, 6.16101, 8, 0, 'Overlord Drakuru - Group 1 - Blightblood Troll');
-- 54105 Blight Fog
DELETE FROM `spell_script_names` WHERE (`spell_id` = 54105);
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(54105, 'spell_blight_fog');

View File

@ -308,6 +308,76 @@ float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist,
}
}
static inline bool _finiteF(float v) { return std::isfinite(v); }
namespace { constexpr float INV_SQRT2 = 0.70710678118654752440f; }
float DynamicMapTree::getHeightAccurate(float x, float y, float z, float maxSearchDist, uint32 phasemask,
float radius, float yaw, float blend, float clamp, float sampleDelta) const
{
auto sample = [&](float sx, float sy) -> float
{
G3D::Vector3 v(sx, sy, z);
G3D::Ray r(v, G3D::Vector3(0, 0, -1));
float dist = maxSearchDist;
DynamicTreeIntersectionCallback cb(phasemask, VMAP::ModelIgnoreFlags::Nothing);
impl->intersectZAllignedRay(r, cb, dist);
if (cb.didHit())
return z - dist;
return std::numeric_limits<float>::quiet_NaN();
};
const float h0 = sample(x, y);
if (!_finiteF(h0))
return -G3D::finf();
if (radius <= 0.0f)
return h0;
const float d = (sampleDelta > 0.0f) ? sampleDelta
: std::max(0.05f, std::min(0.5f, radius * 0.5f));
const float hx1 = sample(x + d, y);
const float hx2 = sample(x - d, y);
const float hy1 = sample(x, y + d);
const float hy2 = sample(x, y - d);
auto diff = [&](float p, float m) -> float
{
if (_finiteF(p) && _finiteF(m)) return (p - m) / (2.0f * d);
if (_finiteF(p)) return (p - h0) / d;
if (_finiteF(m)) return (h0 - m) / d;
return 0.0f;
};
float gx = diff(hx1, hx2); // dz/dx
float gy = diff(hy1, hy2); // dz/dy
if (clamp > 0.0f)
{
const float g2 = gx*gx + gy*gy;
const float c2 = clamp*clamp;
if (g2 > c2)
{
const float s = clamp / std::sqrt(g2);
gx *= s; gy *= s;
}
}
const float slopeL2 = std::sqrt(std::max(0.0f, gx*gx + gy*gy));
float totalSlope = slopeL2;
if (blend < 1.0f)
{
float c = std::cos(yaw), s = std::sin(yaw);
float rx = gx * c + gy * s;
float ry = -gx * s + gy * c;
float slopeL1 = std::abs(rx) + std::abs(ry);
totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1);
}
return h0 + radius * totalSlope;
}
bool DynamicMapTree::GetAreaInfo(float x, float y, float& z, uint32 phasemask, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
G3D::Vector3 v(x, y, z + 0.5f);

View File

@ -55,6 +55,8 @@ public:
float pModifyDist) const;
[[nodiscard]] float getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const;
[[nodiscard]] float getHeightAccurate(float x, float y, float z, float maxSearchDist, uint32 phasemask,
float radius, float yaw, float blend, float clamp, float sampleDelta) const;
void insert(const GameObjectModel&);
void remove(const GameObjectModel&);

View File

@ -1397,6 +1397,79 @@ vmap.BlizzlikeLOSInOpenWorld = 1
vmap.enableIndoorCheck = 1
#
# Accurate ground settlement (cylinderplane) settings
# 0 = cylinder-circle footprint (recommended),
# 1 = square footprint (upside-down-pyramid)
#
Height.Accurate.Shape = 0
#
# Scale factor applied to the probe radius (safety margin, do not abuse)
#
Height.Accurate.RadiusScale = 1.0
#
# Square and cylinder mix [0..1]
# 0.00 = pure square
# 1.00 = cylinder (circle) (equal to Shape=0)
# Recommended for QA: 0.150.25
#
Height.Accurate.SquareBlend = 0.2
#
# Optional slope clamp (rise/run). 0.0 disables clamping.
# This limits the gradient magnitude used by both circle and square footprints,
# preventing extreme lifts on near-vertical faces. Safe upper bound: ~5.0 (≈78.7°).
#
Height.Accurate.SlopeClamp = 0.0
#
# Gradient mode
# 0 = Plane (exact face-plane gradient; maximizes geometric accuracy)
# 1 = LS (least-squares gradient; smoother, slightly less exact)
#
Height.Accurate.GradientMode = 0
#
# Normal epsilon used to detect degenerate faces (|nz| < eps)
# Recommended: 1e-6 .. 1e-5
#
Height.Accurate.NormalEps = 0.000001
#
# Accurate VMAP height (apply same footprint & slope logic on VMAP)
# 1 = enable (recommended), 0 = legacy point-ray height
#
Height.Accurate.VMap.Enable = 1
#
# Lateral sample delta (meters) to estimate VMAP gradient with 4 extra rays:
# (x±delta,y) and (x,y±delta). Typical 0.15..0.35. Affects precision & cost.
#
Height.Accurate.VMap.Delta = 0.25
#
# -- Accurate DYNAMIC (MMAP) planar offset (gradient sampling) --
# 1 = enable (recommended), 0 = legacy point ray
#
Height.Accurate.Dynamic.Enable = 1
#
# Gradient sampling delta for Dynamic (meters). Typical 0.15..0.35
#
Height.Accurate.Dynamic.Delta = 0.25
#
# DetectPosCollision
# Description: Check final move position, summon position, etc for visible collision with

View File

@ -3042,6 +3042,36 @@ float WorldObject::GetMapHeight(float x, float y, float z, bool vmap/* = true*/,
return GetMap()->GetHeight(GetPhaseMask(), x, y, z, vmap, distanceToSearch);
}
float WorldObject::GetGroundProbeRadius() const
{
if (Unit const* u = ToUnit())
return std::max(0.25f, u->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS));
return 0.3f;
}
float WorldObject::GetMapHeightAccurate(float x, float y, float z, bool vmap/* = true*/,
float distanceToSearch/* = DEFAULT_HEIGHT_SEARCH*/, float radius/* = 0.3f*/) const
{
if (radius <= 0.0f)
{
if (Unit const* u = ToUnit())
{
radius = std::max(0.25f, u->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS));
}
else
radius = 0.3f;
}
float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE);
radius *= rScale;
float yaw = GetOrientation();
if (z != MAX_HEIGHT)
z += std::max(GetCollisionHeight(), radius + 0.2f);
return GetMap()->GetHeightAccurate(GetPhaseMask(), x, y, z, radius, yaw, vmap, distanceToSearch);
}
float WorldObject::GetMapWaterOrGroundLevel(float x, float y, float z, float* ground/* = nullptr*/) const
{
return GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), x, y, z, ground,

View File

@ -703,6 +703,8 @@ public:
};
[[nodiscard]] float GetMapWaterOrGroundLevel(float x, float y, float z, float* ground = nullptr) const;
[[nodiscard]] float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f) const; // DEFAULT_HEIGHT_SEARCH in map.h
[[nodiscard]] float GetGroundProbeRadius() const;
[[nodiscard]] float GetMapHeightAccurate(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f, float radius = 0.3f) const;
[[nodiscard]] float GetFloorZ() const;
[[nodiscard]] float GetMinHeightInWater() const;

View File

@ -3,6 +3,7 @@
#include "GridTerrainData.h"
#include "Log.h"
#include "MapDefines.h"
#include "World.h"
#include <filesystem>
#include <G3D/Ray.h>
@ -615,3 +616,212 @@ LiquidData const GridTerrainData::GetLiquidData(float x, float y, float z, float
return liquidData;
}
namespace
{
constexpr float INV_SQRT2 = 0.70710678118654752440f; // 1/sqrt(2)
}
static inline float CELL_SIZE() { return SIZE_OF_GRIDS / float(MAP_RESOLUTION); } // ≈ 4.1666667f
bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& h2, float& h3, float& h4, float& h5) const
{
if (!_loadedHeightData)
return false;
// FLOAT
if (_loadedHeightData->floatHeightData)
{
auto const& v9 = _loadedHeightData->floatHeightData->v9;
auto const& v8 = _loadedHeightData->floatHeightData->v8;
h1 = v9[xInt * 129 + yInt];
h2 = v9[(xInt + 1) * 129 + yInt];
h3 = v9[xInt * 129 + yInt + 1];
h4 = v9[(xInt + 1) * 129 + yInt + 1];
h5 = v8[xInt * 128 + yInt];
return true;
}
// UINT8
if (_loadedHeightData->uint8HeightData)
{
auto const& d = *_loadedHeightData->uint8HeightData;
float k = d.gridIntHeightMultiplier;
float base = _loadedHeightData->gridHeight;
auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt
h1 = float(v9ptr[0]) * k + base;
h2 = float(v9ptr[129]) * k + base;
h3 = float(v9ptr[1]) * k + base;
h4 = float(v9ptr[130]) * k + base;
uint8 v8val = d.v8[xInt * 128 + yInt];
h5 = float(v8val) * k + base;
return true;
}
// UINT16
if (_loadedHeightData->uint16HeightData)
{
auto const& d = *_loadedHeightData->uint16HeightData;
float k = d.gridIntHeightMultiplier;
float base = _loadedHeightData->gridHeight;
auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt
h1 = float(v9ptr[0]) * k + base;
h2 = float(v9ptr[129]) * k + base;
h3 = float(v9ptr[1]) * k + base;
h4 = float(v9ptr[130]) * k + base;
uint16 v8val = d.v8[xInt * 128 + yInt];
h5 = float(v8val) * k + base;
return true;
}
return false;
}
float GridTerrainData::GetHeightAccurate(float x, float y, float radius) const
{
return GetHeightAccurate(x, y, radius, GroundFootprintShape::Circle, 0.0f, 1.0f, 0.0f, 0u, 1.0e-6f);
}
float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const
{
return GetHeightAccurate(x, y, radius, shape, 0.0f, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f, 0u, 1.0e-6f);
}
float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw) const
{
// Wrapper with default blend = 1 for circle, 0 for square.
return GetHeightAccurate(x, y, radius, shape, yaw, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f, 0u, 1.0e-6f);
}
float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw,
float squareBlend, float slopeClamp, uint32 gradientMode, float normalEps) const
{
if (!_loadedHeightData)
return INVALID_HEIGHT;
float xf = MAP_RESOLUTION * (32.0f - x / SIZE_OF_GRIDS);
float yf = MAP_RESOLUTION * (32.0f - y / SIZE_OF_GRIDS);
int xInt = static_cast<int>(std::floor(xf));
int yInt = static_cast<int>(std::floor(yf));
float fx = xf - static_cast<float>(xInt);
float fy = yf - static_cast<float>(yInt);
if (fx < 0.0f) { fx += 1.0f; --xInt; }
if (fy < 0.0f) { fy += 1.0f; --yInt; }
if (fx >= 1.0f) { fx -= 1.0f; ++xInt; }
if (fy >= 1.0f) { fy -= 1.0f; ++yInt; }
xInt &= (MAP_RESOLUTION - 1);
yInt &= (MAP_RESOLUTION - 1);
if (isHole(xInt, yInt))
return INVALID_HEIGHT;
float h1, h2, h3, h4, h5;
if (!SampleHeights(xInt, yInt, h1, h2, h3, h4, h5))
return INVALID_HEIGHT;
// h1 -> (0,0)
// h2 -> (S,0)
// h3 -> (0,S)
// h4 -> (S,S)
// h5 -> (S/2, S/2)
const float S = CELL_SIZE();
const float S2 = S * 0.5f;
G3D::Vector3 P(fx * S, fy * S, 0.0f);
G3D::Vector3 A(S2, S2, h5);
const float eps = 1e-6f;
const float blend = std::max(0.0f, std::min(1.0f, squareBlend));
const bool right = (P.x >= A.x);
const bool top = (P.y >= A.y);
G3D::Vector3 B, C;
if (right && !top)
{
B = G3D::Vector3(S, 0.0f, h2);
C = G3D::Vector3(0.0f, 0.0f, h1);
} // BR
else if (right && top)
{
B = G3D::Vector3(S, S, h4);
C = G3D::Vector3(S, 0.0f, h2);
} // TR
else if (!right && top)
{
B = G3D::Vector3(0.0f, S, h3);
C = G3D::Vector3(S, S, h4);
} // TL
else /* !right && !top */
{
B = G3D::Vector3(0.0f, 0.0f, h1);
C = G3D::Vector3(0.0f, S, h3);
} // BL
const G3D::Vector3 U = B - A;
const G3D::Vector3 V = C - A;
const G3D::Vector3 n = U.cross(V);
const float nzAbs = std::abs(n.z);
if (nzAbs < std::max(eps, normalEps))
return getHeight(x, y);
const float zPlane = A.z - (n.x * (P.x - A.x) + n.y * (P.y - A.y)) / n.z;
const float inv2S = 1.0f / (2.0f * S);
float gx, gy;
if (gradientMode == 0u)
{
// Plane-exact gradient: z = (-n.x*x - n.y*y - d)/n.z => ∇z = (-n.x/n.z, -n.y/n.z)
gx = -n.x / n.z;
gy = -n.y / n.z;
}
else
{
// LS gradient (smoother)
gx = ((h2 + h4) - (h1 + h3)) * inv2S;
gy = ((h3 + h4) - (h1 + h2)) * inv2S;
}
// Optional clamp
if (slopeClamp > 0.0f)
{
const float g2 = gx*gx + gy*gy;
const float c2 = slopeClamp * slopeClamp;
if (g2 > c2)
{
const float s = slopeClamp / std::sqrt(g2);
gx *= s; gy *= s;
}
}
const float slopeL2 = std::sqrt(std::max(0.0f, gx*gx + gy*gy));
// Fastpath radius 0
if (radius <= 0.0f)
return zPlane;
float totalSlope = slopeL2; // por defecto: círculo (o blend==1)
if (shape == GroundFootprintShape::Square && blend < 1.0f)
{
float slopeL1 = 0.0f;
if (!(gx == 0.0f && gy == 0.0f))
{
const float c = std::cos(yaw);
const float s = std::sin(yaw);
const float rx = gx * c + gy * s;
const float ry = -gx * s + gy * c;
slopeL1 = std::abs(rx) + std::abs(ry);
}
totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1);
}
// Final height (Minkowski): base + radius * slope
return zPlane + radius * totalSlope;
}

View File

@ -245,11 +245,37 @@ public:
~GridTerrainData() { };
TerrainMapDataReadResult Load(std::string const& mapFileName);
enum class GroundFootprintShape : uint8
{
Circle = 0, // cylinder-plane
Square = 1 // upside-down pyramid
};
static inline const char* ToString(GroundFootprintShape s)
{
switch (s)
{
case GroundFootprintShape::Circle: return "circle";
case GroundFootprintShape::Square: return "square";
}
return "unknown";
}
// Accurate height with footprint & yaw. squareBlend in [0..1]. slopeClamp in [0..10] (0 disables).
float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw,
float squareBlend, float slopeClamp, uint32 gradientMode, float normalEps) const;
// Convenience wrappers (kept for compatibility).
float GetHeightAccurate(float x, float y, float radius) const;
float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const;
float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw /*rads*/) const;
inline std::string to_string(GridTerrainData::GroundFootprintShape s) { return GridTerrainData::ToString(s); }
uint16 getArea(float x, float y) const;
inline float getHeight(float x, float y) const { return (this->*_gridGetHeight)(x, y); }
float getMinHeight(float x, float y) const;
float getLiquidLevel(float x, float y) const;
LiquidData const GetLiquidData(float x, float y, float z, float collisionHeight, uint8 ReqLiquidType) const;
private:
bool SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& h2, float& h3, float& h4, float& h5) const;
};
#endif

View File

@ -1175,6 +1175,123 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
return mapHeight; // explicitly use map data
}
namespace { constexpr float INV_SQRT2 = 0.70710678118654752440f; }
float Map::GetVMapHeightAccurate(float x, float y, float z, float radius, float yaw,
GridTerrainData::GroundFootprintShape shape, float blend, float clamp, float sampleDelta) const
{
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
auto sample = [&](float sx, float sy) -> float
{
float h = vmgr->getHeight(GetId(), sx, sy, z, DEFAULT_HEIGHT_SEARCH);
return (h > INVALID_HEIGHT) ? h : std::numeric_limits<float>::quiet_NaN();
};
float h0 = sample(x, y);
if (!std::isfinite(h0))
return VMAP_INVALID_HEIGHT_VALUE;
if (radius <= 0.0f)
return h0;
const float d = (sampleDelta > 0.0f) ? sampleDelta
: std::max(0.05f, std::min(0.5f, radius * 0.5f));
float hx1 = sample(x + d, y), hx2 = sample(x - d, y);
float hy1 = sample(x, y + d), hy2 = sample(x, y - d);
auto diff = [&](float p, float m) -> float
{
if (std::isfinite(p) && std::isfinite(m)) return (p - m) / (2.0f * d);
if (std::isfinite(p)) return (p - h0) / d;
if (std::isfinite(m)) return (h0 - m) / d;
return 0.0f;
};
float gx = diff(hx1, hx2); // dz/dx
float gy = diff(hy1, hy2); // dz/dy
if (clamp > 0.0f)
{
float g2 = gx*gx + gy*gy, c2 = clamp*clamp;
if (g2 > c2)
{
float s = clamp / std::sqrt(g2);
gx *= s; gy *= s;
}
}
float slopeL2 = std::sqrt(std::max(0.0f, gx*gx + gy*gy));
float totalSlope = slopeL2;
if (shape == GridTerrainData::GroundFootprintShape::Square && blend < 1.0f)
{
float c = std::cos(yaw), s = std::sin(yaw);
float rx = gx * c + gy * s, ry = -gx * s + gy * c;
float slopeL1 = std::abs(rx) + std::abs(ry);
totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1);
}
return h0 + radius * totalSlope;
}
float Map::GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
{
return GetHeightAccurate(x, y, z, radius, 0.0f, checkVMap, maxSearchDist);
}
float Map::GetHeightAccurate(float x, float y, float z, float radius, float yaw, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
{
// find raw .map surface under Z coordinates
float mapHeight = VMAP_INVALID_HEIGHT_VALUE;
float gridHeight = GetGridHeightAccurate(x, y, radius, yaw);
if (gridHeight > INVALID_HEIGHT)
{
const float tol = std::max(0.1f, 0.5f * radius); // dynamic tolerance based on the size of the collider
if (G3D::fuzzyGe(z, gridHeight - tol))
mapHeight = gridHeight;
}
float vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
if (checkVMap)
{
const bool useAccurateVMap = (sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE) != 0);
if (useAccurateVMap)
{
auto shape = static_cast<GridTerrainData::GroundFootprintShape>(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE));
float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND);
float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP);
float delta = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_VMAP_DELTA);
vmapHeight = GetVMapHeightAccurate(x, y, z, radius, yaw, shape, blend, clamp, delta);
}
else
{
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist);
}
}
// mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
// vmapheight set for any under Z value or <= INVALID_HEIGHT
if (vmapHeight > INVALID_HEIGHT)
{
if (mapHeight > INVALID_HEIGHT)
{
// we have mapheight and vmapheight and must select more appropriate
// we are already under the surface or vmap height above map heigt
// or if the distance of the vmap height is less the land height distance
if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z))
return vmapHeight;
else
return mapHeight; // better use .map surface height
}
else
return vmapHeight; // we have only vmapHeight (if have)
}
return mapHeight; // explicitly use map data
}
float Map::GetGridHeight(float x, float y) const
{
if (GridTerrainData* gmap = const_cast<Map*>(this)->GetGridTerrainData(x, y))
@ -1183,6 +1300,26 @@ float Map::GetGridHeight(float x, float y) const
return INVALID_HEIGHT;
}
float Map::GetGridHeightAccurate(float x, float y, float radius, float yaw) const
{
if (GridTerrainData* gmap = const_cast<Map*>(this)->GetGridTerrainData(x, y))
{
const auto shape = static_cast<GridTerrainData::GroundFootprintShape>(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE));
const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND);
const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP);
const uint32 mode = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE);
const float eps = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_NORMAL_EPS);
return gmap->GetHeightAccurate(x, y, radius, shape, yaw, blend, clamp, mode, eps);
}
return INVALID_HEIGHT;
}
float Map::GetGridHeightAccurate(float x, float y, float radius) const
{
return GetGridHeightAccurate(x, y, radius, 0.0f);
}
float Map::GetMinHeight(float x, float y) const
{
if (GridTerrainData const* grid = const_cast<Map*>(this)->GetGridTerrainData(x, y))
@ -1598,6 +1735,35 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr
return std::max<float>(h1, h2);
}
float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius,
bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
{
return GetHeightAccurate(phasemask, x, y, z, radius, 0.0f, vmap, maxSearchDist);
}
float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, float yaw,
bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
{
const float hMapMix = GetHeightAccurate(x, y, z, radius, yaw, vmap, maxSearchDist);
float hDyn;
const bool dynAcc = (sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE) != 0);
if (dynAcc)
{
const auto shape = static_cast<GridTerrainData::GroundFootprintShape>(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE));
const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND);
const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP);
const float dlt = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA);
const float effBlend = (shape == GridTerrainData::GroundFootprintShape::Square) ? blend : 1.0f;
hDyn = _dynamicTree.getHeightAccurate(x, y, z, maxSearchDist, phasemask, radius, yaw, effBlend, clamp, dlt);
}
else
{
hDyn = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask);
}
return std::max<float>(hMapMix, hDyn);
}
bool Map::IsInWater(uint32 phaseMask, float x, float y, float pZ, float collisionHeight) const
{
LiquidData const& liquidData = const_cast<Map*>(this)->GetLiquidData(phaseMask, x, y, pZ, collisionHeight, MAP_ALL_LIQUIDS);

View File

@ -238,8 +238,12 @@ public:
// some calls like isInWater should not use vmaps due to processor power
// can return INVALID_HEIGHT if under z+2 z coord not found height
[[nodiscard]] float GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; // wrapper yaw=0
[[nodiscard]] float GetHeightAccurate(float x, float y, float z, float radius, float yaw, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
[[nodiscard]] float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
[[nodiscard]] float GetGridHeight(float x, float y) const;
[[nodiscard]] float GetGridHeightAccurate(float x, float y, float radius) const; // wrapper yaw=0
[[nodiscard]] float GetGridHeightAccurate(float x, float y, float radius, float yaw) const;
[[nodiscard]] float GetMinHeight(float x, float y) const;
Transport* GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject = nullptr);
@ -382,6 +386,8 @@ public:
float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = DEFAULT_COLLISION_HEIGHT) const;
[[nodiscard]] float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
[[nodiscard]] float GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; // wrapper yaw=0
[[nodiscard]] float GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, float yaw, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
[[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const;
bool CanReachPositionAndGetValidCoords(WorldObject const* source, PathGenerator *path, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const;
bool CanReachPositionAndGetValidCoords(WorldObject const* source, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const;
@ -539,6 +545,9 @@ private:
bool EnsureGridLoaded(Cell const& cell);
MapGridType* GetMapGrid(uint16 const x, uint16 const y);
[[nodiscard]] float GetVMapHeightAccurate(float x, float y, float z, float radius, float yaw,
GridTerrainData::GroundFootprintShape shape, float blend, float clamp, float sampleDelta) const;
void ScriptsProcess();
void SendObjectUpdates();

View File

@ -2187,12 +2187,6 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(1);
});
// Halls of Lightning, Arcing Burn
ApplySpellFix({ 52671, 59834 }, [](SpellInfo* spellInfo)
{
spellInfo->AttributesEx3 |= SPELL_ATTR3_DOT_STACKING_RULE;
});
// Trial of the Champion, Death's Respite
ApplySpellFix({ 68306 }, [](SpellInfo* spellInfo)
{

View File

@ -285,6 +285,17 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<bool>(CONFIG_GM_LOWER_SECURITY, "GM.LowerSecurity", false);
SetConfigValue<float>(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50.0f);
SetConfigValue<uint32>(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1");
SetConfigValue<float>(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.1f && value <= 4.0f; }, ">= 0.1 and <= 4.0");
SetConfigValue<float>(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", 0.20f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 1.0f; }, ">= 0.0 and <= 1.0");
SetConfigValue<float>(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP, "Height.Accurate.SlopeClamp", 0.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 10.0f; }, ">= 0.0 and <= 10.0");
SetConfigValue<uint32>(CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE, "Height.Accurate.GradientMode", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1");
SetConfigValue<float>(CONFIG_HEIGHT_ACCURATE_NORMAL_EPS, "Height.Accurate.NormalEps", 1.0e-6f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 1e-8f && value <= 1e-3f; }, ">= 1e-8f and <= 1e-3f");
SetConfigValue<uint32>(CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE, "Height.Accurate.VMap.Enable", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1");
SetConfigValue<float>(CONFIG_HEIGHT_ACCURATE_VMAP_DELTA, "Height.Accurate.VMap.Delta", 0.25f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.01f && value <= 1.0f; }, ">= 0.01f and <= 1.0f");
SetConfigValue<uint32>(CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE, "Height.Accurate.Dynamic.Enable", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1");
SetConfigValue<float>(CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA, "Height.Accurate.Dynamic.Delta", 0.25f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.01f && value <= 1.0f; }, ">= 0.01f and <= 1.0f");
SetConfigValue<uint32>(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1);
SetConfigValue<bool>(CONFIG_OBJECT_SPARKLES, "Visibility.ObjectSparkles", true);

View File

@ -484,7 +484,16 @@ enum ServerConfigs
RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER,
CONFIG_NEW_CHAR_STRING,
CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS,
CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE,
CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND,
CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP,
CONFIG_HEIGHT_ACCURATE_SHAPE,
CONFIG_HEIGHT_ACCURATE_NORMAL_EPS,
CONFIG_HEIGHT_ACCURATE_VMAP_DELTA,
CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA,
CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE,
CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE,
CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE,
MAX_NUM_SERVER_CONFIGS
};

View File

@ -619,6 +619,7 @@ public:
AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId);
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId);
Map* map = object->GetMap();
float zoneX = object->GetPositionX();
float zoneY = object->GetPositionY();
@ -626,6 +627,22 @@ public:
float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT);
float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ());
const float probeR = object->GetGroundProbeRadius(); // pre-scale
const float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE);
const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND);
const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP);
const uint32 gmode = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE);
const float neps = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_NORMAL_EPS);
const uint32 vmapAcc = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE);
const float vdelta = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_VMAP_DELTA);
const uint32 dynAcc = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE);
const float dyndelta = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA);
const float yaw = object->GetOrientation();
const float probeRScaled = probeR * rScale;
const float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeRScaled, yaw);
const float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), true, DEFAULT_HEIGHT_SEARCH, probeR);
const char* shapeStr = GridTerrainData::ToString(static_cast<GridTerrainData::GroundFootprintShape>(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE)));
uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0;
uint32 haveVMap = GridTerrainLoader::ExistVMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0;
@ -656,6 +673,12 @@ public:
cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(),
zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMAP);
handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f}, SlopeClamp: {:0.2f}, GradMode: {}, NormalEps: {:0.6f}, VMapAccurate: {}, VMapDelta: {:0.2f}, DynAccurate: {}, DynDelta: {:0.2f})",
shapeStr, blend, rScale, clamp, gmode, neps, vmapAcc, vdelta, dynAcc, dyndelta);
handler->PSendSysMessage("Accurate Height Grid: {}", GridZAccurate);
handler->PSendSysMessage("Accurate Height Map: {}", MapZAccurate);
handler->PSendSysMessage("Probe radius (pre-scale): {:0.3f}", probeR);
LiquidData const& liquidData = object->GetLiquidData();
if (liquidData.Status)

View File

@ -41,6 +41,7 @@ enum VolkhanOther
NPC_VOLKHAN_ANVIL = 28823,
NPC_MOLTEN_GOLEM = 28695,
NPC_BRITTLE_GOLEM = 28681,
NPC_SLAG = 28585,
// Misc
ACTION_SHATTER = 1,
@ -77,7 +78,7 @@ enum Yells
struct boss_volkhan : public BossAI
{
boss_volkhan(Creature* creature) : BossAI(creature, DATA_VOLKHAN), summons(creature) { }
boss_volkhan(Creature* creature) : BossAI(creature, DATA_VOLKHAN) { }
void Reset() override
{
@ -104,6 +105,18 @@ struct boss_volkhan : public BossAI
{
_JustDied();
Talk(SAY_DEATH);
std::list<Creature*> slags;
GetCreatureListWithEntryInGrid(slags, me, NPC_SLAG, 100.0f);
if (!slags.empty())
{
for (Creature* slag : slags)
{
if (slag)
slag->DespawnOrUnsummon();
}
}
}
void GetNextPos()
@ -286,8 +299,6 @@ struct boss_volkhan : public BossAI
}
private:
EventMap events;
SummonList summons;
float x, y, z;
uint8 PointID;
uint8 ShatteredCount;

View File

@ -17,6 +17,7 @@
#include "CreatureScript.h"
#include "GameObjectScript.h"
#include "GridNotifiers.h"
#include "PassiveAI.h"
#include "Player.h"
#include "ScriptedCreature.h"
@ -26,6 +27,7 @@
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "Vehicle.h"
#include <algorithm>
enum AlchemistItemRequirements
{
@ -234,297 +236,376 @@ public:
}
};
enum overlordDrakuru
enum OverlordDrakuru
{
SPELL_SHADOW_BOLT = 54113,
SPELL_SCOURGE_DISGUISE_EXPIRING = 52010,
SPELL_THROW_BRIGHT_CRYSTAL = 54087,
SPELL_TELEPORT_EFFECT = 52096,
SPELL_SCOURGE_DISGUISE = 51966,
SPELL_SCOURGE_DISGUISE_INSTANT_CAST = 52192,
SPELL_BLIGHT_FOG = 54104,
SPELL_THROW_PORTAL_CRYSTAL = 54209,
SPELL_ARTHAS_PORTAL = 51807,
SPELL_TOUCH_OF_DEATH = 54236,
SPELL_DRAKURU_DEATH = 54248,
SPELL_SUMMON_SKULL = 54253,
SPELL_SHADOW_BOLT = 54113,
SPELL_SCOURGE_DISGUISE_EXPIRING = 52010,
SPELL_DROP_DISGUISE = 54089,
SPELL_THROW_BRIGHT_CRYSTAL = 54087,
SPELL_TELEPORT_EFFECT = 52096,
SPELL_SCOURGE_SPOTLIGHT = 53104,
SPELL_SCOURGE_DISGUISE = 51966,
SPELL_SCOURGE_DISGUISE_INSTANT_CAST = 52192,
SPELL_BLIGHT_FOG = 54104,
SPELL_THROW_PORTAL_CRYSTAL = 54209,
SPELL_ARTHAS_PORTAL = 51807,
SPELL_TOUCH_OF_DEATH = 54236,
SPELL_DRAKURU_DEATH = 54248,
SPELL_SUMMON_SKULL = 54253,
SPELL_BLOATED_ABOMINATION_FEIGN_DEATH = 52593,
SPELL_EXPLODE_ABOMINATION_BLOODY_MEAT = 52523,
SPELL_EXPLODE_ABOMINATION_MEAT = 52520,
SPELL_DRAKURUS_SKULL_MISSILE = 54250,
SPELL_BURST_AT_THE_SEAMS_BONE = 52516,
QUEST_BETRAYAL = 12713,
QUEST_BETRAYAL = 12713,
NPC_BLIGHTBLOOD_TROLL = 28931,
NPC_LICH_KING = 28498,
NPC_BLIGHTBLOOD_TROLL = 28931,
NPC_LICH_KING = 28498,
NPC_TOTALLY_GENERIC_BUNNY = 29100,
NPC_TOTALLY_GENERIC_BUNNY_JSB = 28960,
GO_DRAKURUS_LAST_WISH = 202357,
EVENT_BETRAYAL_1 = 1,
EVENT_BETRAYAL_2 = 2,
EVENT_BETRAYAL_3 = 3,
EVENT_BETRAYAL_4 = 4,
EVENT_BETRAYAL_5 = 5,
EVENT_BETRAYAL_6 = 6,
EVENT_BETRAYAL_7 = 7,
EVENT_BETRAYAL_8 = 8,
EVENT_BETRAYAL_9 = 9,
EVENT_BETRAYAL_10 = 10,
EVENT_BETRAYAL_11 = 11,
EVENT_BETRAYAL_12 = 12,
EVENT_BETRAYAL_13 = 13,
EVENT_BETRAYAL_14 = 14,
EVENT_BETRAYAL_SHADOW_BOLT = 20,
EVENT_BETRAYAL_CRYSTAL = 21,
EVENT_BETRAYAL_COMBAT_TALK = 22,
ACTION_SUMMON_DRAKURU_LAST_WISH = 1,
ACTION_DESTROY_DRAKURU_LAST_WISH = 2,
ACTION_REMOVE_SPOTLIGHTS = 3,
SAY_DRAKURU_0 = 0,
SAY_DRAKURU_1 = 1,
SAY_DRAKURU_2 = 2,
SAY_DRAKURU_3 = 3,
SAY_DRAKURU_4 = 4,
SAY_DRAKURU_5 = 5,
SAY_DRAKURU_6 = 6,
SAY_DRAKURU_7 = 7,
SAY_LICH_7 = 7,
SAY_LICH_8 = 8,
SAY_LICH_9 = 9,
SAY_LICH_10 = 10,
SAY_LICH_11 = 11,
SAY_LICH_12 = 12,
SUMMON_GROUP_BLIGHTBLOOD_TROLL = 1,
EVENT_BETRAYAL_INTRO_1 = 1,
EVENT_BETRAYAL_INTRO_2 = 2,
EVENT_BETRAYAL_INTRO_3 = 3,
EVENT_BETRAYAL_INTRO_4 = 4,
EVENT_BETRAYAL_EVADE_CHECK = 5,
EVENT_BETRAYAL_EPILOGUE_1 = 6,
EVENT_BETRAYAL_EPILOGUE_2 = 7,
EVENT_BETRAYAL_EPILOGUE_3 = 8,
EVENT_BETRAYAL_EPILOGUE_4 = 9,
EVENT_BETRAYAL_EPILOGUE_5 = 10,
EVENT_BETRAYAL_EPILOGUE_6 = 11,
EVENT_BETRAYAL_EPILOGUE_7 = 12,
EVENT_BETRAYAL_EPILOGUE_8 = 13,
EVENT_BETRAYAL_EPILOGUE_9 = 14,
EVENT_BETRAYAL_EPILOGUE_10 = 15,
SAY_DRAKURU_0 = 0,
SAY_DRAKURU_1 = 1,
SAY_DRAKURU_2 = 2,
SAY_DRAKURU_3 = 3,
SAY_DRAKURU_4 = 4,
SAY_DRAKURU_5 = 5,
SAY_DRAKURU_6 = 6,
SAY_DRAKURU_7 = 7,
SAY_LICH_7 = 7,
SAY_LICH_8 = 8,
SAY_LICH_9 = 9,
SAY_LICH_10 = 10,
SAY_LICH_11 = 11,
SAY_LICH_12 = 12,
};
class npc_overlord_drakuru_betrayal : public CreatureScript
enum BetrayalState
{
public:
npc_overlord_drakuru_betrayal() : CreatureScript("npc_overlord_drakuru_betrayal") { }
BETRAYAL_NOT_STARTED,
BETRAYAL_IN_PROGRESS,
BETRAYAL_EPILOGUE,
BETRAYAL_EVADE,
};
CreatureAI* GetAI(Creature* creature) const override
struct npc_overlord_drakuru_betrayal : public ScriptedAI
{
npc_overlord_drakuru_betrayal(Creature* creature) : ScriptedAI(creature), _summons(me), _state(BETRAYAL_NOT_STARTED)
{
return new npc_overlord_drakuru_betrayalAI(creature);
me->SetCombatMovement(false);
}
struct npc_overlord_drakuru_betrayalAI : public ScriptedAI
void EnterEvadeMode(EvadeReason why) override
{
npc_overlord_drakuru_betrayalAI(Creature* creature) : ScriptedAI(creature), summons(me)
{
}
if (_state != BETRAYAL_EVADE)
return;
me->SetFaction(FACTION_UNDEAD_SCOURGE);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
ScriptedAI::EnterEvadeMode(why);
}
EventMap events;
SummonList summons;
ObjectGuid playerGUID;
ObjectGuid lichGUID;
void Reset() override
{
events.Reset();
scheduler.CancelAll();
_summons.DespawnAll();
_playerGUID.Clear();
_lichGUID.Clear();
me->SetFaction(FACTION_UNDEAD_SCOURGE);
me->SetVisible(false);
DoAction(ACTION_SUMMON_DRAKURU_LAST_WISH);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetImmuneToPC(true);
_state = BETRAYAL_NOT_STARTED;
DoAction(ACTION_REMOVE_SPOTLIGHTS);
}
void EnterEvadeMode(EvadeReason why) override
void DoAction(int32 action) override
{
switch (action)
{
if (playerGUID)
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
if (player->IsWithinDistInMap(me, 80))
return;
me->SetFaction(FACTION_UNDEAD_SCOURGE);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
ScriptedAI::EnterEvadeMode(why);
}
void Reset() override
{
events.Reset();
summons.DespawnAll();
playerGUID.Clear();
lichGUID.Clear();
me->SetFaction(FACTION_UNDEAD_SCOURGE);
me->SetVisible(false);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
}
void MoveInLineOfSight(Unit* who) override
{
if (who->IsPlayer())
case ACTION_SUMMON_DRAKURU_LAST_WISH:
if (!me->FindNearestGameObject(GO_DRAKURUS_LAST_WISH, 80.0f))
me->SummonGameObject(GO_DRAKURUS_LAST_WISH, 6185.989, -2029.6979, 590.87787, 0, 0, 0, 0, 0, 0, true, GO_SUMMON_TIMED_DESPAWN);
break;
case ACTION_DESTROY_DRAKURU_LAST_WISH:
if (GameObject* go = me->FindNearestGameObject(GO_DRAKURUS_LAST_WISH, 80.0f))
go->Delete();
break;
case ACTION_REMOVE_SPOTLIGHTS:
{
if (playerGUID)
std::list<Creature*> creatures;
me->GetCreatureListWithEntryInGrid(creatures, NPC_TOTALLY_GENERIC_BUNNY, 55.0f);
for (Creature* creature : creatures)
creature->RemoveAurasDueToSpell(SPELL_SCOURGE_SPOTLIGHT);
}
}
}
bool IsPlayerOnQuest(Player* player)
{
return player->GetQuestStatus(QUEST_BETRAYAL) == QUEST_STATUS_INCOMPLETE;
}
void MoveInLineOfSight(Unit* who) override
{
if (Player* player = who->ToPlayer())
{
bool shouldStartEvent = (_state == BETRAYAL_NOT_STARTED) && IsPlayerOnQuest(player) && player->HasAura(SPELL_SCOURGE_DISGUISE) && player->IsWithinDistInMap(me, 80.0f);
if (shouldStartEvent)
{
me->SetVisible(true);
_state = BETRAYAL_IN_PROGRESS;
DoAction(ACTION_DESTROY_DRAKURU_LAST_WISH);
_playerGUID = who->GetGUID();
events.ScheduleEvent(EVENT_BETRAYAL_INTRO_1, 6s);
events.ScheduleEvent(EVENT_BETRAYAL_EVADE_CHECK, 10s);
}
}
else
ScriptedAI::MoveInLineOfSight(who);
}
void JustSummoned(Creature* summon) override
{
_summons.Summon(summon);
switch (summon->GetEntry())
{
case NPC_BLIGHTBLOOD_TROLL:
if (Creature* target = summon->FindNearestCreature(NPC_TOTALLY_GENERIC_BUNNY, 10.0f, true))
target->CastSpell(target, SPELL_TELEPORT_EFFECT, true);
break;
case NPC_LICH_KING:
me->SetFacingToObject(summon);
_lichGUID = summon->GetGUID();
summon->GetMotionMaster()->MovePoint(0, 6164.2695, -2016.8978, 590.8636);
break;
default:
break;
}
}
void JustEngagedWith(Unit* /*who*/) override
{
scheduler.Schedule(0s, [this](TaskContext context)
{
if (!me->IsWithinMeleeRange(me->GetVictim()))
DoCastVictim(SPELL_SHADOW_BOLT);
context.Repeat(2s);
}).Schedule(5s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
DoCast(target, SPELL_THROW_BRIGHT_CRYSTAL);
context.Repeat(6s, 15s);
}).Schedule(20s, [this](TaskContext context)
{
Talk(SAY_DRAKURU_4);
context.Repeat(10s, 20s);
});
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*dmgType*/, SpellSchoolMask /*school*/) override
{
if (damage >= me->GetHealth() && !me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
{
damage = 0;
me->RemoveAllAuras();
me->CombatStop();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(2082);
me->SetImmuneToPC(true);
events.Reset();
scheduler.CancelAll();
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_1, 4200ms);
_state = BETRAYAL_EPILOGUE;
}
}
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
switch (spellInfo->Id)
{
case SPELL_THROW_PORTAL_CRYSTAL:
if (Aura* aura = target->AddAura(SPELL_ARTHAS_PORTAL, target))
aura->SetDuration(77'000);
break;
case SPELL_DRAKURUS_SKULL_MISSILE:
target->CastSpell(target, SPELL_SUMMON_SKULL, true);
break;
case SPELL_DROP_DISGUISE:
target->CastSpell(target, SPELL_SCOURGE_DISGUISE_EXPIRING, true);
break;
}
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_TOUCH_OF_DEATH)
{
DoCastAOE(SPELL_DRAKURUS_SKULL_MISSILE, true);
DoCastSelf(SPELL_BLOATED_ABOMINATION_FEIGN_DEATH, true);
DoCastSelf(SPELL_BURST_AT_THE_SEAMS_BONE, true);
DoCastSelf(SPELL_BURST_AT_THE_SEAMS_BONE, true);
DoCastSelf(SPELL_BURST_AT_THE_SEAMS_BONE, true);
DoCastSelf(SPELL_EXPLODE_ABOMINATION_MEAT, true);
DoCastSelf(SPELL_EXPLODE_ABOMINATION_BLOODY_MEAT, true);
DoCastSelf(SPELL_EXPLODE_ABOMINATION_BLOODY_MEAT, true);
DoCastSelf(SPELL_EXPLODE_ABOMINATION_BLOODY_MEAT, true);
DoCastSelf(SPELL_DRAKURU_DEATH, true);
DoAction(ACTION_SUMMON_DRAKURU_LAST_WISH);
me->SetImmuneToPC(true);
}
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_BETRAYAL_EVADE_CHECK:
{
if (_state == BETRAYAL_IN_PROGRESS)
{
if (who->GetGUID() != playerGUID)
float radius = 80.0f;
std::list<Player*> players;
Acore::AnyPlayerInObjectRangeCheck checker(me, radius, true, true);
Acore::PlayerListSearcher<Acore::AnyPlayerInObjectRangeCheck> searcher(me, players, checker);
Cell::VisitObjects(me, searcher, radius);
if (std::ranges::any_of(players, [this](Player* player)
{
Player* player = ObjectAccessor::GetPlayer(*me, playerGUID);
if (player && player->IsWithinDistInMap(me, 80))
who->ToPlayer()->NearTeleportTo(6143.76f, -1969.7f, 417.57f, 2.08f);
else
{
EnterEvadeMode(EVADE_REASON_OTHER);
return;
}
return IsPlayerOnQuest(player);
}))
{
events.Repeat(10s);
}
else
ScriptedAI::MoveInLineOfSight(who);
{
_state = BETRAYAL_EVADE;
EnterEvadeMode(EVADE_REASON_OTHER);
}
}
else if (who->ToPlayer()->GetQuestStatus(QUEST_BETRAYAL) == QUEST_STATUS_INCOMPLETE && who->HasAura(SPELL_SCOURGE_DISGUISE))
break;
}
case EVENT_BETRAYAL_INTRO_1:
Talk(SAY_DRAKURU_0);
events.ScheduleEvent(EVENT_BETRAYAL_INTRO_2, 4s);
events.ScheduleEvent(EVENT_BETRAYAL_INTRO_3, 6600ms);
break;
case EVENT_BETRAYAL_INTRO_2:
me->SummonCreatureGroup(SUMMON_GROUP_BLIGHTBLOOD_TROLL);
break;
case EVENT_BETRAYAL_INTRO_3:
Talk(SAY_DRAKURU_1);
DoCastAOE(SPELL_DROP_DISGUISE);
events.ScheduleEvent(EVENT_BETRAYAL_INTRO_4, 9600ms);
break;
case EVENT_BETRAYAL_INTRO_4:
{
Talk(SAY_DRAKURU_2);
Talk(SAY_DRAKURU_3);
me->SetImmuneToPC(false);
std::list<Creature*> creatures;
me->GetCreatureListWithEntryInGrid(creatures, NPC_TOTALLY_GENERIC_BUNNY, 55.0f);
for (Creature* creature : creatures)
creature->CastSpell(creature, SPELL_SCOURGE_SPOTLIGHT, true);
break;
}
case EVENT_BETRAYAL_EPILOGUE_1:
{
Talk(SAY_DRAKURU_5);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_2, 4800ms);
DoAction(ACTION_REMOVE_SPOTLIGHTS);
break;
}
case EVENT_BETRAYAL_EPILOGUE_2:
Talk(SAY_DRAKURU_6);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_3, 1800ms);
break;
case EVENT_BETRAYAL_EPILOGUE_3:
DoCastSelf(SPELL_THROW_PORTAL_CRYSTAL, true);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_4, 3600ms);
break;
case EVENT_BETRAYAL_EPILOGUE_4:
me->SummonCreature(NPC_LICH_KING, 6140.4233, -2010.9938, 589.1911, 6.126106, TEMPSUMMON_TIMED_DESPAWN, 77'000);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_5, 8400ms);
break;
case EVENT_BETRAYAL_EPILOGUE_5:
Talk(SAY_DRAKURU_7);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_6, 9600ms);
break;
case EVENT_BETRAYAL_EPILOGUE_6:
if (Creature* lich = ObjectAccessor::GetCreature(*me, _lichGUID))
{
me->SetVisible(true);
playerGUID = who->GetGUID();
events.ScheduleEvent(EVENT_BETRAYAL_1, 5s);
lich->AI()->Talk(SAY_LICH_7);
lich->AI()->Talk(SAY_LICH_8, 5400ms);
}
}
else
ScriptedAI::MoveInLineOfSight(who);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_7, 7800ms);
break;
case EVENT_BETRAYAL_EPILOGUE_7:
if (Creature* lich = ObjectAccessor::GetCreature(*me, _lichGUID))
lich->CastSpell(me, SPELL_TOUCH_OF_DEATH, false);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_8, 4200ms);
break;
case EVENT_BETRAYAL_EPILOGUE_8:
me->SetVisible(false);
if (Creature* lich = ObjectAccessor::GetCreature(*me, _lichGUID))
{
lich->AI()->Talk(SAY_LICH_9, 3600ms);
lich->AI()->Talk(SAY_LICH_10, 8400ms);
lich->AI()->Talk(SAY_LICH_11, 22800ms);
lich->AI()->Talk(SAY_LICH_12, 27600ms);
}
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_9, 32600ms);
events.ScheduleEvent(EVENT_BETRAYAL_EPILOGUE_10, 37200ms);
break;
case EVENT_BETRAYAL_EPILOGUE_9:
if (Creature* lich = ObjectAccessor::GetCreature(*me, _lichGUID))
lich->GetMotionMaster()->MovePoint(0, 6141.2393, -2011.2728, 589.8653);
break;
case EVENT_BETRAYAL_EPILOGUE_10:
EnterEvadeMode(EVADE_REASON_OTHER);
break;
}
void JustSummoned(Creature* cr) override
{
summons.Summon(cr);
if (cr->GetEntry() == NPC_BLIGHTBLOOD_TROLL)
cr->CastSpell(cr, SPELL_TELEPORT_EFFECT, true);
else
{
me->SetFacingToObject(cr);
lichGUID = cr->GetGUID();
float o = me->GetAngle(cr);
cr->GetMotionMaster()->MovePoint(0, me->GetPositionX() + cos(o) * 6.0f, me->GetPositionY() + std::sin(o) * 6.0f, me->GetPositionZ());
}
}
if (me->GetFaction() == 2082 || me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED))
return;
void JustEngagedWith(Unit*) override
{
Talk(SAY_DRAKURU_3);
events.ScheduleEvent(EVENT_BETRAYAL_SHADOW_BOLT, 2s);
events.ScheduleEvent(EVENT_BETRAYAL_CRYSTAL, 5s);
events.ScheduleEvent(EVENT_BETRAYAL_COMBAT_TALK, 20s);
}
if (!UpdateVictim())
return;
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth() && !me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
{
damage = 0;
me->RemoveAllAuras();
me->CombatStop();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(FACTION_FRIENDLY);
events.Reset();
events.ScheduleEvent(EVENT_BETRAYAL_4, 1s);
}
}
scheduler.Update(diff);
DoMeleeAttackIfReady();
}
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_THROW_PORTAL_CRYSTAL)
if (Aura* aura = target->AddAura(SPELL_ARTHAS_PORTAL, target))
aura->SetDuration(48000);
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_TOUCH_OF_DEATH)
{
me->CastSpell(me, SPELL_DRAKURU_DEATH, true);
me->CastSpell(me, SPELL_SUMMON_SKULL, true);
}
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_BETRAYAL_1:
Talk(SAY_DRAKURU_0);
events.ScheduleEvent(EVENT_BETRAYAL_2, 5s);
break;
case EVENT_BETRAYAL_2:
me->SummonCreature(NPC_BLIGHTBLOOD_TROLL, 6184.1f, -1969.9f, 586.76f, 4.5f);
me->SummonCreature(NPC_BLIGHTBLOOD_TROLL, 6222.9f, -2026.5f, 586.76f, 2.9f);
me->SummonCreature(NPC_BLIGHTBLOOD_TROLL, 6166.2f, -2065.4f, 586.76f, 1.4f);
me->SummonCreature(NPC_BLIGHTBLOOD_TROLL, 6127.5f, -2008.7f, 586.76f, 0.0f);
events.ScheduleEvent(EVENT_BETRAYAL_3, 5s);
break;
case EVENT_BETRAYAL_3:
Talk(SAY_DRAKURU_1);
Talk(SAY_DRAKURU_2);
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
player->CastSpell(player, SPELL_SCOURGE_DISGUISE_EXPIRING, true);
if (Aura* aur = me->AddAura(SPELL_BLIGHT_FOG, me))
aur->SetDuration(22000);
break;
case EVENT_BETRAYAL_4:
Talk(SAY_DRAKURU_5);
events.ScheduleEvent(EVENT_BETRAYAL_5, 6s);
break;
case EVENT_BETRAYAL_5:
Talk(SAY_DRAKURU_6);
me->CastSpell(me, SPELL_THROW_PORTAL_CRYSTAL, true);
events.ScheduleEvent(EVENT_BETRAYAL_6, 8s);
break;
case EVENT_BETRAYAL_6:
me->SummonCreature(NPC_LICH_KING, 6142.9f, -2011.6f, 590.86f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 41000);
events.ScheduleEvent(EVENT_BETRAYAL_7, 8s);
break;
case EVENT_BETRAYAL_7:
Talk(SAY_DRAKURU_7);
events.ScheduleEvent(EVENT_BETRAYAL_8, 5s);
break;
case EVENT_BETRAYAL_8:
if (Creature* lich = ObjectAccessor::GetCreature(*me, lichGUID))
lich->AI()->Talk(SAY_LICH_7);
events.ScheduleEvent(EVENT_BETRAYAL_9, 6s);
break;
case EVENT_BETRAYAL_9:
if (Creature* lich = ObjectAccessor::GetCreature(*me, lichGUID))
{
lich->AI()->Talk(SAY_LICH_8);
lich->CastSpell(me, SPELL_TOUCH_OF_DEATH, false);
}
events.ScheduleEvent(EVENT_BETRAYAL_10, 4s);
break;
case EVENT_BETRAYAL_10:
me->SetVisible(false);
if (Creature* lich = ObjectAccessor::GetCreature(*me, lichGUID))
lich->AI()->Talk(SAY_LICH_9);
events.ScheduleEvent(EVENT_BETRAYAL_11, 4s);
break;
case EVENT_BETRAYAL_11:
if (Creature* lich = ObjectAccessor::GetCreature(*me, lichGUID))
lich->AI()->Talk(SAY_LICH_10);
events.ScheduleEvent(EVENT_BETRAYAL_12, 6s);
break;
case EVENT_BETRAYAL_12:
if (Creature* lich = ObjectAccessor::GetCreature(*me, lichGUID))
lich->AI()->Talk(SAY_LICH_11);
events.ScheduleEvent(EVENT_BETRAYAL_13, 3s);
break;
case EVENT_BETRAYAL_13:
if (Creature* lich = ObjectAccessor::GetCreature(*me, lichGUID))
{
lich->AI()->Talk(SAY_LICH_12);
lich->GetMotionMaster()->MovePoint(0, 6143.8f, -2011.5f, 590.9f);
}
events.ScheduleEvent(EVENT_BETRAYAL_14, 7s);
break;
case EVENT_BETRAYAL_14:
playerGUID.Clear();
EnterEvadeMode(EVADE_REASON_OTHER);
break;
}
if (me->GetFaction() == FACTION_FRIENDLY || me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED))
return;
if (!UpdateVictim())
return;
switch (events.ExecuteEvent())
{
case EVENT_BETRAYAL_SHADOW_BOLT:
if (!me->IsWithinMeleeRange(me->GetVictim()))
me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false);
events.Repeat(2s);
break;
case EVENT_BETRAYAL_CRYSTAL:
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
me->CastSpell(player, SPELL_THROW_BRIGHT_CRYSTAL, true);
events.Repeat(6s, 15s);
break;
case EVENT_BETRAYAL_COMBAT_TALK:
Talk(SAY_DRAKURU_4);
events.Repeat(20s);
break;
}
DoMeleeAttackIfReady();
}
};
private:
SummonList _summons;
ObjectGuid _playerGUID;
ObjectGuid _lichGUID;
BetrayalState _state;
};
/*####
@ -864,11 +945,32 @@ class spell_scourge_disguise_instability : public AuraScript
}
};
// 54105 - Blight Fog
class spell_blight_fog : public SpellScript
{
PrepareSpellScript(spell_blight_fog);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if([](WorldObject* target) -> bool
{
float z = target->GetPositionZ();
bool isInBlightFog = (582.0f <= z && z <= 583.0f) || (586.0f <= z && z <= 587.0f);
return !isInBlightFog;
});
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_blight_fog::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
void AddSC_zuldrak()
{
new npc_finklestein();
new go_finklestein_cauldron();
new npc_overlord_drakuru_betrayal();
RegisterCreatureAI(npc_overlord_drakuru_betrayal);
new npc_drakuru_shackles();
new npc_captured_rageclaw();
new npc_released_offspring_harkoa();
@ -876,4 +978,5 @@ void AddSC_zuldrak()
new go_scourge_enclosure();
RegisterSpellScript(spell_scourge_disguise_instability);
RegisterSpellScript(spell_blight_fog);
}