Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2025-01-31 18:29:34 +08:00
38 changed files with 671 additions and 278 deletions

109
.github/SECURITY.md vendored
View File

@@ -4,60 +4,91 @@
We support the following versions of dependencies.
:white_check_mark: = supported
| Icon | Meaning |
| :------------------- | :---------------: |
| :white_check_mark: | **Supported** |
| :red_circle: | **NOT** Supported |
| :large_blue_diamond: | **Recommended** |
:red_circle: = NOT supported
### Versions of AzerothCore:
unspecified = might work but no guarantee
| AzerothCore Branch | Status | Recommended |
| ---------------------------- | :----------------: | :------------------: |
| **master** | :white_check_mark: | :large_blue_diamond: |
| Any non-official fork | :red_circle: | |
| Any Playerbots fork | :red_circle: | |
| Any NPCBots fork | :red_circle: | |
| Any AC (non-official) repack | :red_circle: | |
Versions of AzerothCore:
### Supported Operating Systems
| AzerothCore Branch | Supported |
| ------------------ | ------------------ |
| master | :white_check_mark: |
| Any playerbot fork | :red_circle: |
| Any NPCBot fork | :red_circle: |
| Linux (Ubuntu) | Status | Recommended |
| :------------- | :----------------: | :------------------: |
| 24.04 | :white_check_mark: | :large_blue_diamond: |
| 22.04 | :white_check_mark: | |
| 20.04 ≤ | :red_circle: | |
Versions of MySQL:
| macOS | Status | Recommended |
| :---- | :----------------: | :------------------: |
| 14 | :white_check_mark: | :large_blue_diamond: |
| 12 ≤ | :red_circle: | |
| MySQL Version | Supported |
| ------------- | ------------------ |
| 8.4 | :white_check_mark: |
| 8.0 | :white_check_mark: |
| 5.7 and lower | :red_circle: |
| Windows | Status | Recommended |
| :------------ | :----------------: | :------------------: |
| Windows 11 | :white_check_mark: | :large_blue_diamond: |
| Windows 10 | :white_check_mark: |
| Windows 8.1 ≤ | :red_circle: |
Versions of CLang:
<br>
| CLang Version | Supported |
| ------------- | ------------------ |
| 18 | :white_check_mark: |
| 15 | :white_check_mark: |
| 14 and lower | :red_circle: |
### Supported Boost Versions:
Versions of GCC:
| Boost | Status | Recommended |
| :----- | :----------------: | :------------------: |
| 1.70 ≥ | :white_check_mark: | :large_blue_diamond: |
| GCC Version | Supported |
| ------------ | ------------------ |
| 14 | :white_check_mark: |
| 12 | :white_check_mark: |
| 11 and lower | :red_circle: |
### Supported OpenSSL Versions:
Versions of Ubuntu:
| OpenSSL | Status | Recommended |
| :------ | :----------------: | :------------------: |
| 3.X.X ≥ | :white_check_mark: | :large_blue_diamond: |
| Ubuntu version | Supported |
| --------------- | ------------------ |
| 24.04 | :white_check_mark: |
| 22.04 | :white_check_mark: |
| 20.04 and lower | :red_circle: |
### Supported CMake Versions:
Versions of macOS:
| CMake | Status | Recommended |
| :----- | :----------------: | :------------------: |
| 3.16 ≥ | :white_check_mark: | :large_blue_diamond: |
| macOS Version | Supported |
| ------------- | ------------------ |
| 14 | :white_check_mark: |
| 12 and lower | :red_circle: |
### Supported MySQL Versions:
**Note**: We do NOT support any repacks that may or may not have been made based on AzerothCore. This is because they are usually based on older versions and there is no way to know what is in the precompiled binaries. Instead, you should compile your binaries from the AzerothCore source. To get started, read the [Installation Guide](https://www.azerothcore.org/wiki/installation).
| MySQL | Status | Recommended |
| :---- | :----------------: | :------------------: |
| 8.4 ≥ | :white_check_mark: | :large_blue_diamond: |
| 8.0 | :white_check_mark: | |
| 8.1 | :red_circle: | |
| 8.0 < | :red_circle: | |
### Supported CLang Versions:
| CLang | Status | Recommended |
| :---- | :----------------: | :------------------: |
| 18 | :white_check_mark: | :large_blue_diamond: |
| 15 | :white_check_mark: | |
| 14 ≤ | :red_circle: | |
### Supported GCC Versions:
| GCC | Status | Recommended |
| :--- | :----------------: | :------------------: |
| 14 | :white_check_mark: | :large_blue_diamond: |
| 12 | :white_check_mark: | |
| 11 ≤ | :red_circle: | |
> [!NOTE]
> We do **NOT** support any repacks that may or may not have been made based on AzerothCore. This is because they are usually based on older versions and there is no way to know what is in the precompiled binaries. Instead, you should compile your binaries from the AzerothCore source. To get started, read the [Installation Guide](https://www.azerothcore.org/wiki/installation).
> [!CAUTION]
> [Why you should not use repacks to run your WoW server](https://www.mangosrumors.org/why-you-should-not-use-repacks-to-run-your-wow-server/)
## Reporting a Vulnerability

View File

@@ -112,7 +112,7 @@ def sql_check(file: io, file_path: str) -> None:
check_failed = True
if "EntryOrGuid" in line:
print(
f"Please use entryorguid syntax instead of EntryOrgGuid in {file_path} at line {line_number}\nWe recommend to use keira to have the right syntax in auto-query generation")
f"Please use entryorguid syntax instead of EntryOrGuid in {file_path} at line {line_number}\nWe recommend to use keira to have the right syntax in auto-query generation")
check_failed = True
if [match for match in [';;'] if match in line]:
print(
@@ -142,6 +142,8 @@ def insert_safety_check(file: io, file_path: str) -> None:
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if line.startswith("--"):
continue
if "INSERT" in line and "DELETE" not in previous_line:
print(f"No DELETE keyword found after the INSERT in {file_path} at line {line_number}\nIf this error is intended, please advert a maintainer")
check_failed = True
@@ -163,7 +165,11 @@ def semicolon_check(file: io, file_path: str) -> None:
total_lines = len(lines)
for line_number, line in enumerate(lines, start=1):
stripped_line = line.rstrip() # Remove trailing whitespace including newline
if line.startswith('--'):
continue
# Remove trailing whitespace including newline
# Remove comments from the line
stripped_line = line.split('--', 1)[0].strip()
# Check if one keyword is in the line
if not query_open and any(keyword in stripped_line for keyword in sql_keywords):

View File

@@ -0,0 +1,12 @@
-- DB update 2024_12_15_00 -> 2025_01_26_00
DROP TABLE IF EXISTS `autobroadcast_locale`;
CREATE TABLE `autobroadcast_locale` (
`realmid` INT NOT NULL,
`id` INT NOT NULL,
`locale` VARCHAR(4) NOT NULL,
`text` VARCHAR(45) NULL,
PRIMARY KEY (`realmid`, `id`))
CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci
ENGINE = InnoDB
;

View File

@@ -0,0 +1,3 @@
-- DB update 2025_01_23_03 -> 2025_01_23_04
--
UPDATE `creature_template_movement` SET `Rooted`= 1, `Flight` = 1 WHERE `CreatureId` = 24666;

View File

@@ -0,0 +1,22 @@
-- DB update 2025_01_23_04 -> 2025_01_24_00
-- Fix Kaelthas HC
DELETE FROM `creature_loot_template` WHERE (`Entry` = 24857) AND (`Item` IN (23572, 25028, 25029, 34609, 34610, 34611, 34612, 34613, 34614, 34615, 34616));
INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES
(24857, 23572, 0, 100, 0, 1, 0, 1, 1, 'Kael\'thas Sunstrider (1) - Primal Nether'),
(24857, 34612, 0, 0, 0, 1, 1, 1, 1, 'Kael\'thas Sunstrider (1) - Greaves of the Penitent Knight'),
(24857, 34609, 0, 0, 0, 1, 1, 1, 1, 'Kael\'thas Sunstrider (1) - Quickening Blade of the Prince'),
(24857, 34611, 0, 0, 0, 1, 1, 1, 1, 'Kael\'thas Sunstrider (1) - Cudgel of Consecration'),
(24857, 34610, 0, 0, 0, 1, 1, 1, 1, 'Kael\'thas Sunstrider (1) - Scarlet Sin\'dorei Robes'),
(24857, 34614, 0, 0, 0, 1, 2, 1, 1, 'Kael\'thas Sunstrider (1) - Tunic of the Ranger Lord'),
(24857, 34615, 0, 0, 0, 1, 2, 1, 1, 'Kael\'thas Sunstrider (1) - Netherforce Chestplate'),
(24857, 34616, 0, 0, 0, 1, 2, 1, 1, 'Kael\'thas Sunstrider (1) - Breeching Comet'),
(24857, 34613, 0, 0, 0, 1, 2, 1, 1, 'Kael\'thas Sunstrider (1) - Shoulderpads of the Silvermoon Retainer');
-- Remove normal loot from Delrissa HC
DELETE FROM `creature_loot_template` WHERE (`Entry` = 25560) AND (`Item` IN (25027));
-- Remove normal loot from Vexallus HC
DELETE FROM `creature_loot_template` WHERE (`Entry` = 25573) AND (`Item` IN (25026));
-- Remove normal loot from Selin Fireheart HC
DELETE FROM `creature_loot_template` WHERE (`Entry` = 25562) AND (`Item` IN (25025));

View File

@@ -0,0 +1,4 @@
-- DB update 2025_01_24_00 -> 2025_01_24_01
--
-- Remove extra Sanctum Planetarium
DELETE FROM `gameobject` WHERE (`id` = 188081) AND (`guid` IN (27809));

View File

@@ -0,0 +1,26 @@
-- DB update 2025_01_24_01 -> 2025_01_25_00
--
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24674);
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
(24674, 0, 0, 0, 37, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 44196, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - On Initialize - Cast \'Rebirth\''),
(24674, 0, 1, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - On Reset - Set Invincibility Hp 5%'),
(24674, 0, 2, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 44197, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - On Aggro - Cast \'Burn\''),
(24674, 0, 3, 0, 24, 0, 100, 0, 44226, 1, 5000, 5000, 0, 0, 11, 44202, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - On Target Buffed With \'Gravity Lapse\' - Cast \'Fireball\''),
(24674, 0, 4, 5, 2, 0, 100, 0, 0, 5, 10000, 10000, 0, 0, 11, 44199, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - Between 0-5% Health - Cast \'Ember Blast\''),
(24674, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 12, 24675, 3, 20000, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - Between 0-5% Health - Summon Creature \'Phoenix Egg\''),
(24674, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - Between 0-5% Health - Set Reactstate Passive'),
(24674, 0, 7, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - Between 0-5% Health - Stop Combat'),
(24674, 0, 8, 9, 8, 0, 100, 0, 44199, 0, 0, 0, 0, 0, 28, 44197, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - On Spellhit \'Ember Blast\' - Remove Aura \'Burn\''),
(24674, 0, 9, 10, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - On Spellhit \'Ember Blast\' - Set Visibility Off'),
(24674, 0, 10, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 204, 24675, 0, 0, 0, 0, 0, 0, 0, 'Phoenix - On Spellhit \'Ember Blast\' - Despawn Instant');
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24675);
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
(24675, 0, 0, 0, 37, 0, 100, 512, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Initialize - Set Reactstate Passive'),
(24675, 0, 1, 2, 60, 0, 100, 0, 15000, 15000, 0, 0, 0, 0, 142, 100, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Update - Set HP to 100%'),
(24675, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 28, 44199, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Update - Remove Aura \'Ember Blast\''),
(24675, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 2, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Update - Set Reactstate Aggressive'),
(24675, 0, 4, 5, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Update - Set In Combat With Zone'),
(24675, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Update - Set Visibility On'),
(24675, 0, 6, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Update - Despawn Instant'),
(24675, 0, 8, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Phoenix Egg - On Just Died - Despawn In 3000 ms');

View File

@@ -0,0 +1,10 @@
-- DB update 2025_01_25_00 -> 2025_01_26_00
--
DROP TABLE IF EXISTS `creature_sparring`;
CREATE TABLE `creature_sparring` (
`GUID` int unsigned NOT NULL,
`SparringPCT` float NOT NULL,
PRIMARY KEY (`GUID`),
FOREIGN KEY (`GUID`) REFERENCES creature(`guid`),
CONSTRAINT `creature_sparring_chk_1` CHECK (`SparringPCT` BETWEEN 0 AND 100)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,61 @@
-- DB update 2025_01_26_00 -> 2025_01_26_01
-- Dragonflayer Vrykul
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23652;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23652);
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
(23652, 0, 0, 0, 8, 0, 100, 1, 43381, 0, 0, 0, 0, 0, 11, 43384, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Vrykul - On Spellhit \'Plague Spray\' - Cast \'Spray Credit\' (No Repeat)'),
(23652, 0, 1, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 38557, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Vrykul - On Aggro - Cast \'Throw\''),
(23652, 0, 2, 0, 0, 0, 100, 0, 4000, 6000, 12000, 20000, 0, 0, 11, 43410, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Vrykul - In Combat - Cast \'Chop\''),
(23652, 0, 3, 0, 0, 0, 100, 0, 25000, 30000, 30000, 45000, 0, 0, 11, 38557, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Vrykul - In Combat - Cast \'Throw\'');
-- Dragonflayer Tribesman
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23651;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23651);
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
(23651, 0, 0, 0, 9, 0, 100, 0, 8000, 10000, 8000, 10000, 8, 25, 11, 35570, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Tribesman - Within 8-25 Range - Cast \'Charge\''),
(23651, 0, 1, 0, 0, 0, 100, 0, 12000, 18000, 50000, 60000, 0, 0, 11, 48193, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Tribesman - In Combat - Cast \'Enrage\''),
(23651, 0, 2, 0, 0, 0, 100, 0, 2000, 6000, 8000, 12000, 0, 0, 11, 15496, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Tribesman - In Combat - Cast \'Cleave\'');
-- Dragonflayer Thane
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23660;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23660);
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
(23660, 0, 0, 0, 0, 0, 100, 0, 12000, 18000, 12000, 24000, 0, 0, 11, 11971, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Thane - In Combat - Cast \'Sunder Armor\''),
(23660, 0, 1, 0, 0, 0, 100, 0, 8000, 12000, 12000, 16000, 0, 0, 11, 9080, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Thane - In Combat - Cast \'Hamstring\'');
-- Dragonflayer Death Weaver
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23658;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23658);
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
(23658, 0, 0, 0, 1, 0, 100, 0, 1000, 1000, 1000, 1000, 0, 0, 11, 43159, 0, 0, 0, 0, 0, 19, 24158, 30, 0, 0, 0, 0, 0, 0, 'Dragonflayer Death Weaver - Out of Combat - Cast \'Soul Infusion\''),
(23658, 0, 1, 0, 0, 0, 100, 0, 2000, 4000, 8000, 12000, 0, 0, 11, 9613, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Death Weaver - In Combat - Cast \'Shadow Bolt\''),
(23658, 0, 2, 0, 0, 0, 100, 0, 6000, 8000, 12000, 16000, 0, 0, 11, 43417, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Death Weaver - In Combat - Cast \'Drain Life\'');
-- Dragonflayer Harpooner
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 24635;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24635);
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
(24635, 0, 0, 0, 0, 0, 100, 0, 12000, 18000, 50000, 60000, 0, 0, 11, 48193, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Tribesman - In Combat - Cast \'Enrage\''),
(24635, 0, 1, 0, 0, 0, 100, 0, 2000, 6000, 8000, 12000, 0, 0, 11, 43325, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Tribesman - In Combat - Cast \'Oluf`s Harpoon\'');
-- Dragonflayer Lieutenant
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 24169;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24169);
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
(24169, 0, 0, 0, 0, 0, 100, 0, 6000, 8000, 12000, 14000, 0, 0, 11, 48245, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Lieutenant - In Combat - Cast \'Head Slash\''),
(24169, 0, 1, 0, 0, 0, 100, 0, 10000, 14000, 18000, 25000, 0, 0, 11, 48250, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Dragonflayer Lieutenant - In Combat - Cast \'Risky Feint\'');
-- Yanis the Mystic
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23932;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23932);
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
(23932, 0, 0, 0, 0, 0, 100, 0, 6000, 8000, 12000, 14000, 0, 0, 11, 42870, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Yanis the Mystic - In Combat - Cast \'Throw Dragonflayer Harpoon\''),
(23932, 0, 1, 0, 0, 0, 100, 0, 2000, 4000, 16000, 18000, 0, 0, 11, 58747, 0, 0, 0, 0, 0, 5, 25, 0, 0, 0, 0, 0, 0, 0, 'Yanis the Mystic - In Combat - Cast \'Intercept\'');

View File

@@ -0,0 +1,9 @@
-- DB update 2025_01_26_01 -> 2025_01_26_02
--
-- Updates the Action from "Action Invoker" (7) to "Self" (1).
UPDATE `smart_scripts` SET `target_type` = 1 WHERE `entryorguid` = 9456 AND `source_type` = 0 AND `id` = 1;
-- Adds Strike sniffed from: 4.4.1.58158 and 1.15.5.57979
DELETE FROM `smart_scripts` WHERE `entryorguid` = 9456 AND `source_type` = 0 AND `id` = 0;
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(9456, 0, 0, 0, 0, 0, 100, 0, 0, 1000, 4000, 6000, 0, 0, 11, 11976, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Warlord Krom\'zar - In Combat - Cast \'Strike\'');

View File

@@ -0,0 +1,6 @@
-- DB update 2025_01_26_02 -> 2025_01_27_00
--
DELETE FROM `acore_string` WHERE `entry` IN (56, 82);
INSERT INTO `acore_string` (`entry`, `content_default`, `locale_deDE`, `locale_zhCN`, `locale_esES`, `locale_esMX`) VALUES
(56, 'Current Message of the day:', 'Aktuelle Nachricht des Tages:', '当前每日信息:', 'Mensaje actual del día:', 'Mensaje actual del día:'),
(82, '{}: {}', '{}: {}', '{}: {}', '{}: {}', '{}: {}');

View File

@@ -0,0 +1,27 @@
-- DB update 2025_01_27_00 -> 2025_01_27_01
--
UPDATE `creature_template` SET `flags_extra` = `flags_extra` |134217728 WHERE `entry` IN (24698, 24684, 24697, 24696, 24683, 24686);
DELETE FROM `smart_scripts` WHERE (`entryorguid` = -96841) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(-96841, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 205, 3, 1, 0, 0, 0, 0, 0, 0, 'Coilskar Witch - On Just Died - Felblood Kaeltas Do Action ID 0');
DELETE FROM `smart_scripts` WHERE (`entryorguid` = -96781) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(-96781, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 205, 3, 1, 0, 0, 0, 0, 0, 0, 'Sunblade Blood Knight - On Just Died - Felblood Kaeltas Do Action ID 0');
DELETE FROM `smart_scripts` WHERE (`entryorguid` = -96809) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(-96809, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 205, 3, 1, 0, 0, 0, 0, 0, 0, 'Sunblade Warlock - On Just Died - Felblood Kaeltas Do Action ID 0');
DELETE FROM `smart_scripts` WHERE (`entryorguid` = -96770) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(-96770, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 205, 3, 1, 0, 0, 0, 0, 0, 0, 'Sunblade Mage Guard - On Just Died - Felblood Kaeltas Do Action ID 0');
DELETE FROM `smart_scripts` WHERE (`entryorguid` = -96850) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(-96850, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 205, 3, 1, 0, 0, 0, 0, 0, 0, 'Ethereum Smuggler - On Just Died - Felblood Kaeltas Do Action ID 0');
DELETE FROM `smart_scripts` WHERE (`entryorguid` = -96847) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(-96847, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 205, 3, 1, 0, 0, 0, 0, 0, 0, 'Sister of Torment - On Just Died - Felblood Kaeltas Do Action ID 0');

View File

@@ -0,0 +1,8 @@
-- DB update 2025_01_27_01 -> 2025_01_27_02
-- Fix Amani Trainer Abilities
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23774;
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 23774) AND (`source_type` = 0) AND (`id` IN (0, 1));
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
(23774, 0, 0, 0, 0, 0, 100, 0, 5000, 8000, 12000, 15000, 0, 0, 11, 43292, 0, 0, 0, 0, 0, 19, 23834, 30, 0, 0, 0, 0, 0, 0, 'Target Amashi Dragonhawk for Incite Rage'),
(23774, 0, 1, 0, 0, 0, 100, 0, 10000, 12000, 20000, 25000, 0, 0, 11, 20989, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Cast Sleep on Random Enemy');

View File

@@ -0,0 +1,8 @@
-- DB update 2025_01_27_02 -> 2025_01_28_00
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23774;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23774);
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
(23774, 0, 0, 0, 0, 0, 100, 0, 5000, 8000, 12000, 15000, 0, 0, 11, 43292, 0, 0, 0, 0, 0, 19, 23834, 30, 0, 0, 0, 0, 0, 0, 'Amani\'shi Trainer - In Combat - Cast \'Incite Rage\''),
(23774, 0, 1, 0, 0, 0, 100, 0, 10000, 12000, 20000, 25000, 0, 0, 11, 20989, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Amani\'shi Trainer - In Combat - Cast \'Sleep\'');

View File

@@ -30,13 +30,20 @@ char const* localeNames[TOTAL_LOCALES] =
"ruRU"
};
bool IsLocaleValid(std::string const& locale)
{
for (int i = 0; i < TOTAL_LOCALES; ++i)
if (locale == localeNames[i])
return true;
return false;
}
LocaleConstant GetLocaleByName(const std::string& name)
{
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
if (name == localeNames[i])
{
return LocaleConstant(i);
}
return LOCALE_enUS; // including enGB case
}

View File

@@ -83,6 +83,7 @@ enum LocaleConstant
AC_COMMON_API extern char const* localeNames[TOTAL_LOCALES];
AC_COMMON_API bool IsLocaleValid(std::string const& locale);
AC_COMMON_API LocaleConstant GetLocaleByName(const std::string& name);
AC_COMMON_API const std::string GetNameByLocaleConstant(LocaleConstant localeConstant);
AC_COMMON_API void CleanStringForMysqlQuery(std::string& str);

View File

@@ -116,10 +116,11 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED, "SELECT id, locale, text FROM autobroadcast_locale WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_MOTD, "SELECT text FROM motd WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_MOTD_LOCALE, "SELECT locale, text FROM motd_localized WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_MOTD, "INSERT INTO motd (realmid, text) VALUES (?, ?) ON DUPLICATE KEY UPDATE text = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_MOTD_LOCALE, "INSERT INTO motd_localized (realmid, locale, text) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE text = ?;", CONNECTION_ASYNC);
PrepareStatement(LOGIN_REP_MOTD, "REPLACE INTO motd (realmid, text) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_REP_MOTD_LOCALE, "REPLACE INTO motd_localized (realmid, locale, text) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_ACCOUNT_MUTE, "INSERT INTO account_muted VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?", CONNECTION_ASYNC);

View File

@@ -98,10 +98,11 @@ enum LoginDatabaseStatements : uint32
LOGIN_SEL_REALMLIST_SECURITY_LEVEL,
LOGIN_DEL_ACCOUNT,
LOGIN_SEL_AUTOBROADCAST,
LOGIN_SEL_AUTOBROADCAST_LOCALIZED,
LOGIN_SEL_MOTD,
LOGIN_SEL_MOTD_LOCALE,
LOGIN_INS_MOTD,
LOGIN_INS_MOTD_LOCALE,
LOGIN_REP_MOTD,
LOGIN_REP_MOTD_LOCALE,
LOGIN_SEL_LAST_ATTEMPT_IP,
LOGIN_SEL_LAST_IP,
LOGIN_INS_ALDL_IP_LOGGING,

View File

@@ -42,7 +42,6 @@ void AutobroadcastMgr::LoadAutobroadcasts()
if (!result)
{
LOG_WARN("autobroadcast", ">> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for this realm!");
LOG_INFO("autobroadcast", " ");
return;
}
@@ -54,35 +53,66 @@ void AutobroadcastMgr::LoadAutobroadcasts()
_announceType = AnnounceType::World;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint8 textId = fields[0].Get<uint8>();
ObjectMgr::AddLocaleString(fields[2].Get<std::string>(), DEFAULT_LOCALE, _autobroadcasts[textId]);
_autobroadcastsWeights[textId] = fields[1].Get<uint8>();
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} Autobroadcast Definitions in {} ms", _autobroadcasts.size(), GetMSTimeDiffToNow(oldMSTime));
}
void AutobroadcastMgr::LoadAutobroadcastsLocalized()
{
uint32 oldMSTime = getMSTime();
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
if (_autobroadcasts.empty())
return;
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED);
stmt->SetData(0, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 localized autobroadcasts definitions. DB table `autobroadcast_localized` is empty for this realm!");
LOG_INFO("server.loading", " ");
return;
}
uint8 count = 0;
do
{
Field* fields = result->Fetch();
uint8 id = fields[0].Get<uint8>();
uint8 textId = fields[0].Get<uint8>();
LocaleConstant locale = GetLocaleByName(fields[1].Get<std::string>());
_autobroadcasts[id] = fields[2].Get<std::string>();
_autobroadcastsWeights[id] = fields[1].Get<uint8>();
if (locale == DEFAULT_LOCALE || ObjectMgr::GetLocaleString(_autobroadcasts[textId], DEFAULT_LOCALE).empty())
continue;
++count;
ObjectMgr::AddLocaleString(fields[2].Get<std::string>(), locale, _autobroadcasts[textId]);
count++;
} while (result->NextRow());
LOG_INFO("autobroadcast", ">> Loaded {} Autobroadcast Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("autobroadcast", " ");
LOG_INFO("server.loading", ">> Loaded {} Localized Autobroadcast Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
void AutobroadcastMgr::SendAutobroadcasts()
{
if (_autobroadcasts.empty())
{
return;
}
uint32 weight = 0;
uint8 textId = 0;
AutobroadcastsWeightMap selectionWeights;
std::string msg;
for (AutobroadcastsWeightMap::const_iterator it = _autobroadcastsWeights.begin(); it != _autobroadcastsWeights.end(); ++it)
{
if (it->second)
@@ -101,42 +131,78 @@ void AutobroadcastMgr::SendAutobroadcasts()
weight += it->second;
if (selectedWeight < weight)
{
msg = _autobroadcasts[it->first];
textId = it->first;
break;
}
}
}
else
{
msg = _autobroadcasts[urand(0, _autobroadcasts.size())];
textId = urand(0, _autobroadcasts.size());
}
switch (_announceType)
{
case AnnounceType::World:
SendWorldAnnouncement(msg);
SendWorldAnnouncement(textId);
break;
case AnnounceType::Notification:
SendNotificationAnnouncement(msg);
SendNotificationAnnouncement(textId);
break;
case AnnounceType::Both:
SendWorldAnnouncement(msg);
SendNotificationAnnouncement(msg);
SendWorldAnnouncement(textId);
SendNotificationAnnouncement(textId);
default:
break;
}
LOG_DEBUG("autobroadcast", "AutobroadcastMgr::SendAutobroadcasts: '{}'", msg);
LOG_DEBUG("autobroadcast", "AutobroadcastMgr::SendAutobroadcasts: '{}'", textId);
}
void AutobroadcastMgr::SendWorldAnnouncement(std::string msg)
void AutobroadcastMgr::SendWorldAnnouncement(uint8 textId)
{
ChatHandler(nullptr).SendWorldTextOptional(LANG_AUTO_BROADCAST, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST, msg.data());
// Send localized messages to all sessions
ChatHandler(nullptr).DoForAllValidSessions([&](Player* player)
{
// Get player's locale
LocaleConstant locale = player->GetSession()->GetSessionDbLocaleIndex();
if (!_autobroadcasts.empty())
return;
std::string_view localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], locale);
// Check if there is a localized message if not use default one.
if (localizedMessage.empty())
localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], DEFAULT_LOCALE);
// Send the localized or fallback message
ChatHandler(player->GetSession()).SendWorldTextOptional(localizedMessage, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST);
});
}
void AutobroadcastMgr::SendNotificationAnnouncement(std::string msg)
void AutobroadcastMgr::SendNotificationAnnouncement(uint8 textId)
{
WorldPacket data(SMSG_NOTIFICATION, (msg.size() + 1));
data << msg.data();
sWorld->SendGlobalMessage(&data);
ChatHandler(nullptr).DoForAllValidSessions([&](Player* player)
{
// Retrieve player's locale
LocaleConstant locale = player->GetSession()->GetSessionDbLocaleIndex();
if (!_autobroadcasts.count(textId))
return;
// Get localized message
std::string_view localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], locale);
// Check if there is a localized message if not use default one.
if (localizedMessage.empty())
localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], DEFAULT_LOCALE);
// Prepare the WorldPacket
WorldPacket data(SMSG_NOTIFICATION, (localizedMessage.size() + 1));
data << localizedMessage;
// Send packet to the player
player->GetSession()->SendPacket(&data);
});
}

