mirror of
https://github.com/liyunfan1223/azerothcore-wotlk.git
synced 2026-08-07 15:28:02 +00:00
Merge branch 'master' into test-staging
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -54,9 +54,11 @@ CMakeLists.txt.user
|
||||
*.LOCAL.*
|
||||
|
||||
#
|
||||
# AI tools
|
||||
# Claude
|
||||
#
|
||||
.claude
|
||||
.claude/worktrees
|
||||
.claude/settings.local.json
|
||||
.claude/plans/**
|
||||
|
||||
#
|
||||
# IDE & other software
|
||||
|
||||
3
data/sql/updates/db_auth/2026_05_09_00.sql
Normal file
3
data/sql/updates/db_auth/2026_05_09_00.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
-- DB update 2026_05_03_00 -> 2026_05_09_00
|
||||
DELETE FROM `rbac_linked_permissions` WHERE `id` = 198 AND `linkedId` = 471;
|
||||
DELETE FROM `rbac_permissions` WHERE `id` = 471;
|
||||
18
data/sql/updates/db_auth/2026_05_14_00.sql
Normal file
18
data/sql/updates/db_auth/2026_05_14_00.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- DB update 2026_05_09_00 -> 2026_05_14_00
|
||||
--
|
||||
-- RBAC permissions for `respawn creature guid`, `respawn gameobject guid`,
|
||||
-- `respawn creature entry`, and `respawn gameobject entry` commands (Console::Yes/No, security level 3 - GM)
|
||||
--
|
||||
DELETE FROM `rbac_permissions` WHERE `id` IN (916, 917, 918, 919);
|
||||
INSERT INTO `rbac_permissions` (`id`, `name`) VALUES
|
||||
(916, 'Command: respawn creature guid'),
|
||||
(917, 'Command: respawn gameobject guid'),
|
||||
(918, 'Command: respawn creature entry'),
|
||||
(919, 'Command: respawn gameobject entry');
|
||||
|
||||
DELETE FROM `rbac_linked_permissions` WHERE `linkedId` IN (916, 917, 918, 919);
|
||||
INSERT INTO `rbac_linked_permissions` (`id`, `linkedId`) VALUES
|
||||
(197, 916),
|
||||
(197, 917),
|
||||
(197, 918),
|
||||
(197, 919);
|
||||
5
data/sql/updates/db_world/2026_05_09_00.sql
Normal file
5
data/sql/updates/db_world/2026_05_09_00.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- DB update 2026_05_08_00 -> 2026_05_09_00
|
||||
--
|
||||
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 27292) AND (`source_type` = 0) AND (`id` IN (5));
|
||||
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
|
||||
(27292, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 134, 48600, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Flamebringer - On Just Summoned - Invoker Cast \'Ride Flamebringer\'');
|
||||
5
data/sql/updates/db_world/2026_05_09_01.sql
Normal file
5
data/sql/updates/db_world/2026_05_09_01.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- DB update 2026_05_09_00 -> 2026_05_09_01
|
||||
--
|
||||
DELETE FROM `acore_string` WHERE `entry` = 35440;
|
||||
INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES
|
||||
(35440, 'Showing only first 50 results. Please use a more specific search string.', '처음 50개의 결과만 표시됩니다. 더 구체적인 검색어를 사용해 주세요.', 'Affichage des 50 premiers résultats uniquement. Veuillez utiliser une chaîne de recherche plus spécifique.', 'Es werden nur die ersten 50 Ergebnisse angezeigt. Bitte verwende eine genauere Suchzeichenkette.', '仅显示前50个结果,请使用更具体的搜索字符串。', '僅顯示前50個結果,請使用更具體的搜尋字串。', 'Solo se muestran los primeros 50 resultados. Por favor, utilice una cadena de búsqueda más específica.', 'Solo se muestran los primeros 50 resultados. Por favor, utilice una cadena de búsqueda más específica.', 'Отображаются только первые 50 результатов. Пожалуйста, используйте более конкретную строку поиска.');
|
||||
7
data/sql/updates/db_world/2026_05_10_00.sql
Normal file
7
data/sql/updates/db_world/2026_05_10_00.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- DB update 2026_05_09_01 -> 2026_05_10_00
|
||||
-- Restore TEMPSUMMON_MANUAL_DESPAWN (8) with no timer for Thiassi -> Antiok
|
||||
-- accessory. With timer 0 this is rewritten to TEMPSUMMON_DEAD_DESPAWN at
|
||||
-- runtime, so Antiok only despawns when he dies and no longer ticks down
|
||||
-- while seated on Thiassi pre-engage.
|
||||
UPDATE `vehicle_template_accessory` SET `summontype` = 8, `summontimer` = 0
|
||||
WHERE `entry` = 28018 AND `accessory_entry` = 28006;
|
||||
10
data/sql/updates/db_world/2026_05_10_01.sql
Normal file
10
data/sql/updates/db_world/2026_05_10_01.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
-- DB update 2026_05_10_00 -> 2026_05_10_01
|
||||
--
|
||||
-- Show character level in `.character deleted list` output
|
||||
--
|
||||
DELETE FROM `acore_string` WHERE `entry` IN (1016, 1017, 1018, 1026);
|
||||
INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES
|
||||
(1016, '| GUID | Name | Level | Account | Delete Date |', NULL, NULL, '| GUID | Name | Level | Account | gelöscht am |', '| 唯一标识符 | 名称 | 等级 | 账号 | 删除日期 |', NULL, NULL, NULL, NULL),
|
||||
(1017, '| {} | {} | {} | {} ({}) | {} |', NULL, NULL, '| {} | {} | {} | {} ({}) | {} |', '| {} | {} | {} | {} ({}) | {} |', NULL, NULL, NULL, NULL),
|
||||
(1018, '==================================================================================================', NULL, NULL, '==================================================================================================', '错误:510', NULL, NULL, NULL, NULL),
|
||||
(1026, 'GUID: {} Name: {} Level: {} Account: {} ({}) Date: {}', NULL, NULL, 'GUID: {} Name: {} Level: {} Account: {} ({}) Datum: {}', '唯一标识符: {} 名称: {} 等级: {} 账号: {} ({}) 时间: {}', NULL, NULL, NULL, NULL);
|
||||
37
data/sql/updates/db_world/2026_05_14_00.sql
Normal file
37
data/sql/updates/db_world/2026_05_14_00.sql
Normal file
@@ -0,0 +1,37 @@
|
||||
-- DB update 2026_05_10_01 -> 2026_05_14_00
|
||||
--
|
||||
-- Strings for `respawn creature guid` / `respawn gameobject guid` /
|
||||
-- `respawn creature entry` / `respawn gameobject entry` commands and updated `list respawns` command
|
||||
--
|
||||
DELETE FROM `acore_string` WHERE `entry` IN (35441, 35442, 35443, 35444, 35445, 35446, 35447, 35448, 35449, 35450, 35451, 35452);
|
||||
INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES
|
||||
(35441, 'No creature with spawn GUID {} found.', '스폰 GUID {}를 가진 생물을 찾을 수 없습니다.', 'Aucune créature avec le GUID d''apparition {} trouvée.', 'Keine Kreatur mit Spawn-GUID {} gefunden.', '未找到生成 GUID 为 {} 的生物。', '未找到生成 GUID 為 {} 的生物。', 'No se encontró ninguna criatura con el GUID de generación {}.', 'No se encontró ninguna criatura con el GUID de generación {}.', 'Существо с GUID спавна {} не найдено.'),
|
||||
(35442, 'Map {} is not currently loaded.', '맵 {}이(가) 현재 로드되어 있지 않습니다.', 'La carte {} n''est pas actuellement chargée.', 'Karte {} ist derzeit nicht geladen.', '地图 {} 当前未加载。', '地圖 {} 目前未載入。', 'El mapa {} no está cargado actualmente.', 'El mapa {} no está cargado actualmente.', 'Карта {} в данный момент не загружена.'),
|
||||
(35443, 'Creature (spawn GUID {}, entry {}) is already alive.', '생물 (스폰 GUID {}, 항목 {})이(가) 이미 살아 있습니다.', 'La créature (GUID d''apparition {}, entrée {}) est déjà en vie.', 'Kreatur (Spawn-GUID {}, Eintrag {}) ist bereits am Leben.', '生物(生成 GUID {},条目 {})已经存活。', '生物(生成 GUID {},條目 {})已經存活。', 'La criatura (GUID de generación {}, entrada {}) ya está viva.', 'La criatura (GUID de generación {}, entrada {}) ya está viva.', 'Существо (GUID спавна {}, запись {}) уже живо.'),
|
||||
(35444, 'Creature (spawn GUID {}, entry {}) queued for respawn.', '생물 (스폰 GUID {}, 항목 {})이(가) 리스폰 대기열에 추가되었습니다.', 'La créature (GUID d''apparition {}, entrée {}) est mise en file d''attente pour réapparition.', 'Kreatur (Spawn-GUID {}, Eintrag {}) für Respawn in Warteschlange.', '生物(生成 GUID {},条目 {})已加入重生队列。', '生物(生成 GUID {},條目 {})已加入重生佇列。', 'La criatura (GUID de generación {}, entrada {}) está en cola para reaparecer.', 'La criatura (GUID de generación {}, entrada {}) está en cola para reaparecer.', 'Существо (GUID спавна {}, запись {}) добавлено в очередь на респаун.'),
|
||||
(35445, 'Gameobject (spawn GUID {}, entry {}) is already active.', '게임오브젝트 (스폰 GUID {}, 항목 {})이(가) 이미 활성화되어 있습니다.', 'L''objet de jeu (GUID d''apparition {}, entrée {}) est déjà actif.', 'Spielobjekt (Spawn-GUID {}, Eintrag {}) ist bereits aktiv.', '游戏对象(生成 GUID {},条目 {})已经激活。', '遊戲物件(生成 GUID {},條目 {})已經激活。', 'El objeto de juego (GUID de generación {}, entrada {}) ya está activo.', 'El objeto de juego (GUID de generación {}, entrada {}) ya está activo.', 'Игровой объект (GUID спавна {}, запись {}) уже активен.'),
|
||||
(35446, 'Gameobject (spawn GUID {}, entry {}) queued for respawn.', '게임오브젝트 (스폰 GUID {}, 항목 {})이(가) 리스폰 대기열에 추가되었습니다.', 'L''objet de jeu (GUID d''apparition {}, entrée {}) est mis en file d''attente pour réapparition.', 'Spielobjekt (Spawn-GUID {}, Eintrag {}) für Respawn in Warteschlange.', '游戏对象(生成 GUID {},条目 {})已加入重生队列。', '遊戲物件(生成 GUID {},條目 {})已加入重生佇列。', 'El objeto de juego (GUID de generación {}, entrada {}) está en cola para reaparecer.', 'El objeto de juego (GUID de generación {}, entrada {}) está en cola para reaparecer.', 'Игровой объект (GUID спавна {}, запись {}) добавлен в очередь на респаун.'),
|
||||
(35447, 'A map ID is required when using this command from the console.', '콘솔에서 이 명령을 사용하려면 맵 ID가 필요합니다.', 'Un ID de carte est requis lors de l''utilisation de cette commande depuis la console.', 'Eine Karten-ID ist erforderlich, wenn dieser Befehl über die Konsole verwendet wird.', '从控制台使用此命令时需要提供地图 ID。', '從控制台使用此命令時需要提供地圖 ID。', 'Se requiere un ID de mapa al usar este comando desde la consola.', 'Se requiere un ID de mapa al usar este comando desde la consola.', 'При использовании этой команды из консоли требуется ID карты.'),
|
||||
(35448, 'No gameobject with spawn GUID {} found.', '스폰 GUID {}를 가진 게임오브젝트를 찾을 수 없습니다.', 'Aucun objet de jeu avec le GUID d''apparition {} trouvé.', 'Kein Spielobjekt mit Spawn-GUID {} gefunden.', '未找到生成 GUID 为 {} 的游戏对象。', '未找到生成 GUID 為 {} 的遊戲物件。', 'No se encontró ningún objeto de juego con el GUID de generación {}.', 'No se encontró ningún objeto de juego con el GUID de generación {}.', 'Игровой объект с GUID спавна {} не найден.'),
|
||||
(35449, 'No creature template with entry {} found.', '항목 {}을(를) 가진 생물 템플릿을 찾을 수 없습니다.', 'Aucun modèle de créature avec l''entrée {} trouvé.', 'Kein Kreaturvorlage mit Eintrag {} gefunden.', '未找到条目为 {} 的生物模板。', '未找到條目為 {} 的生物模板。', 'No se encontró ninguna plantilla de criatura con la entrada {}.', 'No se encontró ninguna plantilla de criatura con la entrada {}.', 'Шаблон существа с записью {} не найден.'),
|
||||
(35450, 'No gameobject template with entry {} found.', '항목 {}을(를) 가진 게임오브젝트 템플릿을 찾을 수 없습니다.', 'Aucun modèle d''objet de jeu avec l''entrée {} trouvé.', 'Kein Spielobjektvorlage mit Eintrag {} gefunden.', '未找到条目为 {} 的游戏对象模板。', '未找到條目為 {} 的遊戲物件模板。', 'No se encontró ninguna plantilla de objeto de juego con la entrada {}.', 'No se encontró ninguna plantilla de objeto de juego con la entrada {}.', 'Шаблон игрового объекта с записью {} не найден.'),
|
||||
(35451, 'Queued {} creature spawn(s) with entry {} for respawn on this map.', '이 맵에서 항목 {}을(를) 가진 생물 스폰 {}개가 리스폰 대기열에 추가되었습니다.', '{} apparition(s) de créature avec l''entrée {} mise(s) en file d''attente pour réapparition sur cette carte.', '{} Kreaturspawn(s) mit Eintrag {} für Respawn auf dieser Karte in Warteschlange.', '在此地图上,已将 {} 个条目为 {} 的生物生成加入重生队列。', '在此地圖上,已將 {} 個條目為 {} 的生物生成加入重生佇列。', 'Se pusieron en cola {} aparición(es) de criatura con entrada {} para reaparecer en este mapa.', 'Se pusieron en cola {} aparición(es) de criatura con entrada {} para reaparecer en este mapa.', 'В очередь на респаун на этой карте добавлено {} спавн(ов) существа с записью {}.'),
|
||||
(35452, 'Queued {} gameobject spawn(s) with entry {} for respawn on this map.', '이 맵에서 항목 {}을(를) 가진 게임오브젝트 스폰 {}개가 리스폰 대기열에 추가되었습니다.', '{} apparition(s) d''objet de jeu avec l''entrée {} mise(s) en file d''attente pour réapparition sur cette carte.', '{} Spielobjektspawn(s) mit Eintrag {} für Respawn auf dieser Karte in Warteschlange.', '在此地图上,已将 {} 个条目为 {} 的游戏对象生成加入重生队列。', '在此地圖上,已將 {} 個條目為 {} 的遊戲物件生成加入重生佇列。', 'Se pusieron en cola {} aparición(es) de objeto de juego con entrada {} para reaparecer en este mapa.', 'Se pusieron en cola {} aparición(es) de objeto de juego con entrada {} para reaparecer en este mapa.', 'В очередь на респаун на этой карте добавлено {} спавн(ов) игрового объекта с записью {}.');
|
||||
|
||||
--
|
||||
-- Update `list respawns` command help text
|
||||
--
|
||||
DELETE FROM `command` WHERE `name` = 'list respawns';
|
||||
INSERT INTO `command` (`name`, `security`, `help`) VALUES
|
||||
('list respawns', 2, 'Syntax: .list respawns [entryId]\r\nIn-game: shows all pending creature and gameobject respawns on the current map, filtered by entryId if provided.\r\nConsole: .list respawns #mapId [#instanceId [#entryId]] - specify map and optional instance ID and entry filter.');
|
||||
|
||||
--
|
||||
-- Register `respawn creature guid`, `respawn gameobject guid`,
|
||||
-- `respawn creature entry`, and `respawn gameobject entry` commands
|
||||
--
|
||||
DELETE FROM `command` WHERE `name` IN ('respawn id', 'respawn guid', 'respawn creature guid', 'respawn gameobject guid', 'respawn creature entry', 'respawn gameobject entry');
|
||||
INSERT INTO `command` (`name`, `security`, `help`) VALUES
|
||||
('respawn creature guid', 3, 'Syntax: .respawn creature guid #spawnGuid\r\nForces the creature with the given spawn GUID (database GUID) to respawn. Usable from console.'),
|
||||
('respawn gameobject guid', 3, 'Syntax: .respawn gameobject guid #spawnGuid\r\nForces the gameobject with the given spawn GUID (database GUID) to respawn. Usable from console.'),
|
||||
('respawn creature entry', 3, 'Syntax: .respawn creature entry #entry [#mapId [#instanceId]]\r\nIn-game: forces all creatures with the given #entry to respawn on the current map.\r\nConsole: specify #mapId (required) and optionally #instanceId. Pooled creatures are skipped in the queue phase to prevent duplicate spawns.'),
|
||||
('respawn gameobject entry', 3, 'Syntax: .respawn gameobject entry #entry [#mapId [#instanceId]]\r\nIn-game: forces all gameobjects with the given #entry to respawn on the current map.\r\nConsole: specify #mapId (required) and optionally #instanceId. Pooled gameobjects are skipped in the queue phase to prevent duplicate spawns.');
|
||||
70
data/sql/updates/db_world/2026_05_15_00.sql
Normal file
70
data/sql/updates/db_world/2026_05_15_00.sql
Normal file
@@ -0,0 +1,70 @@
|
||||
-- DB update 2026_05_14_00 -> 2026_05_15_00
|
||||
-- Remove trigger bunny SAI scripts that interfere with the chain mechanic
|
||||
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` IN (-113478, -113479, -113550, -113551));
|
||||
|
||||
-- === CHAIN BUNNIES: add On Spawn event (id=0) so chains arm immediately on spawn ===
|
||||
-- Phase 1 = chain intact and ready to be broken; Phase 0 = chain broken
|
||||
-- id=0: SPAWN -> CAST chain spell, link=1
|
||||
-- id=1: LINK -> SET PHASE 1
|
||||
-- id=2: ACTION(10) -> CAST chain spell, link=1 (Akali reset: re-arm)
|
||||
-- id=3: SPELLHIT(52816), phase=1, flags=512 -> REMOVE_AURA from trigger bunny, link=4
|
||||
-- id=4: LINK, phase=1 -> DO_ACTION(11) on Akali, link=5
|
||||
-- id=5: LINK -> SET PHASE 0
|
||||
|
||||
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` IN (-113549, -113548, -61994, -61995));
|
||||
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
|
||||
-- Right Front Paw GUID -113549 / chain spell 52834 / trigger bunny 113551
|
||||
(-113549, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52834, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn: Cast \'Rampage: Akali`s Chains - Right Front Paw\''),
|
||||
(-113549, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn/Reset: Set Event Phase 1'),
|
||||
(-113549, 0, 2, 1, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52834, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Action 10 (Reset): Cast \'Rampage: Akali`s Chains - Right Front Paw\''),
|
||||
(-113549, 0, 3, 4, 8, 1, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52834, 0, 0, 0, 0, 0, 10, 113551, 26298, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Rampage: Akali`s Chains - Right Front Paw\''),
|
||||
(-113549, 0, 4, 5, 61, 1, 100, 0, 0, 0, 0, 0, 0, 0, 223, 11, 0, 0, 0, 0, 0, 10, 98159, 28952, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Do Action 11 On Akali - Right Front Paw Freed'),
|
||||
(-113549, 0, 5, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Set Event Phase 0'),
|
||||
-- Left Front Paw GUID -113548 / chain spell 52833 / trigger bunny 113550
|
||||
(-113548, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52833, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn: Cast \'Rampage: Akali`s Chains - Left Front Paw\''),
|
||||
(-113548, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn/Reset: Set Event Phase 1'),
|
||||
(-113548, 0, 2, 1, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52833, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Action 10 (Reset): Cast \'Rampage: Akali`s Chains - Left Front Paw\''),
|
||||
(-113548, 0, 3, 4, 8, 1, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52833, 0, 0, 0, 0, 0, 10, 113550, 26298, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Rampage: Akali`s Chains - Left Front Paw\''),
|
||||
(-113548, 0, 4, 5, 61, 1, 100, 0, 0, 0, 0, 0, 0, 0, 223, 11, 0, 0, 0, 0, 0, 10, 98159, 28952, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Do Action 11 On Akali - Left Front Paw Freed'),
|
||||
(-113548, 0, 5, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Set Event Phase 0'),
|
||||
-- Right Rear Paw GUID -61994 / chain spell 52837 / trigger bunny 113478
|
||||
(-61994, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52837, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn: Cast \'Rampage: Akali`s Chains - Right Rear Paw\''),
|
||||
(-61994, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn/Reset: Set Event Phase 1'),
|
||||
(-61994, 0, 2, 1, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52837, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Action 10 (Reset): Cast \'Rampage: Akali`s Chains - Right Rear Paw\''),
|
||||
(-61994, 0, 3, 4, 8, 1, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52837, 0, 0, 0, 0, 0, 10, 113478, 26298, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Rampage: Akali`s Chains - Right Rear Paw\''),
|
||||
(-61994, 0, 4, 5, 61, 1, 100, 0, 0, 0, 0, 0, 0, 0, 223, 11, 0, 0, 0, 0, 0, 10, 98159, 28952, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Do Action 11 On Akali - Right Rear Paw Freed'),
|
||||
(-61994, 0, 5, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Set Event Phase 0'),
|
||||
-- Left Rear Paw GUID -61995 / chain spell 52838 / trigger bunny 113479
|
||||
(-61995, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52838, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn: Cast \'Rampage: Akali`s Chains - Left Rear Paw\''),
|
||||
(-61995, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spawn/Reset: Set Event Phase 1'),
|
||||
(-61995, 0, 2, 1, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52838, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Action 10 (Reset): Cast \'Rampage: Akali`s Chains - Left Rear Paw\''),
|
||||
(-61995, 0, 3, 4, 8, 1, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52838, 0, 0, 0, 0, 0, 10, 113479, 26298, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Rampage: Akali`s Chains - Left Rear Paw\''),
|
||||
(-61995, 0, 4, 5, 61, 1, 100, 0, 0, 0, 0, 0, 0, 0, 223, 11, 0, 0, 0, 0, 0, 10, 98159, 28952, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Do Action 11 On Akali - Left Rear Paw Freed'),
|
||||
(-61995, 0, 5, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny (scale x0.01) Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Set Event Phase 0');
|
||||
|
||||
-- === FIRE BUNNIES: add On Spawn cast so fire visual appears immediately on spawn ===
|
||||
-- id=0: SPAWN -> CAST fire aura on self
|
||||
-- id=1: ACTION(10) -> CAST fire aura on self (Akali reset relay)
|
||||
-- id=2: SPELLHIT(52816), flags=512 -> REMOVE_AURA fire aura from self
|
||||
|
||||
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` IN (-100336, -100333, -100335, -100334));
|
||||
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
|
||||
-- Right Front Fire GUID -100336
|
||||
(-100336, 0, 0, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spawn: Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100336, 0, 1, 0, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Action 10 (Reset): Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100336, 0, 2, 0, 8, 0, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
-- Left Front Fire GUID -100333
|
||||
(-100333, 0, 0, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spawn: Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100333, 0, 1, 0, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Action 10 (Reset): Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100333, 0, 2, 0, 8, 0, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
-- Right Rear Fire GUID -100335
|
||||
(-100335, 0, 0, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spawn: Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100335, 0, 1, 0, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Action 10 (Reset): Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100335, 0, 2, 0, 8, 0, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
-- Left Rear Fire GUID -100334
|
||||
(-100334, 0, 0, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spawn: Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100334, 0, 1, 0, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 11, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Action 10 (Reset): Cast \'Cosmetic - Low Poly Fire (with Sound)\''),
|
||||
(-100334, 0, 2, 0, 8, 0, 100, 512, 52816, 0, 0, 0, 0, 0, 28, 52855, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'ELM General Purpose Bunny Large - On Spellhit \'Rampage: Akali Chain Anchor On Disturb\': Remove Aura \'Cosmetic - Low Poly Fire (with Sound)\'');
|
||||
|
||||
-- Fix comment on Akali Reset Script relay action
|
||||
UPDATE `smart_scripts` SET `comment` = 'Akali - Reset Script - Do Action ID 10: Relay Reset to All Creatures in 100yd' WHERE (`source_type` = 9 AND `entryorguid` = 2895202 AND `id` = 2);
|
||||
@@ -621,6 +621,15 @@ Allow.IP.Based.Action.Logging = 0
|
||||
|
||||
LogSpamReports = 1
|
||||
|
||||
#
|
||||
# ChatLog.Enable
|
||||
# Description: Enable or disable chat logging (chat_log.cpp).
|
||||
# Default: 0 - (Disabled)
|
||||
# 1 - (Enabled)
|
||||
#
|
||||
|
||||
ChatLog.Enable = 0
|
||||
|
||||
#
|
||||
# Appender config values: Given an appender "name"
|
||||
# Appender.name
|
||||
@@ -2608,6 +2617,14 @@ DurabilityLoss.InPvP = 0
|
||||
|
||||
DurabilityLoss.OnDeath = 10
|
||||
|
||||
#
|
||||
# DurabilityLoss.OnSpiritResurrect
|
||||
# Description: Durability loss percentage on spirit healer resurrection. Does not effect PvP
|
||||
# spirit healers.
|
||||
# Default: 25
|
||||
|
||||
DurabilityLoss.OnSpiritResurrect = 25
|
||||
|
||||
#
|
||||
# DurabilityLossChance.Damage
|
||||
# Description: Chance to lose durability on one equipped item from damage.
|
||||
|
||||
@@ -403,10 +403,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_UPD_CHARACTER_POSITION, "UPDATE characters SET position_x = ?, position_y = ?, position_z = ?, orientation = ?, map = ?, zone = ?, trans_x = 0, trans_y = 0, trans_z = 0, transguid = 0, taxi_path = '', cinematic = 1 WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_ONLINE, "SELECT name, account, map, zone FROM characters WHERE online > 0", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID, "SELECT guid, class, race FROM characters WHERE account = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate, level FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate, level FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%') ORDER BY deleteDate DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME_LIMIT, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate, level FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%') ORDER BY deleteDate DESC LIMIT 51", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_DEL_INFO, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate, level FROM characters WHERE deleteDate IS NOT NULL ORDER BY deleteDate DESC LIMIT 51", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID, "SELECT guid FROM characters WHERE account = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PINFO, "SELECT totaltime, level, money, account, race, class, map, zone, gender, health, playerFlags FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM character_banned WHERE guid = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_PINFO_MAILS, "SELECT SUM(CASE WHEN (checked & 1) THEN 1 ELSE 0 END) AS 'readmail', COUNT(*) AS 'totalmail' FROM mail WHERE `receiver` = ?", CONNECTION_SYNCH);
|
||||
|
||||
@@ -333,6 +333,7 @@ enum CharacterDatabaseStatements : uint32
|
||||
|
||||
CHAR_SEL_CHAR_DEL_INFO_BY_GUID,
|
||||
CHAR_SEL_CHAR_DEL_INFO_BY_NAME,
|
||||
CHAR_SEL_CHAR_DEL_INFO_BY_NAME_LIMIT,
|
||||
CHAR_SEL_CHAR_DEL_INFO,
|
||||
|
||||
CHAR_SEL_CHARS_BY_ACCOUNT_ID,
|
||||
|
||||
@@ -300,7 +300,6 @@ enum RBACPermissions
|
||||
RBAC_PERM_COMMAND_GMNOTIFY = 468,
|
||||
RBAC_PERM_COMMAND_NAMEANNOUNCE = 469,
|
||||
RBAC_PERM_COMMAND_NOTIFY = 470,
|
||||
RBAC_PERM_COMMAND_WHISPERS = 471,
|
||||
RBAC_PERM_COMMAND_GROUP = 472,
|
||||
RBAC_PERM_COMMAND_GROUP_LEADER = 473,
|
||||
RBAC_PERM_COMMAND_GROUP_DISBAND = 474,
|
||||
@@ -678,6 +677,10 @@ enum RBACPermissions
|
||||
RBAC_PERM_COMMAND_BF_QUEUE = 913,
|
||||
RBAC_PERM_COMMAND_PET_LIST = 914,
|
||||
RBAC_PERM_COMMAND_PET_DELETE = 915,
|
||||
RBAC_PERM_COMMAND_RESPAWN_CREATURE_GUID = 916,
|
||||
RBAC_PERM_COMMAND_RESPAWN_GAMEOBJECT_GUID = 917,
|
||||
RBAC_PERM_COMMAND_RESPAWN_CREATURE_ENTRY = 918,
|
||||
RBAC_PERM_COMMAND_RESPAWN_GAMEOBJECT_ENTRY = 919,
|
||||
// custom permissions 1000+
|
||||
RBAC_PERM_MAX
|
||||
};
|
||||
|
||||
@@ -262,7 +262,9 @@ void WorldSession::SendSpiritResurrect()
|
||||
{
|
||||
_player->ResurrectPlayer(0.5f, true);
|
||||
|
||||
_player->DurabilityLossAll(0.25f, true);
|
||||
float durabilityLossOnSpiritResurrect = sWorld->getRate(RATE_DURABILITY_LOSS_ON_SPIRIT_RESURRECT) / 100.0f;
|
||||
if (durabilityLossOnSpiritResurrect)
|
||||
_player->DurabilityLossAll(durabilityLossOnSpiritResurrect, true);
|
||||
|
||||
// get corpse nearest graveyard
|
||||
GraveyardStruct const* corpseGrave = nullptr;
|
||||
|
||||
@@ -2724,12 +2724,18 @@ void Map::ProcessRespawns()
|
||||
|
||||
void Map::ProcessCreatureRespawn(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
// Pool members are handled entirely by PoolMgr
|
||||
if (uint32 poolId = sPoolMgr->IsPartOfAPool<Creature>(spawnId))
|
||||
// Pool members in non-instanced maps are handled entirely by PoolMgr.
|
||||
// In instanced maps the pool system operates globally and Spawn1Object is
|
||||
// a no-op for instanceable maps, so fall through to the normal per-instance
|
||||
// respawn logic instead.
|
||||
if (!Instanceable())
|
||||
{
|
||||
sPoolMgr->UpdatePool<Creature>(poolId, spawnId);
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
return;
|
||||
if (uint32 poolId = sPoolMgr->IsPartOfAPool<Creature>(spawnId))
|
||||
{
|
||||
sPoolMgr->UpdatePool<Creature>(poolId, spawnId);
|
||||
RemoveCreatureRespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
|
||||
@@ -2785,12 +2791,16 @@ void Map::ProcessCreatureRespawn(ObjectGuid::LowType spawnId)
|
||||
|
||||
void Map::ProcessGameObjectRespawn(ObjectGuid::LowType spawnId)
|
||||
{
|
||||
// Pool members are handled entirely by PoolMgr
|
||||
if (uint32 poolId = sPoolMgr->IsPartOfAPool<GameObject>(spawnId))
|
||||
// Same rationale as ProcessCreatureRespawn: pool management via PoolMgr is
|
||||
// only meaningful for non-instanced maps where Spawn1Object actually spawns.
|
||||
if (!Instanceable())
|
||||
{
|
||||
sPoolMgr->UpdatePool<GameObject>(poolId, spawnId);
|
||||
RemoveGORespawnTime(spawnId);
|
||||
return;
|
||||
if (uint32 poolId = sPoolMgr->IsPartOfAPool<GameObject>(spawnId))
|
||||
{
|
||||
sPoolMgr->UpdatePool<GameObject>(poolId, spawnId);
|
||||
RemoveGORespawnTime(spawnId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
|
||||
|
||||
@@ -1487,6 +1487,27 @@ enum AcoreStrings
|
||||
|
||||
// Pet delete command
|
||||
LANG_PET_DELETE_NOT_FOUND = 35438,
|
||||
LANG_PET_DELETE_SUCCESS = 35439
|
||||
LANG_PET_DELETE_SUCCESS = 35439,
|
||||
|
||||
LANG_CHARACTER_DELETED_LIST_LIMIT = 35440,
|
||||
|
||||
// Respawn creature/gameobject by spawn GUID commands
|
||||
LANG_RESPAWN_GUID_CREATURE_NOT_FOUND = 35441,
|
||||
LANG_RESPAWN_GUID_MAP_NOT_LOADED = 35442,
|
||||
LANG_RESPAWN_GUID_CREATURE_ALIVE = 35443,
|
||||
LANG_RESPAWN_GUID_CREATURE_QUEUED = 35444,
|
||||
LANG_RESPAWN_GUID_GAMEOBJECT_ACTIVE = 35445,
|
||||
LANG_RESPAWN_GUID_GAMEOBJECT_QUEUED = 35446,
|
||||
// 35447 is reserved for LANG_LIST_RESPAWNS_NO_MAP (list respawns command, defined at the end of this enum)
|
||||
LANG_RESPAWN_GUID_GAMEOBJECT_NOT_FOUND = 35448,
|
||||
|
||||
// Respawn creature/gameobject by entry commands
|
||||
LANG_RESPAWN_ENTRY_CREATURE_NOT_FOUND = 35449,
|
||||
LANG_RESPAWN_ENTRY_GAMEOBJECT_NOT_FOUND = 35450,
|
||||
LANG_RESPAWN_ENTRY_CREATURE_QUEUED = 35451,
|
||||
LANG_RESPAWN_ENTRY_GAMEOBJECT_QUEUED = 35452,
|
||||
|
||||
// List respawns console support
|
||||
LANG_LIST_RESPAWNS_NO_MAP = 35447
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -138,6 +138,8 @@ void WorldConfig::BuildConfigCache()
|
||||
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_ON_DEATH, "DurabilityLoss.OnDeath", 10.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 100.0f; }, ">= 0 && <= 100");
|
||||
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_ON_SPIRIT_RESURRECT, "DurabilityLoss.OnSpiritResurrect", 25.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 100.0f; }, ">= 0 && <= 100");
|
||||
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_DAMAGE, "DurabilityLossChance.Damage", 0.5f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_ABSORB, "DurabilityLossChance.Absorb", 0.5f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_PARRY, "DurabilityLossChance.Parry", 0.05f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
@@ -602,6 +604,8 @@ void WorldConfig::BuildConfigCache()
|
||||
|
||||
SetConfigValue<bool>(CONFIG_LOGSPAMREPORTS, "LogSpamReports", true);
|
||||
|
||||
SetConfigValue<bool>(CONFIG_CHATLOG_ENABLED, "ChatLog.Enable", false);
|
||||
|
||||
// Whether to use LoS from game objects
|
||||
SetConfigValue<bool>(CONFIG_CHECK_GOBJECT_LOS, "CheckGameObjectLoS", true);
|
||||
|
||||
|
||||
@@ -480,6 +480,7 @@ enum ServerConfigs
|
||||
RATE_CORPSE_DECAY_LOOTED,
|
||||
RATE_INSTANCE_RESET_TIME,
|
||||
RATE_DURABILITY_LOSS_ON_DEATH,
|
||||
RATE_DURABILITY_LOSS_ON_SPIRIT_RESURRECT,
|
||||
RATE_DURABILITY_LOSS_DAMAGE,
|
||||
RATE_DURABILITY_LOSS_PARRY,
|
||||
RATE_DURABILITY_LOSS_ABSORB,
|
||||
@@ -491,6 +492,7 @@ enum ServerConfigs
|
||||
CONFIG_NEW_CHAR_STRING,
|
||||
CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS,
|
||||
CONFIG_ACHIEVEMENT_REALM_FIRST_KILL_WINDOW,
|
||||
CONFIG_CHATLOG_ENABLED,
|
||||
|
||||
MAX_NUM_SERVER_CONFIGS
|
||||
};
|
||||
|
||||
@@ -95,18 +95,22 @@ public:
|
||||
uint32 accountId; ///< the account id
|
||||
std::string accountName; ///< the account name
|
||||
time_t deleteDate; ///< the date at which the character has been deleted
|
||||
uint8 level; ///< the character level at the time of deletion
|
||||
};
|
||||
|
||||
typedef std::list<DeletedInfo> DeletedInfoList;
|
||||
|
||||
static constexpr std::size_t MAX_DELETED_CHAR_RESULTS = 50;
|
||||
|
||||
/**
|
||||
* Collects all GUIDs (and related info) from deleted characters which are still in the database.
|
||||
*
|
||||
* @param foundList a reference to an std::list which will be filled with info data
|
||||
* @param searchString the search string which either contains a player GUID or a part fo the character-name
|
||||
* @param searchString the search string which either contains a player GUID or a part of the character-name
|
||||
* @param limitResults if true, caps results at MAX_DELETED_CHAR_RESULTS + 1 using a DB-level LIMIT
|
||||
* @return returns false if there was a problem while selecting the characters (e.g. player name not normalizeable)
|
||||
*/
|
||||
static bool GetDeletedCharacterInfoList(DeletedInfoList& foundList, std::string searchString)
|
||||
static bool GetDeletedCharacterInfoList(DeletedInfoList& foundList, std::string searchString, bool limitResults = false)
|
||||
{
|
||||
PreparedQueryResult result;
|
||||
CharacterDatabasePreparedStatement* stmt = nullptr;
|
||||
@@ -125,7 +129,8 @@ public:
|
||||
if (!normalizePlayerName(searchString))
|
||||
return false;
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME);
|
||||
CharacterDatabaseStatements nameStmt = limitResults ? CHAR_SEL_CHAR_DEL_INFO_BY_NAME_LIMIT : CHAR_SEL_CHAR_DEL_INFO_BY_NAME;
|
||||
stmt = CharacterDatabase.GetPreparedStatement(nameStmt);
|
||||
stmt->SetData(0, searchString);
|
||||
result = CharacterDatabase.Query(stmt);
|
||||
}
|
||||
@@ -151,6 +156,7 @@ public:
|
||||
// account name will be empty for nonexisting account
|
||||
AccountMgr::GetName(info.accountId, info.accountName);
|
||||
info.deleteDate = time_t(fields[3].Get<uint32>());
|
||||
info.level = fields[4].Get<uint8>();
|
||||
foundList.push_back(info);
|
||||
} while (result->NextRow());
|
||||
}
|
||||
@@ -183,11 +189,13 @@ public:
|
||||
|
||||
if (!handler->GetSession())
|
||||
handler->PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CONSOLE,
|
||||
itr->lowGuid, itr->name, itr->accountName.empty() ? "<Not existing>" : itr->accountName,
|
||||
itr->lowGuid, itr->name, uint32(itr->level),
|
||||
itr->accountName.empty() ? "<Not existing>" : itr->accountName,
|
||||
itr->accountId, dateStr);
|
||||
else
|
||||
handler->PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CHAT,
|
||||
itr->lowGuid, itr->name, itr->accountName.empty() ? "<Not existing>" : itr->accountName,
|
||||
itr->lowGuid, itr->name, uint32(itr->level),
|
||||
itr->accountName.empty() ? "<Not existing>" : itr->accountName,
|
||||
itr->accountId, dateStr);
|
||||
}
|
||||
|
||||
@@ -590,7 +598,7 @@ public:
|
||||
if (needleStr)
|
||||
needle.assign(*needleStr);
|
||||
DeletedInfoList foundList;
|
||||
if (!GetDeletedCharacterInfoList(foundList, needle))
|
||||
if (!GetDeletedCharacterInfoList(foundList, needle, true))
|
||||
return false;
|
||||
|
||||
// if no characters have been found, output a warning
|
||||
@@ -600,8 +608,15 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool truncated = foundList.size() > MAX_DELETED_CHAR_RESULTS;
|
||||
if (truncated)
|
||||
foundList.resize(MAX_DELETED_CHAR_RESULTS);
|
||||
|
||||
HandleCharacterDeletedListHelper(foundList, handler);
|
||||
|
||||
if (truncated)
|
||||
handler->SendSysMessage(LANG_CHARACTER_DELETED_LIST_LIMIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
{ "item", HandleListItemCommand, rbac::RBAC_PERM_COMMAND_LIST_ITEM, Console::Yes },
|
||||
{ "object", HandleListObjectCommand, rbac::RBAC_PERM_COMMAND_LIST_OBJECT, Console::Yes },
|
||||
{ "auras", listAurasCommandTable },
|
||||
{ "respawns", HandleListRespawnsCommand, rbac::RBAC_PERM_COMMAND_LIST_CREATURE, Console::No },
|
||||
{ "respawns", HandleListRespawnsCommand, rbac::RBAC_PERM_COMMAND_LIST_RESPAWNS, Console::Yes },
|
||||
};
|
||||
static ChatCommandTable commandTable =
|
||||
{
|
||||
@@ -521,13 +521,35 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleListRespawnsCommand(ChatHandler* handler)
|
||||
static bool HandleListRespawnsCommand(ChatHandler* handler, Optional<uint32> firstArg, Optional<uint32> secondArg, Optional<uint32> thirdArg)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
if (!player)
|
||||
return false;
|
||||
Map* map = nullptr;
|
||||
Optional<uint32> entryFilter;
|
||||
|
||||
if (handler->GetSession())
|
||||
{
|
||||
// In-game: first arg = entryId (optional), use player's current map
|
||||
map = handler->GetSession()->GetPlayer()->GetMap();
|
||||
entryFilter = firstArg;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Console: first arg = mapId (required), second = instanceId, third = entryId
|
||||
if (!firstArg)
|
||||
{
|
||||
handler->SendSysMessage(LANG_LIST_RESPAWNS_NO_MAP);
|
||||
return false;
|
||||
}
|
||||
map = sMapMgr->FindMap(*firstArg, secondArg.value_or(0));
|
||||
entryFilter = thirdArg;
|
||||
}
|
||||
|
||||
if (!map)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_MAP_NOT_LOADED, firstArg.value_or(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
Map* map = player->GetMap();
|
||||
uint32 count = 0;
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
|
||||
@@ -535,7 +557,7 @@ public:
|
||||
for (auto const& pair : map->GetCreatureRespawnTimes())
|
||||
{
|
||||
CreatureData const* data = sObjectMgr->GetCreatureData(pair.first);
|
||||
if (!data)
|
||||
if (!data || (entryFilter && data->id1 != *entryFilter))
|
||||
continue;
|
||||
|
||||
CreatureTemplate const* cTemplate = sObjectMgr->GetCreatureTemplate(data->id1);
|
||||
@@ -555,7 +577,7 @@ public:
|
||||
for (auto const& pair : map->GetGORespawnTimes())
|
||||
{
|
||||
GameObjectData const* data = sObjectMgr->GetGameObjectData(pair.first);
|
||||
if (!data)
|
||||
if (!data || (entryFilter && data->id != *entryFilter))
|
||||
continue;
|
||||
|
||||
GameObjectTemplate const* goTemplate = sObjectMgr->GetGameObjectTemplate(data->id);
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
{ "gmannounce", HandleGMAnnounceCommand, rbac::RBAC_PERM_COMMAND_GMANNOUNCE, Console::Yes },
|
||||
{ "notify", HandleNotifyCommand, rbac::RBAC_PERM_COMMAND_NOTIFY, Console::Yes },
|
||||
{ "gmnotify", HandleGMNotifyCommand, rbac::RBAC_PERM_COMMAND_GMNOTIFY, Console::Yes },
|
||||
{ "whispers", HandleWhispersCommand, rbac::RBAC_PERM_COMMAND_WHISPERS, Console::No },
|
||||
{ "whispers", HandleWhispersCommand, rbac::RBAC_PERM_CAN_FILTER_WHISPERS, Console::No },
|
||||
};
|
||||
return commandTable;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "CommandScript.h"
|
||||
#include "Common.h"
|
||||
#include "GameGraveyard.h"
|
||||
#include "GameObject.h"
|
||||
#include "GameTime.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridTerrainLoader.h"
|
||||
@@ -177,6 +178,10 @@ public:
|
||||
{ "pinfo", HandlePInfoCommand, rbac::RBAC_PERM_COMMAND_PINFO, Console::Yes },
|
||||
{ "respawn", HandleRespawnCommand, rbac::RBAC_PERM_COMMAND_RESPAWN, Console::No },
|
||||
{ "respawn all", HandleRespawnAllCommand, rbac::RBAC_PERM_COMMAND_RESPAWN_ALL, Console::No },
|
||||
{ "respawn creature guid", HandleRespawnCreatureByGuidCommand, rbac::RBAC_PERM_COMMAND_RESPAWN_CREATURE_GUID, Console::Yes },
|
||||
{ "respawn gameobject guid", HandleRespawnGameObjectByGuidCommand, rbac::RBAC_PERM_COMMAND_RESPAWN_GAMEOBJECT_GUID, Console::Yes },
|
||||
{ "respawn creature entry", HandleRespawnCreatureByEntryCommand, rbac::RBAC_PERM_COMMAND_RESPAWN_CREATURE_ENTRY, Console::Yes },
|
||||
{ "respawn gameobject entry", HandleRespawnGameObjectByEntryCommand, rbac::RBAC_PERM_COMMAND_RESPAWN_GAMEOBJECT_ENTRY, Console::Yes },
|
||||
{ "mute", HandleMuteCommand, rbac::RBAC_PERM_COMMAND_MUTE, Console::Yes },
|
||||
{ "mutehistory", HandleMuteInfoCommand, rbac::RBAC_PERM_COMMAND_MUTEHISTORY, Console::Yes },
|
||||
{ "unmute", HandleUnmuteCommand, rbac::RBAC_PERM_COMMAND_UNMUTE, Console::Yes },
|
||||
@@ -2448,6 +2453,265 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleRespawnCreatureByGuidCommand(ChatHandler* handler, ObjectGuid::LowType spawnId)
|
||||
{
|
||||
CreatureData const* creData = sObjectMgr->GetCreatureData(spawnId);
|
||||
if (!creData)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_RESPAWN_GUID_CREATURE_NOT_FOUND, spawnId);
|
||||
return false;
|
||||
}
|
||||
|
||||
Map* map = nullptr;
|
||||
if (handler->GetSession())
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
if (player->GetMapId() == creData->mapid)
|
||||
map = player->GetMap();
|
||||
}
|
||||
if (!map)
|
||||
map = sMapMgr->FindMap(creData->mapid, 0);
|
||||
|
||||
if (!map)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_MAP_NOT_LOADED, creData->mapid);
|
||||
return true;
|
||||
}
|
||||
|
||||
// First pass: check if any instance is alive
|
||||
bool isAlive = false;
|
||||
auto const creBounds = map->GetCreatureBySpawnIdStore().equal_range(spawnId);
|
||||
for (auto itr = creBounds.first; itr != creBounds.second; ++itr)
|
||||
{
|
||||
if (itr->second->IsAlive())
|
||||
{
|
||||
isAlive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isAlive)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_ALIVE, spawnId, creData->id1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Second pass: respawn any dead corpses in the world
|
||||
for (auto itr = creBounds.first; itr != creBounds.second; ++itr)
|
||||
{
|
||||
if (itr->second->isDead())
|
||||
itr->second->Respawn(true);
|
||||
}
|
||||
// Also trigger via respawn time queue for fully-removed spawns
|
||||
if (map->GetCreatureRespawnTime(spawnId) > 0)
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
map->SaveCreatureRespawnTime(spawnId, now);
|
||||
}
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_CREATURE_QUEUED, spawnId, creData->id1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleRespawnGameObjectByGuidCommand(ChatHandler* handler, ObjectGuid::LowType spawnId)
|
||||
{
|
||||
GameObjectData const* goData = sObjectMgr->GetGameObjectData(spawnId);
|
||||
if (!goData)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_RESPAWN_GUID_GAMEOBJECT_NOT_FOUND, spawnId);
|
||||
return false;
|
||||
}
|
||||
|
||||
Map* map = nullptr;
|
||||
if (handler->GetSession())
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
if (player->GetMapId() == goData->mapid)
|
||||
map = player->GetMap();
|
||||
}
|
||||
if (!map)
|
||||
map = sMapMgr->FindMap(goData->mapid, 0);
|
||||
|
||||
if (!map)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_MAP_NOT_LOADED, goData->mapid);
|
||||
return true;
|
||||
}
|
||||
|
||||
// First pass: check if any instance is already active
|
||||
bool isActive = false;
|
||||
auto const goBounds = map->GetGameObjectBySpawnIdStore().equal_range(spawnId);
|
||||
for (auto itr = goBounds.first; itr != goBounds.second; ++itr)
|
||||
{
|
||||
if (itr->second->isSpawned())
|
||||
{
|
||||
isActive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isActive)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_GAMEOBJECT_ACTIVE, spawnId, goData->id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Second pass: respawn inactive objects in the world
|
||||
for (auto itr = goBounds.first; itr != goBounds.second; ++itr)
|
||||
itr->second->Respawn();
|
||||
// Also trigger via respawn time queue for fully-removed spawns
|
||||
if (map->GetGORespawnTime(spawnId) > 0)
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
map->SaveGORespawnTime(spawnId, now);
|
||||
}
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_GAMEOBJECT_QUEUED, spawnId, goData->id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleRespawnCreatureByEntryCommand(ChatHandler* handler, uint32 entry, Optional<uint32> mapIdArg, Optional<uint32> instanceIdArg)
|
||||
{
|
||||
if (!sObjectMgr->GetCreatureTemplate(entry))
|
||||
{
|
||||
handler->SendErrorMessage(LANG_RESPAWN_ENTRY_CREATURE_NOT_FOUND, entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
Map* map = nullptr;
|
||||
if (handler->GetSession())
|
||||
{
|
||||
// In-game: always use the player's current map
|
||||
map = handler->GetSession()->GetPlayer()->GetMap();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Console: mapId required, instanceId optional
|
||||
if (!mapIdArg)
|
||||
{
|
||||
handler->SendSysMessage(LANG_LIST_RESPAWNS_NO_MAP);
|
||||
return false;
|
||||
}
|
||||
map = sMapMgr->FindMap(*mapIdArg, instanceIdArg.value_or(0));
|
||||
}
|
||||
|
||||
if (!map)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_MAP_NOT_LOADED, mapIdArg.value_or(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
uint32 count = 0;
|
||||
|
||||
// Phase 1: respawn dead corpses that are still tracked in the spawn-id store.
|
||||
// Collect first to avoid iterator invalidation caused by Respawn().
|
||||
std::vector<Creature*> deadCreatures;
|
||||
for (auto const& [spawnId, creature] : map->GetCreatureBySpawnIdStore())
|
||||
{
|
||||
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
|
||||
if (!data || data->id1 != entry)
|
||||
continue;
|
||||
if (creature->isDead())
|
||||
deadCreatures.push_back(creature);
|
||||
}
|
||||
for (Creature* creature : deadCreatures)
|
||||
{
|
||||
creature->Respawn(true);
|
||||
++count;
|
||||
}
|
||||
|
||||
// Phase 2: set respawn time to now for fully-removed spawns, skipping pools
|
||||
std::vector<ObjectGuid::LowType> toRespawn;
|
||||
for (auto const& [spawnId, respawnTime] : map->GetCreatureRespawnTimes())
|
||||
{
|
||||
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
|
||||
if (!data || data->id1 != entry)
|
||||
continue;
|
||||
if (sPoolMgr->IsPartOfAPool<Creature>(spawnId))
|
||||
continue;
|
||||
toRespawn.push_back(spawnId);
|
||||
}
|
||||
for (ObjectGuid::LowType spawnId : toRespawn)
|
||||
{
|
||||
map->SaveCreatureRespawnTime(spawnId, now);
|
||||
++count;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_RESPAWN_ENTRY_CREATURE_QUEUED, count, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleRespawnGameObjectByEntryCommand(ChatHandler* handler, uint32 entry, Optional<uint32> mapIdArg, Optional<uint32> instanceIdArg)
|
||||
{
|
||||
if (!sObjectMgr->GetGameObjectTemplate(entry))
|
||||
{
|
||||
handler->SendErrorMessage(LANG_RESPAWN_ENTRY_GAMEOBJECT_NOT_FOUND, entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
Map* map = nullptr;
|
||||
if (handler->GetSession())
|
||||
{
|
||||
// In-game: always use the player's current map
|
||||
map = handler->GetSession()->GetPlayer()->GetMap();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Console: mapId required, instanceId optional
|
||||
if (!mapIdArg)
|
||||
{
|
||||
handler->SendSysMessage(LANG_LIST_RESPAWNS_NO_MAP);
|
||||
return false;
|
||||
}
|
||||
map = sMapMgr->FindMap(*mapIdArg, instanceIdArg.value_or(0));
|
||||
}
|
||||
|
||||
if (!map)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_RESPAWN_GUID_MAP_NOT_LOADED, mapIdArg.value_or(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
uint32 count = 0;
|
||||
|
||||
// Phase 1: respawn inactive objects that are still tracked in the spawn-id store.
|
||||
// Collect first to avoid iterator invalidation caused by Respawn().
|
||||
std::vector<GameObject*> inactiveGOs;
|
||||
for (auto const& [spawnId, go] : map->GetGameObjectBySpawnIdStore())
|
||||
{
|
||||
GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
|
||||
if (!data || data->id != entry)
|
||||
continue;
|
||||
if (!go->isSpawned())
|
||||
inactiveGOs.push_back(go);
|
||||
}
|
||||
for (GameObject* go : inactiveGOs)
|
||||
{
|
||||
go->Respawn();
|
||||
++count;
|
||||
}
|
||||
|
||||
// Phase 2: set respawn time to now for fully-removed spawns, skipping pools
|
||||
std::vector<ObjectGuid::LowType> toRespawn;
|
||||
for (auto const& [spawnId, respawnTime] : map->GetGORespawnTimes())
|
||||
{
|
||||
GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
|
||||
if (!data || data->id != entry)
|
||||
continue;
|
||||
if (sPoolMgr->IsPartOfAPool<GameObject>(spawnId))
|
||||
continue;
|
||||
toRespawn.push_back(spawnId);
|
||||
}
|
||||
for (ObjectGuid::LowType spawnId : toRespawn)
|
||||
{
|
||||
map->SaveGORespawnTime(spawnId, now);
|
||||
++count;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_RESPAWN_ENTRY_GAMEOBJECT_QUEUED, count, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleRespawnCommand(ChatHandler* handler)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
@@ -77,18 +77,6 @@ struct boss_moroes : public BossAI
|
||||
_activeGuests = 0;
|
||||
}
|
||||
|
||||
void InitializeAI() override
|
||||
{
|
||||
BossAI::InitializeAI();
|
||||
InitializeGuests();
|
||||
}
|
||||
|
||||
void JustReachedHome() override
|
||||
{
|
||||
BossAI::JustReachedHome();
|
||||
InitializeGuests();
|
||||
}
|
||||
|
||||
void InitializeGuests()
|
||||
{
|
||||
if (!me->IsAlive())
|
||||
@@ -96,16 +84,18 @@ struct boss_moroes : public BossAI
|
||||
|
||||
if (_activeGuests == 0)
|
||||
{
|
||||
_activeGuests |= 0x3F;
|
||||
_activeGuests = 0x3F;
|
||||
uint8 rand1 = RAND(0x01, 0x02, 0x04);
|
||||
uint8 rand2 = RAND(0x08, 0x10, 0x20);
|
||||
_activeGuests &= ~(rand1 | rand2);
|
||||
}
|
||||
for (uint8 i = 0; i < MAX_GUEST_COUNT; ++i)
|
||||
|
||||
uint8 positionIndex = 0;
|
||||
for (uint8 i = 0; i < MAX_GUEST_COUNT && positionIndex < ACTIVE_GUEST_COUNT; ++i)
|
||||
{
|
||||
if ((1 << i) & _activeGuests)
|
||||
{
|
||||
me->SummonCreature(GuestEntries[i], GuestsPosition[summons.size()], TEMPSUMMON_MANUAL_DESPAWN);
|
||||
me->SummonCreature(GuestEntries[i], GuestsPosition[positionIndex++], TEMPSUMMON_MANUAL_DESPAWN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +120,8 @@ struct boss_moroes : public BossAI
|
||||
_recentlySpoken = false;
|
||||
_vanished = false;
|
||||
|
||||
InitializeGuests();
|
||||
|
||||
ScheduleHealthCheckEvent(30, [&] {
|
||||
DoCastSelf(SPELL_FRENZY, true);
|
||||
});
|
||||
@@ -203,6 +195,9 @@ struct boss_moroes : public BossAI
|
||||
}
|
||||
}
|
||||
|
||||
if (guestList.empty())
|
||||
return nullptr;
|
||||
|
||||
return Acore::Containers::SelectRandomContainerElement(guestList);
|
||||
}
|
||||
|
||||
@@ -211,12 +206,12 @@ struct boss_moroes : public BossAI
|
||||
bool guestsInRoom = true;
|
||||
summons.DoForAllSummons([&guestsInRoom](WorldObject* summon)
|
||||
{
|
||||
if ((summon->ToCreature()->GetPositionX()) < -11028.f || (summon->ToCreature()->GetPositionY()) < -1955.f) //boundaries of the two doors
|
||||
Creature* creature = summon->ToCreature();
|
||||
if (creature->IsAlive() &&
|
||||
((creature->GetPositionX() < -11028.f) || (creature->GetPositionY() < -1955.f))) // boundaries of the two doors
|
||||
{
|
||||
guestsInRoom = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return guestsInRoom;
|
||||
@@ -229,10 +224,6 @@ struct boss_moroes : public BossAI
|
||||
if (!CheckGuestsInRoom())
|
||||
{
|
||||
EnterEvadeMode();
|
||||
summons.DoForAllSummons([](WorldObject* summon)
|
||||
{
|
||||
summon->ToCreature()->DespawnOnEvade(5s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,9 @@ public:
|
||||
case EVENT_COMPLETE:
|
||||
{
|
||||
DoCast(me, SPELL_IDOM_ROOM_CAMERA_SHAKE);
|
||||
me->SummonGameObject(GO_BELNISTRASZS_BRAZIER, 2577.196f, 947.0781f, 53.16757f, 2.356195f, 0, 0, 0.9238796f, 0.3826832f, 3600);
|
||||
// Summon via the map so the brazier is not tied to Belnistrasz's
|
||||
// m_gameObj list and survives his despawn below.
|
||||
me->GetMap()->SummonGameObject(GO_BELNISTRASZS_BRAZIER, 2577.196f, 947.0781f, 53.16757f, 2.356195f, 0, 0, 0.9238796f, 0.3826832f, 3600);
|
||||
std::list<WorldObject*> ClusterList;
|
||||
Acore::AllWorldObjectsInRange objects(me, 50.0f);
|
||||
Acore::WorldObjectListSearcher<Acore::AllWorldObjectsInRange> searcher(me, ClusterList, objects);
|
||||
@@ -223,7 +225,7 @@ public:
|
||||
}
|
||||
}
|
||||
instance->SetData(GO_BELNISTRASZS_BRAZIER, DONE);
|
||||
me->DespawnOrUnsummon();
|
||||
me->DespawnOrUnsummon(5s);
|
||||
break;
|
||||
}
|
||||
case EVENT_FIREBALL:
|
||||
|
||||
@@ -36,8 +36,8 @@ enum Data
|
||||
// Non-boss data (SetData/GetData only)
|
||||
DATA_INTRO,
|
||||
DATA_FROSTSWORN_GENERAL,
|
||||
DATA_BATTERED_HILT = 6, //For SAI DB
|
||||
DATA_LK_INTRO,
|
||||
DATA_BATTERED_HILT,
|
||||
DATA_WAVE_NUMBER,
|
||||
DATA_LK_BATTLE,
|
||||
DATA_SHIP_CAPTAIN,
|
||||
|
||||
@@ -60,7 +60,6 @@ public:
|
||||
case 5:
|
||||
_owner.SetFacingTo(2.82f);
|
||||
_owner.SetStandState(UNIT_STAND_STATE_KNEEL);
|
||||
_owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, 6), 3s);
|
||||
break;
|
||||
case 6:
|
||||
if (InstanceScript* inst = _owner.GetInstanceScript())
|
||||
|
||||
@@ -155,6 +155,7 @@ struct boss_gal_darah : public BossAI
|
||||
{
|
||||
Talk(SAY_DEATH);
|
||||
BossAI::JustDied(killer);
|
||||
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PUNCTURE);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit*) override
|
||||
|
||||
@@ -133,6 +133,7 @@ enum Events
|
||||
EVENT_UNBOUND_PLAGUE,
|
||||
EVENT_MALLEABLE_GOO,
|
||||
EVENT_CHOKING_GAS_BOMB,
|
||||
EVENT_MUTATED_PLAGUE,
|
||||
};
|
||||
|
||||
#define EVENT_GROUP_ABILITIES 1
|
||||
@@ -653,6 +654,11 @@ public:
|
||||
me->CastSpell(me, SPELL_CHOKING_GAS_BOMB, false);
|
||||
events.ScheduleEvent(EVENT_CHOKING_GAS_BOMB, 35s, 40s, EVENT_GROUP_ABILITIES);
|
||||
break;
|
||||
case EVENT_MUTATED_PLAGUE:
|
||||
if (Unit* target = me->GetVictim())
|
||||
me->CastSpell(target, SPELL_MUTATED_PLAGUE, false);
|
||||
events.ScheduleEvent(EVENT_MUTATED_PLAGUE, 10s, EVENT_GROUP_ABILITIES);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -721,6 +727,7 @@ public:
|
||||
case 2:
|
||||
_phase = 3;
|
||||
events.CancelEvent(EVENT_UNSTABLE_EXPERIMENT);
|
||||
events.ScheduleEvent(EVENT_MUTATED_PLAGUE, 10s, EVENT_GROUP_ABILITIES);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -441,7 +441,6 @@ struct boss_kologarn : public BossAI
|
||||
{
|
||||
events.ScheduleEvent(EVENT_FOCUSED_EYEBEAM, 20s);
|
||||
me->CastSpell(me, SPELL_FOCUSED_EYEBEAM_SUMMON, false);
|
||||
Talk(EMOTE_EYES);
|
||||
return;
|
||||
}
|
||||
case EVENT_RESTORE_ARM_LEFT:
|
||||
@@ -605,6 +604,7 @@ struct boss_kologarn_eyebeam : public ScriptedAI
|
||||
if (Creature* cr = _instance->GetCreature(BOSS_KOLOGARN))
|
||||
{
|
||||
me->CastSpell(cr, me->GetEntry() == NPC_EYE_LEFT ? SPELL_FOCUSED_EYEBEAM_LEFT : SPELL_FOCUSED_EYEBEAM_RIGHT, true);
|
||||
cr->AI()->Talk(EMOTE_EYES, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ enum Actions
|
||||
ACTION_START_ENCOUNTER = 1,
|
||||
ACTION_DRAKE_BREATH = 2,
|
||||
ACTION_PHASE2 = 3,
|
||||
ACTION_HARPOON_HIT = 4,
|
||||
};
|
||||
|
||||
enum CombatPhase
|
||||
@@ -171,8 +170,6 @@ public:
|
||||
_events.Reset();
|
||||
_summons.DespawnAll();
|
||||
_phase = PHASE_GROUND;
|
||||
_harpoonHit = 0;
|
||||
_loveSkadi = 0;
|
||||
_firstWaveSummoned = false;
|
||||
_encounterStarted = false;
|
||||
|
||||
@@ -266,8 +263,6 @@ public:
|
||||
_events.ScheduleEvent(EVENT_SKADI_RESET_CHECK, 6s);
|
||||
break;
|
||||
case ACTION_DRAKE_BREATH:
|
||||
if (_loveSkadi == 1)
|
||||
++_loveSkadi;
|
||||
Talk(SAY_DRAKE_BREATH);
|
||||
break;
|
||||
case ACTION_PHASE2:
|
||||
@@ -283,21 +278,9 @@ public:
|
||||
_events.ScheduleEvent(EVENT_SKADI_SPEAR, 11s);
|
||||
_events.ScheduleEvent(EVENT_SKADI_WHIRLWIND, 23s);
|
||||
break;
|
||||
case ACTION_HARPOON_HIT:
|
||||
++_harpoonHit;
|
||||
if (_harpoonHit == 1)
|
||||
_loveSkadi = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetData(uint32 id) const override
|
||||
{
|
||||
if (id == DATA_LOVE_TO_SKADI)
|
||||
return _loveSkadi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
_events.Update(diff);
|
||||
@@ -384,8 +367,6 @@ public:
|
||||
EventMap _events;
|
||||
SummonList _summons;
|
||||
CombatPhase _phase;
|
||||
uint8 _harpoonHit;
|
||||
uint8 _loveSkadi;
|
||||
bool _firstWaveSummoned;
|
||||
bool _encounterStarted;
|
||||
};
|
||||
@@ -415,6 +396,7 @@ public:
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
me->SetSpeedRate(MOVE_RUN, 2.5f);
|
||||
_flyingToSide = false;
|
||||
_passFreshStart = false;
|
||||
}
|
||||
|
||||
void DoAction(int32 param) override
|
||||
@@ -430,13 +412,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
|
||||
{
|
||||
if (spellInfo->Id == SPELL_LAUNCH_HARPOON)
|
||||
if (Creature* skadi = _instance->GetCreature(DATA_SKADI_THE_RUTHLESS))
|
||||
skadi->AI()->DoAction(ACTION_HARPOON_HIT);
|
||||
}
|
||||
|
||||
void MovementInform(uint32 type, uint32 /*id*/) override
|
||||
{
|
||||
if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized())
|
||||
@@ -470,6 +445,7 @@ public:
|
||||
FORCED_MOVEMENT_RUN);
|
||||
break;
|
||||
case EVENT_GRAUF_BREATH_START:
|
||||
_passFreshStart = me->IsFullHealth();
|
||||
me->GetMotionMaster()->MovePath(
|
||||
_lastSide == POINT_LEFT ? PATH_LEFT : PATH_RIGHT,
|
||||
FORCED_MOVEMENT_RUN);
|
||||
@@ -493,10 +469,11 @@ public:
|
||||
{
|
||||
skadi->ExitVehicle();
|
||||
skadi->AI()->DoAction(ACTION_PHASE2);
|
||||
|
||||
if (skadi->AI()->GetData(DATA_LOVE_TO_SKADI) == 1)
|
||||
_instance->SetData(DATA_SKADI_ACHIEVEMENT, true);
|
||||
}
|
||||
|
||||
if (_passFreshStart)
|
||||
_instance->SetData(DATA_SKADI_ACHIEVEMENT, true);
|
||||
|
||||
me->DespawnOrUnsummon(6s);
|
||||
}
|
||||
|
||||
@@ -506,6 +483,7 @@ public:
|
||||
SummonList _summons;
|
||||
uint8 _lastSide = POINT_LEFT;
|
||||
bool _flyingToSide = false;
|
||||
bool _passFreshStart = false;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -313,6 +313,7 @@ class spell_hun_taming_the_beast : public AuraScript
|
||||
if (Creature* creature = target->ToCreature())
|
||||
{
|
||||
creature->GetThreatMgr().ClearAllThreat();
|
||||
creature->GetCombatManager().EndAllCombat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Guild.h"
|
||||
#include "Log.h"
|
||||
#include "PlayerScript.h"
|
||||
#include "World.h"
|
||||
|
||||
class ChatLogScript : public PlayerScript
|
||||
{
|
||||
@@ -38,6 +39,9 @@ public:
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg) override
|
||||
{
|
||||
if (!sWorld->getBoolConfig(CONFIG_CHATLOG_ENABLED))
|
||||
return true;
|
||||
|
||||
std::string logType = "";
|
||||
std::string chatType = "";
|
||||
|
||||
@@ -67,6 +71,9 @@ public:
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 /*type*/, uint32 lang, std::string& msg, Player* receiver) override
|
||||
{
|
||||
if (!sWorld->getBoolConfig(CONFIG_CHATLOG_ENABLED))
|
||||
return true;
|
||||
|
||||
//! NOTE:
|
||||
//! LANG_ADDON can only be sent by client in "PARTY", "RAID", "GUILD", "BATTLEGROUND", "WHISPER"
|
||||
std::string logType = (lang != LANG_ADDON) ? "chat." : "chat.addon.";
|
||||
@@ -80,6 +87,9 @@ public:
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg, Group* group) override
|
||||
{
|
||||
if (!sWorld->getBoolConfig(CONFIG_CHATLOG_ENABLED))
|
||||
return true;
|
||||
|
||||
//! NOTE:
|
||||
//! LANG_ADDON can only be sent by client in "PARTY", "RAID", "GUILD", "BATTLEGROUND", "WHISPER"
|
||||
std::string logType = (lang != LANG_ADDON) ? "chat." : "chat.addon.";
|
||||
@@ -116,6 +126,9 @@ public:
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg, Guild* guild) override
|
||||
{
|
||||
if (!sWorld->getBoolConfig(CONFIG_CHATLOG_ENABLED))
|
||||
return true;
|
||||
|
||||
//! NOTE:
|
||||
//! LANG_ADDON can only be sent by client in "PARTY", "RAID", "GUILD", "BATTLEGROUND", "WHISPER"
|
||||
std::string logType = (lang != LANG_ADDON) ? "chat." : "chat.addon.";
|
||||
@@ -141,6 +154,9 @@ public:
|
||||
|
||||
bool OnPlayerCanUseChat(Player* player, uint32 /*type*/, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
||||
{
|
||||
if (!sWorld->getBoolConfig(CONFIG_CHATLOG_ENABLED))
|
||||
return true;
|
||||
|
||||
bool isSystem = channel &&
|
||||
(channel->HasFlag(CHANNEL_FLAG_TRADE) ||
|
||||
channel->HasFlag(CHANNEL_FLAG_GENERAL) ||
|
||||
|
||||
Reference in New Issue
Block a user