Merge branch 'master' into test-staging

This commit is contained in:
Keleborn
2026-05-15 22:52:49 -07:00
32 changed files with 604 additions and 90 deletions

6
.gitignore vendored
View File

@@ -54,9 +54,11 @@ CMakeLists.txt.user
*.LOCAL.*
#
# AI tools
# Claude
#
.claude
.claude/worktrees
.claude/settings.local.json
.claude/plans/**
#
# IDE & other software

View 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;

View 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);

View 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\'');

View 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 результатов. Пожалуйста, используйте более конкретную строку поиска.');

View 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;

View 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);

View 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.');

View 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);

View File

@@ -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.

View File

@@ -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);

View File

@@ -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,

View File

@@ -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
};

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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
};

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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,

View File

@@ -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())

View File

@@ -155,6 +155,7 @@ struct boss_gal_darah : public BossAI
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PUNCTURE);
}
void KilledUnit(Unit*) override

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;
};
};

View File

@@ -313,6 +313,7 @@ class spell_hun_taming_the_beast : public AuraScript
if (Creature* creature = target->ToCreature())
{
creature->GetThreatMgr().ClearAllThreat();
creature->GetCombatManager().EndAllCombat();
}
}
}

View File

@@ -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) ||