View File

@@ -20,6 +20,7 @@
#include "Common.h"
#include <map>
#include <vector>
enum class AnnounceType : uint8
{
@@ -34,17 +35,18 @@ public:
static AutobroadcastMgr* instance();
void LoadAutobroadcasts();
void LoadAutobroadcastsLocalized();
void SendAutobroadcasts();
private:
void SendWorldAnnouncement(std::string msg);
void SendNotificationAnnouncement(std::string msg);
void SendWorldAnnouncement(uint8 textId);
void SendNotificationAnnouncement(uint8 textId);
typedef std::map<uint8, std::string> AutobroadcastsMap;
typedef std::map<uint8, std::vector<std::string>> AutobroadcastsMap;
typedef std::map<uint8, uint8> AutobroadcastsWeightMap;
AutobroadcastsMap _autobroadcasts;
AutobroadcastsWeightMap _autobroadcastsWeights;
AutobroadcastsMap _autobroadcasts; // autobroadcast messages
AutobroadcastsWeightMap _autobroadcastsWeights; // Weights for each message
AnnounceType _announceType;
};

View File

@@ -273,7 +273,7 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0),
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f),_sparringPct(0.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0),
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
@@ -608,6 +608,8 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
SetCanModifyStats(true);
UpdateAllStats();
LoadSparringPct();
// checked and error show at loading templates
if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction))
{
@@ -1189,6 +1191,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
}
LoadCreaturesAddon();
LoadSparringPct();
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
m_positionZ += GetHoverHeight();
@@ -2025,6 +2028,8 @@ void Creature::setDeathState(DeathState state, bool despawn)
Motion_Initialize();
LoadCreaturesAddon(true);
LoadSparringPct();
if (GetCreatureData() && GetPhaseMask() != GetCreatureData()->phaseMask)
SetPhaseMask(GetCreatureData()->phaseMask, false);
}
@@ -2798,6 +2803,18 @@ bool Creature::LoadCreaturesAddon(bool reload)
return true;
}
void Creature::LoadSparringPct()
{
ObjectGuid::LowType spawnId = GetSpawnId();
auto const& sparringData = sObjectMgr->GetSparringData();
auto itr = sparringData.find(spawnId);
if (itr != sparringData.end() && !itr->second.empty())
{
_sparringPct = itr->second[0];
}
}
/// Send a message to LocalDefense channel for players opposition team in the zone
void Creature::SendZoneUnderAttackMessage(Player* attacker)
{

View File

@@ -187,6 +187,9 @@ public:
void UpdateAttackPowerAndDamage(bool ranged = false) override;
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
void LoadSparringPct();
[[nodiscard]] float GetSparringPct() const { return _sparringPct; }
bool HasWeapon(WeaponAttackType type) const override;
bool HasWeaponForAttack(WeaponAttackType type) const override { return (Unit::HasWeaponForAttack(type) && HasWeapon(type)); }
void SetCanDualWield(bool value) override;
@@ -483,6 +486,8 @@ protected:
float m_detectionDistance;
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
float _sparringPct;
[[nodiscard]] bool IsInvisibleDueToDespawn() const override;
bool CanAlwaysSee(WorldObject const* obj) const override;
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;

View File

@@ -1032,6 +1032,17 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
}
}
// Sparring
if (victim->CanSparringWith(attacker))
{
if (damage >= victim->GetHealth())
damage = 0;
uint32 sparringHealth = victim->GetHealth() * (victim->ToCreature()->GetSparringPct() / 100);
if (victim->GetHealth() - damage <= sparringHealth)
damage = 0;
}
if (health <= damage)
{
LOG_DEBUG("entities.unit", "DealDamage: victim just died");
@@ -2635,6 +2646,10 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BASE_A
Unit::DealDamageMods(victim, damageInfo.damages[i].damage, &damageInfo.damages[i].absorb);
}
// Related to sparring system. Allow attack animations even if there are no damages
if (victim->CanSparringWith(damageInfo.attacker))
damageInfo.HitInfo |= HITINFO_FAKE_DAMAGE;
SendAttackStateUpdate(&damageInfo);
//TriggerAurasProcOnEvent(damageInfo);
@@ -3954,6 +3969,24 @@ void Unit::_UpdateAutoRepeatSpell()
}
}
bool Unit::CanSparringWith(Unit const* attacker) const
{
if (!IsCreature() || IsCharmedOwnedByPlayerOrPlayer())
return false;
if (!attacker)
return false;
if (!attacker->IsCreature() || attacker->IsCharmedOwnedByPlayerOrPlayer())
return false;
if (Creature const* creature = ToCreature())
if (!creature->GetSparringPct())
return false;
return true;
}
void Unit::SetCurrentCastedSpell(Spell* pSpell)
{
ASSERT(pSpell); // nullptr may be never passed here, use InterruptSpell or InterruptNonMeleeSpells

View File

@@ -726,6 +726,9 @@ public:
void RemoveUnitFlag2(UnitFlags2 flags) { RemoveFlag(UNIT_FIELD_FLAGS_2, flags); }
void ReplaceAllUnitFlags2(UnitFlags2 flags) { SetUInt32Value(UNIT_FIELD_FLAGS_2, flags); }
void SetEmoteState(Emote emoteState) { SetUInt32Value(UNIT_NPC_EMOTESTATE, emoteState); } /// @brief Sets emote state (looping emote). Emotes available in SharedDefines.h
void ClearEmoteState() { SetEmoteState(EMOTE_ONESHOT_NONE); } /// @brief Clears emote state (looping emote)
// NPC flags
NPCFlags GetNpcFlags() const { return NPCFlags(GetUInt32Value(UNIT_NPC_FLAGS)); }
bool HasNpcFlag(NPCFlags flags) const { return HasFlag(UNIT_NPC_FLAGS, flags) != 0; }
@@ -2044,6 +2047,8 @@ protected:
void _UpdateAutoRepeatSpell();
bool CanSparringWith(Unit const* attacker) const; ///@brief: Check if unit is eligible for sparring damages. Work only if attacker and victim are creatures.
bool IsAlwaysVisibleFor(WorldObject const* seer) const override;
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;

View File

@@ -2298,6 +2298,42 @@ void ObjectMgr::LoadCreatures()
LOG_INFO("server.loading", " ");
}
void ObjectMgr::LoadCreatureSparring()
{
uint32 oldMSTime = getMSTime();
QueryResult result = WorldDatabase.Query("SELECT GUID, SparringPCT FROM creature_sparring");
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 sparring data. DB table `creature_sparring` is empty.");
LOG_INFO("server.loading", " ");
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
ObjectGuid::LowType spawnId = fields[0].Get<uint32>();
float sparringHealthPct = fields[1].Get<float>();
if (!sObjectMgr->GetCreatureData(spawnId))
{
LOG_ERROR("sql.sql", "Entry {} has a record in `creature_sparring` but doesn't exist in `creatures` table");
continue;
}
_creatureSparringStore[spawnId].push_back(sparringHealthPct);
++count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} sparring data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const* data)
{
uint8 mask = data->spawnMask;

View File

@@ -751,6 +751,8 @@ public:
typedef std::map<uint32, uint32> CharacterConversionMap;
typedef std::unordered_map<ObjectGuid::LowType, std::vector<float>> CreatureSparringContainer;
GameObjectTemplate const* GetGameObjectTemplate(uint32 entry);
bool IsGameObjectStaticTransport(uint32 entry);
[[nodiscard]] GameObjectTemplateContainer const* GetGameObjectTemplates() const { return &_gameObjectTemplateStore; }
@@ -1029,6 +1031,7 @@ public:
void LoadCreatureQuestItems();
void LoadTempSummons();
void LoadCreatures();
void LoadCreatureSparring();
void LoadLinkedRespawn();
bool SetCreatureLinkedRespawn(ObjectGuid::LowType guid, ObjectGuid::LowType linkedGuid);
void LoadCreatureAddons();
@@ -1202,6 +1205,9 @@ public:
if (itr == _creatureDataStore.end()) return nullptr;
return &itr->second;
}
[[nodiscard]] CreatureSparringContainer const& GetSparringData() const { return _creatureSparringStore; }
CreatureData& NewOrExistCreatureData(ObjectGuid::LowType spawnId) { return _creatureDataStore[spawnId]; }
void DeleteCreatureData(ObjectGuid::LowType spawnId);
[[nodiscard]] ObjectGuid GetLinkedRespawnGuid(ObjectGuid guid) const
@@ -1527,6 +1533,8 @@ private:
PageTextContainer _pageTextStore;
InstanceTemplateContainer _instanceTemplateStore;
CreatureSparringContainer _creatureSparringStore;
private:
void LoadScripts(ScriptsType type);
void LoadQuestRelationsHelper(QuestRelations& map, std::string const& table, bool starter, bool go);

