diff --git a/apps/startup-scripts/src/starter b/apps/startup-scripts/src/starter
index a802f6455..4e58e8e7b 100755
--- a/apps/startup-scripts/src/starter
+++ b/apps/startup-scripts/src/starter
@@ -122,12 +122,26 @@ EOF
fi
else
echo "Starting $BINFILE without GDB"
- if [[ "$AC_LAUNCHED_BY_PM2" == "1" ]]; then
- echo "Running under PM2"
+ # Determine if PM2 is active
+ is_pm2_active="0"
+ [ "$AC_LAUNCHED_BY_PM2" == "1" ] && is_pm2_active="1"
+
+ # Determine if interactive mode is enabled
+ is_interactive_enabled="1"
+ [ "$AC_DISABLE_INTERACTIVE" == "1" ] && is_interactive_enabled="0"
+
+ # use normal execution if we are running the binary under PM2
+ # or when interactive mode is enabled
+ if [[ "$is_pm2_active" == "1" || "$is_interactive_enabled" == "1" ]]; then
+ echo "Running AC"
"$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
else
+ # When AC_DISABLE_INTERACTIVE is set to 1 and we are not in PM2
+ # This means we are using systemd without interactive mode and no session managers
+ # in this case we need to run AC with unbuffer for line-buffered output
+ # NOTE unbuffer doesn't fully support interactive mode
if command -v unbuffer >/dev/null 2>&1; then
- export AC_DISABLE_INTERACTIVE=0
+ echo "Running AC with unbuffer for line-buffered output"
unbuffer "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
else
echo "⚠️ unbuffer not found, the output may not be line-buffered. Try installing expect."
diff --git a/data/sql/updates/db_characters/2025_07_29_00.sql b/data/sql/updates/db_characters/2025_07_29_00.sql
new file mode 100644
index 000000000..faf92897e
--- /dev/null
+++ b/data/sql/updates/db_characters/2025_07_29_00.sql
@@ -0,0 +1,4 @@
+-- DB update 2025_07_11_00 -> 2025_07_29_00
+--
+ALTER TABLE `mail_server_template_conditions`
+ CHANGE COLUMN `conditionType` `conditionType` ENUM('Level','PlayTime','Quest','Achievement','Reputation','Faction','Race','Class','AccountFlags') NOT NULL COLLATE 'utf8mb4_unicode_ci' AFTER `templateID`;
diff --git a/data/sql/updates/db_world/2025_07_26_00.sql b/data/sql/updates/db_world/2025_07_26_00.sql
new file mode 100644
index 000000000..803f5abb2
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_26_00.sql
@@ -0,0 +1,4 @@
+-- DB update 2025_07_25_00 -> 2025_07_26_00
+UPDATE `page_text_locale` SET `Text`='\r\n
\r\n
\r\nО, великий вождь!
да озарит Свет каждый ваш шаг, и да пребудет он вечно в вашем сердце. Я молю вас о пощаде для этого павшего рыцаря, и смиренно прошу выслушать меня до конца.
Податель сего письма некогда $Gбыл:была; героем Орды. Хотя отныне чело $Gего:ее; отмечено печатью смерти и обликом Плети, душой $Gон остался верен:она осталась верна; нашему делу.\r\n
\r\n\r\n' WHERE `ID` = 3539 AND `locale`='ruRU';
+UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\n$N $Gбыл избран:была избрана; мной в качестве представителя нового союза, сражающегося против Короля-лича. Этот орден носит имя Рыцарей Черного Клинка. Да, ваше величество, вы не ошибаетесь: рыцари смерти объединились против своего бывшего повелителя. Их возглавляет сын лорда Александроса Могрейна – сам Испепелитель, и жаждут они того же, что и все мы: положить конец Плети.
Я не смею просить вас принять $N и всех Рыцарей Черного Клинка в Орду, но надеюсь, что вы проявите к ним снисхождение.\r\n
\r\n\r\n' WHERE `ID` = 3540 AND `locale`='ruRU';
+UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\nПомните, вождь, что все мы служим кровью и честью во имя великой цели.
С глубочайшим уважением,
Верховный лорд Тирион Фордринг
P.S. Передайте Эйтриггу мои пожелания долгого здравия и скажите ему, что мне не помешает такой союзник в Нордсколе – разумеется, если вы не будете возражать.\r\n
\r\n\r\n' WHERE `ID`= 3541 AND `locale`='ruRU';
diff --git a/data/sql/updates/db_world/2025_07_26_01.sql b/data/sql/updates/db_world/2025_07_26_01.sql
new file mode 100644
index 000000000..0e5487ea0
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_26_01.sql
@@ -0,0 +1,3 @@
+-- DB update 2025_07_26_00 -> 2025_07_26_01
+--
+UPDATE `spell_dbc` SET `Effect_1` = 6, `EffectBasePoints_1`= 100, `ImplicitTargetA_1`= 1, `EffectAura_1` = 61, `CumulativeAura`= 3 WHERE `ID`= 57059;
diff --git a/data/sql/updates/db_world/2025_07_27_00.sql b/data/sql/updates/db_world/2025_07_27_00.sql
new file mode 100644
index 000000000..159d80deb
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_27_00.sql
@@ -0,0 +1,5 @@
+-- DB update 2025_07_26_01 -> 2025_07_27_00
+--
+DELETE FROM `smart_scripts` WHERE (`entryorguid` = 496101) AND (`source_type` = 9) 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
+(496101, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 26, 1447, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Dashel Stonefist - On Script - Quest Credit \'The Missing Diplomat\'');
diff --git a/data/sql/updates/db_world/2025_07_27_01.sql b/data/sql/updates/db_world/2025_07_27_01.sql
new file mode 100644
index 000000000..8c911b1b5
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_27_01.sql
@@ -0,0 +1,5 @@
+-- DB update 2025_07_27_00 -> 2025_07_27_01
+--
+DELETE FROM `acore_string` WHERE `entry` = 179;
+INSERT INTO `acore_string` (`entry`, `content_default`) VALUES
+(179, '| AccountFlags:');
diff --git a/data/sql/updates/db_world/2025_07_27_02.sql b/data/sql/updates/db_world/2025_07_27_02.sql
new file mode 100644
index 000000000..10a9ac895
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_27_02.sql
@@ -0,0 +1,86 @@
+-- DB update 2025_07_27_01 -> 2025_07_27_02
+
+-- Haerthsinger Forresten
+DELETE FROM `creature_text` WHERE (`CreatureID` = 30551);
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(30551, 0, 0, 'This whole situation seems a bit paranoid, don\'t you think?', 12, 0, 100, 1, 0, 0, 31324, 0, ''),
+(30551, 1, 0, 'Thank the Light for that.', 12, 0, 100, 1, 0, 0, 32573, 0, '');
+
+-- Fras Siabi
+DELETE FROM `creature_text` WHERE (`CreatureID` = 30552);
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(30552, 0, 0, 'It\'s a strange order, you can\'t deny. Suspicious food? Under that definition, you should arrest Belfast!', 12, 0, 100, 1, 0, 0, 31326, 0, ''),
+(30552, 1, 0, '%s nods.', 16, 0, 100, 0, 0, 0, 32046, 0, '');
+
+-- Footman James
+DELETE FROM `creature_text` WHERE (`CreatureID` = 30553);
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(30553, 0, 0, 'Orders are orders. If the Prince says jump...', 12, 0, 100, 1, 0, 0, 31325, 0, ''),
+(30553, 1, 0, 'Don\'t worry too much. By the time I went off duty, we hadn\'t found a scrap of befouled grain here.', 12, 0, 100, 1, 0, 0, 32572, 0, '');
+
+-- Michael Belfast
+DELETE FROM `creature_text` WHERE (`CreatureID` = 30571);
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(30571, 0, 0, 'Hey! Stop rooting around in my cellar! Clear out!', 12, 0, 100, 5, 0, 0, 31322, 0, ''),
+(30571, 0, 1, 'What were you doing in my cellar? There\'s a food scare going on, and the last thing I need is strangers rummaging around in my goods! Shoo!', 12, 0, 100, 5, 0, 0, 31323, 0, ''),
+(30571, 1, 0, 'I HEARD THAT! No more ale for you! Not a drop!', 12, 0, 100, 25, 0, 0, 31327, 0, '');
+
+-- Mal Corricks
+DELETE FROM `creature_text` WHERE (`CreatureID` = 31017);
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(31017, 0, 0, 'Enough, Michael. Business is hurting enough with this scare as it is. We can use every copper.', 12, 0, 100, 274, 0, 0, 32560, 0, ''),
+(31017, 1, 0, '%s grudgingly nods.', 16, 0, 100, 0, 0, 0, 32569, 0, ''),
+(31017, 2, 0, 'I can\'t argue with that.', 12, 0, 100, 1, 0, 0, 32570, 0, '');
+
+-- Gryan Stoutmantle
+DELETE FROM `creature_text` WHERE (`CreatureID` = 30561);
+INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
+(30561, 0, 0, 'The soldiers are doing important work. The safety of the people is more important, Mal, if you\'re interested in your customers living to spend another day.', 12, 0, 0, 1, 0, 0, 32571, 0, '');
+
+-- Move old Waypoint into waypoint_data.
+DELETE FROM `waypoints` WHERE `entry` IN (3057100);
+DELETE FROM `waypoint_data` WHERE `id` IN (3057100);
+INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
+("3057100", 1, 1549.5714, 575.55707, 100.05218, NULL, 10000, 0, 0, 100, 0),
+("3057100", 2, 1550.5083, 579.5038, 99.76226, NULL, 10000, 0, 0, 100, 0),
+("3057100", 3, 1553.4889, 578.15356, 99.76225, NULL, 10000, 0, 0, 100, 0);
+
+-- Set New Waypoint (Michael Belfast)
+DELETE FROM `waypoint_data` WHERE `id` IN (3057101);
+INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
+("3057101", 1, 1554.98, 588.784, 99.7754, NULL, 0, 0, 0, 100, 0);
+
+-- Set New SmartAI (Michael Belfast)
+UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30571;
+
+DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 30571);
+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
+(30571, 0, 0, 0, 38, 0, 100, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 1 - Say Line 0 (No Repeat)'),
+(30571, 0, 1, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 232, 3057100, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Respawn - Start Path 3057100'),
+(30571, 0, 2, 3, 108, 0, 100, 0, 1, 3057100, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 1 of Path 3057100 Reached - Set Emote State 0'),
+(30571, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4.5, 'Michael Belfast - On Point 1 of Path 3057100 Reached - Set Orientation 4.5'),
+(30571, 0, 4, 5, 108, 0, 100, 0, 2, 3057100, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 3, 'Michael Belfast - On Point 2 of Path 3057100 Reached - Set Orientation 3'),
+(30571, 0, 5, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 2 of Path 3057100 Reached - Set Flag Standstate Kneel'),
+(30571, 0, 6, 7, 108, 0, 100, 0, 3, 3057100, 0, 0, 0, 0, 91, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 3 of Path 3057100 Reached - Remove FlagStandstate Kneel'),
+(30571, 0, 7, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 69, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 3 of Path 3057100 Reached - Set Emote State 69'),
+(30571, 0, 8, 0, 52, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 3057100, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Text 0 Over - Run Script'),
+(30571, 0, 9, 10, 38, 0, 100, 0, 0, 2, 0, 0, 0, 0, 91, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 2 - Remove FlagStandstate Kneel'),
+(30571, 0, 10, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 232, 3057101, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 2 - Start Path 3057101'),
+(30571, 0, 12, 0, 38, 0, 100, 0, 0, 3, 0, 0, 0, 0, 232, 3057100, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 3 - Start Path 3057100');
+
+-- Set New Timed Action List and Remove the old ones (Michael Belfast - Conversation).
+DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (3057100, 3057101, 3057102));
+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
+(3057100, 9, 0, 0, 0, 0, 100, 0, 14000, 14000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970878, 30551, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'),
+(3057100, 9, 1, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970880, 30553, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'),
+(3057100, 9, 2, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970879, 30552, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'),
+(3057100, 9, 3, 0, 0, 0, 100, 0, 7000, 7000, 0, 0, 0, 0, 45, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Set Data 0 2'),
+(3057100, 9, 4, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'),
+(3057100, 9, 5, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970865, 31017, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'),
+(3057100, 9, 6, 0, 0, 0, 100, 0, 7000, 7000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970898, 30561, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'),
+(3057100, 9, 7, 0, 0, 0, 100, 0, 12000, 12000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970865, 31017, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'),
+(3057100, 9, 8, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 10, 1970865, 31017, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 2'),
+(3057100, 9, 9, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Set Data 0 3'),
+(3057100, 9, 10, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970880, 30553, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'),
+(3057100, 9, 11, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970878, 30551, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'),
+(3057100, 9, 12, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970879, 30552, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1');
diff --git a/data/sql/updates/db_world/2025_07_29_00.sql b/data/sql/updates/db_world/2025_07_29_00.sql
new file mode 100644
index 000000000..a159d4be9
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_29_00.sql
@@ -0,0 +1,36 @@
+-- DB update 2025_07_27_02 -> 2025_07_29_00
+--
+DELETE FROM `creature_template_addon` WHERE `entry` IN (31461, 31462);
+INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
+(31461, 0, 0, 0, 0, 0, 0, '31690 56740'),
+(31462, 0, 0, 0, 0, 0, 0, '31690 56741');
+
+UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~2&~33554432, `flags_extra` = `flags_extra` &~128, `ScriptName` = '' WHERE `entry` IN (30435, 30391, 31461, 31462);
+
+UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30435;
+
+DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 30435);
+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
+(30435, 0, 0, 0, 60, 0, 100, 1, 1000, 1000, 0, 0, 0, 0, 11, 57059, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Update - Cast \'Serverside - Grow\' (No Repeat)'),
+(30435, 0, 1, 0, 26, 0, 100, 1, 0, 3, 0, 0, 1, 0, 223, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - In Combat LoS - Do Action ID 1 (No Repeat)'),
+(30435, 0, 2, 3, 2, 0, 100, 0, 0, 5, 0, 0, 0, 0, 11, 31691, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Between 0-5% Health - Cast \'Serverside - Shrink\''),
+(30435, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 4000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Between 0-5% Health - Despawn In 4000 ms'),
+(30435, 0, 4, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Respawn - Set Reactstate Passive'),
+(30435, 0, 5, 0, 32, 0, 100, 1, 1, 150, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Damaged Between 1-150 - Do Action ID 1 (No Repeat)'),
+(30435, 0, 6, 0, 72, 0, 100, 1, 1, 0, 0, 0, 0, 0, 80, 3043500, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Action 1 Done - Run Script (No Repeat)'),
+(30435, 0, 7, 0, 101, 0, 100, 0, 1, 0, 4000, 4000, 4000, 0, 28, 56648, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On 1 or More Players in Range - Remove Aura \'Potent Fungus\'');
+
+DELETE FROM `smart_scripts` WHERE (`entryorguid` = 3043500) AND (`source_type` = 9) AND (`id` IN (0, 1, 2));
+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
+(3043500, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 57061, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Actionlist - Cast \'Poison Cloud\''),
+(3043500, 9, 1, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 11, 31691, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Actionlist - Cast \'Serverside - Shrink\''),
+(3043500, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 3000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Actionlist - Despawn In 3000 ms');
+
+UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30391;
+
+DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 30391);
+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
+(30391, 0, 0, 0, 60, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 11, 57059, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Update - Cast \'Serverside - Grow\''),
+(30391, 0, 1, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Respawn - Set Reactstate Passive'),
+(30391, 0, 2, 3, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 56648, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Just Died - Cast \'Potent Fungus\''),
+(30391, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 31691, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Just Died - Cast \'Serverside - Shrink\'');
diff --git a/data/sql/updates/db_world/2025_07_29_01.sql b/data/sql/updates/db_world/2025_07_29_01.sql
new file mode 100644
index 000000000..91ca903fb
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_29_01.sql
@@ -0,0 +1,4 @@
+-- DB update 2025_07_29_00 -> 2025_07_29_01
+-- Moonglade Raiment 2 set
+DELETE FROM `spell_script_names` WHERE `spell_id`=-774 AND `ScriptName`='spell_dru_rejuvenation_moonglade_2_set';
+INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (-774, 'spell_dru_rejuvenation_moonglade_2_set');
diff --git a/data/sql/updates/db_world/2025_07_29_02.sql b/data/sql/updates/db_world/2025_07_29_02.sql
new file mode 100644
index 000000000..17934d00c
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_29_02.sql
@@ -0,0 +1,36 @@
+-- DB update 2025_07_29_01 -> 2025_07_29_02
+-- improved shadow bolt into spell crit debuff
+DELETE FROM `spell_group` WHERE `id` = 1010 AND `spell_id` IN (17800);
+INSERT INTO `spell_group` VALUES (1010, 17800, 0);
+
+-- curse of weakness and spore cloud into low armor% debuff, remove curse of recklessness
+-- combine low armor% and attack power debuff because of curse of weakness
+-- add vindication and demoralizing screech
+DELETE FROM `spell_group` WHERE `id` = 1004 AND `spell_id` IN (702, 50274, 16231, 99, 1160, 67, 24423);
+INSERT INTO `spell_group` VALUES (1004, 702, 0), (1004, 50274, 0), (1004, 99, 0), (1004, 1160, 0), (1004, 67, 0), (1004, 24423, 0);
+
+-- rename
+UPDATE `spell_group_stack_rules` SET `description` = 'Group of minor Armor reducing, hit increase and AP reducing debuffs, effect exclusive' WHERE `group_id` = 1004;
+
+-- remove old ap debuff group
+DELETE FROM `spell_group` WHERE `id` = 1017;
+DELETE FROM `spell_group_stack_rules` WHERE `group_id` = 1017;
+
+-- stampede into bleed debuff
+DELETE FROM `spell_group` WHERE `id` = 1008 AND `spell_id` IN (57386);
+INSERT INTO `spell_group` VALUES (1008, 57386, 0);
+
+-- poison spit, lava breath into spell haste debuff
+DELETE FROM `spell_group` WHERE `id` = 1022 AND `spell_id` IN (35387, 58604);
+INSERT INTO `spell_group` VALUES (1022, 35387, 0), (1022, 58604, 0);
+
+-- master poisoner into crit taken debuff (untested)
+DELETE FROM `spell_group` WHERE `id` = 1013 AND `spell_id` IN (45176);
+INSERT INTO `spell_group` VALUES (1013, 45176, 0);
+
+-- physical damage taken group with savage combat and blood frenzy
+DELETE FROM `spell_group_stack_rules` WHERE `group_id` = 1024;
+INSERT INTO `spell_group_stack_rules` VALUES (1024, 17, 'Group of physical damage taken increasing debuffs');
+
+DELETE FROM `spell_group` WHERE `id` = 1024 AND `spell_id` IN (30069, 58684);
+INSERT INTO `spell_group` VALUES (1024, 30069, 0), (1024, 58684, 0);
diff --git a/data/sql/updates/db_world/2025_07_29_03.sql b/data/sql/updates/db_world/2025_07_29_03.sql
new file mode 100644
index 000000000..f35ca3802
--- /dev/null
+++ b/data/sql/updates/db_world/2025_07_29_03.sql
@@ -0,0 +1,9 @@
+-- DB update 2025_07_29_02 -> 2025_07_29_03
+--
+DELETE FROM `command` WHERE `name` = "go gameobject";
+INSERT INTO `command` (`name`, `security`, `help`) VALUES
+("go gameobject", 1, "Syntax: .go gameobject $gameobject.guid\r\nTeleports you to the gameobject using `guid` value of `gameobject` table.");
+
+UPDATE `command` SET `help` = "Syntax: .go creature $creature.guid \r\nTeleports you to the creature using `guid` value of `creature` table." WHERE `name` LIKE "go creature";
+UPDATE `command` SET `help` = "Syntax: .go creature name $creature_template.name \r\nTeleports you to a creature using the `name` value of `creature_template` table. \r\nIn the case of multiple creature of the same `name` existing in the world, you will be teleported to the lowest `guid` creature.\r\nWhen running the command for names with spaces don't break the string, example: .go creature name Ruby Scalebane" WHERE `name` LIKE "go creature name";
+UPDATE `command` SET `help` = "Syntax: .group revive $characterName \r\nRevives all group members of the given character or self if not provided." WHERE `name` LIKE "group revive";
diff --git a/data/sql/updates/db_world/2025_08_01_00.sql b/data/sql/updates/db_world/2025_08_01_00.sql
new file mode 100644
index 000000000..8762b4e7d
--- /dev/null
+++ b/data/sql/updates/db_world/2025_08_01_00.sql
@@ -0,0 +1,23 @@
+-- DB update 2025_07_29_03 -> 2025_08_01_00
+--
+ALTER TABLE `item_template` DROP COLUMN `StatsCount`;
+
+UPDATE `item_template` SET `fire_res` = `stat_value1`, `frost_res` = `stat_value2`, `shadow_res` = `stat_value3`, `arcane_res` = `stat_value4`, `nature_res` = `stat_value5`, `stat_type1` = 0, `stat_value1` = 0, `stat_type2` = 0, `stat_value2` = 0, `stat_type3` = 0, `stat_value3` = 0, `stat_type4` = 0, `stat_value4` = 0, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` = 5828;
+UPDATE `item_template` SET `fire_res` = `stat_value3`, `nature_res` = `stat_value4`, `stat_type3` = 0, `stat_value3` = 0, `stat_type4` = 0, `stat_value4` = 0, `VerifiedBuild` = 0 WHERE `entry` = 17802;
+UPDATE `item_template` SET `fire_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 18881;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 19065;
+UPDATE `item_template` SET `fire_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 19158;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `fire_res` = `stat_value4`, `frost_res` = `stat_value5`, `shadow_res` = `stat_value6`, `arcane_res` = `stat_value7`, `nature_res` = `stat_value8`, `stat_type3` = 0, `stat_value3` = 0, `stat_type4` = 0, `stat_value4` = 0, `stat_type5` = 0, `stat_value5` = 0, `stat_type6` = 0, `stat_value6` = 0, `stat_type7` = 0, `stat_value7` = 0, `stat_type8` = 0, `stat_value8` = 0, `VerifiedBuild` = 0 WHERE `entry` = 20142;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 20522;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `shadow_res` = `stat_value4`, `stat_type4` = 0, `stat_value4` = 0, `VerifiedBuild` = 0 WHERE `entry` = 20524;
+UPDATE `item_template` SET `nature_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 21614;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value2`, `stat_type2` = 0, `stat_value2` = 0, `VerifiedBuild` = 0 WHERE `entry` = 22230;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value5`, `fire_res` = `stat_value6`, `shadow_res` = `stat_value7`,`stat_type5` = 0, `stat_value5` = 0, `stat_type6` = 0, `stat_value6` = 0, `stat_type7` = 0, `stat_value7` = 0, `VerifiedBuild` = 0 WHERE `entry` = 23362;
+UPDATE `item_template` SET `nature_res` = `stat_value5`, `arcane_res` = `stat_value6`, `stat_type5` = 0, `stat_value5` = 0, `stat_type6` = 0, `stat_value6` = 0, `VerifiedBuild` = 0 WHERE `entry` = 23363;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value5`, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` IN (32113, 32114, 32115, 32116, 32117, 32118, 32119, 32121, 32122, 32123, 32128, 32129, 32130, 32131, 32132);
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value1`, `stat_type1` = 0, `stat_value1` = 0, `VerifiedBuild` = 0 WHERE `entry` = 34187;
+UPDATE `item_template` SET `fire_res` = `stat_value5`, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` = 40762;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value1`, `stat_type1` = 0, `stat_value1` = 0, `VerifiedBuild` = 0 WHERE `entry` = 45860;
+UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value2`, `stat_type2` = 0, `stat_value2` = 0, `VerifiedBuild` = 0 WHERE `entry` = 48945;
+UPDATE `item_template` SET `fire_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 49312;
+UPDATE `item_template` SET `fire_res` = `stat_value5`, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` = 49314;
diff --git a/data/sql/updates/db_world/2025_08_01_01.sql b/data/sql/updates/db_world/2025_08_01_01.sql
new file mode 100644
index 000000000..4bd47948c
--- /dev/null
+++ b/data/sql/updates/db_world/2025_08_01_01.sql
@@ -0,0 +1,12 @@
+-- DB update 2025_08_01_00 -> 2025_08_01_01
+-- fix quest 12616 'Chamber of Secrets'
+UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 190610;
+
+DELETE FROM `smart_scripts` WHERE (`source_type` = 1 AND `entryorguid` = 190610);
+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
+(190610, 1, 0, 0, 70, 0, 100, 0, 2, 0, 0, 0, 0, 0, 56, 38629, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Orders from the Lich King - On Gameobject State Changed - Add Item \'Orders from the Lich King\' 1 Time');
+
+DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 1) AND (`SourceEntry` = 190610) AND (`ConditionTypeOrReference` IN (2, 9)) AND (`ConditionValue1` IN (38629, 12616));
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(22, 1, 190610, 1, 0, 9, 0, 12616, 0, 0, 0, 0, 0, '', '\'Orders from the Lich King\' requires quest \'Chamber of Secrets\''),
+(22, 1, 190610, 1, 0, 2, 0, 38629, 1, 0, 1, 0, 0, '', 'Can\'t receive more than one \'Orders from the Lich King\'');
diff --git a/src/common/Common.h b/src/common/Common.h
index 98f343a45..c8cd5e789 100644
--- a/src/common/Common.h
+++ b/src/common/Common.h
@@ -61,6 +61,7 @@ enum AccountTypes
SEC_CONSOLE = 4 // must be always last in list, accounts must have less security level always also
};
+#define MAX_ACCOUNT_FLAG 32
enum AccountFlag
{
ACCOUNT_FLAG_GM = 0x1, // Account is GM
@@ -96,9 +97,22 @@ enum AccountFlag
// Below might be StarCraft II related
ACCOUNT_FLAG_S2_REQUIRE_IGR = 0x40000000, // NYI UNK
ACCOUNT_FLAG_S2_TRIAL = 0x80000000, // NYI UNK
- ACCOUNT_FLAG_S2_RESTRICTED = 0xFFFFFFFF // NYI UNK
+ // ACCOUNT_FLAG_S2_RESTRICTED = 0xFFFFFFFF, // NYI UNK
};
+constexpr uint32 ACCOUNT_FLAGS_ALL =
+ ACCOUNT_FLAG_GM | ACCOUNT_FLAG_NOKICK | ACCOUNT_FLAG_COLLECTOR |
+ ACCOUNT_FLAG_TRIAL | ACCOUNT_FLAG_CANCELLED | ACCOUNT_FLAG_IGR |
+ ACCOUNT_FLAG_WHOLESALER | ACCOUNT_FLAG_PRIVILEGED | ACCOUNT_FLAG_EU_FORBID_ELV |
+ ACCOUNT_FLAG_EU_FORBID_BILLING | ACCOUNT_FLAG_RESTRICTED | ACCOUNT_FLAG_REFERRAL |
+ ACCOUNT_FLAG_BLIZZARD | ACCOUNT_FLAG_RECURRING_BILLING | ACCOUNT_FLAG_NOELECTUP |
+ ACCOUNT_FLAG_KR_CERTIFICATE | ACCOUNT_FLAG_EXPANSION_COLLECTOR | ACCOUNT_FLAG_DISABLE_VOICE |
+ ACCOUNT_FLAG_DISABLE_VOICE_SPEAK | ACCOUNT_FLAG_REFERRAL_RESURRECT | ACCOUNT_FLAG_EU_FORBID_CC |
+ ACCOUNT_FLAG_OPENBETA_DELL | ACCOUNT_FLAG_PROPASS | ACCOUNT_FLAG_PROPASS_LOCK |
+ ACCOUNT_FLAG_PENDING_UPGRADE | ACCOUNT_FLAG_RETAIL_FROM_TRIAL | ACCOUNT_FLAG_EXPANSION2_COLLECTOR |
+ ACCOUNT_FLAG_OVERMIND_LINKED | ACCOUNT_FLAG_DEMOS | ACCOUNT_FLAG_DEATH_KNIGHT_OK |
+ ACCOUNT_FLAG_S2_REQUIRE_IGR | ACCOUNT_FLAG_S2_TRIAL;
+
enum LocaleConstant
{
LOCALE_enUS = 0,
diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist
index 46dd40c0c..db0936a04 100644
--- a/src/server/apps/worldserver/worldserver.conf.dist
+++ b/src/server/apps/worldserver/worldserver.conf.dist
@@ -1767,6 +1767,16 @@ CleanCharacterDB = 0
PersistentCharacterCleanFlags = 0
+#
+# ValidateSkillLearnedBySpells
+# Description: If enabled, players will lose spells that are invalid for their race/class.
+# Default: 1 - (Enabled, enforce valid spells)
+# 0 - (Disabled, allow invalid spells)
+# Disabling this and then having your character learn spells which require DBC edits can result in the character not being saved in the database
+# Disable AT YOUR OWN RISK
+
+ValidateSkillLearnedBySpells = 1
+
#
###################################################################################################
@@ -3503,6 +3513,14 @@ Wintergrasp.CrashRestartTimer = 10
###################################################################################################
# BATTLEGROUND
+#
+# Battleground.PrepTime
+# Description: Time (in seconds) for battleground preparation phase. Strand of the Ancients will be
+# the exception and will always use the default 120 seconds timer, due to its boat timing mechanic.
+# Default: 120
+
+Battleground.PrepTime = 120
+
#
# Battleground.CastDeserter
# Description: Cast Deserter spell at players who leave battlegrounds in progress.
@@ -3768,6 +3786,13 @@ Battleground.EyeOfTheStorm.CapturePoints = 1600
###################################################################################################
# ARENA
+#
+# Arena.PrepTime
+# Description: Time (in seconds) for arena preparation phase.
+# Default: 60
+
+Arena.PrepTime = 60
+
#
# Arena.MaxRatingDifference
# Description: Maximum rating difference between two teams in rated matches.
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 5bb8b3f41..80ebac719 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -617,6 +617,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"ON DUPLICATE KEY UPDATE state = VALUES(state)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DELETE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id NOT IN (SELECT instance.id FROM instance)", CONNECTION_ASYNC);
+
+ // world_state
+ PrepareStatement(CHAR_SEL_WORLD_STATE, "SELECT Id, Data FROM world_state", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_REP_WORLD_STATE, "REPLACE INTO world_state (Id, Data) VALUES(?, ?)", CONNECTION_ASYNC);
}
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 6f8ac783e..801826b48 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -529,6 +529,9 @@ enum CharacterDatabaseStatements : uint32
CHAR_DELETE_INSTANCE_SAVED_DATA,
CHAR_SANITIZE_INSTANCE_SAVED_DATA,
+ CHAR_SEL_WORLD_STATE,
+ CHAR_REP_WORLD_STATE,
+
MAX_CHARACTERDATABASE_STATEMENTS
};
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index b74767120..207fa369f 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -103,8 +103,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT salt, verifier FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT salt, verifier FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_FLAG, "SELECT Flags FROM account WHERE id = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_UPD_ADD_ACCOUNT_FLAG, "UPDATE account SET Flags = Flags | ? WHERE id = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_UPD_REMOVE_ACCOUNT_FLAG, "UPDATE account SET Flags = Flags &~ ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_SET_ACCOUNT_FLAG, "UPDATE account SET Flags = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.reg_mail, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby, a.failed_logins, a.locked, a.OS FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index 091f55d98..773bdc408 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -87,8 +87,7 @@ enum LoginDatabaseStatements : uint32
LOGIN_SEL_CHECK_PASSWORD,
LOGIN_SEL_CHECK_PASSWORD_BY_NAME,
LOGIN_SEL_ACCOUNT_FLAG,
- LOGIN_UPD_ADD_ACCOUNT_FLAG,
- LOGIN_UPD_REMOVE_ACCOUNT_FLAG,
+ LOGIN_UPD_SET_ACCOUNT_FLAG,
LOGIN_SEL_PINFO,
LOGIN_SEL_PINFO_BANS,
LOGIN_SEL_GM_ACCOUNTS,
diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp
index c4f0649c1..4129609db 100644
--- a/src/server/game/Accounts/AccountMgr.cpp
+++ b/src/server/game/Accounts/AccountMgr.cpp
@@ -299,39 +299,6 @@ namespace AccountMgr
return false;
}
- bool HasAccountFlag(uint32 accountId, uint32 flag)
- {
- LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_FLAG);
- stmt->SetData(0, accountId);
- if (PreparedQueryResult result = LoginDatabase.Query(stmt))
- {
- uint32 flags = (*result)[0].Get();
- return (flags & flag) != 0;
- }
-
- return false;
- }
-
- void UpdateAccountFlag(uint32 accountId, uint32 flag, bool remove /*= false*/)
- {
- LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(
- remove ? LOGIN_UPD_REMOVE_ACCOUNT_FLAG : LOGIN_UPD_ADD_ACCOUNT_FLAG
- );
- stmt->SetData(0, flag);
- stmt->SetData(1, accountId);
- LoginDatabase.Execute(stmt);
- }
-
- void ValidateAccountFlags(uint32 accountId, uint32 flags, uint32 security)
- {
- bool hasGMFlag = (flags & ACCOUNT_FLAG_GM) != 0;
-
- if (IsGMAccount(security) && !hasGMFlag)
- UpdateAccountFlag(accountId, ACCOUNT_FLAG_GM);
- else if (hasGMFlag && !IsGMAccount(security))
- UpdateAccountFlag(accountId, ACCOUNT_FLAG_GM, true);
- }
-
uint32 GetCharactersCount(uint32 accountId)
{
// check character count
diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h
index d2fa715b5..3f2eb983e 100644
--- a/src/server/game/Accounts/AccountMgr.h
+++ b/src/server/game/Accounts/AccountMgr.h
@@ -56,10 +56,6 @@ namespace AccountMgr
bool IsGMAccount(uint32 gmlevel);
bool IsAdminAccount(uint32 gmlevel);
bool IsConsoleAccount(uint32 gmlevel);
-
- bool HasAccountFlag(uint32 accountId, uint32 flag);
- void UpdateAccountFlag(uint32 accountId, uint32 flag, bool remove = false);
- void ValidateAccountFlags(uint32 accountId, uint32 flags, uint32 security);
};
#endif
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
index f922e77fc..736fd46e1 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
@@ -155,7 +155,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas
}
}
else
- sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans);
+ RemoveAItem(auction->item_guid, true, &trans);
}
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail)
@@ -256,7 +256,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDat
}
}
else
- sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans);
+ RemoveAItem(auction->item_guid, true, &trans);
}
//this function sends mail to old bidder
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 1d87e34e8..7ddd4e87e 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -195,6 +195,8 @@ Battleground::Battleground()
m_HonorMode = BG_NORMAL;
+ m_SetupCompleted = false;
+
StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
@@ -475,10 +477,8 @@ inline void Battleground::_ProcessJoin(uint32 diff)
itr->second->ResetAllPowers();
}
- if (!(m_Events & BG_STARTING_EVENT_1))
+ if (!m_SetupCompleted)
{
- m_Events |= BG_STARTING_EVENT_1;
-
if (!FindBgMap())
{
LOG_ERROR("bg.battleground", "Battleground::_ProcessJoin: map (map id: {}, instance id: {}) is not created!", m_MapId, m_InstanceID);
@@ -494,13 +494,52 @@ inline void Battleground::_ProcessJoin(uint32 diff)
}
StartingEventCloseDoors();
- SetStartDelayTime(StartDelayTimes[BG_STARTING_EVENT_FIRST]);
- // First start warning - 2 or 1 minute
+ // Get the configured prep time
+ uint32 configuredPrepTime;
+
+ // Special case for Strand of the Ancients - always use 120 seconds due to boat timing mechanics
+ if (GetBgTypeID() == BATTLEGROUND_SA)
+ configuredPrepTime = 120 * IN_MILLISECONDS;
+ else
+ configuredPrepTime = isArena() ?
+ sWorld->getIntConfig(CONFIG_ARENA_PREP_TIME) * IN_MILLISECONDS :
+ sWorld->getIntConfig(CONFIG_BATTLEGROUND_PREP_TIME) * IN_MILLISECONDS;
+
+ SetStartDelayTime(configuredPrepTime);
+
+ // Pre-mark events for announcements that should be skipped based on configured prep time
+ if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_FIRST])
+ {
+ // Skip first announcement (120s for BG, 60s for Arena)
+ m_Events |= BG_STARTING_EVENT_1;
+
+ if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_SECOND])
+ {
+ // Skip second announcement (60s for BG, 30s for Arena)
+ m_Events |= BG_STARTING_EVENT_2;
+
+ if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_THIRD])
+ {
+ // Skip third announcement (30s for BG, 15s for Arena)
+ m_Events |= BG_STARTING_EVENT_3;
+ }
+ }
+ }
+
+ // Mark setup as completed
+ m_SetupCompleted = true;
+ }
+
+ // First announcement at 120s or 60s (Depending on BG or Arena and configured time)
+ if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_FIRST] && !(m_Events & BG_STARTING_EVENT_1))
+ {
+ m_Events |= BG_STARTING_EVENT_1;
+
if (StartMessageIds[BG_STARTING_EVENT_FIRST])
SendBroadcastText(StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
- // After 1 minute or 30 seconds, warning is signaled
+ // Second announcement at 60s or 30s
else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2))
{
m_Events |= BG_STARTING_EVENT_2;
@@ -508,7 +547,7 @@ inline void Battleground::_ProcessJoin(uint32 diff)
if (StartMessageIds[BG_STARTING_EVENT_SECOND])
SendBroadcastText(StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
- // After 30 or 15 seconds, warning is signaled
+ // Third announcement at 30s or 15s
else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3))
{
m_Events |= BG_STARTING_EVENT_3;
@@ -543,11 +582,12 @@ inline void Battleground::_ProcessJoin(uint32 diff)
break;
}
}
- // Delay expired (after 2 or 1 minute)
+ // Delay expired (after configured prep time)
else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
{
m_Events |= BG_STARTING_EVENT_4;
+ // Start the battle
StartingEventOpenDoors();
if (StartMessageIds[BG_STARTING_EVENT_FOURTH])
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h
index b2ec4ff4a..ea982ca68 100644
--- a/src/server/game/Battlegrounds/Battleground.h
+++ b/src/server/game/Battlegrounds/Battleground.h
@@ -615,6 +615,9 @@ protected:
void _ProcessJoin(uint32 diff);
void _CheckSafePositions(uint32 diff);
+ // Setup completion marker
+ bool m_SetupCompleted;
+
// Scorekeeping
BattlegroundScoreMap PlayerScores; // Player scores
// must be implemented in BG subclass
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index e25878669..3e3a9d053 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -296,7 +296,6 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
ResetLootMode(); // restore default loot mode
TriggerJustRespawned = false;
- m_isTempWorldObject = false;
_focusSpell = nullptr;
m_respawnedTime = time_t(0);
@@ -802,7 +801,7 @@ void Creature::Update(uint32 diff)
// Periodically check if able to move, if not, extend leash timer
if (diff >= m_extendLeashTime)
{
- if (!CanFreeMove())
+ if (HasUnitState(UNIT_STATE_LOST_CONTROL))
UpdateLeashExtensionTime();
m_extendLeashTime = EXTEND_LEASH_CHECK_INTERVAL;
}
@@ -2685,14 +2684,13 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
if (skipDistCheck)
return true;
- float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS);
-
- if (GetCharmerOrOwner())
+ if (Unit* unit = GetCharmerOrOwner())
{
- dist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f);
- return IsWithinDist(victim, dist);
+ float visibilityDist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, DEFAULT_VISIBILITY_DISTANCE);
+ return victim->IsWithinDist(unit, visibilityDist);
}
+ float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS);
if (!dist)
return true;
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 49a7854cc..06444ef46 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -376,8 +376,6 @@ public:
float m_SightDistance, m_CombatDistance;
- bool m_isTempWorldObject; //true when possessed
-
// Handling caster facing during spellcast
void SetTarget(ObjectGuid guid = ObjectGuid::Empty) override;
void ClearTarget() { SetTarget(); };
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index f458ba8a6..c87395169 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1072,25 +1072,6 @@ void WorldObject::Update(uint32 diff)
sScriptMgr->OnWorldObjectUpdate(this, diff);
}
-void WorldObject::SetWorldObject(bool on)
-{
- if (!IsInWorld())
- return;
-
- GetMap()->AddObjectToSwitchList(this, on);
-}
-
-bool WorldObject::IsWorldObject() const
-{
- if (m_isWorldObject)
- return true;
-
- if (ToCreature() && ToCreature()->m_isTempWorldObject)
- return true;
-
- return false;
-}
-
void WorldObject::setActive(bool on)
{
if (m_isActive == on)
@@ -1944,8 +1925,8 @@ bool WorldObject::CanDetectInvisibilityOf(WorldObject const* obj) const
bool isPermInvisibleCreature = false;
if (Creature const* baseObj = ToCreature())
{
- auto auraEffects = baseObj->GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY);
- for (auto const effect : auraEffects)
+ Unit::AuraEffectList const& auraEffects = baseObj->GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY);
+ for (AuraEffect* const effect : auraEffects)
{
if (SpellInfo const* spell = effect->GetSpellInfo())
{
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index db634761a..c432d8575 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -635,9 +635,7 @@ public:
[[nodiscard]] bool IsFarVisible() const { return m_isFarVisible; }
[[nodiscard]] bool IsVisibilityOverridden() const { return m_visibilityDistanceOverride.has_value(); }
void SetVisibilityDistanceOverride(VisibilityDistanceType type);
- void SetWorldObject(bool apply);
- [[nodiscard]] bool IsPermanentWorldObject() const { return m_isWorldObject; }
- [[nodiscard]] bool IsWorldObject() const;
+ [[nodiscard]] bool IsWorldObject() const { return m_isWorldObject; }
[[nodiscard]] bool IsInWintergrasp() const
{
diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h
index 2913e587b..bfae02203 100644
--- a/src/server/game/Entities/Object/ObjectDefines.h
+++ b/src/server/game/Entities/Object/ObjectDefines.h
@@ -48,6 +48,8 @@
#define NOMINAL_MELEE_RANGE 5.0f
#define MELEE_RANGE (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players
#define DEFAULT_COLLISION_HEIGHT 2.03128f // Most common value in dbc
+#define LEEWAY_MIN_MOVE_SPEED 4.97f // NYI
+#define LEEWAY_BONUS_RANGE 2.66f
// used for creating values for respawn for example
inline uint32 PAIR64_HIPART(uint64 x);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index e38a20d32..c6a21c557 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -318,6 +318,8 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
m_baseRatingValue[i] = 0;
m_baseSpellPower = 0;
+ m_baseSpellDamage = 0;
+ m_baseSpellHealing = 0;
m_baseFeralAP = 0;
m_baseManaRegen = 0;
m_baseHealthRegen = 0;
@@ -3103,6 +3105,9 @@ bool Player::addSpell(uint32 spellId, uint8 addSpecMask, bool updateActive, bool
bool Player::CheckSkillLearnedBySpell(uint32 spellId)
{
+ if (!sWorld->getBoolConfig(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS))
+ return true;
+
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
uint32 errorSkill = 0;
for (SkillLineAbilityMap::const_iterator sla = skill_bounds.first; sla != skill_bounds.second; ++sla)
@@ -6844,7 +6849,10 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
break;
/// @deprecated item mods
case ITEM_MOD_SPELL_HEALING_DONE:
+ ApplySpellHealingBonus(int32(val), apply);
+ break;
case ITEM_MOD_SPELL_DAMAGE_DONE:
+ ApplySpellDamageBonus(int32(val), apply);
break;
}
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index bb699e15f..34ef37d2c 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1954,6 +1954,8 @@ public:
void UpdateAttackPowerAndDamage(bool ranged = false) override;
void UpdateShieldBlockValue();
void ApplySpellPowerBonus(int32 amount, bool apply);
+ void ApplySpellDamageBonus(int32 amount, bool apply);
+ void ApplySpellHealingBonus(int32 amount, bool apply);
void UpdateSpellDamageAndHealingBonus();
void ApplyRatingMod(CombatRating cr, int32 value, bool apply);
void UpdateRating(CombatRating cr);
@@ -1972,6 +1974,8 @@ public:
[[nodiscard]] float GetRatingMultiplier(CombatRating cr) const;
[[nodiscard]] float GetRatingBonusValue(CombatRating cr) const;
uint32 GetBaseSpellPowerBonus() { return m_baseSpellPower; }
+ uint32 GetBaseSpellDamageBonus() { return m_baseSpellDamage; }
+ uint32 GetBaseSpellHealingBonus() { return m_baseSpellHealing; }
[[nodiscard]] int32 GetSpellPenetrationItemMod() const { return m_spellPenetrationItemMod; }
[[nodiscard]] float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const;
@@ -2831,6 +2835,8 @@ protected:
float m_auraBaseMod[BASEMOD_END][MOD_END];
int32 m_baseRatingValue[MAX_COMBAT_RATING];
uint32 m_baseSpellPower;
+ uint32 m_baseSpellDamage;
+ uint32 m_baseSpellHealing;
uint32 m_baseFeralAP;
uint32 m_baseManaRegen;
uint32 m_baseHealthRegen;
diff --git a/src/server/game/Entities/Player/PlayerQuest.cpp b/src/server/game/Entities/Player/PlayerQuest.cpp
index 3dbb7231c..9f663ff2f 100644
--- a/src/server/game/Entities/Player/PlayerQuest.cpp
+++ b/src/server/game/Entities/Player/PlayerQuest.cpp
@@ -1272,7 +1272,7 @@ bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg) const
if (qInfo->IsDFQuest())
{
- if (!m_DFQuests.empty())
+ if (m_DFQuests.find(qInfo->GetQuestId()) != m_DFQuests.end())
return false;
return true;
diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp
index 43f11d14e..01595faa5 100644
--- a/src/server/game/Entities/Player/PlayerStorage.cpp
+++ b/src/server/game/Entities/Player/PlayerStorage.cpp
@@ -4616,8 +4616,15 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(enchant_amount), apply);
LOG_DEBUG("entities.player.items", "+ {} BLOCK_VALUE", enchant_amount);
break;
- case ITEM_MOD_SPELL_HEALING_DONE: // deprecated
- case ITEM_MOD_SPELL_DAMAGE_DONE: // deprecated
+ /// @deprecated item mods
+ case ITEM_MOD_SPELL_HEALING_DONE:
+ ApplySpellHealingBonus(enchant_amount, apply);
+ LOG_DEBUG("entities.player.items", "+ {} SPELL_HEALING", enchant_amount);
+ break;
+ case ITEM_MOD_SPELL_DAMAGE_DONE:
+ ApplySpellDamageBonus(enchant_amount, apply);
+ LOG_DEBUG("entities.player.items", "+ {} SPELL_DAMAGE", enchant_amount);
+ break;
default:
break;
}
diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp
index 48c30f7de..e35ac2701 100644
--- a/src/server/game/Entities/Player/PlayerUpdates.cpp
+++ b/src/server/game/Entities/Player/PlayerUpdates.cpp
@@ -181,8 +181,8 @@ void Player::Update(uint32 p_time)
m_swingErrorMsg = 1;
}
}
- // 120 degrees of radiant range
- else if (!HasInArc(2 * M_PI / 3, victim))
+ // 120 degrees of radiant range, if player is not in boundary radius
+ else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
{
setAttackTimer(BASE_ATTACK, 100);
if (m_swingErrorMsg != 2) // send single time (client auto repeat)
@@ -211,8 +211,8 @@ void Player::Update(uint32 p_time)
{
if (!IsWithinMeleeRange(victim))
setAttackTimer(OFF_ATTACK, 100);
- else if (!HasInArc(2 * M_PI / 3, victim))
- setAttackTimer(OFF_ATTACK, 100);
+ else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
+ setAttackTimer(BASE_ATTACK, 100);
else
{
// prevent base and off attack in same time, delay attack at
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index bf1b1464e..d9dce3c37 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -174,6 +174,23 @@ void Player::ApplySpellPowerBonus(int32 amount, bool apply)
ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, amount, apply);
}
+void Player::ApplySpellDamageBonus(int32 amount, bool apply)
+{
+ apply = _ModifyUInt32(apply, m_baseSpellDamage, amount);
+
+ // For speed just update for client
+ for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
+ ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, amount, apply);
+}
+
+void Player::ApplySpellHealingBonus(int32 amount, bool apply)
+{
+ apply = _ModifyUInt32(apply, m_baseSpellHealing, amount);
+
+ // For speed just update for client
+ ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply);
+}
+
void Player::UpdateSpellDamageAndHealingBonus()
{
// Magic damage modifiers implemented in Unit::SpellDamageBonusDone
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index a96b81d88..68eaeaf67 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -674,6 +674,9 @@ bool Unit::IsWithinMeleeRange(Unit const* obj, float dist) const
float maxdist = dist + GetMeleeRange(obj);
+ if ((IsPlayer() || obj->IsPlayer()) && HasLeewayMovement() && obj->HasLeewayMovement())
+ maxdist += LEEWAY_BONUS_RANGE;
+
return distsq < maxdist * maxdist;
}
@@ -698,6 +701,16 @@ bool Unit::IsWithinRange(Unit const* obj, float dist) const
return distsq <= dist * dist;
}
+bool Unit::IsWithinBoundaryRadius(const Unit* obj) const
+{
+ if (!obj || !IsInMap(obj) || !InSamePhase(obj))
+ return false;
+
+ float objBoundaryRadius = std::max(obj->GetBoundaryRadius(), MIN_MELEE_REACH);
+
+ return IsInDist(obj, objBoundaryRadius);
+}
+
bool Unit::GetRandomContactPoint(Unit const* obj, float& x, float& y, float& z, bool force) const
{
float combat_reach = GetCombatReach();
@@ -2256,7 +2269,7 @@ void Unit::CalcAbsorbResist(DamageInfo& dmgInfo, bool Splited)
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSchoolAbsorbCopy(victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB));
- vSchoolAbsorbCopy.sort(Acore::AbsorbAuraOrderPred());
+ std::sort(vSchoolAbsorbCopy.begin(), vSchoolAbsorbCopy.end(), Acore::AbsorbAuraOrderPred());
// absorb without mana cost
for (AuraEffectList::iterator itr = vSchoolAbsorbCopy.begin(); (itr != vSchoolAbsorbCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr)
@@ -2439,7 +2452,7 @@ void Unit::CalcAbsorbResist(DamageInfo& dmgInfo, bool Splited)
// We're going to call functions which can modify content of the list during iteration over it's elements
// Let's copy the list so we can prevent iterator invalidation
AuraEffectList vSplitDamagePctCopy(victim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT));
- for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(), next; (itr != vSplitDamagePctCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr)
+ for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(); (itr != vSplitDamagePctCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr)
{
// Check if aura was removed during iteration - we don't need to work on such auras
AuraApplication const* aurApp = (*itr)->GetBase()->GetApplicationOfTarget(victim->GetGUID());
@@ -2565,7 +2578,7 @@ void Unit::CalcHealAbsorb(HealInfo& healInfo)
{
uint32 removedAuras = healInfo.GetTarget()->m_removedAurasCount;
auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
- if (removedAuras + 1 < healInfo.GetTarget()->m_removedAurasCount)
+ if (healInfo.GetTarget()->m_removedAurasCount > removedAuras)
i = vHealAbsorb.begin();
}
}
@@ -4175,6 +4188,15 @@ void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id, bool withI
InterruptSpell(CURRENT_CHANNELED_SPELL, true, true, bySelf);
}
+Spell* Unit::GetFirstCurrentCastingSpell() const
+{
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ if (m_currentSpells[i] && m_currentSpells[i]->GetCastTimeRemaining() > 0)
+ return m_currentSpells[i];
+
+ return nullptr;
+}
+
Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
{
for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
@@ -4732,7 +4754,7 @@ void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply)
if (apply)
m_modAuras[aurEff->GetAuraType()].push_back(aurEff);
else
- m_modAuras[aurEff->GetAuraType()].remove(aurEff);
+ m_modAuras[aurEff->GetAuraType()].erase(std::remove(m_modAuras[aurEff->GetAuraType()].begin(), m_modAuras[aurEff->GetAuraType()].end(), aurEff), m_modAuras[aurEff->GetAuraType()].end());
}
// All aura base removes should go threw this function!
@@ -5158,7 +5180,7 @@ void Unit::RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID, Aura* exc
{
uint32 removedAuras = m_removedAurasCount;
RemoveAura(aurApp);
- if (m_removedAurasCount > removedAuras + 1)
+ if (m_removedAurasCount > removedAuras)
iter = m_modAuras[auraType].begin();
}
}
@@ -10419,8 +10441,29 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
if (meleeAttack)
AddUnitState(UNIT_STATE_MELEE_ATTACKING);
+ Unit* owner = GetCharmerOrOwner();
+ Creature* ownerCreature = owner ? owner->ToCreature() : nullptr;
+ Creature* controlledCreatureWithSameVictim = nullptr;
+ if (creature && !m_Controlled.empty())
+ {
+ for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
+ {
+ if ((*itr)->ToCreature() && (*itr)->GetVictim() == victim)
+ {
+ controlledCreatureWithSameVictim = (*itr)->ToCreature();
+ break;
+ }
+ }
+ }
+
+ // Share leash timer with controlled unit
+ if (controlledCreatureWithSameVictim)
+ creature->SetLastLeashExtensionTimePtr(controlledCreatureWithSameVictim->GetLastLeashExtensionTimePtr());
+ // Share leash timer with owner
+ else if (creature && ownerCreature && ownerCreature->GetVictim() == victim)
+ creature->SetLastLeashExtensionTimePtr(ownerCreature->GetLastLeashExtensionTimePtr());
// Update leash timer when attacking creatures
- if (victim->IsCreature())
+ else if (victim->IsCreature())
victim->ToCreature()->UpdateLeashExtensionTime();
// set position before any AI calls/assistance
@@ -11229,10 +11272,8 @@ Unit* Unit::GetNextRandomRaidMemberOrPet(float radius)
void Unit::AddPlayerToVision(Player* player)
{
if (m_sharedVision.empty())
- {
setActive(true);
- SetWorldObject(true);
- }
+
m_sharedVision.push_back(player);
player->m_isInSharedVisionOf.insert(this);
}
@@ -11242,11 +11283,10 @@ void Unit::RemovePlayerFromVision(Player* player)
{
m_sharedVision.remove(player);
player->m_isInSharedVisionOf.erase(this);
+
+ /// @todo: This isn't right, if a previously active object was set to active with e.g. Mind Vision this will make them no longer active
if (m_sharedVision.empty())
- {
setActive(false);
- SetWorldObject(false);
- }
}
void Unit::RemoveBindSightAuras()
@@ -12011,6 +12051,7 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask)
{
// Base value
DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
+ DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellDamageBonus();
// Damage bonus from stats
AuraEffectList const& mDamageDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT);
@@ -12773,6 +12814,7 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask)
{
// Base value
AdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();
+ AdvertisedBenefit += ToPlayer()->GetBaseSpellHealingBonus();
// Healing bonus from stats
AuraEffectList const& mHealingDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT);
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 77c299568..933e3f970 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -633,7 +633,7 @@ public:
typedef std::multimap AuraStateAurasMap;
typedef std::pair AuraStateAurasMapBounds;
- typedef std::list AuraEffectList;
+ typedef std::vector AuraEffectList;
typedef std::list AuraList;
typedef std::list AuraApplicationList;
typedef std::list Diminishing;
@@ -818,9 +818,11 @@ public:
bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const;
// Combat range
+ [[nodiscard]] float GetBoundaryRadius() const { return m_floatValues[UNIT_FIELD_BOUNDINGRADIUS]; }
[[nodiscard]] float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
[[nodiscard]] float GetMeleeReach() const { float reach = m_floatValues[UNIT_FIELD_COMBATREACH]; return reach > MIN_MELEE_REACH ? reach : MIN_MELEE_REACH; }
[[nodiscard]] bool IsWithinRange(Unit const* obj, float dist) const;
+ bool IsWithinBoundaryRadius(const Unit* obj) const;
bool IsWithinCombatRange(Unit const* obj, float dist2compare) const;
bool IsWithinMeleeRange(Unit const* obj, float dist = 0.f) const;
float GetMeleeRange(Unit const* target) const;
@@ -1492,6 +1494,7 @@ public:
[[nodiscard]] Player* GetSpellModOwner() const;
[[nodiscard]] Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; }
[[nodiscard]] Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; }
+ [[nodiscard]] Spell* GetFirstCurrentCastingSpell() const;
[[nodiscard]] Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
[[nodiscard]] int32 GetCurrentSpellCastTime(uint32 spell_id) const;
@@ -1636,6 +1639,12 @@ public:
UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) && !GetOwnerGUID();
}
+ [[nodiscard]] bool HasLeewayMovement() const
+ {
+ return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | MOVEMENTFLAG_FALLING)
+ && !IsWalking();
+ }
+
void KnockbackFrom(float x, float y, float speedXY, float speedZ);
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp
index 29b813deb..4e98280fc 100644
--- a/src/server/game/Events/GameEventMgr.cpp
+++ b/src/server/game/Events/GameEventMgr.cpp
@@ -1934,7 +1934,7 @@ void GameEventMgr::SetHolidayEventTime(GameEventData& event)
uint32 GameEventMgr::GetHolidayEventId(uint32 holidayId) const
{
- auto const& events = sGameEventMgr->GetEventMap();
+ auto const& events = GetEventMap();
for (auto const& eventEntry : events)
{
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 911749df0..d6cecdc5d 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -871,7 +871,7 @@ void ObjectMgr::LoadCreatureTemplateAddons()
uint32 entry = fields[0].Get();
- if (!sObjectMgr->GetCreatureTemplate(entry))
+ if (!GetCreatureTemplate(entry))
{
LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_addon`", entry);
continue;
@@ -1501,7 +1501,7 @@ void ObjectMgr::LoadEquipmentTemplates()
uint32 entry = fields[0].Get();
- if (!sObjectMgr->GetCreatureTemplate(entry))
+ if (!GetCreatureTemplate(entry))
{
LOG_ERROR("sql.sql", "Creature template (CreatureID: {}) does not exist but has a record in `creature_equip_template`", entry);
continue;
@@ -2455,7 +2455,7 @@ void ObjectMgr::LoadCreatureSparring()
ObjectGuid::LowType spawnId = fields[0].Get();
float sparringHealthPct = fields[1].Get();
- if (!sObjectMgr->GetCreatureData(spawnId))
+ if (!GetCreatureData(spawnId))
{
LOG_ERROR("sql.sql", "Entry {} has a record in `creature_sparring` but doesn't exist in `creatures` table");
continue;
@@ -2535,7 +2535,7 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl
// We use spawn coords to spawn
if (!map->Instanceable() && map->IsGridLoaded(x, y))
{
- GameObject* go = sObjectMgr->IsGameObjectStaticTransport(data.id) ? new StaticTransport() : new GameObject();
+ GameObject* go = IsGameObjectStaticTransport(data.id) ? new StaticTransport() : new GameObject();
if (!go->LoadGameObjectFromDB(spawnId, map))
{
LOG_ERROR("sql.sql", "AddGOData: cannot add gameobject entry {} to map", entry);
@@ -2862,35 +2862,35 @@ void ObjectMgr::LoadItemTemplates()
// 0 1 2 3 4 5 6 7 8 9 10 11 12
QueryResult result = WorldDatabase.Query("SELECT entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, BuyPrice, SellPrice, InventoryType, "
- // 13 14 15 16 17 18 19 20
+ // 13 14 15 16 17 18 19 20
"AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, "
- // 21 22 23 24 25 26 27 28
- "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, stat_type1, "
- // 29 30 31 32 33 34 35 36 37 38
+ // 21 22 23 24 25 26 27
+ "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, stat_type1, "
+ // 28 29 30 31 32 33 34 35 36 37
"stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, stat_type5, stat_value5, stat_type6, "
- // 39 40 41 42 43 44 45 46 47
+ // 38 39 40 41 42 43 44 45 46
"stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, stat_type9, stat_value9, stat_type10, stat_value10, "
- // 48 49 50 51 52 53 54 55 56 57 58
+ // 47 48 49 50 51 52 53 54 55 56 57
"ScalingStatDistribution, ScalingStatValue, dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, "
- // 59 60 61 62 63 64 65 66 67 68
+ // 58 59 60 61 62 63 64 65 66 67
"nature_res, frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, spellcharges_1, "
- // 69 70 71 72 73 74 75
+ // 68 69 70 71 72 73 74
"spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, spelltrigger_2, spellcharges_2, "
- // 76 77 78 79 80 81 82
+ // 75 76 77 78 79 80 81
"spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, spellid_3, spelltrigger_3, spellcharges_3, "
- // 83 84 85 86 87 88 89
+ // 82 83 84 85 86 87 88
"spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, "
- // 90 91 92 93 94 95 96
+ // 89 90 91 92 93 94 95
"spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, "
- // 97 98 99 100 101 102 103 104 105
+ // 96 97 98 99 100 101 102 103 104
"spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, PageMaterial, "
- // 106 107 108 109 110 111 112 113 114 115 116 117
+ // 105 106 107 108 109 110 111 112 113 114 115 116
"startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, MaxDurability, area, Map, BagFamily, "
- // 118 119 120 121 122 123 124 125
+ // 117 118 119 120 121 122 123 124
"TotemCategory, socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3, socketBonus, "
- // 126 127 128 129 130 131 132 133
+ // 125 126 127 128 129 130 131 132
"GemProperties, RequiredDisenchantSkill, ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, "
- // 134 135 136
+ // 133 134 135 136
"FoodType, minMoneyLoot, maxMoneyLoot, flagsCustom FROM item_template");
if (!result)
@@ -2940,84 +2940,91 @@ void ObjectMgr::LoadItemTemplates()
itemTemplate.MaxCount = fields[24].Get();
itemTemplate.Stackable = fields[25].Get();
itemTemplate.ContainerSlots = uint32(fields[26].Get());
- itemTemplate.StatsCount = uint32(fields[27].Get());
- for (uint8 i = 0; i < itemTemplate.StatsCount; ++i)
+ uint8 statsCount = 0;
+ while (statsCount < MAX_ITEM_PROTO_STATS)
{
- itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i * 2].Get());
- itemTemplate.ItemStat[i].ItemStatValue = fields[29 + i * 2].Get();
- }
+ uint32 statType = uint32(fields[27 + statsCount * 2].Get());
+ int32 statValue = fields[28 + statsCount * 2].Get();
+ if (statType == 0)
+ break;
- itemTemplate.ScalingStatDistribution = uint32(fields[48].Get());
- itemTemplate.ScalingStatValue = fields[49].Get();
+ itemTemplate.ItemStat[statsCount].ItemStatType = statType;
+ itemTemplate.ItemStat[statsCount].ItemStatValue = statValue;
+ statsCount++;
+ }
+ itemTemplate.StatsCount = statsCount;
+
+ itemTemplate.ScalingStatDistribution = uint32(fields[47].Get());
+ itemTemplate.ScalingStatValue = fields[48].Get();
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
{
- itemTemplate.Damage[i].DamageMin = fields[50 + i * 3].Get();
- itemTemplate.Damage[i].DamageMax = fields[51 + i * 3].Get();
- itemTemplate.Damage[i].DamageType = uint32(fields[52 + i * 3].Get());
+ itemTemplate.Damage[i].DamageMin = fields[49 + i * 3].Get();
+ itemTemplate.Damage[i].DamageMax = fields[50 + i * 3].Get();
+ itemTemplate.Damage[i].DamageType = uint32(fields[51 + i * 3].Get());
}
- itemTemplate.Armor = fields[56].Get();
- itemTemplate.HolyRes = fields[57].Get();
- itemTemplate.FireRes = fields[58].Get();
- itemTemplate.NatureRes = fields[59].Get();
- itemTemplate.FrostRes = fields[60].Get();
- itemTemplate.ShadowRes = fields[61].Get();
- itemTemplate.ArcaneRes = fields[62].Get();
- itemTemplate.Delay = uint32(fields[63].Get());
- itemTemplate.AmmoType = uint32(fields[64].Get());
- itemTemplate.RangedModRange = fields[65].Get();
+ itemTemplate.Armor = fields[55].Get();
+ itemTemplate.HolyRes = fields[56].Get();
+ itemTemplate.FireRes = fields[57].Get();
+ itemTemplate.NatureRes = fields[58].Get();
+ itemTemplate.FrostRes = fields[59].Get();
+ itemTemplate.ShadowRes = fields[60].Get();
+ itemTemplate.ArcaneRes = fields[61].Get();
+ itemTemplate.Delay = uint32(fields[62].Get());
+ itemTemplate.AmmoType = uint32(fields[63].Get());
+ itemTemplate.RangedModRange = fields[64].Get();
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
- itemTemplate.Spells[i].SpellId = fields[66 + i * 7 ].Get();
- itemTemplate.Spells[i].SpellTrigger = uint32(fields[67 + i * 7].Get());
- itemTemplate.Spells[i].SpellCharges = int32(fields[68 + i * 7].Get());
- itemTemplate.Spells[i].SpellPPMRate = fields[69 + i * 7].Get();
- itemTemplate.Spells[i].SpellCooldown = fields[70 + i * 7].Get();
- itemTemplate.Spells[i].SpellCategory = uint32(fields[71 + i * 7].Get());
- itemTemplate.Spells[i].SpellCategoryCooldown = fields[72 + i * 7].Get();
+ itemTemplate.Spells[i].SpellId = fields[65 + i * 7 ].Get();
+ itemTemplate.Spells[i].SpellTrigger = uint32(fields[66 + i * 7].Get());
+ itemTemplate.Spells[i].SpellCharges = int32(fields[67 + i * 7].Get());
+ itemTemplate.Spells[i].SpellPPMRate = fields[68 + i * 7].Get();
+ itemTemplate.Spells[i].SpellCooldown = fields[69 + i * 7].Get();
+ itemTemplate.Spells[i].SpellCategory = uint32(fields[70 + i * 7].Get());
+ itemTemplate.Spells[i].SpellCategoryCooldown = fields[71 + i * 7].Get();
}
- itemTemplate.Bonding = uint32(fields[101].Get());
- itemTemplate.Description = fields[102].Get();
- itemTemplate.PageText = fields[103].Get();
- itemTemplate.LanguageID = uint32(fields[104].Get());
- itemTemplate.PageMaterial = uint32(fields[105].Get());
- itemTemplate.StartQuest = fields[106].Get();
- itemTemplate.LockID = fields[107].Get();
- itemTemplate.Material = int32(fields[108].Get());
- itemTemplate.Sheath = uint32(fields[109].Get());
- itemTemplate.RandomProperty = fields[110].Get();
- itemTemplate.RandomSuffix = fields[111].Get();
- itemTemplate.Block = fields[112].Get();
- itemTemplate.ItemSet = fields[113].Get();
- itemTemplate.MaxDurability = uint32(fields[114].Get());
- itemTemplate.Area = fields[115].Get();
- itemTemplate.Map = uint32(fields[116].Get());
- itemTemplate.BagFamily = fields[117].Get();
- itemTemplate.TotemCategory = fields[118].Get();
+ itemTemplate.Bonding = uint32(fields[100].Get());
+ itemTemplate.Description = fields[101].Get();
+ itemTemplate.PageText = fields[102].Get();
+ itemTemplate.LanguageID = uint32(fields[103].Get());
+ itemTemplate.PageMaterial = uint32(fields[104].Get());
+ itemTemplate.StartQuest = fields[105].Get();
+ itemTemplate.LockID = fields[106].Get();
+ itemTemplate.Material = int32(fields[107].Get());
+ itemTemplate.Sheath = uint32(fields[108].Get());
+ itemTemplate.RandomProperty = fields[109].Get();
+ itemTemplate.RandomSuffix = fields[110].Get();
+ itemTemplate.Block = fields[111].Get();
+ itemTemplate.ItemSet = fields[112].Get();
+ itemTemplate.MaxDurability = uint32(fields[113].Get());
+ itemTemplate.Area = fields[114].Get();
+ itemTemplate.Map = uint32(fields[115].Get());
+ itemTemplate.BagFamily = fields[116].Get();
+ itemTemplate.TotemCategory = fields[117].Get();
for (uint8 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
{
- itemTemplate.Socket[i].Color = uint32(fields[119 + i * 2].Get());
- itemTemplate.Socket[i].Content = fields[120 + i * 2].Get();
+ itemTemplate.Socket[i].Color = uint32(fields[118 + i * 2].Get());
+ itemTemplate.Socket[i].Content = fields[119 + i * 2].Get();
}
- itemTemplate.socketBonus = fields[125].Get();
- itemTemplate.GemProperties = fields[126].Get();
- itemTemplate.RequiredDisenchantSkill = uint32(fields[127].Get());
- itemTemplate.ArmorDamageModifier = fields[128].Get();
- itemTemplate.Duration = fields[129].Get();
- itemTemplate.ItemLimitCategory = uint32(fields[130].Get());
- itemTemplate.HolidayId = fields[131].Get();
- itemTemplate.ScriptId = sObjectMgr->GetScriptId(fields[132].Get());
- itemTemplate.DisenchantID = fields[133].Get();
- itemTemplate.FoodType = uint32(fields[134].Get());
- itemTemplate.MinMoneyLoot = fields[135].Get();
- itemTemplate.MaxMoneyLoot = fields[136].Get();
- itemTemplate.FlagsCu = ItemFlagsCustom(fields[137].Get());
+ itemTemplate.socketBonus = fields[124].Get();
+ itemTemplate.GemProperties = fields[125].Get();
+ itemTemplate.RequiredDisenchantSkill = uint32(fields[126].Get());
+ itemTemplate.ArmorDamageModifier = fields[127].Get();
+ itemTemplate.Duration = fields[128].Get();
+ itemTemplate.ItemLimitCategory = uint32(fields[129].Get());
+ itemTemplate.HolidayId = fields[130].Get();
+ itemTemplate.ScriptId = GetScriptId(fields[131].Get());
+ itemTemplate.DisenchantID = fields[132].Get();
+ itemTemplate.FoodType = uint32(fields[133].Get());
+ itemTemplate.MinMoneyLoot = fields[134].Get();
+ itemTemplate.MaxMoneyLoot = fields[135].Get();
+ itemTemplate.FlagsCu = ItemFlagsCustom(fields[136].Get());
// Checks
ItemEntry const* dbcitem = sItemStore.LookupEntry(entry);
@@ -3171,12 +3178,6 @@ void ObjectMgr::LoadItemTemplates()
itemTemplate.ContainerSlots = MAX_BAG_SIZE;
}
- if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS)
- {
- LOG_ERROR("sql.sql", "Item (Entry: {}) has too large value in statscount ({}), replace by hardcoded limit ({}).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS);
- itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS;
- }
-
for (uint8 j = 0; j < itemTemplate.StatsCount; ++j)
{
// for ItemStatValue != 0
@@ -3189,7 +3190,8 @@ void ObjectMgr::LoadItemTemplates()
switch (itemTemplate.ItemStat[j].ItemStatType)
{
case ITEM_MOD_SPELL_HEALING_DONE:
- LOG_ERROR("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j + 1, itemTemplate.ItemStat[j].ItemStatType);
+ case ITEM_MOD_SPELL_DAMAGE_DONE:
+ LOG_WARN("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j + 1, itemTemplate.ItemStat[j].ItemStatType);
break;
default:
break;
@@ -3570,7 +3572,7 @@ void ObjectMgr::LoadItemSetNames()
{
uint32 entry = *itr;
// add data from item_template if available
- pProto = sObjectMgr->GetItemTemplate(entry);
+ pProto = GetItemTemplate(entry);
if (pProto)
{
LOG_ERROR("sql.sql", "Item set part (Entry: {}) does not have entry in `item_set_names`, adding data from `item_template`.", entry);
@@ -3617,13 +3619,13 @@ void ObjectMgr::LoadVehicleTemplateAccessories()
uint8 uiSummonType = fields[4].Get();
uint32 uiSummonTimer = fields[5].Get();
- if (!sObjectMgr->GetCreatureTemplate(uiEntry))
+ if (!GetCreatureTemplate(uiEntry))
{
LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", uiEntry);
continue;
}
- if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
+ if (!GetCreatureTemplate(uiAccessory))
{
LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", uiAccessory);
continue;
@@ -3673,7 +3675,7 @@ void ObjectMgr::LoadVehicleAccessories()
uint8 uiSummonType = fields[4].Get();
uint32 uiSummonTimer = fields[5].Get();
- if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
+ if (!GetCreatureTemplate(uiAccessory))
{
LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
continue;
@@ -3765,7 +3767,7 @@ void ObjectMgr::LoadPetLevelInfo()
Field* fields = result->Fetch();
uint32 creature_id = fields[0].Get();
- if (!sObjectMgr->GetCreatureTemplate(creature_id))
+ if (!GetCreatureTemplate(creature_id))
{
LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id);
continue;
@@ -4998,7 +5000,7 @@ void ObjectMgr::LoadQuests()
if (qinfo->StartItem)
{
- if (!sObjectMgr->GetItemTemplate(qinfo->StartItem))
+ if (!GetItemTemplate(qinfo->StartItem))
{
LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but item with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), qinfo->StartItem, qinfo->StartItem);
@@ -5049,7 +5051,7 @@ void ObjectMgr::LoadQuests()
qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER);
- if (!sObjectMgr->GetItemTemplate(id))
+ if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = {} but item with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5069,7 +5071,7 @@ void ObjectMgr::LoadQuests()
uint32 id = qinfo->ItemDrop[j];
if (id)
{
- if (!sObjectMgr->GetItemTemplate(id))
+ if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5090,14 +5092,14 @@ void ObjectMgr::LoadQuests()
for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
{
int32 id = qinfo->RequiredNpcOrGo[j];
- if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id))
+ if (id < 0 && !GetGameObjectTemplate(-id))
{
LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but gameobject {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, uint32(-id));
qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
}
- if (id > 0 && !sObjectMgr->GetCreatureTemplate(id))
+ if (id > 0 && !GetCreatureTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but creature with entry {} does not exist, quest can't be done.",
qinfo->GetQuestId(), j + 1, id, uint32(id));
@@ -5130,7 +5132,7 @@ void ObjectMgr::LoadQuests()
uint32 id = qinfo->RewardChoiceItemId[j];
if (id)
{
- if (!sObjectMgr->GetItemTemplate(id))
+ if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5176,7 +5178,7 @@ void ObjectMgr::LoadQuests()
uint32 id = qinfo->RewardItemId[j];
if (id)
{
- if (!sObjectMgr->GetItemTemplate(id))
+ if (!GetItemTemplate(id))
{
LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
qinfo->GetQuestId(), j + 1, id, id);
@@ -5760,7 +5762,7 @@ void ObjectMgr::LoadEventScripts()
std::set evt_scripts;
// Load all possible script entries from gameobjects
- GameObjectTemplateContainer const* gotc = sObjectMgr->GetGameObjectTemplates();
+ GameObjectTemplateContainer const* gotc = GetGameObjectTemplates();
for (GameObjectTemplateContainer::const_iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
if (uint32 eventId = itr->second.GetEventScriptId())
evt_scripts.insert(eventId);
@@ -6069,7 +6071,7 @@ void ObjectMgr::LoadInstanceTemplate()
instanceTemplate.AllowMount = fields[3].Get();
instanceTemplate.Parent = uint32(fields[1].Get());
- instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].Get());
+ instanceTemplate.ScriptId = GetScriptId(fields[2].Get());
_instanceTemplateStore[mapID] = instanceTemplate;
@@ -6515,14 +6517,14 @@ void ObjectMgr::LoadQuestGreetings()
switch (type)
{
case 0: // Creature
- if (!sObjectMgr->GetCreatureTemplate(id))
+ if (!GetCreatureTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template entry {} does not exist.", id);
continue;
}
break;
case 1: // GameObject
- if (!sObjectMgr->GetGameObjectTemplate(id))
+ if (!GetGameObjectTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template entry {} does not exist.", id);
continue;
@@ -6568,14 +6570,14 @@ void ObjectMgr::LoadQuestGreetingsLocales()
switch (type)
{
case 0: // Creature
- if (!sObjectMgr->GetCreatureTemplate(id))
+ if (!GetCreatureTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: creature template entry {} does not exist.", id);
continue;
}
break;
case 1: // GameObject
- if (!sObjectMgr->GetGameObjectTemplate(id))
+ if (!GetGameObjectTemplate(id))
{
LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: gameobject template entry {} does not exist.", id);
continue;
@@ -7577,7 +7579,7 @@ void ObjectMgr::LoadGameObjectTemplateAddons()
uint32 entry = fields[0].Get();
- GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(entry);
+ GameObjectTemplate const* got = GetGameObjectTemplate(entry);
if (!got)
{
LOG_ERROR("sql.sql",
@@ -8222,7 +8224,7 @@ void ObjectMgr::LoadNPCSpellClickSpells()
// all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
// NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
- CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
+ CreatureTemplateContainer const* ctc = GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
{
if ((itr->second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && _spellClickInfoStore.find(itr->second.Entry) == _spellClickInfoStore.end())
@@ -8747,7 +8749,7 @@ void ObjectMgr::LoadGameObjectForQuests()
{
uint32 oldMSTime = getMSTime();
- if (sObjectMgr->GetGameObjectTemplates()->empty())
+ if (GetGameObjectTemplates()->empty())
{
LOG_WARN("server.loading", ">> Loaded 0 GameObjects for quests");
LOG_INFO("server.loading", " ");
@@ -8757,7 +8759,7 @@ void ObjectMgr::LoadGameObjectForQuests()
uint32 count = 0;
// collect GO entries for GO that must activated
- GameObjectTemplateContainer* gotc = const_cast(sObjectMgr->GetGameObjectTemplates());
+ GameObjectTemplateContainer* gotc = const_cast(GetGameObjectTemplates());
for (GameObjectTemplateContainer::iterator itr = gotc->begin(); itr != gotc->end(); ++itr)
{
itr->second.IsForQuests = false;
@@ -9666,7 +9668,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru
bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set* /*skip_vendors*/, uint32 /*ORnpcflag*/) const
{
/*
- CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
+ CreatureTemplate const* cInfo = GetCreatureTemplate(vendor_entry);
if (!cInfo)
{
if (player)
@@ -10040,7 +10042,7 @@ void ObjectMgr::LoadCreatureClassLevelStats()
++count;
} while (result->NextRow());
- CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
+ CreatureTemplateContainer const* ctc = GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
{
for (uint16 lvl = itr->second.minlevel; lvl <= itr->second.maxlevel; ++lvl)
@@ -10148,9 +10150,9 @@ void ObjectMgr::LoadFactionChangeQuests()
uint32 alliance = fields[0].Get();
uint32 horde = fields[1].Get();
- if (!sObjectMgr->GetQuestTemplate(alliance))
+ if (!GetQuestTemplate(alliance))
LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
- else if (!sObjectMgr->GetQuestTemplate(horde))
+ else if (!GetQuestTemplate(horde))
LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
else
FactionChangeQuests[alliance] = horde;
diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h
index eebdb3fd3..b70353112 100644
--- a/src/server/game/Grids/GridDefines.h
+++ b/src/server/game/Grids/GridDefines.h
@@ -52,7 +52,7 @@ class ObjectGuid;
#define MAP_HALFSIZE (MAP_SIZE/2)
// Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case)
-typedef TYPELIST_5(GameObject, Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/) AllWorldObjectTypes;
+typedef TYPELIST_4(GameObject, Player, Creature/*pets*/, Corpse/*resurrectable*/) AllWorldObjectTypes;
typedef TYPELIST_4(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/) AllGridObjectTypes;
typedef TYPELIST_5(Creature, GameObject, DynamicObject, Pet, Corpse) AllMapStoredObjectTypes;
diff --git a/src/server/game/Grids/GridObjectLoader.cpp b/src/server/game/Grids/GridObjectLoader.cpp
index 10172a4a1..ff960db65 100644
--- a/src/server/game/Grids/GridObjectLoader.cpp
+++ b/src/server/game/Grids/GridObjectLoader.cpp
@@ -84,20 +84,14 @@ void GridObjectLoader::LoadAllCellsInGrid()
LoadGameObjects(cell_guids.gameobjects, _map);
LoadCreatures(cell_guids.creatures, _map);
- if (std::unordered_set const* corpses = _map->GetCorpsesInCell(_grid.GetId()))
+ if (std::unordered_set const* corpses = _map->GetCorpsesInGrid(_grid.GetId()))
{
for (Corpse* corpse : *corpses)
{
if (corpse->IsInGrid())
continue;
- CellCoord cellCoord = Acore::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY());
- Cell cell(cellCoord);
-
- if (corpse->IsWorldObject())
- _grid.AddWorldObject(cell.CellX(), cell.CellY(), corpse);
- else
- _grid.AddGridObject(cell.CellX(), cell.CellY(), corpse);
+ AddObjectHelper(_map, corpse);
}
}
}
diff --git a/src/server/game/Mails/ServerMailMgr.cpp b/src/server/game/Mails/ServerMailMgr.cpp
index d11595066..8ed35ffc1 100644
--- a/src/server/game/Mails/ServerMailMgr.cpp
+++ b/src/server/game/Mails/ServerMailMgr.cpp
@@ -16,7 +16,9 @@
*/
#include "ServerMailMgr.h"
+#include "AccountMgr.h"
#include "AchievementMgr.h"
+#include "Common.h"
#include "DatabaseEnv.h"
#include "Item.h"
#include "Log.h"
@@ -240,21 +242,28 @@ void ServerMailMgr::LoadMailServerTemplatesConditions()
case ServerMailConditionType::Faction:
if (conditionValue < TEAM_ALLIANCE || conditionValue > TEAM_HORDE)
{
- LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Faction' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID);
+ LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Faction' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
continue;
}
break;
case ServerMailConditionType::Race:
if (conditionValue & ~RACEMASK_ALL_PLAYABLE)
{
- LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Race' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID);
+ LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Race' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
continue;
}
break;
case ServerMailConditionType::Class:
if (conditionValue & ~CLASSMASK_ALL_PLAYABLE)
{
- LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Class' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID);
+ LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Class' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
+ continue;
+ }
+ break;
+ case ServerMailConditionType::AccountFlags:
+ if ((conditionValue & ~ACCOUNT_FLAGS_ALL) != 0)
+ {
+ LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'AccountFlags' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID);
continue;
}
break;
@@ -344,6 +353,8 @@ bool ServerMailCondition::CheckCondition(Player* player) const
return (player->getRaceMask() & value) != 0;
case ServerMailConditionType::Class:
return (player->getClassMask() & value) != 0;
+ case ServerMailConditionType::AccountFlags:
+ return player->GetSession()->HasAccountFlag(value);
default:
[[unlikely]] LOG_ERROR("server.mail", "Unknown server mail condition type '{}'", static_cast(type));
return false;
diff --git a/src/server/game/Mails/ServerMailMgr.h b/src/server/game/Mails/ServerMailMgr.h
index 9cf70a805..12eee5113 100644
--- a/src/server/game/Mails/ServerMailMgr.h
+++ b/src/server/game/Mails/ServerMailMgr.h
@@ -53,6 +53,7 @@ enum class ServerMailConditionType : uint8
Faction = 6, ///< Requires the player to be a part of a specific faction. Horde/Alliance.
Race = 7, ///< Requires the player to be a specific race.
Class = 8, ///< Requires the player to be a specific class.
+ AccountFlags = 9, ///< Requires the player to have a specific AccountFlag (bit)
};
/**
@@ -67,7 +68,8 @@ constexpr std::pair ServerMailConditi
{ "Reputation", ServerMailConditionType::Reputation },
{ "Faction", ServerMailConditionType::Faction },
{ "Race", ServerMailConditionType::Race },
- { "Class", ServerMailConditionType::Class }
+ { "Class", ServerMailConditionType::Class },
+ { "AccountFlags", ServerMailConditionType::AccountFlags }
};
/**
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index bcf7799e9..e98652ce5 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -175,81 +175,6 @@ void Map::AddToGrid(Corpse* obj, Cell const& cell)
}
}
-template
-void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/)
-{
-}
-
-template<>
-void Map::SwitchGridContainers(Creature* obj, bool on)
-{
- ASSERT(!obj->IsPermanentWorldObject());
- CellCoord p = Acore::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
- if (!p.IsCoordValid())
- {
- LOG_ERROR("maps", "Map::SwitchGridContainers: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]",
- obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
- return;
- }
-
- Cell cell(p);
- if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y)))
- return;
-
- LOG_DEBUG("maps", "Switch object {} from grid[{}, {}] {}", obj->GetGUID().ToString(), cell.GridX(), cell.GridY(), on);
- MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY());
- ASSERT(grid);
-
- obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add
-
- if (on)
- {
- grid->AddWorldObject(cell.CellX(), cell.CellY(), obj);
- AddWorldObject(obj);
- }
- else
- {
- grid->AddGridObject(cell.CellX(), cell.CellY(), obj);
- RemoveWorldObject(obj);
- }
-
- obj->m_isTempWorldObject = on;
-}
-
-template<>
-void Map::SwitchGridContainers(GameObject* obj, bool on)
-{
- ASSERT(!obj->IsPermanentWorldObject());
- CellCoord p = Acore::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
- if (!p.IsCoordValid())
- {
- LOG_ERROR("maps", "Map::SwitchGridContainers: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]",
- obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
- return;
- }
-
- Cell cell(p);
- if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y)))
- return;
-
- //LOG_DEBUG(LOG_FILTER_MAPS, "Switch object {} from grid[{}, {}] {}", obj->GetGUID().ToString(), cell.data.Part.grid_x, cell.data.Part.grid_y, on);
- MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY());
- ASSERT(grid);
-
- obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add
-
- if (on)
- {
- grid->AddWorldObject(cell.CellX(), cell.CellY(), obj);
- AddWorldObject(obj);
- }
- else
- {
- grid->AddGridObject(cell.CellX(), cell.CellY(), obj);
- RemoveWorldObject(obj);
- }
-}
-
template
void Map::DeleteFromWorld(T* obj)
{
@@ -1108,7 +1033,7 @@ void Map::UnloadAll()
_transports.clear();
- for (auto& cellCorpsePair : _corpsesByCell)
+ for (auto& cellCorpsePair : _corpsesByGrid)
{
for (Corpse* corpse : cellCorpsePair.second)
{
@@ -1118,7 +1043,7 @@ void Map::UnloadAll()
}
}
- _corpsesByCell.clear();
+ _corpsesByGrid.clear();
_corpsesByPlayer.clear();
_corpseBones.clear();
}
@@ -1849,49 +1774,8 @@ void Map::AddObjectToRemoveList(WorldObject* obj)
//LOG_DEBUG("maps", "Object ({}) added to removing list.", obj->GetGUID().ToString());
}
-void Map::AddObjectToSwitchList(WorldObject* obj, bool on)
-{
- ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId());
- // i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses
- // the contained objects only if IsCreature() , so we can return in all other cases
- if (!obj->IsCreature() && !obj->IsGameObject())
- return;
-
- std::map::iterator itr = i_objectsToSwitch.find(obj);
- if (itr == i_objectsToSwitch.end())
- i_objectsToSwitch.insert(itr, std::make_pair(obj, on));
- else if (itr->second != on)
- i_objectsToSwitch.erase(itr);
- else
- ABORT();
-}
-
void Map::RemoveAllObjectsInRemoveList()
{
- while (!i_objectsToSwitch.empty())
- {
- std::map::iterator itr = i_objectsToSwitch.begin();
- WorldObject* obj = itr->first;
- bool on = itr->second;
- i_objectsToSwitch.erase(itr);
-
- if (!obj->IsPermanentWorldObject())
- {
- switch (obj->GetTypeId())
- {
- case TYPEID_UNIT:
- SwitchGridContainers(obj->ToCreature(), on);
- break;
- case TYPEID_GAMEOBJECT:
- SwitchGridContainers(obj->ToGameObject(), on);
- break;
- default:
- break;
- }
- }
- }
-
- //LOG_DEBUG("maps", "Object remover 1 check.");
while (!i_objectsToRemove.empty())
{
std::unordered_set::iterator itr = i_objectsToRemove.begin();
@@ -1929,8 +1813,6 @@ void Map::RemoveAllObjectsInRemoveList()
break;
}
}
-
- //LOG_DEBUG("maps", "Object remover 2 check.");
}
uint32 Map::GetPlayersCountExceptGMs() const
@@ -1972,6 +1854,12 @@ void Map::AddToActive(GameObject* d)
AddToActiveHelper(d);
}
+template<>
+void Map::AddToActive(Corpse* /*c*/)
+{
+ // do nothing for corpses
+}
+
template
void Map::RemoveFromActive(T* obj)
{
@@ -2791,7 +2679,8 @@ void Map::AddCorpse(Corpse* corpse)
{
corpse->SetMap(this);
- _corpsesByCell[corpse->GetCellCoord().GetId()].insert(corpse);
+ GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY());
+ _corpsesByGrid[gridCoord.GetId()].insert(corpse);
if (corpse->GetType() != CORPSE_BONES)
_corpsesByPlayer[corpse->GetOwnerGUID()] = corpse;
else
@@ -2801,6 +2690,7 @@ void Map::AddCorpse(Corpse* corpse)
void Map::RemoveCorpse(Corpse* corpse)
{
ASSERT(corpse);
+ GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY());
corpse->DestroyForNearbyPlayers();
if (corpse->IsInGrid())
@@ -2811,7 +2701,7 @@ void Map::RemoveCorpse(Corpse* corpse)
corpse->ResetMap();
}
- _corpsesByCell[corpse->GetCellCoord().GetId()].erase(corpse);
+ _corpsesByGrid[gridCoord.GetId()].erase(corpse);
if (corpse->GetType() != CORPSE_BONES)
_corpsesByPlayer.erase(corpse->GetOwnerGUID());
else
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 7f70020dc..53a35efb9 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -306,7 +306,6 @@ public:
}
void AddObjectToRemoveList(WorldObject* obj);
- void AddObjectToSwitchList(WorldObject* obj, bool on);
virtual void DelayedUpdate(const uint32 diff);
void resetMarkedCells() { marked_cells.reset(); }
@@ -336,7 +335,6 @@ public:
template
void RemoveFromActive(T* obj);
- template void SwitchGridContainers(T* obj, bool on);
CreatureGroupHolderType CreatureGroupHolder;
void UpdateIteratorBack(Player* player);
@@ -361,10 +359,10 @@ public:
typedef std::unordered_multimap GameObjectBySpawnIdContainer;
GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; }
- [[nodiscard]] std::unordered_set const* GetCorpsesInCell(uint32 cellId) const
+ [[nodiscard]] std::unordered_set const* GetCorpsesInGrid(uint32 gridId) const
{
- auto itr = _corpsesByCell.find(cellId);
- if (itr != _corpsesByCell.end())
+ auto itr = _corpsesByGrid.find(gridId);
+ if (itr != _corpsesByGrid.end())
return &itr->second;
return nullptr;
@@ -577,7 +575,6 @@ private:
bool i_scriptLock;
std::unordered_set i_objectsToRemove;
- std::map i_objectsToSwitch;
std::unordered_set i_worldObjects;
typedef std::multimap ScriptScheduleMap;
@@ -634,7 +631,7 @@ private:
MapStoredObjectTypesContainer _objectsStore;
CreatureBySpawnIdContainer _creatureBySpawnIdStore;
GameObjectBySpawnIdContainer _gameobjectBySpawnIdStore;
- std::unordered_map> _corpsesByCell;
+ std::unordered_map> _corpsesByGrid;
std::unordered_map _corpsesByPlayer;
std::unordered_set _corpseBones;
diff --git a/src/server/game/Maps/MapMgr.cpp b/src/server/game/Maps/MapMgr.cpp
index f6e78a40a..e113c93d2 100644
--- a/src/server/game/Maps/MapMgr.cpp
+++ b/src/server/game/Maps/MapMgr.cpp
@@ -223,7 +223,7 @@ Map::EnterState MapMgr::PlayerCannotEnter(uint32 mapid, Player* player, bool log
{
uint32 destInstId = sInstanceSaveMgr->PlayerGetDestinationInstanceId(player, mapid, targetDifficulty);
if (destInstId)
- if (Map* boundMap = sMapMgr->FindMap(mapid, destInstId))
+ if (Map* boundMap = FindMap(mapid, destInstId))
if (Map::EnterState denyReason = boundMap->CannotEnter(player, loginCheck))
return denyReason;
}
diff --git a/src/server/game/Maps/MapUpdater.cpp b/src/server/game/Maps/MapUpdater.cpp
index 8f8704da5..ea5fe9602 100644
--- a/src/server/game/Maps/MapUpdater.cpp
+++ b/src/server/game/Maps/MapUpdater.cpp
@@ -18,7 +18,9 @@
#include "MapUpdater.h"
#include "DatabaseEnv.h"
#include "LFGMgr.h"
+#include "Log.h"
#include "Map.h"
+#include "MapMgr.h"
#include "Metric.h"
class UpdateRequest
@@ -52,6 +54,27 @@ private:
uint32 s_diff;
};
+class MapPreloadRequest : public UpdateRequest
+{
+public:
+ MapPreloadRequest(uint32 mapId, MapUpdater& updater)
+ : _mapId(mapId), _updater(updater)
+ {
+ }
+
+ void call() override
+ {
+ Map* map = sMapMgr->CreateBaseMap(_mapId);
+ LOG_INFO("server.loading", ">> Loading All Grids For Map {} ({})", map->GetId(), map->GetMapName());
+ map->LoadAllGrids();
+ _updater.update_finished();
+ }
+
+private:
+ uint32 _mapId;
+ MapUpdater& _updater;
+};
+
class LFGUpdateRequest : public UpdateRequest
{
public:
@@ -120,6 +143,11 @@ void MapUpdater::schedule_update(Map& map, uint32 diff, uint32 s_diff)
schedule_task(new MapUpdateRequest(map, *this, diff, s_diff));
}
+void MapUpdater::schedule_map_preload(uint32 mapid)
+{
+ schedule_task(new MapPreloadRequest(mapid, *this));
+}
+
void MapUpdater::schedule_lfg_update(uint32 diff)
{
schedule_task(new LFGUpdateRequest(*this, diff));
diff --git a/src/server/game/Maps/MapUpdater.h b/src/server/game/Maps/MapUpdater.h
index 174cef977..9576e7e1f 100644
--- a/src/server/game/Maps/MapUpdater.h
+++ b/src/server/game/Maps/MapUpdater.h
@@ -35,6 +35,7 @@ public:
void schedule_task(UpdateRequest* request);
void schedule_update(Map& map, uint32 diff, uint32 s_diff);
+ void schedule_map_preload(uint32 mapid);
void schedule_lfg_update(uint32 diff);
void wait();
void activate(std::size_t num_threads);
diff --git a/src/server/game/Misc/GameGraveyard.cpp b/src/server/game/Misc/GameGraveyard.cpp
index 3fe1d72d3..03a945084 100644
--- a/src/server/game/Misc/GameGraveyard.cpp
+++ b/src/server/game/Misc/GameGraveyard.cpp
@@ -91,7 +91,7 @@ GraveyardStruct const* Graveyard::GetDefaultGraveyard(TeamId teamId)
ALLIANCE_GRAVEYARD = 4, // Westfall
};
- return sGraveyard->GetGraveyard(teamId == TEAM_HORDE ? HORDE_GRAVEYARD : ALLIANCE_GRAVEYARD);
+ return GetGraveyard(teamId == TEAM_HORDE ? HORDE_GRAVEYARD : ALLIANCE_GRAVEYARD);
}
GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId teamId, bool nearCorpse)
@@ -100,7 +100,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId tea
sScriptMgr->OnPlayerBeforeChooseGraveyard(player, teamId, nearCorpse, graveyardOverride);
if (graveyardOverride)
{
- return sGraveyard->GetGraveyard(graveyardOverride);
+ return GetGraveyard(graveyardOverride);
}
WorldLocation loc = player->GetWorldLocation();
@@ -187,7 +187,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(uint32 mapId, float x, flo
for (; range.first != range.second; ++range.first)
{
GraveyardData const& graveyardLink = range.first->second;
- GraveyardStruct const* entry = sGraveyard->GetGraveyard(graveyardLink.safeLocId);
+ GraveyardStruct const* entry = GetGraveyard(graveyardLink.safeLocId);
if (!entry)
{
LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing `game_graveyard` table {}, skipped.", graveyardLink.safeLocId);
@@ -393,7 +393,7 @@ void Graveyard::LoadGraveyardZones()
uint32 team = fields[2].Get();
TeamId teamId = team == 0 ? TEAM_NEUTRAL : (team == ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE);
- GraveyardStruct const* entry = sGraveyard->GetGraveyard(safeLocId);
+ GraveyardStruct const* entry = GetGraveyard(safeLocId);
if (!entry)
{
LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for not existing `game_graveyard` table {}, skipped.", safeLocId);
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index 3565a6cce..5658e4147 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -217,7 +217,11 @@ enum AcoreStrings
LANG_INVALID_GAMEOBJECT_TYPE = 176,
LANG_GAMEOBJECT_DAMAGED = 177,
LANG_GRID_POSITION = 178,
- // 179-185 used in other client versions
+
+ LANG_ACCOUNT_FLAGS_PINFO = 179,
+
+ // Free 180-185
+
LANG_TRANSPORT_POSITION = 186,
LANG_PROFANITY_NAME = 187,
LANG_2FA_SECRET_TOO_LONG = 188,
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index 7dec2d8ce..76fb0840a 100644
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -72,15 +72,27 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff)
return false;
Creature* cOwner = owner->ToCreature();
+ bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting();
// the owner might be unable to move (rooted or casting), or we have lost the target, pause movement
- if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || (cOwner && cOwner->IsMovementPreventedByCasting()))
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || isStoppedBecauseOfCasting)
{
owner->StopMoving();
_lastTargetPosition.reset();
if (cOwner)
{
- cOwner->UpdateLeashExtensionTime();
+ if (isStoppedBecauseOfCasting)
+ {
+ // Don't reset leash timer if it's a spell like Shoot with a short cast time.
+ /// @todo: Research how it should actually work.
+ Spell *spell = cOwner->GetFirstCurrentCastingSpell();
+ bool spellHasLongCast = spell && spell->GetCastTime() > 1 * SECOND * IN_MILLISECONDS;
+ if (spellHasLongCast)
+ cOwner->UpdateLeashExtensionTime();
+ }
+ else
+ cOwner->UpdateLeashExtensionTime();
+
cOwner->SetCannotReachTarget();
}
return true;
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index e1ecf2ced..66d25fe39 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -104,7 +104,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
}
/// WorldSession constructor
-WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion,
+WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr sock, AccountTypes sec, uint8 expansion,
time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool isBot) :
m_muteTime(mute_time),
m_timeOutTime(0),
@@ -116,6 +116,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptrSetData(0, _accountFlags);
+ stmt->SetData(1, GetAccountId());
+ LoginDatabase.Execute(stmt);
+}
+
+void WorldSession::ValidateAccountFlags()
+{
+ bool hasGMFlag = HasAccountFlag(ACCOUNT_FLAG_GM);
+
+ if (IsGMAccount() && !hasGMFlag)
+ UpdateAccountFlag(ACCOUNT_FLAG_GM);
+ else if (hasGMFlag && !IsGMAccount())
+ UpdateAccountFlag(ACCOUNT_FLAG_GM, true);
+}
+
bool WorldSession::IsGMAccount() const
{
return GetSecurity() >= SEC_GAMEMASTER;
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 0816b734e..55919f7ea 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -348,10 +348,14 @@ struct PacketCounter
class WorldSession
{
public:
- WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale,
- uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool isBot = false);
+ WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime, bool is_bot = false);
~WorldSession();
+ uint32 GetAccountFlags() const { return _accountFlags; }
+ bool HasAccountFlag(uint32 flag) const { return (_accountFlags & flag) != 0; }
+ void UpdateAccountFlag(uint32 flag, bool remove = false);
+ void ValidateAccountFlags();
+
bool IsGMAccount() const;
bool PlayerLoading() const { return m_playerLoading; }
@@ -1188,6 +1192,7 @@ private:
bool _skipQueue;
uint32 _accountId;
std::string _accountName;
+ uint32 _accountFlags;
uint8 m_expansion;
uint32 m_total_time;
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index 9c4d9de8e..51705d18e 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -698,8 +698,6 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSes
LoginDatabase.Execute(stmt);
- AccountMgr::ValidateAccountFlags(account.Id, account.Flags, account.Security);
-
// At this point, we can safely hook a successful login
sScriptMgr->OnAccountLogin(account.Id);
@@ -707,7 +705,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSes
sScriptMgr->OnLastIpUpdate(account.Id, address);
- _worldSession = new WorldSession(account.Id, std::move(authSession->Account), shared_from_this(), account.Security,
+ _worldSession = new WorldSession(account.Id, std::move(authSession->Account), account.Flags, shared_from_this(), account.Security,
account.Expansion, account.MuteTime, account.Locale, account.Recruiter, account.IsRectuiter, account.Security ? true : false, account.TotalTime);
_worldSession->ReadAddonsInfo(authSession->AddonInfo);
@@ -718,6 +716,8 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSes
_worldSession->InitWarden(account.SessionKey, account.OS);
}
+ _worldSession->ValidateAccountFlags();
+
sWorldSessionMgr->AddSession(_worldSession);
AsyncRead();
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 6ea6d5d62..439713ee7 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -2238,21 +2238,15 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar
if (isBouncingFar)
searchRadius *= chainTargets;
+ WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target;
std::list tempTargets;
- SearchAreaTargets(tempTargets, searchRadius, target, m_caster, objectType, selectType, condList);
+ SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList);
tempTargets.remove(target);
// remove targets which are always invalid for chain spells
// for some spells allow only chain targets in front of caster (swipe for example)
if (!isBouncingFar)
- {
- for (std::list::iterator itr = tempTargets.begin(); itr != tempTargets.end();)
- {
- std::list::iterator checkItr = itr++;
- if (!m_caster->HasInArc(static_cast(M_PI), *checkItr))
- tempTargets.erase(checkItr);
- }
- }
+ tempTargets.remove_if([this](WorldObject* target) { return !m_caster->HasInArc(static_cast(M_PI), target); });
while (chainTargets)
{
@@ -2267,7 +2261,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar
if (Unit* unit = (*itr)->ToUnit())
{
uint32 deficit = unit->GetMaxHealth() - unit->GetHealth();
- if (deficit > maxHPDeficit && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2))
+ if (deficit > maxHPDeficit && chainSource->IsWithinDist(unit, jumpRadius) && chainSource->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2))
{
foundItr = itr;
maxHPDeficit = deficit;
@@ -2282,19 +2276,22 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar
{
if (foundItr == tempTargets.end())
{
- if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
+ if ((!isBouncingFar || chainSource->IsWithinDist(*itr, jumpRadius)) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
foundItr = itr;
}
- else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
+ else if (chainSource->GetDistanceOrder(*itr, *foundItr) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2))
foundItr = itr;
}
}
// not found any valid target - chain ends
if (foundItr == tempTargets.end())
break;
- target = *foundItr;
+
+ if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER))
+ chainSource = *foundItr;
+
+ targets.push_back(*foundItr);
tempTargets.erase(foundItr);
- targets.push_back(target);
--chainTargets;
}
}
@@ -7112,7 +7109,7 @@ SpellCastResult Spell::CheckRange(bool strict)
if (range_type == SPELL_RANGE_MELEE)
{
float real_max_range = max_range;
- if (!m_caster->IsCreature() && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking())
+ if (!m_caster->IsCreature() && m_caster->HasLeewayMovement() && target->HasLeewayMovement())
real_max_range -= MIN_MELEE_REACH; // Because of lag, we can not check too strictly here (is only used if both caster and target are moving)
else
real_max_range -= 2 * MIN_MELEE_REACH;
@@ -7129,7 +7126,7 @@ SpellCastResult Spell::CheckRange(bool strict)
return SPELL_FAILED_TOO_CLOSE;
}
- if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target))
+ if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target) && !m_caster->IsWithinBoundaryRadius(target))
return SPELL_FAILED_UNIT_NOT_INFRONT;
}
@@ -9040,7 +9037,7 @@ namespace Acore
case TARGET_CHECK_PARTY:
if (unitTarget->IsTotem())
return false;
- if (unitTarget->IsGuardian())
+ if (unitTarget->IsGuardian() && !unitTarget->IsControllableGuardian())
return false;
if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo))
return false;
@@ -9054,7 +9051,7 @@ namespace Acore
case TARGET_CHECK_RAID:
if (unitTarget->IsTotem())
return false;
- if (unitTarget->IsGuardian())
+ if (unitTarget->IsGuardian() && !unitTarget->IsControllableGuardian())
return false;
if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo))
return false;
@@ -9132,7 +9129,7 @@ namespace Acore
}
else
{
- if (!_caster->isInFront(target, _coneAngle))
+ if (!_caster->IsWithinBoundaryRadius(target->ToUnit()) && !_caster->isInFront(target, _coneAngle))
return false;
}
return WorldObjectSpellAreaTargetCheck::operator ()(target);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 74a1a1bba..dbacbb56b 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2731,7 +2731,7 @@ void Spell::EffectAddFarsight(SpellEffIndex effIndex)
// Remove old farsight if exist
bool updateViewerVisibility = m_caster->RemoveDynObject(m_spellInfo->Id);
- DynamicObject* dynObj = new DynamicObject(true);
+ DynamicObject* dynObj = new DynamicObject(false);
if (!dynObj->CreateDynamicObject(m_caster->GetMap()->GenerateLowGuid(), m_caster, m_spellInfo->Id, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS))
{
delete dynObj;
diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp
index a61c81b02..4ba8f62c6 100644
--- a/src/server/game/Spells/SpellInfoCorrections.cpp
+++ b/src/server/game/Spells/SpellInfoCorrections.cpp
@@ -592,6 +592,12 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx3 &= ~SPELL_ATTR3_SUPPRESS_CASTER_PROCS;
});
+ // Vindication
+ ApplySpellFix({ 67, 26017}, [](SpellInfo* spellInfo)
+ {
+ spellInfo->Effects[EFFECT_0].MiscValue = 0;
+ });
+
// Arcane Missiles
ApplySpellFix({ 5143, 5144, 5145, 8416, 8417, 10211, 10212, 25345, 27075, 38699, 38704, 42843, 42846 }, [](SpellInfo* spellInfo)
{
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 3c5a4ccf6..534388fd4 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -1614,7 +1614,7 @@ void SpellMgr::LoadSpellTargetPositions()
}
if (found)
{
- if (!sSpellMgr->GetSpellTargetPosition(i))
+ if (!GetSpellTargetPosition(i))
LOG_DEBUG("spells.aura", "Spell (ID: {}) does not have record in `spell_target_position`", i);
}
}*/
@@ -2348,7 +2348,7 @@ void SpellMgr::LoadPetLevelupSpellMap()
LOG_INFO("server.loading", " ");
}
-bool LoadPetDefaultSpells_helper(CreatureTemplate const* cInfo, PetDefaultSpellsEntry& petDefSpells)
+static bool LoadPetDefaultSpells_helper(CreatureTemplate const* cInfo, PetDefaultSpellsEntry& petDefSpells)
{
// skip empty list;
bool have_spell = false;
@@ -3448,9 +3448,9 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
spellInfo->_InitializeExplicitTargetMask();
- if (sSpellMgr->HasSpellCooldownOverride(spellInfo->Id))
+ if (HasSpellCooldownOverride(spellInfo->Id))
{
- SpellCooldownOverride spellOverride = sSpellMgr->GetSpellCooldownOverride(spellInfo->Id);
+ SpellCooldownOverride spellOverride = GetSpellCooldownOverride(spellInfo->Id);
if (spellInfo->RecoveryTime != spellOverride.RecoveryTime)
{
@@ -3497,7 +3497,7 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT:
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
- if (SpellInfo const* triggerSpell = sSpellMgr->GetSpellInfo(spellInfo->Effects[j].TriggerSpell))
+ if (SpellInfo const* triggerSpell = GetSpellInfo(spellInfo->Effects[j].TriggerSpell))
{
overrideAttr = true;
if (triggerSpell->AttributesCu & SPELL_ATTR0_CU_BINARY_SPELL)
diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp
index f339be0e1..543b40cd5 100644
--- a/src/server/game/Tickets/TicketMgr.cpp
+++ b/src/server/game/Tickets/TicketMgr.cpp
@@ -286,7 +286,7 @@ void TicketMgr::ResetTickets()
{
uint32 ticketId = itr->second->GetId();
++itr;
- sTicketMgr->RemoveTicket(ticketId);
+ RemoveTicket(ticketId);
}
else
++itr;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 2c5e93062..b98e4d32b 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -218,10 +218,10 @@ void World::LoadConfigSettings(bool reload)
//visibility on continents
_maxVisibleDistanceOnContinents = sConfigMgr->GetOption("Visibility.Distance.Continents", DEFAULT_VISIBILITY_DISTANCE);
- if (_maxVisibleDistanceOnContinents < 45 * sWorld->getRate(RATE_CREATURE_AGGRO))
+ if (_maxVisibleDistanceOnContinents < 45 * getRate(RATE_CREATURE_AGGRO))
{
- LOG_ERROR("server.loading", "Visibility.Distance.Continents can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO));
- _maxVisibleDistanceOnContinents = 45 * sWorld->getRate(RATE_CREATURE_AGGRO);
+ LOG_ERROR("server.loading", "Visibility.Distance.Continents can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO));
+ _maxVisibleDistanceOnContinents = 45 * getRate(RATE_CREATURE_AGGRO);
}
else if (_maxVisibleDistanceOnContinents > MAX_VISIBILITY_DISTANCE)
{
@@ -231,10 +231,10 @@ void World::LoadConfigSettings(bool reload)
//visibility in instances
_maxVisibleDistanceInInstances = sConfigMgr->GetOption("Visibility.Distance.Instances", DEFAULT_VISIBILITY_INSTANCE);
- if (_maxVisibleDistanceInInstances < 45 * sWorld->getRate(RATE_CREATURE_AGGRO))
+ if (_maxVisibleDistanceInInstances < 45 * getRate(RATE_CREATURE_AGGRO))
{
- LOG_ERROR("server.loading", "Visibility.Distance.Instances can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO));
- _maxVisibleDistanceInInstances = 45 * sWorld->getRate(RATE_CREATURE_AGGRO);
+ LOG_ERROR("server.loading", "Visibility.Distance.Instances can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO));
+ _maxVisibleDistanceInInstances = 45 * getRate(RATE_CREATURE_AGGRO);
}
else if (_maxVisibleDistanceInInstances > MAX_VISIBILITY_DISTANCE)
{
@@ -244,10 +244,10 @@ void World::LoadConfigSettings(bool reload)
//visibility in BG/Arenas
_maxVisibleDistanceInBGArenas = sConfigMgr->GetOption("Visibility.Distance.BGArenas", DEFAULT_VISIBILITY_BGARENAS);
- if (_maxVisibleDistanceInBGArenas < 45 * sWorld->getRate(RATE_CREATURE_AGGRO))
+ if (_maxVisibleDistanceInBGArenas < 45 * getRate(RATE_CREATURE_AGGRO))
{
- LOG_ERROR("server.loading", "Visibility.Distance.BGArenas can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO));
- _maxVisibleDistanceInBGArenas = 45 * sWorld->getRate(RATE_CREATURE_AGGRO);
+ LOG_ERROR("server.loading", "Visibility.Distance.BGArenas can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO));
+ _maxVisibleDistanceInBGArenas = 45 * getRate(RATE_CREATURE_AGGRO);
}
else if (_maxVisibleDistanceInBGArenas > MAX_VISIBILITY_DISTANCE)
{
@@ -1001,7 +1001,7 @@ void World::SetInitialWorldSettings()
sScriptMgr->OnBeforeWorldInitialized();
- if (sWorld->getBoolConfig(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS))
+ if (getBoolConfig(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS))
{
LOG_INFO("server.loading", "Loading All Grids For All Non-Instanced Maps...");
@@ -1011,15 +1011,23 @@ void World::SetInitialWorldSettings()
if (mapEntry && !mapEntry->Instanceable())
{
- Map* map = sMapMgr->CreateBaseMap(mapEntry->MapID);
-
- if (map)
+ if (sMapMgr->GetMapUpdater()->activated())
+ sMapMgr->GetMapUpdater()->schedule_map_preload(mapEntry->MapID);
+ else
{
- LOG_INFO("server.loading", ">> Loading All Grids For Map {}", map->GetId());
- map->LoadAllGrids();
+ Map* map = sMapMgr->CreateBaseMap(mapEntry->MapID);
+
+ if (map)
+ {
+ LOG_INFO("server.loading", ">> Loading All Grids For Map {}", map->GetId());
+ map->LoadAllGrids();
+ }
}
}
}
+
+ if (sMapMgr->GetMapUpdater()->activated())
+ sMapMgr->GetMapUpdater()->wait();
}
uint32 startupDuration = GetMSTimeDiffToNow(startupBegin);
@@ -1191,7 +1199,7 @@ void World::Update(uint32 diff)
}
/// Clean logs table
- if (sWorld->getIntConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer
+ if (getIntConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer
{
if (_timers[WUPDATE_CLEANDB].Passed())
{
@@ -1200,7 +1208,7 @@ void World::Update(uint32 diff)
_timers[WUPDATE_CLEANDB].Reset();
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_OLD_LOGS);
- stmt->SetData(0, sWorld->getIntConfig(CONFIG_LOGDB_CLEARTIME));
+ stmt->SetData(0, getIntConfig(CONFIG_LOGDB_CLEARTIME));
stmt->SetData(1, uint32(currentGameTime.count()));
LoginDatabase.Execute(stmt);
}
@@ -1217,7 +1225,7 @@ void World::Update(uint32 diff)
sMapMgr->Update(diff);
}
- if (sWorld->getBoolConfig(CONFIG_AUTOBROADCAST))
+ if (getBoolConfig(CONFIG_AUTOBROADCAST))
{
if (_timers[WUPDATE_AUTOBROADCAST].Passed())
{
diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp
index 5db780298..7a0b1db40 100644
--- a/src/server/game/World/WorldConfig.cpp
+++ b/src/server/game/World/WorldConfig.cpp
@@ -160,6 +160,7 @@ void WorldConfig::BuildConfigCache()
SetConfigValue(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 900000);
SetConfigValue(CONFIG_INTERVAL_DISCONNECT_TOLERANCE, "DisconnectToleranceInterval", 0);
SetConfigValue(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true);
+ SetConfigValue(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, "ValidateSkillLearnedBySpells", true);
SetConfigValue(CONFIG_MIN_LEVEL_STAT_SAVE, "PlayerSave.Stats.MinLevel", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value < MAX_LEVEL; }, "< MAX_LEVEL");
@@ -406,6 +407,7 @@ void WorldConfig::BuildConfigCache()
SetConfigValue(CONFIG_LISTEN_RANGE_TEXTEMOTE, "ListenRange.TextEmote", 40.0f);
SetConfigValue(CONFIG_LISTEN_RANGE_YELL, "ListenRange.Yell", 300.0f);
+ SetConfigValue(CONFIG_BATTLEGROUND_PREP_TIME, "Battleground.PrepTime", 120);
SetConfigValue(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, "Battleground.Override.LowLevels.MinPlayers", 0);
SetConfigValue(CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, "Battleground.DisableQuestShareInBG", false);
SetConfigValue(CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG, "Battleground.DisableReadyCheckInBG", false);
@@ -436,6 +438,7 @@ void WorldConfig::BuildConfigCache()
SetConfigValue(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH, "Battleground.Alterac.ReputationOnBossDeath", 350);
SetConfigValue(CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, "Battleground.EyeOfTheStorm.CapturePoints", 1600);
+ SetConfigValue(CONFIG_ARENA_PREP_TIME, "Arena.PrepTime", 60);
SetConfigValue(CONFIG_ARENA_MAX_RATING_DIFFERENCE, "Arena.MaxRatingDifference", 150);
SetConfigValue(CONFIG_ARENA_RATING_DISCARD_TIMER, "Arena.RatingDiscardTimer", 600000);
SetConfigValue(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, "Arena.PreviousOpponentsDiscardTimer", 120000);
diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h
index 989746b81..3776fcac9 100644
--- a/src/server/game/World/WorldConfig.h
+++ b/src/server/game/World/WorldConfig.h
@@ -254,6 +254,7 @@ enum ServerConfigs
CONFIG_DEATH_SICKNESS_LEVEL,
CONFIG_INSTANT_LOGOUT,
CONFIG_DISABLE_BREATHING,
+ CONFIG_BATTLEGROUND_PREP_TIME,
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS,
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY,
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER,
@@ -274,6 +275,7 @@ enum ServerConfigs
CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH,
CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS,
CONFIG_WINTERGRASP_ENABLE,
+ CONFIG_ARENA_PREP_TIME,
CONFIG_ARENA_MAX_RATING_DIFFERENCE,
CONFIG_ARENA_RATING_DISCARD_TIMER,
CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER,
@@ -472,6 +474,7 @@ enum ServerConfigs
RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE,
RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER,
CONFIG_NEW_CHAR_STRING,
+ CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS,
MAX_NUM_SERVER_CONFIGS
};
diff --git a/src/server/game/World/WorldState.cpp b/src/server/game/World/WorldState.cpp
index cbdf4a91e..0ca7ea0c0 100644
--- a/src/server/game/World/WorldState.cpp
+++ b/src/server/game/World/WorldState.cpp
@@ -47,7 +47,9 @@ WorldState::~WorldState()
void WorldState::Load()
{
- QueryResult result = CharacterDatabase.Query("SELECT Id, Data FROM world_state");
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_WORLD_STATE);
+ PreparedQueryResult result = CharacterDatabase.Query(stmt);
+
if (result)
{
do
@@ -205,8 +207,10 @@ void WorldState::Save(WorldStateSaveIds saveId)
void WorldState::SaveHelper(std::string& stringToSave, WorldStateSaveIds saveId)
{
- CharacterDatabase.Execute("DELETE FROM world_state WHERE Id='{}'", saveId);
- CharacterDatabase.Execute("INSERT INTO world_state(Id,Data) VALUES('{}','{}')", saveId, stringToSave.data());
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_WORLD_STATE);
+ stmt->SetData(0, saveId);
+ stmt->SetData(1, stringToSave);
+ CharacterDatabase.Execute(stmt);
}
bool WorldState::IsConditionFulfilled(uint32 conditionId, uint32 state) const
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index cac1142df..9549698fe 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -22,6 +22,7 @@
#include "CharacterCache.h"
#include "Chat.h"
#include "CommandScript.h"
+#include "Common.h"
#include "GameGraveyard.h"
#include "GameTime.h"
#include "GridNotifiers.h"
@@ -57,6 +58,49 @@
constexpr auto SPELL_STUCK = 7355;
constexpr auto SPELL_FREEZE = 9454;
+struct AccountFlagText
+{
+ AccountFlag flag;
+ std::string text;
+};
+
+AccountFlagText const accountFlagText[MAX_ACCOUNT_FLAG] =
+{
+ { ACCOUNT_FLAG_GM, "ACCOUNT_FLAG_GM" },
+ { ACCOUNT_FLAG_NOKICK, "ACCOUNT_FLAG_NOKICK" },
+ { ACCOUNT_FLAG_COLLECTOR, "ACCOUNT_FLAG_COLLECTOR" },
+ { ACCOUNT_FLAG_TRIAL, "ACCOUNT_FLAG_TRIAL" },
+ { ACCOUNT_FLAG_CANCELLED, "ACCOUNT_FLAG_CANCELLED" },
+ { ACCOUNT_FLAG_IGR, "ACCOUNT_FLAG_IGR" },
+ { ACCOUNT_FLAG_WHOLESALER, "ACCOUNT_FLAG_WHOLESALER" },
+ { ACCOUNT_FLAG_PRIVILEGED, "ACCOUNT_FLAG_PRIVILEGED" },
+ { ACCOUNT_FLAG_EU_FORBID_ELV, "ACCOUNT_FLAG_EU_FORBID_ELV" },
+ { ACCOUNT_FLAG_EU_FORBID_BILLING, "ACCOUNT_FLAG_EU_FORBID_BILLING" },
+ { ACCOUNT_FLAG_RESTRICTED, "ACCOUNT_FLAG_RESTRICTED" },
+ { ACCOUNT_FLAG_REFERRAL, "ACCOUNT_FLAG_REFERRAL" },
+ { ACCOUNT_FLAG_BLIZZARD, "ACCOUNT_FLAG_BLIZZARD" },
+ { ACCOUNT_FLAG_RECURRING_BILLING, "ACCOUNT_FLAG_RECURRING_BILLING" },
+ { ACCOUNT_FLAG_NOELECTUP, "ACCOUNT_FLAG_NOELECTUP" },
+ { ACCOUNT_FLAG_KR_CERTIFICATE, "ACCOUNT_FLAG_KR_CERTIFICATE" },
+ { ACCOUNT_FLAG_EXPANSION_COLLECTOR, "ACCOUNT_FLAG_EXPANSION_COLLECTOR" },
+ { ACCOUNT_FLAG_DISABLE_VOICE, "ACCOUNT_FLAG_DISABLE_VOICE" },
+ { ACCOUNT_FLAG_DISABLE_VOICE_SPEAK, "ACCOUNT_FLAG_DISABLE_VOICE_SPEAK" },
+ { ACCOUNT_FLAG_REFERRAL_RESURRECT, "ACCOUNT_FLAG_REFERRAL_RESURRECT" },
+ { ACCOUNT_FLAG_EU_FORBID_CC, "ACCOUNT_FLAG_EU_FORBID_CC" },
+ { ACCOUNT_FLAG_OPENBETA_DELL, "ACCOUNT_FLAG_OPENBETA_DELL" },
+ { ACCOUNT_FLAG_PROPASS, "ACCOUNT_FLAG_PROPASS" },
+ { ACCOUNT_FLAG_PROPASS_LOCK, "ACCOUNT_FLAG_PROPASS_LOCK" },
+ { ACCOUNT_FLAG_PENDING_UPGRADE, "ACCOUNT_FLAG_PENDING_UPGRADE" },
+ { ACCOUNT_FLAG_RETAIL_FROM_TRIAL, "ACCOUNT_FLAG_RETAIL_FROM_TRIAL" },
+ { ACCOUNT_FLAG_EXPANSION2_COLLECTOR, "ACCOUNT_FLAG_EXPANSION2_COLLECTOR" },
+ { ACCOUNT_FLAG_OVERMIND_LINKED, "ACCOUNT_FLAG_OVERMIND_LINKED" },
+ { ACCOUNT_FLAG_DEMOS, "ACCOUNT_FLAG_DEMOS" },
+ { ACCOUNT_FLAG_DEATH_KNIGHT_OK, "ACCOUNT_FLAG_DEATH_KNIGHT_OK" },
+ { ACCOUNT_FLAG_S2_REQUIRE_IGR, "ACCOUNT_FLAG_S2_REQUIRE_IGR" },
+ { ACCOUNT_FLAG_S2_TRIAL, "ACCOUNT_FLAG_S2_TRIAL" },
+ // { ACCOUNT_FLAG_S2_RESTRICTED, "ACCOUNT_FLAG_S2_RESTRICTED" }
+};
+
std::string const GetLocalizeCreatureName(Creature* creature, LocaleConstant locale)
{
auto creatureTemplate = sObjectMgr->GetCreatureTemplate(creature->GetEntry());
@@ -2193,6 +2237,15 @@ public:
// Output V. LANG_PINFO_ACC_ACCOUNT
handler->PSendSysMessage(LANG_PINFO_ACC_ACCOUNT, userName, accId, security);
+ if (playerTarget)
+ {
+ uint32 accountFlags = playerTarget->GetSession()->GetAccountFlags();
+ handler->PSendSysMessage(LANG_ACCOUNT_FLAGS_PINFO);
+ for (uint8 i = 0; i < MAX_ACCOUNT_FLAG; i++)
+ if (accountFlags & static_cast(accountFlagText[i].flag))
+ handler->PSendSysMessage(LANG_SUBCMDS_LIST_ENTRY, accountFlagText[i].text);
+ }
+
// Output VI. LANG_PINFO_ACC_LASTLOGIN
handler->PSendSysMessage(LANG_PINFO_ACC_LASTLOGIN, lastLogin, failedLogins);
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
index b9b8cef94..32ec43e80 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
@@ -692,6 +692,7 @@ public:
// Start Event
Start(true, false);
SetDespawnAtEnd(false);
+ SetDespawnAtFar(false);
ScheduleNextEvent(currentEvent, 9000);
break;
diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp
index 15a562623..fcebd8c01 100644
--- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp
@@ -31,14 +31,7 @@ enum Spells
SPELL_REMOVE_MUSHROOM_POWER = 57283,
// Mushroom
- SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648,
- SPELL_POISONOUS_MUSHROOM_POISON_CLOUD = 57061,
- SPELL_POISONOUS_MUSHROOM_VISUAL_AURA = 56741,
- SPELL_POISONOUS_MUSHROOM_VISUAL_AREA = 61566, // Self
- SPELL_HEALTHY_MUSHROOM_VISUAL_AURA = 56740,
- SPELL_PUTRID_MUSHROOM = 31690,
- SPELL_GROW = 57059,
- SPELL_SHRINK = 31691,
+ SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648
};
enum Creatures
@@ -94,13 +87,12 @@ Position const MushroomPositions[MAX_MUSHROOMS_COUNT] =
struct boss_amanitar : public BossAI
{
- boss_amanitar(Creature* creature) : BossAI(creature, DATA_AMANITAR), mushroomsSummoned(false) { }
+ boss_amanitar(Creature* creature) : BossAI(creature, DATA_AMANITAR) { }
void Reset() override
{
_Reset();
_mushroomsDeque.clear();
- mushroomsSummoned = false;
}
void JustEngagedWith(Unit* /*attacker*/) override
@@ -110,7 +102,7 @@ struct boss_amanitar : public BossAI
}, 10s, 15s);
ScheduleTimedEvent(10s, 14s, [&] {
- DoCastVictim(SPELL_BASH, false);
+ DoCastVictim(SPELL_BASH);
}, 15s, 20s);
ScheduleTimedEvent(15s, 20s, [&] {
@@ -153,10 +145,10 @@ struct boss_amanitar : public BossAI
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI);
}
- void SummonedCreatureDies(Creature* summon, Unit* killer) override
+ void SummonedCreatureDespawn(Creature* summon) override
{
_mushroomsDeque.push_back(summon->GetPosition());
- BossAI::SummonedCreatureDies(summon, killer);
+ BossAI::SummonedCreatureDespawn(summon);
}
void EnterEvadeMode(EvadeReason why) override
@@ -167,7 +159,6 @@ struct boss_amanitar : public BossAI
private:
std::deque _mushroomsDeque;
- bool mushroomsSummoned;
void SummonMushroom(Position const& pos)
{
@@ -175,98 +166,6 @@ private:
}
};
-struct npc_amanitar_mushrooms : public ScriptedAI
-{
- npc_amanitar_mushrooms(Creature* pCreature) : ScriptedAI(pCreature)
- {
- me->SetCombatMovement(false);
-
- //TODO: this prolly needs to be done in database
- pCreature->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- pCreature->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
- pCreature->SetRegeneratingHealth(false);
- }
-
- // Disabled events
- void JustEngagedWith(Unit* /*who*/) override {}
- void AttackStart(Unit* /*victim*/) override {}
- void EnterEvadeMode(EvadeReason /*why*/) override {}
-
- void Reset() override
- {
- me->SetReactState(REACT_PASSIVE);
- DoCastSelf(SPELL_PUTRID_MUSHROOM);
-
- if (me->GetEntry() == NPC_POISONOUS_MUSHROOM)
- {
- DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AURA, true);
- }
- else
- {
- DoCastSelf(SPELL_HEALTHY_MUSHROOM_VISUAL_AURA, true);
- }
-
- events.ScheduleEvent(EVENT_GROW, 800ms);
-
- if (me->GetEntry() == NPC_POISONOUS_MUSHROOM)
- {
- events.ScheduleEvent(EVENT_CHECK_PLAYER, 250ms);
- }
- }
-
- void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
- {
- if (me->GetEntry() == NPC_HEALTHY_MUSHROOM && damage >= me->GetHealth())
- {
- DoCastSelf(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS, true);
- }
- }
-
- void UpdateAI(uint32 diff) override
- {
- if (events.Empty())
- return;
-
- events.Update(diff);
- while (uint32 const eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_GROW:
- {
- DoCastSelf(SPELL_GROW);
- break;
- }
- case EVENT_CHECK_PLAYER:
- {
- if (Player* plr = me->SelectNearestPlayer(2.0f))
- {
- plr->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS);
- DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AREA);
- DoCastSelf(SPELL_POISONOUS_MUSHROOM_POISON_CLOUD);
- DoCastSelf(SPELL_SHRINK);
- events.ScheduleEvent(EVENT_KILLSELF, 4s);
- }
- else
- {
- events.Repeat(250ms);
- }
-
- break;
- }
- case EVENT_KILLSELF:
- {
- me->DisappearAndDie();
- break;
- }
- }
- }
- }
-
-private:
- EventMap events;
-};
-
// 57283 Remove Mushroom Power
class spell_amanitar_remove_mushroom_power : public AuraScript
{
@@ -274,8 +173,7 @@ class spell_amanitar_remove_mushroom_power : public AuraScript
void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
- if (Unit* target = GetTarget())
- target->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS);
+ GetTarget()->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS);
}
void Register() override
@@ -287,8 +185,5 @@ class spell_amanitar_remove_mushroom_power : public AuraScript
void AddSC_boss_amanitar()
{
RegisterAhnKahetCreatureAI(boss_amanitar);
- RegisterAhnKahetCreatureAI(npc_amanitar_mushrooms);
-
- // Spells
RegisterSpellScript(spell_amanitar_remove_mushroom_power);
}
diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp
index 6a24d9e8e..de9d3e176 100644
--- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp
@@ -56,6 +56,8 @@ enum Misc
MAX_EMBRACE_DMG_H = 40000,
SUMMON_GROUP_TRIGGERS = 0,
+
+ GROUP_COMBAT_ABILITIES = 1,
};
enum Actions
@@ -64,15 +66,6 @@ enum Actions
ACTION_SPHERE,
};
-enum Event
-{
- EVENT_PRINCE_FLAME_SPHERES = 1,
- EVENT_PRINCE_VANISH,
- EVENT_PRINCE_BLOODTHIRST,
- EVENT_PRINCE_VANISH_RUN,
- EVENT_PRINCE_RESCHEDULE,
-};
-
enum Yells
{
//SAY_SPHERE_ACTIVATED = 0,
@@ -178,9 +171,12 @@ private:
struct boss_taldaram : public BossAI
{
- boss_taldaram(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM),
- vanishDamage(0)
+ boss_taldaram(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM), vanishDamage(0)
{
+ scheduler.SetValidator([this]
+ {
+ return !me->HasUnitState(UNIT_STATE_CASTING);
+ });
}
void InitializeAI() override
@@ -193,19 +189,16 @@ struct boss_taldaram : public BossAI
me->SetImmuneToAll(true);
me->SetDisableGravity(true);
me->SetHover(true);
+
if (!me->HasAura(SPELL_BEAM_VISUAL))
- {
DoCastSelf(SPELL_BEAM_VISUAL, true);
- }
me->SummonCreatureGroup(SUMMON_GROUP_TRIGGERS);
return;
}
if (instance->GetPersistentData(DATA_TELDRAM_SPHERE1) == DONE && instance->GetPersistentData(DATA_TELDRAM_SPHERE2) == DONE)
- {
DoAction(ACTION_REMOVE_PRISON_AT_RESET);
- }
}
void Reset() override
@@ -213,7 +206,45 @@ struct boss_taldaram : public BossAI
_Reset();
vanishDamage = 0;
- vanishTarget_GUID.Clear();
+
+ me->SetReactState(REACT_AGGRESSIVE);
+
+ ScheduleHealthCheckEvent({ 75, 50, 25 }, [&] {
+ scheduler.Schedule(1s, [this](TaskContext)
+ {
+ Talk(SAY_VANISH);
+ DoCastSelf(SPELL_VANISH);
+ me->SetReactState(REACT_PASSIVE);
+ me->GetMotionMaster()->Clear();
+ DoStopAttack();
+ scheduler.CancelGroup(GROUP_COMBAT_ABILITIES);
+ });
+ });
+ }
+
+ void OnAuraRemove(AuraApplication* auraApp, AuraRemoveMode mode) override
+ {
+ if (auraApp->GetBase()->GetId() == SPELL_VANISH && mode == AURA_REMOVE_BY_EXPIRE)
+ {
+ scheduler.Schedule(2500ms, [this](TaskContext)
+ {
+ if (Unit* vanishTarget = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
+ {
+ vanishDamage = 0;
+ DoCast(vanishTarget, SPELL_SHADOWSTEP);
+ DoCast(vanishTarget, SPELL_EMBRACE_OF_THE_VAMPYR);
+ }
+
+ me->m_Events.AddEventAtOffset([&] {
+ if (!scheduler.IsGroupScheduled(GROUP_COMBAT_ABILITIES))
+ {
+ ScheduleCombatEvents();
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->ResumeChasingVictim();
+ }
+ }, 20s);
+ });
+ }
}
void DoAction(int32 action) override
@@ -256,20 +287,20 @@ struct boss_taldaram : public BossAI
}
}
- void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*school*/) override
+ void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageType, SpellSchoolMask school) override
{
- if (vanishTarget_GUID)
+ BossAI::DamageTaken(attacker, damage, damageType, school);
+
+ if (me->FindCurrentSpellBySpellId(SPELL_EMBRACE_OF_THE_VAMPYR))
{
- if (me->FindCurrentSpellBySpellId(SPELL_EMBRACE_OF_THE_VAMPYR))
+ vanishDamage += damage;
+ if (vanishDamage >= DUNGEON_MODE(MAX_EMBRACE_DMG, MAX_EMBRACE_DMG_H))
{
- vanishDamage += damage;
- if (vanishDamage >= DUNGEON_MODE(MAX_EMBRACE_DMG, MAX_EMBRACE_DMG_H))
- {
- ScheduleCombatEvents();
- me->CastStop();
- vanishTarget_GUID.Clear();
- vanishDamage = 0;
- }
+ ScheduleCombatEvents();
+ me->CastStop();
+ me->ResumeChasingVictim();
+ me->SetReactState(REACT_AGGRESSIVE);
+ vanishDamage = 0;
}
}
}
@@ -283,17 +314,9 @@ struct boss_taldaram : public BossAI
void KilledUnit(Unit* victim) override
{
if (!victim->IsPlayer())
- {
return;
- }
Talk(SAY_SLAY);
-
- if (vanishTarget_GUID && victim->GetGUID() == vanishTarget_GUID)
- {
- vanishTarget_GUID.Clear();
- vanishDamage = 0;
- }
}
void JustEngagedWith(Unit* /*who*/) override
@@ -309,9 +332,7 @@ struct boss_taldaram : public BossAI
void SpellHitTarget(Unit* /*target*/, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_CONJURE_FLAME_SPHERE)
- {
summons.DoAction(ACTION_SPHERE);
- }
}
void JustSummoned(Creature* summon) override
@@ -324,9 +345,7 @@ struct boss_taldaram : public BossAI
case NPC_FLAME_SPHERE_3:
{
if (npc_taldaram_flamesphere* summonAI = dynamic_cast(summon->AI()))
- {
summonAI->SetVictimPos(victimSperePos);
- }
break;
}
@@ -338,130 +357,27 @@ struct boss_taldaram : public BossAI
}
}
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- {
- return;
- }
-
- events.Update(diff);
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- {
- return;
- }
-
- while (uint32 const eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_PRINCE_BLOODTHIRST:
- {
- DoCastVictim(SPELL_BLOODTHIRST);
- events.Repeat(10s);
- break;
- }
- case EVENT_PRINCE_FLAME_SPHERES:
- {
- if (Unit* victim = me->GetVictim())
- {
- DoCast(victim, SPELL_CONJURE_FLAME_SPHERE);
- victimSperePos = victim->GetPosition();
- }
-
- if (!events.GetNextEventTime(EVENT_PRINCE_VANISH))
- {
- events.RescheduleEvent(EVENT_PRINCE_VANISH, 14s);
- }
- else
- {
- // Make sure that Vanish won't get triggered at same time as sphere summon
- events.DelayEvents(4s);
- }
-
- events.Repeat(15s);
- break;
- }
- case EVENT_PRINCE_VANISH:
- {
- //Count alive players
- uint8 count = 0;
- std::list const t_list = me->GetThreatMgr().GetThreatList();
- if (!t_list.empty())
- {
- for (HostileReference const* reference : t_list)
- {
- if (reference)
- {
- Unit const* pTarget = ObjectAccessor::GetUnit(*me, reference->getUnitGuid());
- if (pTarget && pTarget->IsPlayer() && pTarget->IsAlive())
- {
- ++count;
- }
- }
- }
- }
-
- // He only vanishes if there are 3 or more alive players
- if (count > 2)
- {
- Talk(SAY_VANISH);
- DoCastSelf(SPELL_VANISH, false);
- if (Unit* pEmbraceTarget = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
- {
- vanishTarget_GUID = pEmbraceTarget->GetGUID();
- }
-
- events.CancelEvent(EVENT_PRINCE_FLAME_SPHERES);
- events.CancelEvent(EVENT_PRINCE_BLOODTHIRST);
- events.ScheduleEvent(EVENT_PRINCE_VANISH_RUN, 2499ms);
- }
- break;
- }
- case EVENT_PRINCE_VANISH_RUN:
- {
- if (Unit* _vanishTarget = ObjectAccessor::GetUnit(*me, vanishTarget_GUID))
- {
- vanishDamage = 0;
- DoCast(_vanishTarget, SPELL_SHADOWSTEP);
- me->CastSpell(_vanishTarget, SPELL_EMBRACE_OF_THE_VAMPYR, false);
- me->RemoveAura(SPELL_VANISH);
- }
-
- events.ScheduleEvent(EVENT_PRINCE_RESCHEDULE, 20s);
- break;
- }
- case EVENT_PRINCE_RESCHEDULE:
- {
- ScheduleCombatEvents();
- break;
- }
- }
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- {
- return;
- }
- }
-
- if (me->IsVisible())
- {
- DoMeleeAttackIfReady();
- }
- }
-
private:
Position victimSperePos;
- ObjectGuid vanishTarget_GUID;
uint32 vanishDamage;
void ScheduleCombatEvents()
{
- events.Reset();
- events.RescheduleEvent(EVENT_PRINCE_FLAME_SPHERES, 10s);
- events.RescheduleEvent(EVENT_PRINCE_BLOODTHIRST, 10s);
- vanishTarget_GUID.Clear();
+ scheduler.Schedule(10s, GROUP_COMBAT_ABILITIES, [this](TaskContext context)
+ {
+ DoCastVictim(SPELL_BLOODTHIRST);
+ context.Repeat(15s);
+ }).Schedule(10s, GROUP_COMBAT_ABILITIES, [this](TaskContext context)
+ {
+ if (Unit* victim = me->GetVictim())
+ {
+ DoCast(victim, SPELL_CONJURE_FLAME_SPHERE);
+ victimSperePos = victim->GetPosition();
+ }
+
+ context.Repeat(15s);
+ });
+
vanishDamage = 0;
}
};
diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp
index 8841b63dd..d1d371710 100644
--- a/src/server/scripts/Spells/spell_dk.cpp
+++ b/src/server/scripts/Spells/spell_dk.cpp
@@ -74,6 +74,9 @@ enum DeathKnightSpells
SPELL_DK_UNHOLY_PRESENCE_TRIGGERED = 49772,
SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189,
SPELL_DK_WILL_OF_THE_NECROPOLIS_AURA_R1 = 52284,
+ SPELL_DK_ICY_TALONS_TALENT_R1 = 50880,
+ SPELL_DK_CRYPT_FEVER_R1 = 50508,
+ SPELL_DK_EBON_PLAGUE_R1 = 51726,
// Risen Ally
SPELL_DK_RAISE_ALLY = 46619,
SPELL_DK_THRASH = 47480,
@@ -342,7 +345,7 @@ class spell_dk_death_and_decay_aura : public AuraScript
if (GetCaster() && GetTarget())
{
int32 basePoints0 = aurEff->GetAmount();
- GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, true, 0, aurEff);
+ GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, false, 0, aurEff);
}
}
@@ -1746,7 +1749,8 @@ class spell_dk_pestilence : public SpellScript
{
SPELL_DK_GLYPH_OF_DISEASE,
SPELL_DK_BLOOD_PLAGUE,
- SPELL_DK_FROST_FEVER
+ SPELL_DK_FROST_FEVER,
+ SPELL_DK_ICY_TALONS_TALENT_R1
});
}
@@ -1758,46 +1762,36 @@ class spell_dk_pestilence : public SpellScript
if (!target)
return;
- if (target != hitUnit || caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE))
+ // Spread on others
+ if (target != hitUnit)
{
- // xinef: checked in target selection
- //if (!m_targets.GetUnitTarget()->IsWithinLOSInMap(unitTarget))
- // return;
-
- // And spread them on target
// Blood Plague
- if (Aura* disOld = target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
- if (AuraEffect* effOld = disOld->GetEffect(EFFECT_0))
- {
- float pctMods = effOld->GetPctMods();
- float crit = effOld->GetCritChance();
- caster->CastSpell(hitUnit, SPELL_DK_BLOOD_PLAGUE, true);
-
- if (Aura* disNew = hitUnit->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
- if (AuraEffect* effNew = disNew->GetEffect(EFFECT_0))
- {
- effNew->SetPctMods(pctMods);
- effNew->SetCritChance(crit);
- effNew->SetAmount(effNew->CalculateAmount(effNew->GetCaster()));
- }
- }
+ if (target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
+ caster->CastSpell(hitUnit, SPELL_DK_BLOOD_PLAGUE, true);
// Frost Fever
- if (Aura* disOld = target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
- if (AuraEffect* effOld = disOld->GetEffect(EFFECT_0))
- {
- float pctMods = effOld->GetPctMods();
- float crit = effOld->GetCritChance();
- caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true);
+ if (target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
+ caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true);
+ }
+ // Refresh on target
+ else if (caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE))
+ {
+ // Blood Plague
+ if (Aura* disease = target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID()))
+ disease->RefreshDuration();
- if (Aura* disNew = hitUnit->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
- if (AuraEffect* effNew = disNew->GetEffect(EFFECT_0))
- {
- effNew->SetPctMods(pctMods);
- effNew->SetCritChance(crit);
- effNew->SetAmount(effNew->CalculateAmount(effNew->GetCaster()));
- }
- }
+ // Frost Fever
+ if (Aura* disease = target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID()))
+ {
+ disease->RefreshDuration();
+ if (Aura const* talons = caster->GetAuraOfRankedSpell(SPELL_DK_ICY_TALONS_TALENT_R1))
+ caster->CastSpell(caster, talons->GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true);
+ }
+
+ if (Aura* disease = target->GetAuraOfRankedSpell(SPELL_DK_EBON_PLAGUE_R1, caster->GetGUID()))
+ disease->RefreshDuration();
+ else if (Aura* disease = target->GetAuraOfRankedSpell(SPELL_DK_CRYPT_FEVER_R1, caster->GetGUID()))
+ disease->RefreshDuration();
}
}
diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp
index 0813b9eca..ededde81e 100644
--- a/src/server/scripts/Spells/spell_druid.cpp
+++ b/src/server/scripts/Spells/spell_druid.cpp
@@ -60,6 +60,7 @@ enum DruidSpells
SPELL_DRUID_ENRAGE = 5229,
SPELL_DRUID_ENRAGED_DEFENSE = 70725,
SPELL_DRUID_ITEM_T10_FERAL_4P_BONUS = 70726,
+ SPELL_DRUID_MOONGLADE_2P_BONUS = 37286
};
enum DruidIcons
@@ -1194,6 +1195,60 @@ class spell_dru_moonkin_form_passive_proc : public AuraScript
}
};
+// -774 - Rejuvenation
+class spell_dru_rejuvenation_moonglade_2_set : public AuraScript
+{
+ PrepareAuraScript(spell_dru_rejuvenation_moonglade_2_set);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ return ValidateSpellInfo({ SPELL_DRUID_MOONGLADE_2P_BONUS });
+ }
+
+ bool Load() override
+ {
+ _casterGUID.Clear();
+ return true;
+ }
+
+ void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Player* caster = ObjectAccessor::FindPlayer(GetCasterGUID()))
+ if (caster->HasAura(SPELL_DRUID_MOONGLADE_2P_BONUS))
+ {
+ Player* target = GetTarget()->ToPlayer();
+ if (!target)
+ return;
+
+ _casterGUID = GetCasterGUID();
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_DRUID_MOONGLADE_2P_BONUS);
+ target->ApplyRatingMod(CR_DODGE, spellInfo->Effects[EFFECT_0].CalcValue(), true); // 35 rating
+ }
+ }
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (_casterGUID)
+ {
+ Player* target = GetTarget()->ToPlayer();
+ if (!target)
+ return;
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_DRUID_MOONGLADE_2P_BONUS);
+ target->ApplyRatingMod(CR_DODGE, spellInfo->Effects[EFFECT_0].CalcValue(), false); // 35 rating
+ }
+ }
+
+ void Register() override
+ {
+ AfterEffectApply += AuraEffectApplyFn(spell_dru_rejuvenation_moonglade_2_set::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_dru_rejuvenation_moonglade_2_set::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
+ }
+
+private:
+ ObjectGuid _casterGUID;
+};
+
void AddSC_druid_spell_scripts()
{
RegisterSpellScript(spell_dru_bear_form_passive);
@@ -1229,4 +1284,5 @@ void AddSC_druid_spell_scripts()
RegisterSpellScript(spell_dru_t10_restoration_4p_bonus);
RegisterSpellScript(spell_dru_wild_growth);
RegisterSpellScript(spell_dru_moonkin_form_passive_proc);
+ RegisterSpellScript(spell_dru_rejuvenation_moonglade_2_set);
}
diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp
index faf4af9be..7552b5029 100644
--- a/src/server/scripts/Spells/spell_warrior.cpp
+++ b/src/server/scripts/Spells/spell_warrior.cpp
@@ -910,7 +910,7 @@ class spell_warr_heroic_strike : public SpellScript
Unit* target = GetHitUnit();
if (!target)
return;
- std::list AuraEffectList = target->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED);
+ Unit::AuraEffectList const& AuraEffectList = target->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED);
bool bonusDamage = false;
for (AuraEffect* eff : AuraEffectList)
{
diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h
index 573e9ed12..8b75e97dc 100644
--- a/src/server/shared/SharedDefines.h
+++ b/src/server/shared/SharedDefines.h
@@ -465,7 +465,7 @@ enum SpellAttr2 : uint32
SPELL_ATTR2_ALWAYS_CAST_AS_UNIT = 0x00000200, // TITLE Unknown attribute 9@Attr2
SPELL_ATTR2_SPECIAL_TAMING_FLAG = 0x00000400, // TITLE Unknown attribute 10@Attr2 DESCRIPTION Related to taming?
SPELL_ATTR2_NO_TARGET_PER_SECOND_COST = 0x00000800, // TITLE Health Funnel
- SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Unknown attribute 12@Attr2 DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe
+ SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Chain from caster DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe
SPELL_ATTR2_ENCHANT_OWN_ITEM_ONLY = 0x00002000, // TITLE Enchant persists when entering arena
SPELL_ATTR2_ALLOW_WHILE_INVISIBLE = 0x00004000, // TITLE Unknown attribute 14@Attr2
SPELL_ATTR2_DO_NOT_CONSUME_IF_GAINED_DURING_CAST = 0x00008000, // TITLE Unused attribute 15@Attr2 DESCRIPTION not set in 3.3.5a