Merge remote-tracking branch 'acore/HEAD' into test-staging

This commit is contained in:
Keleborn
2026-06-12 15:33:01 -07:00
34 changed files with 1015 additions and 90 deletions
+1
View File
@@ -2,6 +2,7 @@
# AzerothCore # AzerothCore
# #
.cache
/conf/* /conf/*
!/conf/dist !/conf/dist
/modules/* /modules/*
@@ -0,0 +1,3 @@
-- DB update 2026_06_05_00 -> 2026_06_08_00
-- Remove the XP gains from "Risen Alliance Soldier" and "Icy Ghoul"
UPDATE `creature_template` SET `ExperienceModifier` = 0 WHERE `entry` IN (31205, 31142);
@@ -0,0 +1,4 @@
-- DB update 2026_06_08_00 -> 2026_06_08_01
-- Reverts changes from https://github.com/azerothcore/azerothcore-wotlk/pull/26112
-- Adds back the ExperienceModifier and Adds the correct flag "CREATURE_FLAG_EXTRA_NO_XP" for "Risen Alliance Soldier" and "Icy Ghoul"
UPDATE `creature_template` SET `ExperienceModifier` = 1, `flags_extra` = `flags_extra` | 64 WHERE `entry` IN (31205, 31142);
@@ -0,0 +1,14 @@
-- DB update 2026_06_08_01 -> 2026_06_10_00
-- Stoneclaw Totem pulses should not break stealth (Patch 3.2.2).
DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (5729, 6393, 6394, 6395, 10423, 10424, 25512, 58586, 58587, 58588);
INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES
(5729, 64),
(6393, 64),
(6394, 64),
(6395, 64),
(10423, 64),
(10424, 64),
(25512, 64),
(58586, 64),
(58587, 64),
(58588, 64);
@@ -0,0 +1,3 @@
-- DB update 2026_06_10_00 -> 2026_06_10_01
-- Earthgrab is a hostile root and should reveal stealthed targets.
DELETE FROM `spell_custom_attr` WHERE `spell_id` = 64695;
@@ -0,0 +1,4 @@
-- DB update 2026_06_10_01 -> 2026_06_10_02
--
DELETE FROM `spell_script_names` WHERE `spell_id` = 62976 AND `ScriptName` = 'spell_thorim_lightning_pillar_P2';
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (62976, 'spell_thorim_lightning_pillar_P2');
@@ -0,0 +1,15 @@
-- DB update 2026_06_10_02 -> 2026_06_10_03
DELETE FROM `waypoint_data` WHERE `id` = 253190;
INSERT INTO `waypoint_data`
(`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`)
VALUES
(253190, 1, 1735.393, 593.6747, 140.5267, NULL, 0, 0, 1, 1, 0, 100),
(253190, 2, 1746.861, 621.0573, 141.2209, NULL, 0, 0, 1, 1, 0, 100),
(253190, 3, 1734.7, 663.6804, 139.3877, NULL, 0, 0, 1, 1, 0, 100),
(253190, 4, 1695.096, 677.7586, 139.9433, NULL, 0, 0, 1, 1, 0, 100),
(253190, 5, 1663.575, 664.397, 139.952, NULL, 0, 0, 1, 1, 0, 100),
(253190, 6, 1651.432, 634.5519, 140.1377, NULL, 0, 0, 1, 1, 0, 100),
(253190, 7, 1666.394, 594.8594, 139.1098, NULL, 0, 0, 1, 1, 0, 100),
(253190, 8, 1704.55, 579.4996, 140.3171, NULL, 0, 0, 1, 1, 0, 100);
UPDATE `creature_template` SET `speed_run` = 1.71429, `flags_extra` = `flags_extra` | 512 WHERE `entry` = 25319;
@@ -0,0 +1,3 @@
-- DB update 2026_06_10_03 -> 2026_06_11_00
UPDATE `reference_loot_template` SET `Item` = 49667 WHERE `Item` = 48679 AND `entry` = 10016;
UPDATE `conditions` SET `SourceEntry` = 49667, `Comment` = 'Only drop "Waterlogged Recipe" (49667) from "Bag of Fishing Treasures" (46007) if the player has at least 300 cooking skill' WHERE (`SourceTypeOrReferenceId` = 10) AND (`SourceGroup` = 10016) AND (`SourceEntry` = 48679) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 7) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 185) AND (`ConditionValue2` = 300) AND (`ConditionValue3` = 0);
+110
View File
@@ -0,0 +1,110 @@
-- DB update 2026_06_11_00 -> 2026_06_11_01
--
-- https://www.wowhead.com/quest=24743/shadows-edge
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24743 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24743, 'esMX', 'Escucha bien, $c. Al aceptar esta arma, tu destino está sellado.$B$BVencer o sucumbir.$B$BHe puesto tus pies en este camino. Por lo tanto, eres mi responsabilidad personal. Si fallas, tengo el deber de liberarte de esta vida.$B$BRecuerda mis palabras, $r, y no falles.', 0),
(24743, 'esES', 'Escucha bien, $c. Al aceptar esta arma, tu destino está sellado.$B$BVencer o sucumbir.$B$BHe puesto tus pies en este camino. Por lo tanto, eres mi responsabilidad personal. Si fallas, tengo el deber de liberarte de esta vida.$B$BRecuerda mis palabras, $r, y no falles.', 0);
-- https://www.wowhead.com/quest=24547/a-feast-of-souls
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24547 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24547, 'esMX', '<El Alto Señor Mograine te observa en silencio por un momento.>$b$bParece que has soportado esta etapa de tu prueba admirablemente, $r.$b$bReza para que el destino siga sonriéndote en tu esfuerzo...', 0),
(24547, 'esES', '<El Alto Señor Mograine te observa en silencio por un momento.>$b$bParece que has soportado esta etapa de tu prueba admirablemente, $r.$b$bReza para que el destino siga sonriéndote en tu esfuerzo...', 0);
-- https://www.wowhead.com/quest=24749/unholy-infusion
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24749 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24749, 'esMX', 'Bien hecho, $c.$b$bLa finalización de nuestro trabajo se acerca.', 0),
(24749, 'esES', 'Bien hecho, $c.$b$bLa finalización de nuestro trabajo se acerca.', 0);
-- https://www.wowhead.com/quest=24756/blood-infusion
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24756 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24756, 'esMX', 'Bien hecho, $c.$b$bAgonía de Sombras está casi a nuestro alcance.', 0),
(24756, 'esES', 'Bien hecho, $c.$b$bAgonía de Sombras está casi a nuestro alcance.', 0);
-- https://www.wowhead.com/quest=24757/frost-infusion
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24757 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24757, 'esMX', 'El hacha ha bebido hasta saciarse, $r, y la forja está casi lista para la creación de Agonía de Sombras...', 0),
(24757, 'esES', 'El hacha ha bebido hasta saciarse, $r, y la forja está casi lista para la creación de Agonía de Sombras...', 0);
-- https://www.wowhead.com/quest=24548/the-splintered-throne
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24548 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24548, 'esMX', 'Esto debería ser suficiente, $c.$b$bHáblame cuando Filo de la Sombra esté completamente potenciado e intentaré completarlo.', 0),
(24548, 'esES', 'Esto debería ser suficiente, $c.$b$bHáblame cuando Filo de la Sombra esté completamente potenciado e intentaré completarlo.', 0);
DELETE FROM `quest_request_items_locale` WHERE `ID` = 24548 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_request_items_locale` (`ID`, `locale`, `CompletionText`, `VerifiedBuild`) VALUES
(24548, 'esMX', '¿Has conseguido los fragmentos del trono helado, $C?', 0),
(24548, 'esES', '¿Has conseguido los fragmentos del trono helado, $C?', 0);
-- https://www.wowhead.com/quest=24912/empowerment
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24912 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24912, 'esMX', 'Manténgase alejado, $c, y manténgase estable.', 0),
(24912, 'esES', 'Manténgase alejado, $c, y manténgase estable.', 0);
-- https://www.wowhead.com/quest=24549/shadowmourne
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24549 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24549, 'esMX', 'Enhorabuena, $c.$b$bA pesar de todo lo contrario, has superado la tormenta.$b$bConfío en que encontrarás una recompensa proporcional a tu convicción...', 0),
(24549, 'esES', 'Enhorabuena, $c.$b$bA pesar de todo lo contrario, has superado la tormenta.$b$bConfío en que encontrarás una recompensa proporcional a tu convicción...', 0);
-- https://www.wowhead.com/quest=24748/the-lich-kings-last-stand
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24748 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24748, 'esMX', 'Así que, por fin, está hecho.$b$bQue la gente de Azeroth recuerde nuestras hazañas aquí para siempre. Que los sacrificios de tantos ardan en sus corazones eternamente. Que nunca dejen de arriesgarse al peligro mortal ante un gran mal.', 0),
(24748, 'esES', 'Así que, por fin, está hecho.$b$bQue la gente de Azeroth recuerde nuestras hazañas aquí para siempre. Que los sacrificios de tantos ardan en sus corazones eternamente. Que nunca dejen de arriesgarse al peligro mortal ante un gran mal.', 0);
-- https://www.wowhead.com/quest=24914/personal-property
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24914 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24914, 'esMX', '<Después de trazar un patrón complejo de runas en cada una de las superficies de la caja, aparece el contorno de una tapa y un clic resuena en la cámara.>$B$BAquí tienes, $R.', 0),
(24914, 'esES', '<Después de trazar un patrón complejo de runas en cada una de las superficies de la caja, aparece el contorno de una tapa y un clic resuena en la cámara.>$B$BAquí tienes, $R.', 0);
-- https://www.wowhead.com/quest=24915/mograines-reunion
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24915 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24915, 'esMX', 'Imposible...$B$B¡Padre, ¿eres tú?!$B$BMe has hecho un gran favor, héroe. Te ofrezco las riendas de mi fiel corcel. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0),
(24915, 'esES', 'Imposible...$B$B¡Padre, ¿eres tú?!$B$BMe has hecho un gran favor, héroe. Te ofrezco las riendas de mi fiel corcel. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0);
DELETE FROM `quest_request_items_locale` WHERE `ID` = 24915 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_request_items_locale` (`ID`, `locale`, `CompletionText`, `VerifiedBuild`) VALUES
(24915, 'esMX', '¿Qué es eso que tienes, $c?$B$BMe llama, despertando sentimientos que creía muertos hace mucho tiempo.', 0),
(24915, 'esES', '¿Qué es eso que tienes, $c?$B$BMe llama, despertando sentimientos que creía muertos hace mucho tiempo.', 0);
-- https://www.wowhead.com/quest=24916/jainas-locket
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24916 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24916, 'esMX', '¿Cómo... cómo conseguiste esto?$B$BMe has hecho un gran favor, héroe. No podría soportar conservar este relicario, pero le pondré un encantamiento que te será útil. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0),
(24916, 'esES', '¿Cómo... cómo conseguiste esto?$B$BMe has hecho un gran favor, héroe. No podría soportar conservar este relicario, pero le pondré un encantamiento que te será útil. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0);
DELETE FROM `quest_request_items_locale` WHERE `ID` = 24916 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_request_items_locale` (`ID`, `locale`, `CompletionText`, `VerifiedBuild`) VALUES
(24916, 'esMX', 'Sí, $c. ¿Cómo puedo ayudarte?', 0),
(24916, 'esES', 'Sí, $c. ¿Cómo puedo ayudarte?', 0);
-- https://www.wowhead.com/quest=24917/muradins-lament
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24917 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24917, 'esMX', 'Sí. Conozco esta espada... y siempre la atesoraré: un instante que se perderá para siempre.$B$BMe has hecho un gran favor, héroe. Te ofrezco un regalo de los enanos nacidos en la escarcha. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0),
(24917, 'esES', 'Sí. Conozco esta espada... y siempre la atesoraré: un instante que se perderá para siempre.$B$BMe has hecho un gran favor, héroe. Te ofrezco un regalo de los enanos nacidos en la escarcha. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0);
DELETE FROM `quest_request_items_locale` WHERE `ID` = 24917 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_request_items_locale` (`ID`, `locale`, `CompletionText`, `VerifiedBuild`) VALUES
(24917, 'esMX', '¿Qué tienes ahí, $r?', 0),
(24917, 'esES', '¿Qué tienes ahí, $r?', 0);
-- https://www.wowhead.com/quest=24919/the-lightbringers-redemption
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 24919 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES
(24919, 'esMX', '<Uther mira la medalla, sin palabras por un momento.>$B$BLa medalla de Arthas.$B$B Recuerdo bien el día que se la entregué...$B$BMe has hecho un gran favor, héroe. Mi alma puede descansar en paz. Te ofrezco un recuerdo perdido en el tiempo. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0),
(24919, 'esES', '<Uther mira la medalla, sin palabras por un momento.>$B$BLa medalla de Arthas.$B$B Recuerdo bien el día que se la entregué...$B$BMe has hecho un gran favor, héroe. Mi alma puede descansar en paz. Te ofrezco un recuerdo perdido en el tiempo. Haz lo que quieras con él, pero no olvides a quienes te ayudaron en esta hazaña monumental.', 0);
DELETE FROM `quest_request_items_locale` WHERE `ID` = 24919 AND `locale` IN ('esMX', 'esES');
INSERT INTO `quest_request_items_locale` (`ID`, `locale`, `CompletionText`, `VerifiedBuild`) VALUES
(24919, 'esMX', '¿En qué puedo serle útil, $r?', 0),
(24919, 'esES', '¿En qué puedo serle útil, $r?', 0);
+587
View File
@@ -0,0 +1,587 @@
-- DB update 2026_06_11_01 -> 2026_06_11_02
--
DELETE FROM `spell_cone` WHERE `Id` > 0;
INSERT INTO `spell_cone` (`ID`, `ConeDegrees`) VALUES
(4520, 53),
(5010, 53),
(5106, 53),
(5708, 71),
(6749, 53),
(7342, 53),
(7769, 90),
(8374, 71),
(8435, 53),
(8873, 53),
(9573, 53),
(10733, 53),
(11019, 71),
(11082, 53),
(11828, 53),
(11837, 53),
(12252, 53),
(12278, 53),
(12533, 53),
(12549, 53),
(12557, 53),
(12611, 53),
(12882, 71),
(12884, 53),
(12887, 71),
(12891, 53),
(13184, 53),
(15244, 53),
(15847, -82),
(15851, 53),
(16054, 53),
(16094, 71),
(16099, 71),
(16104, 53),
(16169, 71),
(16340, 71),
(16350, 71),
(16359, 71),
(16390, 71),
(16396, 82),
(17283, 53),
(17294, 53),
(17446, 53),
(18144, 71),
(18435, 60),
(18500, 82),
(19630, 82),
(19641, 82),
(19813, 180),
(20299, 53),
(20667, 71),
(20712, 82),
(20716, 71),
(20717, 71),
(20828, 53),
(21071, 82),
(21099, 82),
(21189, 71),
(21333, 180),
(21559, 53),
(21910, 53),
(22479, 71),
(22539, 82),
(22558, 71),
(22559, 71),
(22560, 71),
(22561, 71),
(22642, 71),
(22741, 53),
(22746, 53),
(22833, 82),
(22945, 82),
(23011, 53),
(23023, 53),
(23339, 82),
(23364, -82),
(23461, 82),
(23919, 82),
(24011, 53),
(24192, 82),
(24334, 53),
(24682, 82),
(24818, 82),
(24821, 45),
(24822, 135),
(24823, 90),
(24835, 180),
(24836, 225),
(24837, 270),
(24838, 315),
(24839, 82),
(24933, 7),
(25029, 53),
(25030, 71),
(25034, 53),
(25145, 71),
(25149, 71),
(25150, 71),
(25322, 71),
(25653, -82),
(25814, 53),
(25993, 82),
(26029, 5),
(27604, 53),
(27807, 71),
(28127, 71),
(28157, 42),
(28168, 82),
(28405, 180),
(28438, 180),
(28459, 71),
(28741, 90),
(28783, 29),
(29221, 53),
(29477, 53),
(29717, 53),
(29905, 82),
(29935, 180),
(29939, 180),
(30043, 71),
(30056, 180),
(30095, 53),
(30210, 82),
(30213, 180),
(30418, 195),
(30469, 5),
(30527, 60),
(30732, 180),
(30761, 53),
(30815, -180),
(30913, 42),
(30926, 82),
(30930, 71),
(31306, 82),
(31409, 53),
(31473, 71),
(31478, 71),
(31485, 53),
(31661, 90),
(31914, 71),
(32227, 63),
(32228, 63),
(32914, 82),
(33128, 53),
(33551, 82),
(33678, 71),
(34072, 82),
(34154, 7),
(34240, 71),
(34261, 71),
(34267, 82),
(34268, 53),
(34325, 53),
(34359, 53),
(34583, 71),
(35236, 71),
(35250, 53),
(35311, 53),
(35351, 71),
(35953, 82),
(36039, 71),
(36249, 82),
(36252, 82),
(36307, 5),
(36486, 71),
(36489, 82),
(36617, 180),
(36619, 180),
(36693, 71),
(36694, 71),
(36695, 71),
(36696, 71),
(36697, 71),
(36698, 71),
(36699, 71),
(36700, 71),
(36820, 82),
(36876, 82),
(36921, 82),
(36951, 71),
(37012, 71),
(37142, 63),
(37143, 63),
(37147, 63),
(37149, 63),
(37150, 63),
(37220, 63),
(37265, 53),
(37289, 53),
(37317, 180),
(37319, 82),
(37337, 63),
(37339, 63),
(37345, 63),
(37348, 63),
(37406, 63),
(37413, 63),
(37433, 10),
(37453, 63),
(37454, 63),
(37459, 20),
(37461, 20),
(37488, 82),
(37498, 82),
(37502, 82),
(37624, 71),
(37638, 53),
(37672, 53),
(37730, 120),
(37734, 53),
(37779, 71),
(37918, 53),
(37919, 53),
(37993, 53),
(38052, 53),
(38110, 82),
(38145, 53),
(38163, 82),
(38309, 53),
(38354, 82),
(38367, 53),
(38369, 53),
(38384, 53),
(38458, 42),
(38468, 53),
(38491, 82),
(38524, 45),
(38576, 180),
(38644, 53),
(38737, 82),
(38761, 71),
(38783, 53),
(38810, 180),
(38811, 180),
(38814, 180),
(38973, 71),
(38974, 53),
(39033, 71),
(39034, 71),
(39036, 71),
(39037, 71),
(39049, 71),
(39128, 53),
(39144, 82),
(39222, 71),
(39263, 53),
(39385, 82),
(39583, 53),
(39631, 180),
(39686, 53),
(39692, 7),
(39942, 71),
(40077, 82),
(40083, 60),
(40088, 60),
(40100, 60),
(40457, 82),
(40508, 60),
(40562, 90),
(40595, 60),
(40599, 82),
(40631, 90),
(40840, 10),
(40904, 60),
(40939, 82),
(41063, 60),
(41068, 60),
(41168, 60),
(41172, 180),
(41438, 53),
(41444, 82),
(41445, 82),
(41545, 60),
(41572, 82),
(42143, 150),
(42584, 10),
(42638, 150),
(42669, 60),
(42723, 60),
(42835, 10),
(43050, 60),
(43140, 30),
(43207, 90),
(43210, 1),
(43215, 90),
(43294, 60),
(43582, 60),
(43954, 30),
(44037, 60),
(44273, 53),
(44799, 60),
(44804, 90),
(45065, 60),
(45122, -60),
(45150, 120),
(45385, 53),
(45467, 60),
(45761, 60),
(45856, 90),
(45859, 60),
(45860, 90),
(45968, 120),
(45969, 120),
(45970, 120),
(46150, 53),
(46158, 53),
(46186, 53),
(46236, 60),
(46293, 71),
(46294, 71),
(46295, 71),
(46296, 71),
(46297, 71),
(46298, 71),
(46299, 71),
(46300, 71),
(46442, 75),
(46802, 71),
(46968, 90),
(46984, 53),
(47250, 60),
(47251, 60),
(47563, 90),
(47593, 90),
(47921, 90),
(48096, 60),
(48133, 60),
(48260, 71),
(48623, 60),
(48642, 120),
(49106, 60),
(49197, 180),
(49398, 180),
(49475, 180),
(49668, 75),
(49692, 20),
(49701, 60),
(49703, 60),
(49725, 90),
(49728, 45),
(49761, 30),
(49860, 45),
(49862, 60),
(49867, 45),
(49870, 60),
(49884, 53),
(50155, -82),
(50269, 60),
(50283, 60),
(50302, 90),
(50337, 20),
(50348, 180),
(50350, 60),
(50409, 20),
(50505, 71),
(50652, 90),
(50653, 120),
(50676, 120),
(50677, 60),
(50990, 60),
(51144, 82),
(51218, 53),
(51219, 53),
(51337, 53),
(51338, 60),
(51415, 53),
(51640, 180),
(51678, 90),
(51768, 53),
(51785, 82),
(51817, 40),
(52201, 20),
(52316, 180),
(52331, 200),
(52531, 53),
(52640, 60),
(52720, 90),
(52835, 60),
(52870, 82),
(52973, 90),
(53167, 53),
(53363, 82),
(53399, 71),
(53472, 120),
(53643, 120),
(54107, 90),
(54109, 90),
(54122, 90),
(54282, 60),
(54326, 71),
(54364, 42),
(54383, 1),
(54400, 40),
(54682, 60),
(54819, 10),
(55087, 40),
(55138, 75),
(55429, 180),
(55626, 10),
(55636, 90),
(55696, -82),
(55697, -82),
(55765, 71),
(55814, 30),
(55856, 60),
(55909, 60),
(55916, 60),
(55918, 90),
(56032, 71),
(56090, 29),
(56272, 60),
(56524, 60),
(56748, 60),
(56861, 40),
(56908, 82),
(56910, 82),
(57051, 90),
(57076, 60),
(57091, 60),
(57385, 20),
(57412, 20),
(57477, 60),
(57495, 90),
(57570, 60),
(57652, 60),
(57728, 90),
(57741, 90),
(57801, 82),
(57828, 60),
(57962, 60),
(58112, 30),
(58446, 53),
(58463, 60),
(58688, 60),
(58690, 90),
(58852, 90),
(58947, 90),
(58956, 82),
(58957, 82),
(58977, 90),
(58993, 10),
(59004, 75),
(59015, 60),
(59018, 60),
(59126, 60),
(59141, 53),
(59152, 53),
(59197, 53),
(59258, 60),
(59271, 60),
(59281, 60),
(59283, 90),
(59433, 120),
(59434, 82),
(59469, 60),
(59514, 60),
(59622, 30),
(59692, 120),
(59706, 60),
(59709, 60),
(59792, 180),
(60072, 60),
(60206, 60),
(60287, 30),
(60502, 90),
(60903, 60),
(61090, 60),
(61125, 60),
(61146, 71),
(61391, 90),
(61407, 90),
(61673, 60),
(61757, 90),
(62308, 100),
(62315, 60),
(62339, 60),
(62345, 100),
(62355, 90),
(62414, 60),
(62415, 60),
(62466, 75),
(62522, 60),
(62553, 60),
(62625, 60),
(62645, 20),
(62654, 20),
(62882, 60),
(62883, 60),
(62888, 180),
(62936, 20),
(62973, 10),
(62974, 50),
(62991, 10),
(63226, 120),
(63247, 40),
(63293, 10),
(63317, 75),
(63387, 60),
(63473, 60),
(63541, 53),
(63551, 120),
(63568, 40),
(64019, 60),
(64021, 75),
(64147, 10),
(64319, 180),
(64348, 60),
(64352, 60),
(64422, 120),
(64531, 60),
(64532, 60),
(64536, 60),
(64537, 60),
(64619, 45),
(64645, 53),
(64649, 60),
(64655, 53),
(64688, 120),
(64716, 35),
(64720, 60),
(64721, 60),
(64825, 60),
(64847, 60),
(64871, 90),
(64944, 60),
(64988, 60),
(65016, 60),
(65112, 60),
(65242, 35),
(65248, 71),
(65260, 60),
(65919, 50),
(66184, 10),
(66513, 60),
(66514, 35),
(66689, 60),
(66819, 60),
(66820, 60),
(67028, 71),
(67038, 60),
(67441, 60),
(67575, 35),
(67576, 35),
(67577, 60),
(67796, 90),
(67798, 90),
(68607, 30),
(68778, 60),
(68867, -82),
(68873, 5),
(68948, 30),
(69242, 60),
(69507, 25),
(69527, 71),
(69649, 75),
(70169, 60),
(70176, 60),
(70367, 71),
(70461, 20),
(70512, 60),
(70513, 60),
(70633, 15),
(70670, 60),
(71029, 20),
(71250, 180),
(71369, -82),
(71386, 60),
(71807, 60),
(72133, 25),
(72149, 90),
(72848, 60),
(73061, 75),
(73740, 90),
(74117, 180),
(74403, 60),
(74441, 35),
(74525, 60),
(74531, 82),
(74806, 60),
(75126, 60),
(75343, 90),
(75417, 90),
(76560, 40);
@@ -0,0 +1,5 @@
-- DB update 2026_06_11_02 -> 2026_06_12_00
-- DB update 2026_06_11_02 -> 2026_06_11_03
-- Spell (ID: 30213) was added to spell_cone in 2026_06_11_02 but does not have
-- a cone implicit target, causing a server warning on startup.
DELETE FROM `spell_cone` WHERE `ID` = 30213;
@@ -0,0 +1,6 @@
-- DB update 2026_06_12_00 -> 2026_06_12_01
--
-- Remove "We made it! Thank you for getting me out of that hell hole. Tell Hemet to expect me!"
DELETE FROM `creature_text` WHERE (`CreatureID` = 28787) AND `GroupID` = 0 AND `ID` = 0;
-- Remove "Let's get the hell out of here"
DELETE FROM `creature_text` WHERE (`CreatureID` = 28787) AND `GroupID` = 6 AND `ID` = 0;
@@ -0,0 +1,4 @@
-- DB update 2026_06_12_01 -> 2026_06_12_02
DELETE FROM `creature_loot_template` WHERE `Entry` = 31610 AND `Item` = 41796;
INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES
(31610, 41796, 0, 100, 0, 1, 0, 1, 1, 'Anub\'arak (1) - Design: Infused Twilight Opal');
@@ -4393,13 +4393,16 @@ PreserveCustomChannels = 0
PreserveCustomChannelDuration = 14 PreserveCustomChannelDuration = 14
# #
# ChatFilter # ChatFilter.Whisper / ChatFilter.Say / ChatFilter.Yell / ChatFilter.Emote
# Description: Blocks chat messages when they contain any entry from the `chat_filter` # Description: Block chat messages that contain any entry from the `chat_filter` table
# table (substring, case-insensitive). # (substring match, case-insensitive).
# Manage entries with .chatfilter add / remove / list and .reload chat_filter. # Manage entries with: .chatfilter add / .chatfilter remove / .chatfilter list
# Blizzlike is only whispers are enabled. # and .reload chat_filter.
# Default: 0 - (Disbaled) # Blizzlike: only Whisper filtering is enabled.
# 1 - (Enabled) # Default: ChatFilter.Whisper = 1 - (Enabled)
# ChatFilter.Say = 0 - (Disabled)
# ChatFilter.Yell = 0 - (Disabled)
# ChatFilter.Emote = 0 - (Disabled)
# #
ChatFilter.Whisper = 1 ChatFilter.Whisper = 1
+29 -6
View File
@@ -74,12 +74,13 @@ Battlefield::~Battlefield()
CapturePoints.clear(); CapturePoints.clear();
} }
void Battlefield::RemovePlayerFromTracking(ObjectGuid playerGuid) void Battlefield::RemovePlayerFromTracking(ObjectGuid playerGuid, bool removeFromQueue /*= true*/)
{ {
for (uint8 i = 0; i < PVP_TEAMS_COUNT; ++i) for (uint8 i = 0; i < PVP_TEAMS_COUNT; ++i)
{ {
InvitedPlayers[i].erase(playerGuid); InvitedPlayers[i].erase(playerGuid);
PlayersInQueue[i].erase(playerGuid); if (removeFromQueue)
PlayersInQueue[i].erase(playerGuid);
PlayersWillBeKick[i].erase(playerGuid); PlayersWillBeKick[i].erase(playerGuid);
Players[i].erase(playerGuid); Players[i].erase(playerGuid);
} }
@@ -96,7 +97,8 @@ void Battlefield::HandlePlayerEnterZone(Player* player, uint32 /*zone*/)
TryRejoinAfterLogout(player); // relog: auto-rejoin, skip invite below TryRejoinAfterLogout(player); // relog: auto-rejoin, skip invite below
// Xinef: do not invite players on taxi // Xinef: do not invite players on taxi
if (!player->IsInFlight()) // GMs are not invited to war (see InvitePlayerToWar), so skip queue and slot tracking too
if (!player->IsInFlight() && !player->IsGameMaster())
{ {
// If battle is started, // If battle is started,
// If not full of players > invite player to join the war // If not full of players > invite player to join the war
@@ -108,7 +110,7 @@ void Battlefield::HandlePlayerEnterZone(Player* player, uint32 /*zone*/)
else else
{ {
/// @todo: Send a packet to announce it to player /// @todo: Send a packet to announce it to player
PlayersWillBeKick[player->GetTeamId()][player->GetGUID()] = GameTime::GetGameTime().count() + (player->IsGameMaster() ? 30 * MINUTE : 10); PlayersWillBeKick[player->GetTeamId()][player->GetGUID()] = GameTime::GetGameTime().count() + 10;
InvitePlayerToQueue(player); InvitePlayerToQueue(player);
} }
} }
@@ -152,7 +154,7 @@ void Battlefield::HandlePlayerLeaveZone(Player* player, uint32 /*zone*/)
for (BfCapturePoint* cp : CapturePoints) for (BfCapturePoint* cp : CapturePoints)
cp->HandlePlayerLeave(player); cp->HandlePlayerLeave(player);
RemovePlayerFromTracking(player->GetGUID()); RemovePlayerFromTracking(player->GetGUID(), /*removeFromQueue=*/false);
SendRemoveWorldStates(player); SendRemoveWorldStates(player);
RemovePlayerFromResurrectQueue(player->GetGUID()); RemovePlayerFromResurrectQueue(player->GetGUID());
OnPlayerLeaveZone(player); OnPlayerLeaveZone(player);
@@ -193,7 +195,7 @@ bool Battlefield::Update(uint32 diff)
SendUpdateWorldStates(); SendUpdateWorldStates();
} }
_scheduler.Update(diff); _scheduler.Update();
bool objectiveChanged = false; bool objectiveChanged = false;
if (IsWarTime()) if (IsWarTime())
@@ -213,6 +215,9 @@ void Battlefield::InvitePlayersInZoneToQueue()
void Battlefield::InvitePlayerToQueue(Player* player) void Battlefield::InvitePlayerToQueue(Player* player)
{ {
if (player->IsGameMaster()) // GMs are not invited to war, so don't queue them either
return;
if (PlayersInQueue[player->GetTeamId()].count(player->GetGUID())) if (PlayersInQueue[player->GetTeamId()].count(player->GetGUID()))
return; return;
@@ -237,6 +242,10 @@ void Battlefield::InvitePlayersInZoneToWar()
{ {
ForEachPlayerInZone([this](Player* player) ForEachPlayerInZone([this](Player* player)
{ {
// Skip GMs: they are never invited, so they would linger in PlayersWillBeKick forever
if (player->IsGameMaster())
return;
if (IsPlayerInWarOrInvited(player)) if (IsPlayerInWarOrInvited(player))
return; return;
@@ -298,6 +307,20 @@ bool Battlefield::IsPlayerInWarOrInvited(Player* player) const
return PlayersInWar[teamId].count(player->GetGUID()) || InvitedPlayers[teamId].count(player->GetGUID()); return PlayersInWar[teamId].count(player->GetGUID()) || InvitedPlayers[teamId].count(player->GetGUID());
} }
bool Battlefield::IsPlayerInBattlefield(ObjectGuid guid) const
{
if (!IsWarTime())
return false;
// PlayersInWar is split into per-team sets, but a GUID alone does not indicate the team, so check both sets.
// This also stays correct if an OnBattlefieldPlayerJoinWar handler reassigned the team GetTeamId() returns.
for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team)
if (PlayersInWar[team].count(guid))
return true;
return false;
}
void Battlefield::KickAfkPlayers() void Battlefield::KickAfkPlayers()
{ {
ForEachPlayerInWar([this](Player* player) ForEachPlayerInWar([this](Player* player)
+4 -1
View File
@@ -365,6 +365,9 @@ public:
/// Returns the set of players actively fighting in the war (per team, read-only). /// Returns the set of players actively fighting in the war (per team, read-only).
GuidUnorderedSet const& GetPlayersInWarSet(TeamId teamId) const { return PlayersInWar[teamId]; } GuidUnorderedSet const& GetPlayersInWarSet(TeamId teamId) const { return PlayersInWar[teamId]; }
// Returns true if the player is enrolled as a participant in the currently active battle.
bool IsPlayerInBattlefield(ObjectGuid guid) const;
void DoPlaySoundToAll(uint32 soundId); void DoPlaySoundToAll(uint32 soundId);
void InvitePlayerToQueue(Player* player); void InvitePlayerToQueue(Player* player);
@@ -441,7 +444,7 @@ protected:
/// Returns true if the player is already tracked as actively in the war or invited to join it. /// Returns true if the player is already tracked as actively in the war or invited to join it.
bool IsPlayerInWarOrInvited(Player* player) const; bool IsPlayerInWarOrInvited(Player* player) const;
void RemovePlayerFromTracking(ObjectGuid playerGuid); void RemovePlayerFromTracking(ObjectGuid playerGuid, bool removeFromQueue = true);
// Player-iteration helpers: resolve each GUID to a live Player* and call fn(player). // Player-iteration helpers: resolve each GUID to a live Player* and call fn(player).
// Using templates avoids std::function overhead and works naturally with lambdas. // Using templates avoids std::function overhead and works naturally with lambdas.
+8 -13
View File
@@ -246,9 +246,13 @@ void Arena::EndBattleground(TeamId winnerTeamId)
trans->Append(stmt2); trans->Append(stmt2);
uint8 memberId = 0; uint8 memberId = 0;
for (auto const& [playerGuid, arenaLogEntryData] : ArenaLogEntries) for (auto& [playerGuid, arenaLogEntryData] : ArenaLogEntries)
{ {
auto const& score = PlayerScores.find(playerGuid.GetCounter()); auto const& score = PlayerScores.find(playerGuid.GetCounter());
// Update stats from PlayerScores if player is still in arena
if (score != PlayerScores.end())
arenaLogEntryData.SaveStats(score->second->GetDamageDone(), score->second->GetHealingDone(), score->second->GetKillingBlows());
stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_MEMBERSTATS); stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_MEMBERSTATS);
stmt2->SetData(0, fightId); stmt2->SetData(0, fightId);
stmt2->SetData(1, ++memberId); stmt2->SetData(1, ++memberId);
@@ -257,18 +261,9 @@ void Arena::EndBattleground(TeamId winnerTeamId)
stmt2->SetData(4, arenaLogEntryData.ArenaTeamId); stmt2->SetData(4, arenaLogEntryData.ArenaTeamId);
stmt2->SetData(5, arenaLogEntryData.Acc); stmt2->SetData(5, arenaLogEntryData.Acc);
stmt2->SetData(6, arenaLogEntryData.IP); stmt2->SetData(6, arenaLogEntryData.IP);
if (score != PlayerScores.end()) stmt2->SetData(7, arenaLogEntryData.DamageDone);
{ stmt2->SetData(8, arenaLogEntryData.HealingDone);
stmt2->SetData(7, score->second->GetDamageDone()); stmt2->SetData(9, arenaLogEntryData.KillingBlows);
stmt2->SetData(8, score->second->GetHealingDone());
stmt2->SetData(9, score->second->GetKillingBlows());
}
else
{
stmt2->SetData(7, 0);
stmt2->SetData(8, 0);
stmt2->SetData(9, 0);
}
trans->Append(stmt2); trans->Append(stmt2);
} }
@@ -1061,6 +1061,11 @@ void Battleground::RemovePlayerAtLeave(Player* player)
auto const& itr2 = PlayerScores.find(player->GetGUID().GetCounter()); auto const& itr2 = PlayerScores.find(player->GetGUID().GetCounter());
if (itr2 != PlayerScores.end()) if (itr2 != PlayerScores.end())
{ {
// Save stats to ArenaLogEntries before deleting score (for arena logging)
auto itr3 = ArenaLogEntries.find(player->GetGUID());
if (itr3 != ArenaLogEntries.end())
itr3->second.SaveStats(itr2->second->GetDamageDone(), itr2->second->GetHealingDone(), itr2->second->GetKillingBlows());
delete itr2->second; delete itr2->second;
PlayerScores.erase(itr2); PlayerScores.erase(itr2);
} }
@@ -259,11 +259,21 @@ public:
IP = ip; IP = ip;
} }
void SaveStats(uint32 damageDone, uint32 healingDone, uint32 killingBlows)
{
DamageDone = damageDone;
HealingDone = healingDone;
KillingBlows = killingBlows;
}
std::string Name{}; std::string Name{};
ObjectGuid::LowType Guid{0}; ObjectGuid::LowType Guid{0};
uint32 Acc{0}; uint32 Acc{0};
uint32 ArenaTeamId{0}; uint32 ArenaTeamId{0};
std::string IP{}; std::string IP{};
uint32 DamageDone{0};
uint32 HealingDone{0};
uint32 KillingBlows{0};
}; };
enum BGHonorMode enum BGHonorMode
@@ -1039,42 +1039,43 @@ void BattlegroundQueue::BattlegroundQueueAnnouncerUpdate(uint32 diff, Battlegrou
return; return;
} }
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED)) // Armed per-bracket timer drives both Timed mode and the deferred immediate
{ // announcement; the spam-window/Limit throttle gates only immediate mode.
uint32 qPlayers = 0; bool const isTimed = sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED);
if (_queueAnnouncementCrossfactioned) uint32 qPlayers = 0;
if (_queueAnnouncementCrossfactioned)
qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_CFBG);
else
qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_HORDE) + GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_ALLIANCE);
if (!qPlayers)
{
_queueAnnouncementTimer[bracket_id] = -1;
return;
}
if (_queueAnnouncementTimer[bracket_id] >= 0)
{
if (_queueAnnouncementTimer[bracket_id] <= static_cast<int32>(diff))
{ {
qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_CFBG); _queueAnnouncementTimer[bracket_id] = -1;
uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32) 80);
if (!isTimed && !sBGSpam->CanAnnounce(bg_template, bracket_id, q_min_level, qPlayers))
return;
auto bgName = bg_template->GetName();
uint32 MaxPlayers = GetMinPlayersPerTeam(bg_template, bracketEntry) * 2;
uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32) 80);
ChatHandler(nullptr).SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName.c_str(), q_min_level, q_max_level, qPlayers, MaxPlayers);
} }
else else
{ {
qPlayers = GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_HORDE) + GetPlayersCountInGroupsQueue(bracket_id, BG_QUEUE_NORMAL_ALLIANCE); _queueAnnouncementTimer[bracket_id] -= static_cast<int32>(diff);
}
if (!qPlayers)
{
_queueAnnouncementTimer[bracket_id] = -1;
return;
}
if (_queueAnnouncementTimer[bracket_id] >= 0)
{
if (_queueAnnouncementTimer[bracket_id] <= static_cast<int32>(diff))
{
_queueAnnouncementTimer[bracket_id] = -1;
auto bgName = bg_template->GetName();
uint32 MaxPlayers = GetMinPlayersPerTeam(bg_template, bracketEntry) * 2;
uint32 q_min_level = std::min(bracketEntry->minLevel, (uint32) 80);
uint32 q_max_level = std::min(bracketEntry->maxLevel, (uint32) 80);
ChatHandler(nullptr).SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName.c_str(), q_min_level, q_max_level, qPlayers, MaxPlayers);
}
else
{
_queueAnnouncementTimer[bracket_id] -= static_cast<int32>(diff);
}
} }
} }
} }
@@ -1145,12 +1146,10 @@ void BattlegroundQueue::SendMessageBGQueue(Player* leader, Battleground* bg, PvP
} }
else else
{ {
if (!sBGSpam->CanAnnounce(leader, bg, q_min_level, qTotal)) // Arm the per-bracket debounce; first join arms, rest are no-ops.
{ // BattlegroundQueueAnnouncerUpdate emits the aggregated line later.
return; if (_queueAnnouncementTimer[bracketId] < 0)
} SetQueueAnnouncementTimer(bracketId, BG_QUEUE_ANNOUNCER_IMMEDIATE_DEBOUNCE, false);
ChatHandler(nullptr).SendWorldTextOptional(LANG_BG_QUEUE_ANNOUNCE_WORLD, ANNOUNCER_FLAG_DISABLE_BG_QUEUE, bgName.c_str(), q_min_level, q_max_level, qAlliance + qHorde, MaxPlayers);
} }
} }
} }
@@ -27,6 +27,10 @@
constexpr auto COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME = 10; constexpr auto COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME = 10;
// Immediate-mode announcer debounce (ms): fires on the next periodic pass, by
// which point a same-tick queue burst has collapsed into one aggregated line.
constexpr int32 BG_QUEUE_ANNOUNCER_IMMEDIATE_DEBOUNCE = 1;
struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!) struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!)
{ {
GuidSet Players; // player guid set GuidSet Players; // player guid set
@@ -25,6 +25,7 @@
namespace namespace
{ {
std::unordered_map<ObjectGuid /*player guid*/, uint32 /*time*/> _players; std::unordered_map<ObjectGuid /*player guid*/, uint32 /*time*/> _players;
std::unordered_map<uint32 /*bracket+bg key*/, uint32 /*time*/> _brackets;
void AddTime(ObjectGuid guid) void AddTime(ObjectGuid guid)
{ {
@@ -47,6 +48,26 @@ namespace
// Skip if spam time < 30 secs (default) // Skip if spam time < 30 secs (default)
return GameTime::GetGameTime().count() - GetTime(guid) >= sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY); return GameTime::GetGameTime().count() - GetTime(guid) >= sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY);
} }
void AddTime(uint32 key)
{
_brackets.insert_or_assign(key, GameTime::GetGameTime().count());
}
uint32 GetTime(uint32 key)
{
auto const& itr = _brackets.find(key);
if (itr != _brackets.end())
return itr->second;
return 0;
}
bool IsCorrectDelay(uint32 key)
{
// Skip if spam time < 30 secs (default)
return GameTime::GetGameTime().count() - GetTime(key) >= sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY);
}
} }
BGSpamProtect* BGSpamProtect::instance() BGSpamProtect* BGSpamProtect::instance()
@@ -84,3 +105,29 @@ bool BGSpamProtect::CanAnnounce(Player* player, Battleground* bg, uint32 minLeve
AddTime(guid); AddTime(guid);
return true; return true;
} }
bool BGSpamProtect::CanAnnounce(Battleground* bg, BattlegroundBracketId bracketId, uint32 minLevel, uint32 queueTotal)
{
if (!bg)
return false;
uint32 key = uint32(bg->GetBgTypeID()) * MAX_BATTLEGROUND_BRACKETS + uint32(bracketId);
// Check prev time
if (!IsCorrectDelay(key))
return false;
// When limited, it announces only if there are at least CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS in queue
auto limitQueueMinLevel = sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL);
if (limitQueueMinLevel && minLevel >= limitQueueMinLevel)
{
// limit only RBG for 80, WSG for lower levels
auto bgTypeToLimit = minLevel == 80 ? BATTLEGROUND_RB : BATTLEGROUND_WS;
if (bg->GetBgTypeID() == bgTypeToLimit && queueTotal < sWorld->getIntConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS))
return false;
}
AddTime(key);
return true;
}
@@ -19,6 +19,7 @@
#define _BATTLEGROUND_SPAM_PROTECT_H_ #define _BATTLEGROUND_SPAM_PROTECT_H_
#include "Define.h" #include "Define.h"
#include "DBCEnums.h"
class Player; class Player;
class Battleground; class Battleground;
@@ -29,6 +30,7 @@ public:
static BGSpamProtect* instance(); static BGSpamProtect* instance();
bool CanAnnounce(Player* player, Battleground* bg, uint32 minLevel, uint32 queueTotal); bool CanAnnounce(Player* player, Battleground* bg, uint32 minLevel, uint32 queueTotal);
bool CanAnnounce(Battleground* bg, BattlegroundBracketId bracketId, uint32 minLevel, uint32 queueTotal);
}; };
#define sBGSpam BGSpamProtect::instance() #define sBGSpam BGSpamProtect::instance()
@@ -12236,6 +12236,12 @@ Battleground* Player::GetBattleground(bool create) const
return (create || (bg && bg->FindBgMap()) ? bg : nullptr); return (create || (bg && bg->FindBgMap()) ? bg : nullptr);
} }
bool Player::InBattlefield() const
{
Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(GetZoneId());
return bf && bf->IsWarTime();
}
bool Player::InBattlegroundQueue(bool ignoreArena) const bool Player::InBattlegroundQueue(bool ignoreArena) const
{ {
for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
+1
View File
@@ -2293,6 +2293,7 @@ public:
/*********************************************************/ /*********************************************************/
[[nodiscard]] bool InBattleground() const { return m_bgData.bgInstanceID != 0; } [[nodiscard]] bool InBattleground() const { return m_bgData.bgInstanceID != 0; }
[[nodiscard]] bool InBattlefield() const; // True if the player is in Wintergrasp and it's war time.
[[nodiscard]] bool InArena() const; [[nodiscard]] bool InArena() const;
[[nodiscard]] uint32 GetBattlegroundId() const { return m_bgData.bgInstanceID; } [[nodiscard]] uint32 GetBattlegroundId() const { return m_bgData.bgInstanceID; }
[[nodiscard]] BattlegroundTypeId GetBattlegroundTypeId() const { return m_bgData.bgTypeID; } [[nodiscard]] BattlegroundTypeId GetBattlegroundTypeId() const { return m_bgData.bgTypeID; }
+18 -10
View File
@@ -10023,7 +10023,12 @@ bool Unit::IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spel
return false; return false;
} }
bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell) bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit const* caster)
{
return IsImmunedToSpell(spellInfo, caster, spellInfo ? spellInfo->GetSchoolMask() : SPELL_SCHOOL_MASK_NONE);
}
bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit const* caster, SpellSchoolMask spellSchoolMask)
{ {
if (!spellInfo) if (!spellInfo)
return false; return false;
@@ -10069,7 +10074,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
continue; continue;
// Xinef: if target is immune to one effect, and the spell has transform aura - it is immune to whole spell // Xinef: if target is immune to one effect, and the spell has transform aura - it is immune to whole spell
if (IsImmunedToSpellEffect(spellInfo, i)) if (IsImmunedToSpellEffect(spellInfo, i, caster))
{ {
if (spellInfo->HasAura(SPELL_AURA_TRANSFORM)) if (spellInfo->HasAura(SPELL_AURA_TRANSFORM))
return true; return true;
@@ -10084,13 +10089,6 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{ {
SpellSchoolMask spellSchoolMask = spellInfo->GetSchoolMask();
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
if (spell)
{
spellSchoolMask = spell->GetSpellSchoolMask();
}
if (spellSchoolMask != SPELL_SCHOOL_MASK_NONE) if (spellSchoolMask != SPELL_SCHOOL_MASK_NONE)
{ {
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
@@ -10103,7 +10101,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
if (!(itr->first & spellSchoolMask)) if (!(itr->first & spellSchoolMask))
continue; continue;
if (IgnoresSchoolImmunityFromFriendlyCaster(spellCaster, itr->second, immuneSpellInfo)) if (IgnoresSchoolImmunityFromFriendlyCaster(caster, itr->second, immuneSpellInfo))
continue; continue;
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo)) if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
@@ -10117,6 +10115,16 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
return false; return false;
} }
bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
{
if (!spellInfo)
return false;
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
SpellSchoolMask spellSchoolMask = spell ? spell->GetSpellSchoolMask() : spellInfo->GetSchoolMask();
return IsImmunedToSpell(spellInfo, spellCaster, spellSchoolMask);
}
bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const
{ {
if (!spellInfo || !spellInfo->Effects[index].IsEffect()) if (!spellInfo || !spellInfo->Effects[index].IsEffect())
+2
View File
@@ -1637,6 +1637,8 @@ public:
// Spells immunities // Spells immunities
void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL); void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL);
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr); virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr);
bool IsImmunedToSpell(SpellInfo const* spellInfo, Unit const* caster);
bool IsImmunedToSpell(SpellInfo const* spellInfo, Unit const* caster, SpellSchoolMask spellSchoolMask);
bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster = nullptr); bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster = nullptr);
bool IgnoresSchoolImmunityFromFriendlyCaster(Unit const* caster, uint32 immunityAuraId, SpellInfo const* immunitySpellInfo) const; bool IgnoresSchoolImmunityFromFriendlyCaster(Unit const* caster, uint32 immunityAuraId, SpellInfo const* immunitySpellInfo) const;
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const; [[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const;
+14 -1
View File
@@ -1739,12 +1739,25 @@ uint32 Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, uint32 respaw
if (obj->GetMap()->Instanceable()) if (obj->GetMap()->Instanceable())
return respawnDelay; return respawnDelay;
// No quest givers or world bosses
if (Creature const* creature = obj->ToCreature()) if (Creature const* creature = obj->ToCreature())
{
// Temporary spawns (no DB spawn id, e.g. summons / battlefield-spawned
// creatures such as Wintergrasp turrets) are not part of the respawn system.
if (!creature->GetSpawnId())
return respawnDelay;
// No quest givers or world bosses
if (creature->IsQuestGiver() || creature->isWorldBoss() if (creature->IsQuestGiver() || creature->isWorldBoss()
|| (creature->GetCreatureTemplate()->rank == CREATURE_ELITE_RARE) || (creature->GetCreatureTemplate()->rank == CREATURE_ELITE_RARE)
|| (creature->GetCreatureTemplate()->rank == CREATURE_ELITE_RAREELITE)) || (creature->GetCreatureTemplate()->rank == CREATURE_ELITE_RAREELITE))
return respawnDelay; return respawnDelay;
}
// Temporary gameobjects (no DB spawn id) are likewise excluded.
else if (GameObject const* go = obj->ToGameObject())
{
if (!go->GetSpawnId())
return respawnDelay;
}
auto it = _zonePlayerCountMap.find(obj->GetZoneId()); auto it = _zonePlayerCountMap.find(obj->GetZoneId());
if (it == _zonePlayerCountMap.end()) if (it == _zonePlayerCountMap.end())
+1 -1
View File
@@ -597,7 +597,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster())) if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster()))
itr->second &= ~(1 << effIndex); itr->second &= ~(1 << effIndex);
} }
if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo()) || !CanBeAppliedOn(itr->first)) if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), GetCaster()) || !CanBeAppliedOn(itr->first))
addUnit = false; addUnit = false;
if (addUnit && !itr->first->IsHighestExclusiveAura(this, true)) if (addUnit && !itr->first->IsHighestExclusiveAura(this, true))
+2 -1
View File
@@ -2629,10 +2629,11 @@ void Spell::EffectDispel(SpellEffIndex effIndex)
bool alreadyListed = false; bool alreadyListed = false;
for (DispelChargesList::iterator successItr = success_list.begin(); successItr != success_list.end(); ++successItr) for (DispelChargesList::iterator successItr = success_list.begin(); successItr != success_list.end(); ++successItr)
{ {
if (successItr->first->GetId() == itr->first->GetId()) if (successItr->first == itr->first)
{ {
++successItr->second; ++successItr->second;
alreadyListed = true; alreadyListed = true;
break;
} }
} }
if (!alreadyListed) if (!alreadyListed)
@@ -5207,13 +5207,6 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].BasePoints = 1; spellInfo->Effects[EFFECT_0].BasePoints = 1;
}); });
// 26025 Impale
ApplySpellFix({ 26025 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_1].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CONE_ENEMY_54);
spellInfo->Effects[EFFECT_1].TargetB = SpellImplicitTargetInfo(0);
});
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{ {
SpellInfo* spellInfo = mSpellInfoMap[i]; SpellInfo* spellInfo = mSpellInfoMap[i];
@@ -86,6 +86,10 @@ enum Spells
// Misc // Misc
SPELL_ANVEENA_ENERGY_DRAIN = 46410, SPELL_ANVEENA_ENERGY_DRAIN = 46410,
SPELL_ARCANE_BOLT = 45670,
// TODO
// 45670 is an aura that triggers 45666; 45666 needs to be scripted
// SPELL_ARCANE_BOLT_DAMAGE = 45666,
SPELL_RING_OF_BLUE_FLAMES = 45825, SPELL_RING_OF_BLUE_FLAMES = 45825,
SPELL_SUMMON_BLUE_DRAKE = 45836, SPELL_SUMMON_BLUE_DRAKE = 45836,
SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT = 45839, SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT = 45839,
@@ -102,7 +106,8 @@ enum Misc
PHASE_SACRIFICE = 5, PHASE_SACRIFICE = 5,
ACTION_START_POST_EVENT = 1, ACTION_START_POST_EVENT = 1,
ACTION_NO_KILL_TALK = 2 ACTION_NO_KILL_TALK = 2,
ACTION_START_AERIAL_SUPPORT = 3
}; };
class CastArmageddon : public BasicEvent class CastArmageddon : public BasicEvent
@@ -173,6 +178,7 @@ struct npc_kiljaeden_controller : public NullCreatureAI
void JustSummoned(Creature* summon) override void JustSummoned(Creature* summon) override
{ {
summons.Summon(summon); summons.Summon(summon);
if (summon->GetEntry() == NPC_SINISTER_REFLECTION) if (summon->GetEntry() == NPC_SINISTER_REFLECTION)
{ {
summon->m_Events.AddEventAtOffset([summon] { summon->m_Events.AddEventAtOffset([summon] {
@@ -181,7 +187,14 @@ struct npc_kiljaeden_controller : public NullCreatureAI
}, 5s); }, 5s);
} }
else if (summon->GetEntry() == NPC_KALECGOS_KJ) else if (summon->GetEntry() == NPC_KALECGOS_KJ)
{
summon->setActive(true); summon->setActive(true);
summon->SetCanFly(true);
summon->SetDisableGravity(true);
summon->SetAnimTier(AnimTier::Fly);
summon->GetMotionMaster()->MoveWaypoint(NPC_KALECGOS_KJ * 10, true);
summon->AI()->DoAction(ACTION_START_AERIAL_SUPPORT);
}
} }
void SummonedCreatureDies(Creature* summon, Unit*) override void SummonedCreatureDies(Creature* summon, Unit*) override
@@ -209,7 +222,7 @@ struct npc_kiljaeden_controller : public NullCreatureAI
{ {
me->RemoveAurasDueToSpell(SPELL_ANVEENA_ENERGY_DRAIN); me->RemoveAurasDueToSpell(SPELL_ANVEENA_ENERGY_DRAIN);
me->SummonCreature(NPC_KILJAEDEN, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 1.5f, 4.3f, TEMPSUMMON_MANUAL_DESPAWN); me->SummonCreature(NPC_KILJAEDEN, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 1.5f, 4.3f, TEMPSUMMON_MANUAL_DESPAWN);
me->SummonCreature(NPC_KALECGOS_KJ, 1726.80f, 661.43f, 138.65f, 3.95f, TEMPSUMMON_MANUAL_DESPAWN); me->SummonCreature(NPC_KALECGOS_KJ, 1734.465f, 592.5678f, 142.3971f, 4.533074f, TEMPSUMMON_MANUAL_DESPAWN);
} }
} }
} }
@@ -705,8 +718,15 @@ struct npc_kalecgos_kj : public NullCreatureAI
void DoAction(int32 param) override void DoAction(int32 param) override
{ {
if (param == ACTION_START_POST_EVENT) if (param == ACTION_START_AERIAL_SUPPORT)
{ {
DoCastSelf(SPELL_ARCANE_BOLT, true);
}
else if (param == ACTION_START_POST_EVENT)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
me->RemoveAurasDueToSpell(SPELL_ARCANE_BOLT);
me->SetCanFly(false); me->SetCanFly(false);
me->SetDisableGravity(false); me->SetDisableGravity(false);
me->CastSpell(me, SPELL_TELEPORT_AND_TRANSFORM, true); me->CastSpell(me, SPELL_TELEPORT_AND_TRANSFORM, true);
@@ -1592,6 +1592,7 @@ public:
} }
}; };
// 63238 Lightning Pillar
class spell_thorim_lightning_pillar_P2_aura : public AuraScript class spell_thorim_lightning_pillar_P2_aura : public AuraScript
{ {
PrepareAuraScript(spell_thorim_lightning_pillar_P2_aura); PrepareAuraScript(spell_thorim_lightning_pillar_P2_aura);
@@ -1609,6 +1610,29 @@ class spell_thorim_lightning_pillar_P2_aura : public AuraScript
} }
}; };
// 62976 Lightning Pillar
class spell_thorim_lightning_pillar_P2 : public SpellScript
{
PrepareSpellScript(spell_thorim_lightning_pillar_P2);
void SelectPillar(WorldObject*& target)
{
if (Unit* caster = GetCaster())
{
std::list<Creature*> pillars;
caster->GetCreatureListWithEntryInGrid(pillars, NPC_PILLAR, GetSpellInfo()->GetMaxRange());
if (!pillars.empty())
target = Acore::Containers::SelectRandomContainerElement(pillars);
}
}
void Register() override
{
OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_thorim_lightning_pillar_P2::SelectPillar, EFFECT_0, TARGET_UNIT_NEARBY_ENTRY);
}
};
class spell_thorim_trash_impale_aura : public AuraScript class spell_thorim_trash_impale_aura : public AuraScript
{ {
PrepareAuraScript(spell_thorim_trash_impale_aura); PrepareAuraScript(spell_thorim_trash_impale_aura);
@@ -1679,6 +1703,7 @@ void AddSC_boss_thorim()
new go_thorim_lever(); new go_thorim_lever();
// Spells // Spells
RegisterSpellScript(spell_thorim_lightning_pillar_P2);
RegisterSpellScript(spell_thorim_lightning_pillar_P2_aura); RegisterSpellScript(spell_thorim_lightning_pillar_P2_aura);
RegisterSpellScript(spell_thorim_trash_impale_aura); RegisterSpellScript(spell_thorim_trash_impale_aura);
@@ -116,13 +116,19 @@ struct boss_murmur : public BossAI
} }
} }
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override
{
BossAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
if (attacker && !me->GetVictim() && attacker->IsControlledByPlayer())
AttackStart(attacker);
}
void JustEngagedWith(Unit* who) override void JustEngagedWith(Unit* who) override
{ {
// Boss engages mobs during roleplay, this checks prevents it from setting the zone in combat before players engage it. // Boss engages mobs during roleplay, this checks prevents it from setting the zone in combat before players engage it.
if (who->IsPlayer() || who->IsPet() || who->IsGuardian()) if (who->IsControlledByPlayer())
{
_JustEngagedWith(); _JustEngagedWith();
}
scheduler.Schedule(28s, [this](TaskContext context) scheduler.Schedule(28s, [this](TaskContext context)
{ {