View File

@@ -749,6 +749,9 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData)
if (usable)
{
AuctionHouseUsablePlayerInfo usablePlayerInfo;
usablePlayerInfo.classMask = GetPlayer()->getClassMask();
usablePlayerInfo.raceMask = GetPlayer()->getRaceMask();
usablePlayerInfo.level = GetPlayer()->GetLevel();
SkillStatusMap const& skillMap = GetPlayer()->GetSkillStatusMap();
for (auto const& pair : skillMap)

View File

@@ -111,7 +111,8 @@ enum AcoreStrings
LANG_RBAC_PERM_REVOKED_NOT_IN_LIST = 79,
LANG_PVPSTATS = 80,
LANG_PVPSTATS_DISABLED = 81,
// Free 82 - 86
LANG_GENERIC_TWO_CURLIES_WITH_COLON = 82,
// Free 83 - 86
LANG_UNKNOWN_ERROR = 87,
LANG_2FA_COMMANDS_NOT_SETUP = 88,

View File

@@ -39,11 +39,6 @@ MotdMgr* MotdMgr::instance()
return &instance;
}
bool MotdMgr::IsValidLocale(std::string const& locale) {
// Use std::find to search for the locale in the array
return std::find(std::begin(localeNames), std::end(localeNames), locale) != std::end(localeNames);
}
void MotdMgr::SetMotd(std::string motd, LocaleConstant locale)
{
// scripts may change motd
@@ -53,31 +48,76 @@ void MotdMgr::SetMotd(std::string motd, LocaleConstant locale)
MotdPackets[locale] = CreateWorldPacket(motd);
}
void MotdMgr::CreateWorldPackages()
{
for (auto const& [locale, motd] : MotdMap)
// Store the constructed packet in MotdPackets with the locale as the key
MotdPackets[locale] = CreateWorldPacket(motd);
}
void MotdMgr::LoadMotd()
{
uint32 oldMSTime = getMSTime();
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD);
stmt->SetData(0, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
// Load the main motd for the realm and assign it to enUS if available
std::string motd = LoadDefaultMotd(realmId);
if (result)
{
Field* fields = result->Fetch();
std::string motd = fields[0].Get<std::string>();
// Check if motd was loaded; if not, set default only for enUS
if (motd.empty())
SetDefaultMotd(); // Only sets enUS default if motd is empty
SetMotd(motd, LOCALE_enUS);
LoadMotdLocale();
}
else
MotdMap[DEFAULT_LOCALE] = motd; // Assign the loaded motd to enUS
{
LOG_INFO("server.loading", ">> Loaded 0 motd definitions. DB table `motd` is empty for this realm!");
LOG_INFO("server.loading", ">> Loaded 0 motd locale definitions. DB table `motd` needs an entry to be able to load DB table `motd_locale`!");
LOG_INFO("server.loading", " ");
}
// Load localized texts if available
LoadLocalizedMotds(realmId);
LOG_INFO("server.loading", ">> Loaded motd definitions in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
// Create all world packages after loading motd and localized texts
CreateWorldPackages();
void MotdMgr::LoadMotdLocale()
{
uint32 oldMSTime = getMSTime();
uint32 count = 0;
LOG_INFO("server.loading", "Loading Motd locale...");
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD_LOCALE);
stmt->SetData(0, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (result)
{
do
{
Field* fields = result->Fetch();
// fields[0] is the locale string and fields[1] is the localized motd text
std::string locale = fields[0].Get<std::string>();
std::string localizedText = fields[1].Get<std::string>();
if (!IsLocaleValid(locale))
{
LOG_ERROR("server.loading", "DB table `motd_localized` has invalid locale ({}), skipped.", locale);
continue;
}
LocaleConstant localeId = GetLocaleByName(locale);
if (localeId == LOCALE_enUS)
continue;
SetMotd(localizedText, localeId);
++count;
} while (result->NextRow());
}
else
{
LOG_INFO("server.loading", ">> Loaded 0 motd locale definitions. DB table `motd_localized` is empty for this realm!");
LOG_INFO("server.loading", " ");
}
LOG_INFO("server.loading", ">> Loaded {} motd locale definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
}
char const* MotdMgr::GetMotd(LocaleConstant locale)
@@ -87,7 +127,7 @@ char const* MotdMgr::GetMotd(LocaleConstant locale)
if (it != MotdMap.end())
return it->second.c_str();
return MotdMap[DEFAULT_LOCALE].c_str(); // Fallback to enUS if locale is not found
return MotdMap[LOCALE_enUS].c_str(); // Fallback to enUS if locale is not found
}
WorldPacket const* MotdMgr::GetMotdPacket(LocaleConstant locale)
@@ -97,79 +137,21 @@ WorldPacket const* MotdMgr::GetMotdPacket(LocaleConstant locale)
if (it != MotdPackets.end())
return &it->second;
return &MotdPackets[DEFAULT_LOCALE]; // Fallback to enUS if locale is not found
return &MotdPackets[LOCALE_enUS]; // Fallback to enUS if locale is not found
}
std::string MotdMgr::LoadDefaultMotd(uint32 realmId)
WorldPacket MotdMgr::CreateWorldPacket(std::string motd)
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD);
stmt->SetData(0, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
// Create a new WorldPacket for this locale
WorldPacket data(SMSG_MOTD); // new in 2.0.1
if (result)
{
Field* fields = result->Fetch();
return fields[0].Get<std::string>(); // Return the main motd if found
}
return ""; // Return empty string if no motd found
}
void MotdMgr::SetDefaultMotd()
{
std::string motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */
/*"d3"+"ce"*/ std::string("@|") + "cf" +/*"as"+"k4"*/"fF" + "F4" +/*"d5"+"f3"*/"A2" + "DT"/*"F4"+"Az"*/ + "hi" + "s "
motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */ motd
/*"d3"+"ce"*/ + "@|" + "cf" +/*"as"+"k4"*/"fF" + "F4" +/*"d5"+"f3"*/"A2" + "DT"/*"F4"+"Az"*/ + "hi" + "s "
/*"fd"+"hy"*/ + "se" + "rv" +/*"nh"+"k3"*/"er" + " r" +/*"x1"+"A2"*/"un" + "s "/*"F2"+"Ay"*/ + "on" + " Az"
/*"xs"+"5n"*/ + "er" + "ot" +/*"xs"+"A2"*/"hC" + "or" +/*"a4"+"f3"*/"e|" + "r "/*"f2"+"A2"*/ + "|c" + "ff"
/*"5g"+"A2"*/ + "3C" + "E7" +/*"k5"+"AX"*/"FF" + "ww" +/*"sx"+"Gj"*/"w." + "az"/*"a1"+"vf"*/ + "er" + "ot"
/*"ds"+"sx"*/ + "hc" + "or" +/*"F4"+"k5"*/"e." + "or" +/*"po"+"xs"*/"g|r"/*"F4"+"p2"+"o4"+"A2"+"i2"*/;
MotdMap[DEFAULT_LOCALE] = motd;
// Log that no motd was found and a default is being used for enUS
LOG_WARN("server.loading", ">> Loaded 0 motd definitions. DB table `motd` is empty for this realm!");
LOG_INFO("server.loading", " ");
}
void MotdMgr::LoadLocalizedMotds(uint32 realmId) {
// First, check if base MOTD exists
LoginDatabasePreparedStatement* baseStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD);
baseStmt->SetData(0, realmId);
PreparedQueryResult baseResult = LoginDatabase.Query(baseStmt);
if (!baseResult)
{
LOG_ERROR("server.loading", "No base MOTD found for realm {}. Localized MOTDs will not be loaded.", realmId);
return;
}
// Now load localized versions
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD_LOCALE);
stmt->SetData(0, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (result)
{
do {
Field* fields = result->Fetch();
// fields[0] is the locale string and fields[1] is the localized motd text
std::string localizedText = fields[1].Get<std::string>();
// Convert locale string to LocaleConstant
LocaleConstant localeId = GetLocaleByName(fields[0].Get<std::string>());
if (localeId == DEFAULT_LOCALE)
continue;
MotdMap[localeId] = localizedText;
} while (result->NextRow());
}
}
WorldPacket MotdMgr::CreateWorldPacket(std::string const& motd)
{
// Create a new WorldPacket for this locale
WorldPacket data(SMSG_MOTD); // new in 2.0.1
// Tokenize the motd string by '@'
std::vector<std::string_view> motdTokens = Acore::Tokenize(motd, '@', true);
data << uint32(motdTokens.size()); // line count

View File

@@ -29,9 +29,6 @@ class AC_GAME_API MotdMgr
public:
static MotdMgr* instance();
/// Converts the localized string to world packages
void CreateWorldPackages();
/// Set a new Message of the Day
void SetMotd(std::string motd, LocaleConstant locale);
@@ -44,18 +41,12 @@ public:
/// Returns the current motd packet for the given locale
WorldPacket const* GetMotdPacket(LocaleConstant locale);
// Checks if string is valid locale
bool IsValidLocale(std::string const& locale);
private:
// Loads the default motd from the motd table
std::string LoadDefaultMotd(uint32 realmId);
// Loads all available localized motd for the realm
void LoadLocalizedMotds(uint32 realmId);
// Sets the default mode if none is found in the database
void SetDefaultMotd();
void LoadMotdLocale();
// Create a worldpacket for a given motd localization
WorldPacket CreateWorldPacket(std::string const& motd);
WorldPacket CreateWorldPacket(std::string motd);
};
#define sMotdMgr MotdMgr::instance()

View File

@@ -1761,6 +1761,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Creature Data...");
sObjectMgr->LoadCreatures();
LOG_INFO("server.loading", "Loading Creature sparring...");
sObjectMgr->LoadCreatureSparring();
LOG_INFO("server.loading", "Loading Temporary Summon Data...");
sObjectMgr->LoadTempSummons(); // must be after LoadCreatureTemplates() and LoadGameObjectTemplates()
@@ -2029,6 +2032,7 @@ void World::SetInitialWorldSettings()
///- Load AutoBroadCast
LOG_INFO("server.loading", "Loading Autobroadcasts...");
sAutobroadcastMgr->LoadAutobroadcasts();
sAutobroadcastMgr->LoadAutobroadcastsLocalized();
///- Load Motd
LOG_INFO("server.loading", "Loading Motd...");

View File

@@ -297,11 +297,9 @@ public:
// Display the 'Message of the day' for the realm
static bool HandleServerMotdCommand(ChatHandler* handler)
{
LocaleConstant localeConstant = DEFAULT_LOCALE;
if (Player* player = handler->GetPlayer())
localeConstant = player->GetSession()->GetSessionDbLocaleIndex();
handler->PSendSysMessage(LANG_MOTD_CURRENT, sMotdMgr->GetMotd(localeConstant));
handler->PSendSysMessage(LANG_MOTD_CURRENT);
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
handler->PSendSysMessage(LANG_GENERIC_TWO_CURLIES_WITH_COLON, GetNameByLocaleConstant(LocaleConstant(i)), sMotdMgr->GetMotd(LocaleConstant(i)));
return true;
}
@@ -533,7 +531,7 @@ public:
}
// Define the 'Message of the day' for the realm
static bool HandleServerSetMotdCommand(ChatHandler* handler, Optional<int32> realmId, Optional<std::string> locale, Tail motd)
static bool HandleServerSetMotdCommand(ChatHandler* handler, Optional<int32> realmId, std::string locale, Tail motd)
{
std::wstring wMotd = std::wstring();
std::string strMotd = std::string();
@@ -542,39 +540,21 @@ public:
if (!realmId)
realmId = static_cast<int32>(realm.Id.Realm);
// Determine the locale; default to "enUS" if not provided
LocaleConstant localeConstant;
if (IsLocaleValid(locale))
localeConstant = GetLocaleByName(locale);
else
{
handler->SendErrorMessage("locale ({}) is not valid. Valid locales: enUS, koKR, frFR, deDE, zhCN, zhWE, esES, esMX, ruRU.", locale);
return false;
}
if (motd.empty())
return false;
// Convert Tail (motd) to std::string
std::ostringstream motdStream;
motdStream << motd;
std::string motdString = motdStream.str(); // Convert Tail to std::string
// Determine the locale; default to "enUS" if not provided
LocaleConstant localeConstant = DEFAULT_LOCALE;
if (locale.has_value())
{
if (sMotdMgr->IsValidLocale(locale.value()))
{
localeConstant = GetLocaleByName(locale.value());
}
else
{
motdStream.str("");
motdStream << locale.value() << " " << motd;
motdString = motdStream.str();
localeConstant = DEFAULT_LOCALE;
locale = GetNameByLocaleConstant(localeConstant);
}
}
else
{
// Set to default locale string
localeConstant = DEFAULT_LOCALE;
locale = GetNameByLocaleConstant(localeConstant);
}
// Convert the concatenated motdString to UTF-8 and ensure encoding consistency
if (!Utf8toWStr(motdString, wMotd))
if (!Utf8toWStr(motd, wMotd))
return false;
if (!WStrToUtf8(wMotd, strMotd))
@@ -583,34 +563,29 @@ public:
// Start a transaction for the database operations
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
if (localeConstant == DEFAULT_LOCALE)
if (localeConstant == LOCALE_enUS)
{
// Insert or update in the main motd table for enUS
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_MOTD);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_MOTD);
stmt->SetData(0, realmId.value()); // realmId for insertion
stmt->SetData(1, strMotd); // motd text for insertion
stmt->SetData(2, strMotd); // motd text for ON DUPLICATE KEY UPDATE
trans->Append(stmt);
}
else
{
// Insert or update in the motd_localized table for other locales
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_MOTD_LOCALE);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_MOTD_LOCALE);
stmt->SetData(0, realmId.value()); // realmId for insertion
stmt->SetData(1, locale.value()); // locale for insertion
stmt->SetData(1, locale); // locale for insertion
stmt->SetData(2, strMotd); // motd text for insertion
stmt->SetData(3, strMotd); // motd text for ON DUPLICATE KEY UPDATE
trans->Append(stmt);
}
// Commit the transaction & update db
LoginDatabase.CommitTransaction(trans);
// Update the in-memory maps for the current realm. Otherwise, do not update
if (realmId == -1 || realmId == static_cast<int32>(realm.Id.Realm))
sMotdMgr->SetMotd(strMotd, localeConstant);
handler->PSendSysMessage(LANG_MOTD_NEW, realmId.value(), locale.value(), strMotd);
sMotdMgr->SetMotd(strMotd, localeConstant);
handler->PSendSysMessage(LANG_MOTD_NEW, realmId.value(), locale, strMotd);
return true;
}

View File

@@ -52,7 +52,12 @@ enum Spells
SPELL_GRAVITY_LAPSE_FLY = 44227,
SPELL_GRAVITY_LAPSE_DOT = 44226,
SPELL_GRAVITY_LAPSE_CHANNEL = 44251,
SPELL_POWER_FEEDBACK = 44233
SPELL_POWER_FEEDBACK = 44233,
SPELL_CLEAR_FLIGHT = 44232, // Does nothing currently
SPELL_EMOTE_EXCLAMATION = 48348,
SPELL_EMOTE_POINT = 48349,
SPELL_EMOTE_ROAR = 48350
};
enum Misc
@@ -67,19 +72,13 @@ enum Misc
struct boss_felblood_kaelthas : public BossAI
{
boss_felblood_kaelthas(Creature* creature) : BossAI(creature, DATA_KAELTHAS)
{
_hasDoneIntro = false;
}
boss_felblood_kaelthas(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { }
void Reset() override
{
BossAI::Reset();
_OOCScheduler.CancelAll();
_gravityLapseCounter = 0;
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, false);
me->SetImmuneToAll(false);
summons.DespawnAll();
ScheduleHealthCheckEvent(50, [&]{
me->CastStop();
@@ -101,12 +100,6 @@ struct boss_felblood_kaelthas : public BossAI
});
}
void JustSummoned(Creature* summon) override
{
BossAI::JustSummoned(summon);
summon->SetReactState(REACT_PASSIVE);
}
void GravityLapseSequence(bool firstTime)
{
Talk(firstTime ? SAY_GRAVITY_LAPSE : SAY_RECAST_GRAVITY);
@@ -132,12 +125,6 @@ struct boss_felblood_kaelthas : public BossAI
});
}
void InitializeAI() override
{
BossAI::InitializeAI();
me->SetImmuneToAll(true);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
@@ -168,20 +155,32 @@ struct boss_felblood_kaelthas : public BossAI
}, 50s);
}
void MoveInLineOfSight(Unit* who) override
void DoAction(int32 actionId) override
{
if (!_hasDoneIntro && me->IsWithinDistInMap(who, 40.0f) && who->IsPlayer())
if (actionId == DATA_KAEL_INTRO)
{
Talk(SAY_AGGRO);
Talk(SAY_AGGRO_2, 20s);
_hasDoneIntro = true;
_OOCScheduler.Schedule(35s, [this](TaskContext){
me->SetReactState(REACT_AGGRESSIVE);
me->SetImmuneToAll(false);
me->SetInCombatWithZone();
});
uint32 counter = instance->GetPersistentData(DATA_KAEL_INTRO);
instance->StorePersistentData(DATA_KAEL_INTRO, ++counter);
if (counter == 6 && !me->IsInCombat())
{
me->SetEmoteState(EMOTE_STATE_TALK);
Talk(SAY_AGGRO);
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH_NO_SHEATHE);
}, 15s);
Talk(SAY_AGGRO_2, 20s);
me->SetImmuneToAll(true);
me->m_Events.AddEventAtOffset([&] {
me->ClearEmoteState();
me->SetReactState(REACT_AGGRESSIVE);
me->SetImmuneToAll(false);
}, 35s);
}
}
BossAI::MoveInLineOfSight(who);
}
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override
@@ -191,19 +190,35 @@ struct boss_felblood_kaelthas : public BossAI
damage = me->GetHealth() - 1;
if (me->isRegeneratingHealth())
{
me->CombatStop();
me->CastStop();
me->SetRegeneratingHealth(false);
me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
me->SetImmuneToAll(true);
me->SetStandState(UNIT_STAND_STATE_KNEEL);
me->CombatStop();
me->SetReactState(REACT_PASSIVE);
LapseAction(ACTION_REMOVE_FLY);
scheduler.CancelAll();
_OOCScheduler.Schedule(6s, [this](TaskContext){
me->KillSelf();
});
summons.DespawnAll();
Talk(SAY_DEATH);
DoCastSelf(SPELL_EMOTE_EXCLAMATION);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_EMOTE_POINT);
}, 3s);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_EMOTE_ROAR);
}, 7s);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_EMOTE_ROAR);
DoCastSelf(SPELL_CLEAR_FLIGHT);
}, 9s);
me->m_Events.AddEventAtOffset([&] {
me->KillSelf();
}, 11s);
}
}
BossAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
@@ -214,6 +229,9 @@ struct boss_felblood_kaelthas : public BossAI
_gravityLapseCounter = 0;
me->GetMap()->DoForAllPlayers([&](Player* player)
{
if (player->IsGameMaster())
return;
if (action == ACTION_TELEPORT_PLAYERS)
DoCast(player, SPELL_GRAVITY_LAPSE_PLAYER + _gravityLapseCounter, true);
else if (action == ACTION_KNOCKUP)
@@ -228,15 +246,7 @@ struct boss_felblood_kaelthas : public BossAI
++_gravityLapseCounter;
});
}
void UpdateAI(uint32 diff) override
{
_OOCScheduler.Update(diff);
BossAI::UpdateAI(diff);
}
private:
TaskScheduler _OOCScheduler;
bool _hasDoneIntro;
uint8 _gravityLapseCounter;
};

View File

@@ -48,6 +48,7 @@ DoorData const doorData[] =
{ GO_SELIN_ENCOUNTER_DOOR, DATA_SELIN_FIREHEART, DOOR_TYPE_ROOM },
{ GO_VEXALLUS_DOOR, DATA_VEXALLUS, DOOR_TYPE_PASSAGE },
{ GO_DELRISSA_DOOR, DATA_DELRISSA, DOOR_TYPE_PASSAGE },
{ GO_KAEL_DOOR, DATA_KAELTHAS, DOOR_TYPE_ROOM },
{ 0, 0, DOOR_TYPE_ROOM } // END
};
@@ -64,6 +65,7 @@ public:
{
SetHeaders(DataHeader);
SetBossNumber(MAX_ENCOUNTER);
SetPersistentDataCount(MAX_PERSISTENT_DATA);
LoadObjectData(creatureData, gameobjectData);
LoadDoorData(doorData);
LoadSummonData(summonerData);

View File

@@ -32,7 +32,11 @@ enum MTData
MAX_ENCOUNTER = 4,
DATA_KALECGOS = 5,
DATA_ESCAPE_ORB = 6
DATA_ESCAPE_ORB = 6,
// Persistent data
DATA_KAEL_INTRO = 0,
MAX_PERSISTENT_DATA = 1
};
enum MTCreatures
@@ -40,8 +44,8 @@ enum MTCreatures
NPC_DELRISSA = 24560,
NPC_FEL_CRYSTAL = 24722,
NPC_KAEL_THAS = 24664,
NPC_PHOENIX = 21362,
NPC_PHOENIX_EGG = 21364,
NPC_PHOENIX = 24674,
NPC_PHOENIX_EGG = 24675,
NPC_KALECGOS = 24844
};

View File

@@ -189,10 +189,12 @@ struct npc_madrigosa : public NullCreatureAI
{
if (param == ACTION_START_EVENT)
{
me->NearTeleportTo(1570.97f, 725.51f, 79.77f, 3.82f);
me->SetDisableGravity(true);
me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveDynamicFlag(UNIT_DYNFLAG_DEAD);
me->NearTeleportTo(1570.97f, 725.51f, 79.77f, 3.82f);
me->SendMovementFlagUpdate();
events.ScheduleEvent(EVENT_MAD_1, 2000);
}
else if (param == ACTION_SPAWN_FELMYST)
@@ -212,9 +214,9 @@ struct npc_madrigosa : public NullCreatureAI
brutallus->SetReactState(REACT_PASSIVE);
brutallus->setActive(true);
}
me->GetMotionMaster()->MovePoint(1, 1477.94f, 643.22f, 21.21f);
me->GetMotionMaster()->MoveTakeoff(1, 1477.94f, 643.22f, 21.21f);
me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD);
events.ScheduleEvent(EVENT_MAD_2, 6000);
events.ScheduleEvent(EVENT_MAD_2, 4000);
break;
case EVENT_MAD_2:
Talk(SAY_MAD_1);

View File

@@ -333,11 +333,11 @@ struct boss_felmyst : public BossAI
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (!me->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
DoMeleeAttackIfReady();
}

View File

@@ -61,7 +61,6 @@ enum Spells
enum Misc
{
EVENT_SPELL_BERSERK = 1,
GROUP_DELAY = 1
};
@@ -103,6 +102,7 @@ struct boss_gurtogg_bloodboil : public BossAI
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 40.0f, true))
{
me->RemoveAurasByType(SPELL_AURA_MOD_TAUNT);
me->RemoveAurasDueToSpell(SPELL_ACIDIC_WOUND);
DoCastSelf(SPELL_FEL_RAGE_SELF, true);
DoCast(target, SPELL_FEL_RAGE_TARGET, true);
DoCast(target, SPELL_FEL_RAGE_2, true);
@@ -118,6 +118,10 @@ struct boss_gurtogg_bloodboil : public BossAI
DoCastVictim(SPELL_CHARGE);
}, 2s);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_ACIDIC_WOUND, true);
}, 28s);
scheduler.DelayGroup(GROUP_DELAY, 30s);
}
}, 90s);
@@ -139,7 +143,7 @@ struct boss_gurtogg_bloodboil : public BossAI
return !who->IsImmunedToDamage(SPELL_SCHOOL_MASK_ALL) && !who->HasUnitState(UNIT_STATE_CONFUSED);
}
void KilledUnit(Unit* /*victim*/) override
void KilledUnit(Unit* /*victim*/) override
{
if (!_recentlySpoken)
{