Merge branch 'master' into ScheduleCreatureRespawn

This commit is contained in:
天鹭 2025-10-13 08:11:56 +08:00 committed by GitHub
commit ad5b615e28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 1596 additions and 136 deletions

View File

@ -122,5 +122,7 @@
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
"C_Cpp.default.cppStandard": "c++17",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"C_Cpp.default.compilerPath": "/usr/bin/clang"
"C_Cpp.default.compilerPath": "/usr/bin/clang",
"cmake.sourceDirectory": ["${workspaceFolder}"],
"cmake.buildDirectory": "${workspaceFolder}/var/build",
}

View File

@ -453,6 +453,40 @@ This is particularly useful for:
- **Multiple Projects**: Separate service configurations per project
- **Team Collaboration**: Share service setups across development teams
#### Service Configuration Portability
The service manager automatically stores binary and configuration paths as relative paths when they are located under the `AC_SERVICE_CONFIG_DIR`, making service configurations portable across environments:
```bash
# Set up a portable project structure
export AC_SERVICE_CONFIG_DIR="/opt/myproject/services"
mkdir -p "$AC_SERVICE_CONFIG_DIR"/{bin,etc}
# Copy your binaries and configs
cp /path/to/compiled/authserver "$AC_SERVICE_CONFIG_DIR/bin/"
cp /path/to/authserver.conf "$AC_SERVICE_CONFIG_DIR/etc/"
# Create service - paths under AC_SERVICE_CONFIG_DIR will be stored as relative
./service-manager.sh create auth authserver \
--bin-path "$AC_SERVICE_CONFIG_DIR/bin" \
--server-config "$AC_SERVICE_CONFIG_DIR/etc/authserver.conf"
# Registry will contain relative paths like "bin/authserver" and "etc/authserver.conf"
# instead of absolute paths, making the entire directory portable
```
**Benefits:**
- **Environment Independence**: Move the entire services directory between machines
- **Container Friendly**: Perfect for Docker volumes and bind mounts
- **Backup/Restore**: Archive and restore complete service configurations
- **Development/Production Parity**: Same relative structure across environments
**How it works:**
- Paths under `AC_SERVICE_CONFIG_DIR` are automatically stored as relative paths
- Paths outside `AC_SERVICE_CONFIG_DIR` are stored as absolute paths for safety
- When services are restored or started, relative paths are resolved from `AC_SERVICE_CONFIG_DIR`
- If `AC_SERVICE_CONFIG_DIR` is not set, all paths are stored as absolute paths (traditional behavior)
#### Migration from Legacy Format
If you have existing services in the old format, use the migration script:

View File

@ -219,6 +219,13 @@ function parse_arguments() {
export PARSED_CONFIG_FILE="$config_file"
export PARSED_SERVERCONFIG="$serverconfig"
export PARSED_SESSION_MANAGER="$session_manager"
echo "Parsed arguments:"
echo " Mode: $PARSED_MODE"
echo " Server Binary: $PARSED_SERVERBIN"
echo " Config File: $PARSED_CONFIG_FILE"
echo " Server Config: $PARSED_SERVERCONFIG"
echo " Session Manager: $PARSED_SESSION_MANAGER"
}
# Start service (single run or with simple-restarter)

File diff suppressed because it is too large Load Diff

183
apps/startup-scripts/test/test_startup_scripts.bats Normal file → Executable file
View File

@ -160,7 +160,19 @@ teardown() {
# Create registry with pm2 provider service
cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF'
[
{"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"}
{
"name":"test-world",
"provider":"pm2",
"type":"service",
"bin_path":"/bin/worldserver",
"args":"",
"systemd_type":"--user",
"restart_policy":"always",
"exec":{
"command":"/bin/true",
"args":[]
}
}
]
EOF
# Create minimal service config and run-engine config files required by 'send'
@ -215,7 +227,19 @@ EOF
# Create registry and config as in previous test
cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF'
[
{"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"}
{
"name":"test-world",
"provider":"pm2",
"type":"service",
"bin_path":"/bin/worldserver",
"args":"",
"systemd_type":"--user",
"restart_policy":"always",
"exec":{
"command":"/bin/true",
"args":[]
}
}
]
EOF
echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf"
@ -258,6 +282,31 @@ EOF
[ "$status" -eq 0 ]
}
@test "service-manager: restore helper recreates missing configs" {
command -v jq >/dev/null 2>&1 || skip "jq not installed"
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
mkdir -p "$AC_SERVICE_CONFIG_DIR"
source "$SCRIPT_DIR/service-manager.sh"
local service_name="restore-test"
local run_engine_config="$AC_SERVICE_CONFIG_DIR/$service_name-run-engine.conf"
local service_conf="$AC_SERVICE_CONFIG_DIR/$service_name.conf"
rm -f "$run_engine_config" "$service_conf"
mkdir -p "$TEST_DIR/bin" "$TEST_DIR/etc"
touch "$TEST_DIR/bin/worldserver"
touch "$TEST_DIR/etc/worldserver.conf"
ensure_service_configs_restored "$service_name" "world" "systemd" "$TEST_DIR/bin/worldserver" "$TEST_DIR/etc/worldserver.conf" "always" "none" "0" "--user" "" "$run_engine_config"
[ -f "$run_engine_config" ]
[ -f "$service_conf" ]
grep -Fq 'export SESSION_MANAGER="none"' "$run_engine_config"
grep -Fq 'export BINPATH="'$TEST_DIR'/bin"' "$run_engine_config"
grep -Fq "RUN_ENGINE_CONFIG_FILE=\"$run_engine_config\"" "$service_conf"
grep -Fq 'RESTART_POLICY="always"' "$service_conf"
}
@test "service-manager: wait-uptime times out for unknown service" {
command -v jq >/dev/null 2>&1 || skip "jq not installed"
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
@ -279,6 +328,136 @@ EOF
[[ "$output" =~ "Configuration file not found" ]]
}
# ===== PATH PORTABILITY TESTS =====
@test "service-manager: path conversion functions work correctly" {
# Source the service-manager script to access helper functions
source "$SCRIPT_DIR/service-manager.sh"
# Test make_path_relative without AC_SERVICE_CONFIG_DIR
unset AC_SERVICE_CONFIG_DIR
result=$(make_path_relative "/absolute/path/test")
[[ "$result" == "/absolute/path/test" ]]
# Test make_path_relative with AC_SERVICE_CONFIG_DIR
export AC_SERVICE_CONFIG_DIR="/tmp/test-config"
mkdir -p "$AC_SERVICE_CONFIG_DIR/subdir"
result=$(make_path_relative "$AC_SERVICE_CONFIG_DIR/subdir/binary")
[[ "$result" == "subdir/binary" ]]
result=$(make_path_relative "/opt/bin/authserver")
[[ "$result" == "../../opt/bin/authserver" ]]
# Test make_path_absolute
result=$(make_path_absolute "subdir/binary")
[[ "$result" == "$AC_SERVICE_CONFIG_DIR/subdir/binary" ]]
result=$(make_path_absolute "../../opt/bin/authserver")
[[ "$result" == "/opt/bin/authserver" ]]
# Test absolute path stays absolute
result=$(make_path_absolute "/absolute/path")
[[ "$result" == "/absolute/path" ]]
# Cleanup
rm -rf "$AC_SERVICE_CONFIG_DIR"
unset AC_SERVICE_CONFIG_DIR
}
@test "service-manager: registry stores relative paths when possible" {
# Set up test environment
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/service-config"
mkdir -p "$AC_SERVICE_CONFIG_DIR"
# Create a temporary service registry in our test directory
local test_registry="$AC_SERVICE_CONFIG_DIR/test_registry.json"
echo "[]" > "$test_registry"
# Source the service-manager and override REGISTRY_FILE
source "$SCRIPT_DIR/service-manager.sh"
REGISTRY_FILE="$test_registry"
# Create test binary directory under config dir
mkdir -p "$AC_SERVICE_CONFIG_DIR/bin"
# Test that paths under AC_SERVICE_CONFIG_DIR are stored as relative
add_service_to_registry "test-service" "pm2" "auth" "$AC_SERVICE_CONFIG_DIR/bin/authserver" "--config test.conf" "" "always" "none" "0" "" "$AC_SERVICE_CONFIG_DIR/etc/test.conf"
# Check that paths were stored as relative
local stored_bin_path=$(jq -r '.[0].bin_path' "$test_registry")
local stored_config_path=$(jq -r '.[0].server_config' "$test_registry")
[[ "$stored_bin_path" == "bin/authserver" ]]
[[ "$stored_config_path" == "etc/test.conf" ]]
# Test that absolute paths outside config dir are stored as absolute
add_service_to_registry "test-service2" "pm2" "auth" "/opt/azerothcore/bin/authserver" "--config test.conf" "" "always" "none" "0" "" "/opt/azerothcore/etc/test.conf"
local stored_bin_path2=$(jq -r '.[1].bin_path' "$test_registry")
local stored_config_path2=$(jq -r '.[1].server_config' "$test_registry")
local expected_bin_rel=$(make_path_relative "/opt/azerothcore/bin/authserver")
local expected_cfg_rel=$(make_path_relative "/opt/azerothcore/etc/test.conf")
[[ "$stored_bin_path2" == "$expected_bin_rel" ]]
[[ "$stored_config_path2" == "$expected_cfg_rel" ]]
# Cleanup
rm -rf "$AC_SERVICE_CONFIG_DIR"
unset AC_SERVICE_CONFIG_DIR
}
@test "service-manager: restore --sync-only recreates config files" {
command -v jq >/dev/null 2>&1 || skip "jq not installed"
export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services"
mkdir -p "$AC_SERVICE_CONFIG_DIR"
cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" <<'EOF'
[
{
"name": "sync-test",
"provider": "pm2",
"type": "auth",
"bin_path": "bin/authserver",
"exec": {
"command": "../src/run-engine",
"args": [
"start",
"bin/authserver",
"--config",
"sync-test-run-engine.conf"
]
},
"args": "",
"created": "2025-10-12T20:00:54+02:00",
"status": "active",
"systemd_type": "--user",
"restart_policy": "always",
"session_manager": "none",
"gdb_enabled": "0",
"pm2_opts": " ",
"server_config": "etc/authserver.conf"
}
]
EOF
rm -f "$AC_SERVICE_CONFIG_DIR/sync-test.conf" "$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf"
mkdir -p "$AC_SERVICE_CONFIG_DIR/bin" "$AC_SERVICE_CONFIG_DIR/etc"
touch "$AC_SERVICE_CONFIG_DIR/bin/authserver"
touch "$AC_SERVICE_CONFIG_DIR/etc/authserver.conf"
run "$SCRIPT_DIR/service-manager.sh" restore --sync-only
debug_on_failure
[ "$status" -eq 0 ]
[ -f "$AC_SERVICE_CONFIG_DIR/sync-test.conf" ]
[ -f "$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf" ]
grep -Fq "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf\"" "$AC_SERVICE_CONFIG_DIR/sync-test.conf"
grep -Fq "export BINPATH=\"$AC_SERVICE_CONFIG_DIR/bin\"" "$AC_SERVICE_CONFIG_DIR/sync-test-run-engine.conf"
}
@test "examples: restarter-auth should show configuration error" {
run "$SCRIPT_DIR/examples/restarter-auth.sh"
[[ "$output" =~ "Configuration file not found" ]]

View File

@ -0,0 +1,11 @@
-- DB update 2025_10_11_06 -> 2025_10_12_00
--
UPDATE `creature_template` SET `maxlevel` = 33 WHERE `entry` = 940;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 940);
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
(940, 0, 0, 0, 74, 0, 100, 0, 0, 0, 19900, 28900, 30, 0, 11, 6077, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 'Kurzen Medicine Man - On Friendly Between 0-30% Health - Cast \'Renew\''),
(940, 0, 1, 0, 74, 0, 100, 0, 0, 0, 34300, 39100, 30, 0, 11, 6064, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 'Kurzen Medicine Man - On Friendly Between 0-30% Health - Cast \'Heal\''),
(940, 0, 2, 0, 60, 0, 100, 0, 1000, 1000, 70000, 90000, 0, 0, 11, 602, 32, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Kurzen Medicine Man - On Update - Cast \'Inner Fire\''),
(940, 0, 3, 0, 2, 0, 100, 1, 0, 15, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Kurzen Medicine Man - Between 0-15% Health - Flee For Assist'),
(940, 0, 4, 0, 2, 0, 100, 0, 0, 50, 3000, 16000, 0, 0, 11, 6064, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Kurzen Medicine Man - Between 0-50% Health - Cast \'Heal\''),
(940, 0, 5, 0, 2, 0, 100, 0, 0, 90, 16000, 24000, 30, 0, 11, 6077, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Kurzen Medicine Man - Between 0-90% Health - Cast \'Renew\'');

View File

@ -0,0 +1,10 @@
-- DB update 2025_10_12_00 -> 2025_10_12_01
--
-- With this smart script set, the worm will seek the first player within 18 yards to attack, with no regard to the player's level. Being outside of 18 yards when killing a Rotted One will avoid this behavior.
-- Smart Script #2 will ensure the mob dies after 30 seconds, which is how long they are supposed to live.
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 2462;
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 2462);
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
(2462, 0, 0, 0, 101, 0, 100, 0, 0, 18, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 18, 18, 0, 0, 0, 0, 0, 0, 0, 'Flesh Eating Worm - On 0 or More Players in Range - Start Attacking'),
(2462, 0, 1, 0, 0, 0, 100, 0, 30000, 30000, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Flesh Eating Worm - In Combat - Kill Self');

View File

@ -0,0 +1,23 @@
-- DB update 2025_10_12_01 -> 2025_10_12_02
--
-- GM Utility Spell Scripts
DELETE FROM `spell_script_names` WHERE `spell_id` IN (456, 2765, 1509, 18139, 6147, 2763, 20115, 20114, 24676, 24675);
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(456, 'spell_gen_showlabel_off'),
(2765, 'spell_gen_showlabel_on'),
(1509, 'spell_gen_gm_off'),
(18139, 'spell_gen_gm_on'),
(6147, 'spell_gen_invis_off'),
(2763, 'spell_gen_invis_on'),
(20115, 'spell_gen_bm_on'),
(20114, 'spell_gen_bm_off'),
(24676, 'spell_gen_bm_on'),
(24675, 'spell_gen_bm_off');
DELETE FROM `acore_string` WHERE `entry` = 1186;
INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES
(1186, 'Beastmaster mode is {}', NULL, NULL, 'Der Beastmaster mode ist an ({})!', '兽王模式:{}', NULL, NULL, NULL, NULL);
DELETE FROM `command` WHERE `name`='bm';
INSERT INTO `command` (`name`, `security`, `help`) VALUES
('bm', 3, 'Syntax: .bm [on/off]\nEnable or Disable in game Beastmaster mode or show current state if on/off not provided.');

View File

@ -0,0 +1,128 @@
-- DB update 2025_10_12_02 -> 2025_10_12_03
DELETE FROM `creature_template_model` WHERE `CreatureID` IN (15928, 19325, 20794, 26620, 26627, 26628, 26630, 26631, 26632, 26637, 26638, 26641, 26712, 26824, 27483, 27490, 27597, 27598, 27600, 27709, 27753, 27909, 27975, 27977, 27978, 27981, 27982, 27983, 27984, 27985, 28070, 28165, 28546, 28547, 28578, 28579, 28580, 28581, 28582, 28583, 28584, 28585, 28586, 28587, 28684, 28695, 28729, 28730, 28731, 28732, 28733, 28734, 28823, 28826, 28835, 28836, 28837, 28838, 28859, 28860, 28920, 28921, 28922, 28923, 28947, 28961, 28965, 29048, 29062, 29063, 29064, 29240, 29335, 30090, 30118, 30449, 30451, 30452, 30616, 30641, 30643, 30882, 30890, 30897, 30898, 30899, 31218, 31219, 31311, 31317, 31520, 31521, 31534, 31535, 31539, 31540, 31541, 31543, 31734, 31749, 31750, 31751, 31752, 32187);
INSERT INTO `creature_template_model` (`CreatureID`, `Idx`, `CreatureDisplayID`, `DisplayScale`, `Probability`, `VerifiedBuild`) VALUES
(15928, 0, 16137, 1, 0, 51831),
(19325, 0, 18737, 1, 1, 51831),
(20794, 0, 20182, 1, 0, 51831), -- 19978
(26620, 0, 27077, 1, 1, 51831),
(26620, 1, 27078, 1, 1, 51831),
(26627, 0, 24500, 1, 1, 51831),
(26628, 0, 19732, 1, 1, 51831),
(26630, 0, 26352, 1, 0, 51831),
(26631, 0, 26292, 1, 1, 51831),
(26632, 0, 27072, 1, 0, 51831),
(26637, 0, 26860, 1, 100, 51831),
(26638, 0, 27056, 1, 100, 51831),
(26641, 0, 19734, 1, 1, 51831),
(26712, 0, 169, 1, 0, 51831), -- 17188!
(26712, 1, 17188, 1, 1, 51831),
(26824, 0, 6469, 1, 1, 51831),
(27483, 0, 5240, 1, 1, 51831),
(27490, 0, 7897, 1, 0, 51831),
(27597, 0, 22337, 1, 0, 51831),
(27598, 0, 10978, 1, 1, 51831),
(27598, 1, 10972, 1, 1, 51831),
(27600, 0, 2606, 1, 0, 51831),
(27709, 0, 27079, 1, 1, 51831),
(27709, 1, 27080, 1, 1, 51831),
(27709, 2, 27081, 1, 1, 51831),
(27753, 0, 27079, 1, 1, 51831),
(27753, 1, 27080, 1, 1, 51831),
(27753, 2, 27081, 1, 1, 51831),
(27909, 0, 24925, 1, 0, 51831),
(27975, 0, 26657, 1, 0, 51831),
(27977, 0, 20909, 1, 100, 51831),
(27978, 0, 27483, 1, 100, 51831),
(27981, 0, 25177, 1, 100, 51831),
(27982, 0, 25754, 1, 0, 51831),
(27983, 0, 25991, 1, 1, 51831),
(27984, 0, 25987, 1, 1, 51831),
(27985, 0, 26148, 1, 0, 51831),
(28070, 0, 26353, 1, 100, 51831),
(28165, 0, 25176, 1, 100, 51831),
(28546, 0, 27484, 1, 0, 51831),
(28547, 0, 8715, 1, 0, 51831),
(28578, 0, 25984, 1, 0, 51831),
(28579, 0, 25982, 1, 0, 51831),
(28580, 0, 25985, 1, 0, 51831),
(28581, 0, 25759, 1, 0, 51831),
(28582, 0, 25754, 1, 0, 51831),
(28583, 0, 25654, 1, 0, 51831),
(28584, 0, 24905, 1, 0, 51831),
(28585, 0, 2170, 1, 0, 51831),
(28586, 0, 27301, 1, 1, 51831), -- scale 1.3!
(28587, 0, 27071, 1, 1, 51831),
(28684, 0, 27394, 1, 0, 51831),
(28695, 0, 25629, 1, 0, 51831),
(28729, 0, 23984, 1, 0, 51831),
(28730, 0, 23568, 1, 0, 51831),
(28731, 0, 25729, 1, 0, 51831),
(28732, 0, 23567, 1, 0, 51831),
(28733, 0, 25258, 1, 0, 51831),
(28734, 0, 25237, 1, 0, 51831),
(28823, 0, 1126, 1, 0, 51831),
(28823, 1, 11686, 1, 1, 51831),
(28826, 0, 26381, 1, 0, 51831),
(28835, 0, 26143, 1, 1, 51831),
(28836, 0, 25756, 1, 1, 51831),
(28837, 0, 25757, 1, 1, 51831),
(28838, 0, 26053, 1, 0, 51831),
(28859, 0, 26752, 1, 0, 51831),
(28860, 0, 27035, 1, 0, 51831),
(28920, 0, 27092, 1, 0, 51831),
(28921, 0, 26776, 1, 0, 51831),
(28922, 0, 27395, 1, 0, 51831), -- scale 1.15!
(28923, 0, 27485, 1, 1, 51831),
(28947, 0, 169, 1, 0, 51831),
(28947, 1, 11686, 1, 1, 51831),
(28961, 0, 25984, 1, 0, 51831),
(28965, 0, 25985, 1, 0, 51831),
(29048, 0, 26937, 1, 0, 51831),
(29062, 0, 25768, 1, 0, 51831),
(29063, 0, 3004, 1, 0, 51831),
(29064, 0, 23564, 1, 0, 51831),
(29240, 0, 26065, 1, 0, 51831), -- scale 1.3!
(29335, 0, 23565, 1, 0, 51831),
(30090, 0, 169, 1, 0, 51831),
(30090, 1, 11686, 1, 1, 51831),
(30118, 0, 169, 1, 0, 51831),
(30118, 1, 14501, 1, 1, 51831), -- 11686, scale 2!
(30449, 0, 27039, 1, 0, 51831),
(30451, 0, 27421, 1, 0, 51831),
(30452, 0, 27082, 1, 0, 51831),
(30616, 0, 1126, 1, 0, 51831),
(30616, 1, 11686, 1, 1, 51831), -- 16925
(30641, 0, 15294, 1, 0, 51831),
(30643, 0, 2172, 1, 0, 51831),
(30882, 0, 28014, 1, 1, 51831),
(30890, 0, 19295, 1, 0, 51831),
(30897, 0, 18783, 1, 0, 51831),
(30897, 1, 27401, 1, 1, 51831), -- 11686
(30898, 0, 18783, 1, 0, 51831),
(30898, 1, 27401, 1, 1, 51831), -- 11686
(30899, 0, 18783, 1, 0, 51831),
(30899, 1, 16925, 1, 1, 51831), -- 11686
(31218, 0, 8311, 1, 0, 51831),
(31219, 0, 12894, 1, 0, 51831),
(31311, 0, 27035, 1, 0, 51831),
(31317, 0, 2172, 1, 0, 51831),
(31520, 0, 27421, 1, 0, 51831),
(31521, 0, 15294, 1, 0, 51831),
(31534, 0, 27082, 1, 0, 51831),
(31535, 0, 27039, 1, 0, 51831),
(31539, 0, 28014, 1, 1, 51831),
(31540, 0, 19295, 1, 0, 51831),
(31541, 0, 8311, 1, 0, 51831),
(31543, 0, 12894, 1, 0, 51831),
(31734, 0, 26752, 1, 0, 51831),
(31749, 0, 26876, 1, 0, 51831),
(31750, 0, 24316, 1, 1, 51831),
(31750, 1, 24317, 1, 1, 51831),
(31750, 2, 24318, 1, 1, 51831),
(31750, 3, 24319, 1, 1, 51831),
(31751, 0, 24316, 1, 1, 51831),
(31751, 1, 24317, 1, 1, 51831),
(31751, 2, 24318, 1, 1, 51831),
(31751, 3, 24319, 1, 1, 51831),
(31752, 0, 25835, 1, 1, 51831),
(32187, 0, 26753, 1, 0, 51831);

View File

@ -0,0 +1,40 @@
-- DB update 2025_10_12_03 -> 2025_10_12_04
DELETE FROM `creature_template_model` WHERE `CreatureID` IN (34382, 34383, 34435, 34476, 34477, 34478, 34479, 34480, 34481, 34482, 34483, 34484, 34644, 34653, 34654, 34675, 34676, 34677, 34678, 34679, 34708, 34710, 34711, 34712, 34713, 34714, 34744, 34768, 35254, 35256, 35260, 35261, 36479, 36506);
INSERT INTO `creature_template_model` (`CreatureID`, `Idx`, `CreatureDisplayID`, `DisplayScale`, `Probability`, `VerifiedBuild`) VALUES
(34382, 0, 29203, 1, 1, 51831),
(34383, 0, 29204, 1, 1, 51831),
(34435, 0, 29226, 1, 1, 51831),
(34476, 0, 29228, 1, 1, 51831),
(34477, 0, 29229, 1, 1, 51831),
(34478, 0, 29230, 1, 1, 51831),
(34479, 0, 29231, 1, 1, 51831),
(34480, 0, 29232, 1, 1, 51831),
(34481, 0, 29233, 1, 1, 51831),
(34482, 0, 29234, 1, 1, 51831),
(34483, 0, 29235, 1, 1, 51831),
(34484, 0, 29236, 1, 1, 51831),
(34644, 0, 29392, 1, 0, 51831),
(34653, 0, 29341, 1, 0, 51831),
(34654, 0, 29330, 1, 0, 51831),
(34675, 0, 29337, 1, 0, 51831),
(34676, 0, 29391, 1, 0, 51831),
(34677, 0, 29338, 1, 0, 51831),
(34678, 0, 29339, 1, 0, 51831),
(34679, 0, 29395, 1, 0, 51831),
(34708, 0, 29324, 1, 0, 51831),
(34710, 0, 29400, 1, 0, 51831),
(34711, 0, 29388, 1, 0, 51831),
(34712, 0, 29365, 1, 0, 51831),
(34713, 0, 29401, 1, 0, 51831),
(34714, 0, 29399, 1, 0, 51831),
(34744, 0, 29444, 1, 0, 51831),
(34768, 0, 29403, 1, 0, 51831),
(35254, 0, 29717, 1, 3, 51831),
(35254, 1, 29718, 1, 3, 51831),
(35254, 2, 29720, 1, 1, 51831),
(35254, 3, 29721, 1, 1, 51831),
(35256, 0, 29719, 1, 1, 51831),
(35260, 0, 29732, 1, 1, 51831),
(35261, 0, 29733, 1, 1, 51831),
(36479, 0, 30120, 1, 1, 51831),
(36506, 0, 30156, 1, 1, 51831);

View File

@ -0,0 +1,23 @@
-- DB update 2025_10_12_04 -> 2025_10_12_05
DELETE FROM `creature_template_model` WHERE `CreatureID` IN (10184, 11262, 12758, 28681, 30084, 30161, 30245, 30248, 30249, 30282, 30592, 32295, 36561);
INSERT INTO `creature_template_model` (`CreatureID`, `Idx`, `CreatureDisplayID`, `DisplayScale`, `Probability`, `VerifiedBuild`) VALUES
(10184, 0, 8570, 1, 0, 51831),
(11262, 0, 397, 1, 0, 51831),
(12758, 0, 11686, 1, 0, 51831),
(28681, 0, 26131, 1, 0, 51831),
(30084, 0, 26753, 1, 0, 51831),
(30161, 0, 25835, 1, 1, 51831),
(30245, 0, 24316, 1, 1, 51831),
(30245, 1, 24317, 1, 1, 51831),
(30245, 2, 24318, 1, 1, 51831),
(30245, 3, 24319, 1, 1, 51831),
(30248, 0, 26876, 1, 0, 51831),
(30249, 0, 24316, 1, 1, 51831),
(30249, 1, 24317, 1, 1, 51831),
(30249, 2, 24318, 1, 1, 51831),
(30249, 3, 24319, 1, 1, 51831),
(30282, 0, 14501, 1, 0, 51831),
(30592, 0, 169, 1, 0, 51831),
(30592, 1, 11686, 1, 1, 51831),
(32295, 0, 27569, 1, 1, 51831),
(36561, 0, 12891, 1, 0, 51831);

View File

@ -0,0 +1,7 @@
-- DB update 2025_10_12_05 -> 2025_10_12_06
--
DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=23 AND `SourceEntry`=0 AND `SourceId`=0 AND `SourceGroup` IN (3443, 12919, 15471);
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 9087) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 12) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 109) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0);
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
(15, 9087, 0, 0, 0, 12, 0, 109, 0, 0, 0, 0, 0, '', 'event \'Sun\'s Reach Reclamation Phase Anvil\' must be active');

View File

@ -5,7 +5,7 @@
##------------------------------- VARIABLES ---------------------------------##
MODULE_TEMPLATE_URL="https://github.com/azerothcore/skeleton-module/"
GIT_COMMIT_MSG_SETUP="setup_git_commit_template.sh"
GIT_COMMIT_MSG_SETUP="../../apps/git_tools/setup_git_commit_template.sh"
##------------------------------- CODE ---------------------------------##

View File

@ -2350,6 +2350,18 @@ Rate.Reputation.LowLevel.Quest = 1
Rate.Reputation.RecruitAFriendBonus = 0.1
#
# Rate.Reputation.Gain.WSG
# Rate.Reputation.Gain.AB
# Rate.Reputation.Gain.AV
# Description: Reputation bonus rate for WSG, AB and AV battlegrounds.
# This is applied IN ADDITION to the global Rate.Reputation.Gain.
# Default: 1
Rate.Reputation.Gain.WSG = 1
Rate.Reputation.Gain.AB = 1
Rate.Reputation.Gain.AV = 1
#
###################################################################################################

View File

@ -496,17 +496,13 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (ssl == "ssl")
args.emplace_back("--ssl-mode=REQUIRED");
// Execute sql file
args.emplace_back("-e");
args.emplace_back(Acore::StringFormat("BEGIN; SOURCE {}; COMMIT;", path.generic_string()));
// Database
if (!database.empty())
args.emplace_back(database);
// Invokes a mysql process which doesn't leak credentials to logs
int const ret = Acore::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args,
"sql.updates", "", true);
"sql.updates", path.generic_string(), true);
if (ret != EXIT_SUCCESS)
{

View File

@ -115,7 +115,7 @@ void BattlegroundAB::PostUpdateImpl(uint32 diff)
if (honorRewards < uint8(m_TeamScores[teamId] / _honorTics))
RewardHonorToTeam(GetBonusHonorFromKill(1), teamId);
if (reputationRewards < uint8(m_TeamScores[teamId] / _reputationTics))
RewardReputationToTeam(teamId == TEAM_ALLIANCE ? 509 : 510, 10, teamId);
RewardReputationToTeam(teamId == TEAM_ALLIANCE ? 509 : 510, uint32(10 * _abReputationRate), teamId);
if (information < uint8(m_TeamScores[teamId] / BG_AB_WARNING_NEAR_VICTORY_SCORE))
{
if (teamId == TEAM_ALLIANCE)
@ -421,6 +421,7 @@ bool BattlegroundAB::SetupBattleground()
{
_honorTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_AB_HONOR_TICK_WEEKEND : BG_AB_HONOR_TICK_NORMAL;
_reputationTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_AB_REP_TICK_WEEKEND : BG_AB_REP_TICK_NORMAL;
_abReputationRate = sWorld->getRate(RATE_REPUTATION_GAIN_AB);
for (uint32 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
{

View File

@ -299,6 +299,7 @@ private:
EventMap _bgEvents;
uint32 _honorTics;
uint32 _reputationTics;
float _abReputationRate;
uint8 _controlledPoints[PVP_TEAMS_COUNT] {};
bool _teamScores500Disadvantage[PVP_TEAMS_COUNT] {};
uint32 _configurableMaxTeamScore;

View File

@ -194,21 +194,21 @@ void BattlegroundAV::HandleQuestComplete(uint32 questid, Player* player)
case AV_QUEST_A_COMMANDER1:
case AV_QUEST_H_COMMANDER1:
m_Team_QuestStatus[teamId][1]++;
RewardReputationToTeam(teamId, 1, teamId);
RewardReputationToTeam(teamId, uint32(1 * _avReputationRate), teamId);
if (m_Team_QuestStatus[teamId][1] == 30)
LOG_DEBUG("bg.battleground", "BG_AV Quest {} completed (need to implement some events here", questid);
break;
case AV_QUEST_A_COMMANDER2:
case AV_QUEST_H_COMMANDER2:
m_Team_QuestStatus[teamId][2]++;
RewardReputationToTeam(teamId, 1, teamId);
RewardReputationToTeam(teamId, uint32(1 * _avReputationRate), teamId);
if (m_Team_QuestStatus[teamId][2] == 60)
LOG_DEBUG("bg.battleground", "BG_AV Quest {} completed (need to implement some events here", questid);
break;
case AV_QUEST_A_COMMANDER3:
case AV_QUEST_H_COMMANDER3:
m_Team_QuestStatus[teamId][3]++;
RewardReputationToTeam(teamId, 1, teamId);
RewardReputationToTeam(teamId, uint32(1 * _avReputationRate), teamId);
if (m_Team_QuestStatus[teamId][3] == 120)
LOG_DEBUG("bg.battleground", "BG_AV Quest {} completed (need to implement some events here", questid);
break;
@ -316,21 +316,21 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type)
type -= AV_CPLACE_MAX;
cinfoid = uint16(BG_AV_StaticCreaturePos[type][4]);
creature = AddCreature(BG_AV_StaticCreatureInfo[cinfoid],
type + AV_CPLACE_MAX,
BG_AV_StaticCreaturePos[type][0],
BG_AV_StaticCreaturePos[type][1],
BG_AV_StaticCreaturePos[type][2],
BG_AV_StaticCreaturePos[type][3]);
type + AV_CPLACE_MAX,
BG_AV_StaticCreaturePos[type][0],
BG_AV_StaticCreaturePos[type][1],
BG_AV_StaticCreaturePos[type][2],
BG_AV_StaticCreaturePos[type][3]);
isStatic = true;
}
else
{
creature = AddCreature(BG_AV_CreatureInfo[cinfoid],
type,
BG_AV_CreaturePos[type][0],
BG_AV_CreaturePos[type][1],
BG_AV_CreaturePos[type][2],
BG_AV_CreaturePos[type][3]);
type,
BG_AV_CreaturePos[type][0],
BG_AV_CreaturePos[type][1],
BG_AV_CreaturePos[type][2],
BG_AV_CreaturePos[type][3]);
}
if (!creature)
return nullptr;
@ -344,7 +344,7 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type)
(cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3))))
{
if (!isStatic && ((cinfoid >= AV_NPC_A_GRAVEDEFENSE0 && cinfoid <= AV_NPC_A_GRAVEDEFENSE3)
|| (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3)))
|| (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3)))
{
CreatureData& data = sObjectMgr->NewOrExistCreatureData(creature->GetSpawnId());
data.wander_distance = 5;
@ -814,11 +814,11 @@ void BattlegroundAV::PopulateNode(BG_AV_Nodes node)
if (!trigger)
{
trigger = AddCreature(WORLD_TRIGGER,
node + 302,
BG_AV_CreaturePos[node + 302][0],
BG_AV_CreaturePos[node + 302][1],
BG_AV_CreaturePos[node + 302][2],
BG_AV_CreaturePos[node + 302][3]);
node + 302,
BG_AV_CreaturePos[node + 302][0],
BG_AV_CreaturePos[node + 302][1],
BG_AV_CreaturePos[node + 302][2],
BG_AV_CreaturePos[node + 302][3]);
}
//add bonus honor aura trigger creature when node is accupied
@ -1240,25 +1240,28 @@ GraveyardStruct const* BattlegroundAV::GetClosestGraveyard(Player* player)
bool BattlegroundAV::SetupBattleground()
{
_avReputationRate = sWorld->getRate(RATE_REPUTATION_GAIN_AV);
if (sBattlegroundMgr->IsBGWeekend(GetBgTypeID(true)))
{
_reputationTower = 18;
_reputationCaptain = 185;
_reputationBoss = 525;
_reputationPerOwnedGraveyard = 18;
_reputationSurvivingCaptain = 175;
_reputationSurvivingTower = 18;
_reputationPerOwnedMine = 36;
_reputationTower = uint32(18 * _avReputationRate);
_reputationCaptain = uint32(185 * _avReputationRate);
_reputationBoss = uint32(525 * _avReputationRate);
_reputationPerOwnedGraveyard = uint32(18 * _avReputationRate);
_reputationSurvivingCaptain = uint32(175 * _avReputationRate);
_reputationSurvivingTower = uint32(18 * _avReputationRate);
_reputationPerOwnedMine = uint32(36 * _avReputationRate);
}
else
{
_reputationTower = 12;
_reputationCaptain = 125;
_reputationBoss = sWorld->getIntConfig(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH);
_reputationPerOwnedGraveyard = 12;
_reputationSurvivingCaptain = 125;
_reputationSurvivingTower = 12;
_reputationPerOwnedMine = 24;
_reputationTower = uint32(12 * _avReputationRate);
_reputationCaptain = uint32(125 * _avReputationRate);
// Special case: This value comes from another config setting, but we still apply our multiplier
_reputationBoss = uint32(sWorld->getIntConfig(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH) * _avReputationRate);
_reputationPerOwnedGraveyard = uint32(12 * _avReputationRate);
_reputationSurvivingCaptain = uint32(125 * _avReputationRate);
_reputationSurvivingTower = uint32(12 * _avReputationRate);
_reputationPerOwnedMine = uint32(24 * _avReputationRate);
}
// Create starting objects

View File

@ -1846,6 +1846,7 @@ private:
uint32 _reputationSurvivingCaptain = 0; // 125, 175
uint32 _reputationSurvivingTower = 0; // 12, 18
uint32 _reputationPerOwnedMine = 0; // 24, 36
float _avReputationRate;
bool m_IsInformedNearVictory[2] {};
};

View File

@ -426,15 +426,17 @@ void BattlegroundWS::HandleAreaTrigger(Player* player, uint32 trigger)
bool BattlegroundWS::SetupBattleground()
{
_wsReputationRate = sWorld->getRate(RATE_REPUTATION_GAIN_WSG);
if (sBattlegroundMgr->IsBGWeekend(GetBgTypeID(true)))
{
_reputationCapture = 45;
_reputationCapture = uint32(45 * _wsReputationRate);
_honorWinKills = 3;
_honorEndKills = 4;
}
else
{
_reputationCapture = 35;
_reputationCapture = uint32(35 * _wsReputationRate);
_honorWinKills = 1;
_honorEndKills = 2;
}

View File

@ -258,6 +258,7 @@ private:
ObjectGuid _droppedFlagGUID[2];
uint8 _flagState[2];
TeamId _lastFlagCaptureTeam;
float _wsReputationRate;
uint32 _reputationCapture;
uint32 _honorWinKills;
uint32 _honorEndKills;

View File

@ -90,6 +90,7 @@
#include "WorldStateDefines.h"
#include "WorldStatePackets.h"
#include <cmath>
#include <queue>
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
// however, for some reasons removing it would cause a damn linking issue
@ -14364,6 +14365,67 @@ bool Player::CanSeeSpellClickOn(Creature const* c) const
return false;
}
/**
* @brief Checks if any vendor option is available in the gossip menu tree for a given creature.
*
* @param menuId The starting gossip menu ID to check.
* @param creature Pointer to the creature whose gossip menus are being checked.
* @return true if a vendor option is available in any accessible menu; false otherwise.
*/
bool Player::AnyVendorOptionAvailable(uint32 menuId, Creature const* creature) const
{
std::set<uint32> visitedMenus;
std::queue<uint32> menusToCheck;
menusToCheck.push(menuId);
while (!menusToCheck.empty())
{
uint32 const currentMenuId = menusToCheck.front();
menusToCheck.pop();
if (visitedMenus.find(currentMenuId) != visitedMenus.end())
continue;
visitedMenus.insert(currentMenuId);
GossipMenuItemsMapBounds menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(currentMenuId);
if (menuItemBounds.first == menuItemBounds.second && currentMenuId != 0)
continue;
for (auto itr = menuItemBounds.first; itr != menuItemBounds.second; ++itr)
{
if (!sConditionMgr->IsObjectMeetToConditions(const_cast<Player*>(this), const_cast<Creature*>(creature), itr->second.Conditions))
continue;
if (itr->second.OptionType == GOSSIP_OPTION_VENDOR)
return true;
else if (itr->second.ActionMenuID)
{
GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(itr->second.ActionMenuID);
bool menuAccessible = false;
if (menuBounds.first == menuBounds.second)
menuAccessible = true;
else
{
for (auto menuItr = menuBounds.first; menuItr != menuBounds.second; ++menuItr)
if (sConditionMgr->IsObjectMeetToConditions(const_cast<Player*>(this), const_cast<Creature*>(creature), menuItr->second.Conditions))
{
menuAccessible = true;
break;
}
}
if (menuAccessible)
menusToCheck.push(itr->second.ActionMenuID);
}
}
}
return false;
}
bool Player::CanSeeVendor(Creature const* creature) const
{
if (!creature->HasNpcFlag(UNIT_NPC_FLAG_VENDOR))
@ -14371,9 +14433,11 @@ bool Player::CanSeeVendor(Creature const* creature) const
ConditionList conditions = sConditionMgr->GetConditionsForNpcVendorEvent(creature->GetEntry(), 0);
if (!sConditionMgr->IsObjectMeetToConditions(const_cast<Player*>(this), const_cast<Creature*>(creature), conditions))
{
return false;
}
uint32 const menuId = creature->GetCreatureTemplate()->GossipMenuId;
if (!AnyVendorOptionAvailable(menuId, creature))
return false;
return true;
}

View File

@ -41,6 +41,7 @@
#include "TradeData.h"
#include "Unit.h"
#include "WorldSession.h"
#include <set>
#include <string>
#include <vector>
@ -1154,6 +1155,7 @@ public:
void SetCommentator(bool on) { ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_COMMENTATOR2, on); }
[[nodiscard]] bool IsDeveloper() const { return HasPlayerFlag(PLAYER_FLAGS_DEVELOPER); }
void SetDeveloper(bool on) { ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_DEVELOPER, on); }
void SetBeastMaster(bool on) { if (on) SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); else RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); }
[[nodiscard]] bool isAcceptWhispers() const { return m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS; }
void SetAcceptWhispers(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; }
[[nodiscard]] bool IsGameMaster() const { return m_ExtraFlags & PLAYER_EXTRA_GM_ON; }
@ -2547,7 +2549,9 @@ public:
//bool isActiveObject() const { return true; }
bool CanSeeSpellClickOn(Creature const* creature) const;
[[nodiscard]] bool CanSeeVendor(Creature const* creature) const;
private:
[[nodiscard]] bool AnyVendorOptionAvailable(uint32 menuId, Creature const* creature) const;
public:
[[nodiscard]] uint32 GetChampioningFaction() const { return m_ChampioningFaction; }
void SetChampioningFaction(uint32 faction) { m_ChampioningFaction = faction; }
Spell* m_spellModTakingSpell;

View File

@ -20791,7 +20791,10 @@ void Unit::PatchValuesUpdate(ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPoi
appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK;
if (!target->CanSeeVendor(creature))
{
appendValue &= ~UNIT_NPC_FLAG_REPAIR;
appendValue &= ~UNIT_NPC_FLAG_VENDOR_MASK;
}
if (!creature->IsValidTrainerForPlayer(target, &appendValue))
appendValue &= ~UNIT_NPC_FLAG_TRAINER;

View File

@ -1122,8 +1122,22 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
if (!cInfo->Models.size())
LOG_ERROR("sql.sql", "Creature (Entry: {}) does not have any existing display id in creature_template_model.", cInfo->Entry);
else if (std::accumulate(cInfo->Models.begin(), cInfo->Models.end(), 0.0f, [](float sum, CreatureModel const& model) { return sum + model.Probability; }) <= 0.0f)
LOG_ERROR("sql.sql", "Creature (Entry: {}) has zero total chance for all models in creature_template_model.", cInfo->Entry);
else
{
float const totalProbability = std::accumulate(cInfo->Models.begin(), cInfo->Models.end(), 0.0f, [](float sum, CreatureModel const& model) { return sum + model.Probability; });
if (totalProbability <= 0.0f)
{ // There are many cases in official data of all models having a probability of 0. Believe to be treated equivalent to equal chance ONLY if all are zeroed
if (totalProbability == 0.0f)
LOG_DEBUG("sql.sql", "Creature (Entry: {}) has zero total chance for all models in creature_template_model. Setting all to 1.0.", cInfo->Entry);
else // Custom, likely bad data
LOG_ERROR("sql.sql", "Creature (Entry: {}) has less than zero total chance for all models in creature_template_model. Setting all to 1.0.", cInfo->Entry);
auto& models = const_cast<CreatureTemplate*>(cInfo)->Models;
for (auto& model : models)
model.Probability = 1.0f;
}
}
if (!cInfo->unit_class || ((1 << (cInfo->unit_class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
{

View File

@ -978,7 +978,8 @@ enum AcoreStrings
LANG_GUILD_INFO_EXTRA_INFO = 1183,
LANG_GUILD_INFO_RANKS = 1184,
LANG_GUILD_INFO_RANKS_LIST = 1185,
// Room for more level 3 1186-1198 not used
LANG_COMMAND_BEASTMASTER_MODE = 1186,
// Room for more level 3 1187-1198 not used
// Debug commands
LANG_DO_NOT_USE_6X_DEBUG_AREATRIGGER_LEFT = 1999,

View File

@ -880,7 +880,6 @@ void Map::ScriptsProcess()
if (!cSource->IsAlive())
return;
cSource->GetMotionMaster()->MovementExpired();
cSource->GetMotionMaster()->MoveIdle();
switch (step.script->Movement.MovementType)

View File

@ -85,6 +85,9 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<float>(RATE_BUYVALUE_ITEM_HEIRLOOM, "Rate.BuyValue.Item.Heirloom", 1.0f);
SetConfigValue<float>(RATE_REPUTATION_GAIN, "Rate.Reputation.Gain", 1.0f);
SetConfigValue<float>(RATE_REPUTATION_GAIN_AB, "Rate.Reputation.Gain.AB", 1.0f);
SetConfigValue<float>(RATE_REPUTATION_GAIN_AV, "Rate.Reputation.Gain.AV", 1.0f);
SetConfigValue<float>(RATE_REPUTATION_GAIN_WSG, "Rate.Reputation.Gain.WSG", 1.0f);
SetConfigValue<float>(RATE_REPUTATION_LOWLEVEL_KILL, "Rate.Reputation.LowLevel.Kill", 1.0f);
SetConfigValue<float>(RATE_REPUTATION_LOWLEVEL_QUEST, "Rate.Reputation.LowLevel.Quest", 1.0f);
SetConfigValue<float>(RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS, "Rate.Reputation.RecruitAFriendBonus", 0.1f);

View File

@ -437,6 +437,9 @@ enum ServerConfigs
RATE_XP_PET_NEXT_LEVEL,
RATE_REPAIRCOST,
RATE_REPUTATION_GAIN,
RATE_REPUTATION_GAIN_AB,
RATE_REPUTATION_GAIN_AV,
RATE_REPUTATION_GAIN_WSG,
RATE_REPUTATION_LOWLEVEL_KILL,
RATE_REPUTATION_LOWLEVEL_QUEST,
RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS,

View File

@ -193,7 +193,8 @@ public:
{ "skirmish", HandleSkirmishCommand, SEC_ADMINISTRATOR, Console::No },
{ "mailbox", HandleMailBoxCommand, SEC_MODERATOR, Console::No },
{ "string", HandleStringCommand, SEC_GAMEMASTER, Console::No },
{ "opendoor", HandleOpenDoorCommand, SEC_GAMEMASTER, Console::No }
{ "opendoor", HandleOpenDoorCommand, SEC_GAMEMASTER, Console::No },
{ "bm", HandleBMCommand, SEC_GAMEMASTER, Console::No }
};
return commandTable;
@ -504,6 +505,7 @@ public:
if (!session)
{
handler->SendErrorMessage(LANG_USE_BOL);
return false;
}
@ -537,9 +539,6 @@ public:
SetCommentatorMod(false);
return true;
}
handler->SendErrorMessage(LANG_USE_BOL);
return false;
}
static bool HandleDevCommand(ChatHandler* handler, Optional<bool> enableArg)
@ -548,6 +547,7 @@ public:
if (!session)
{
handler->SendErrorMessage(LANG_USE_BOL);
return false;
}
@ -582,9 +582,6 @@ public:
SetDevMod(false);
return true;
}
handler->SendErrorMessage(LANG_USE_BOL);
return false;
}
static bool HandleGPSCommand(ChatHandler* handler, Optional<PlayerIdentifier> target)
@ -3069,6 +3066,48 @@ public:
handler->SendErrorMessage(LANG_CMD_NO_DOOR_FOUND, range ? *range : 5.0f);
return false;
}
static bool HandleBMCommand(ChatHandler* handler, Optional<bool> enableArg)
{
WorldSession* session = handler->GetSession();
if (!session)
return false;
auto SetBMMod = [&](bool enable)
{
char const* enabled = "ON";
char const* disabled = "OFF";
handler->SendNotification(LANG_COMMAND_BEASTMASTER_MODE, enable ? enabled : disabled);
session->GetPlayer()->SetBeastMaster(enable);
};
if (!enableArg)
{
if (!AccountMgr::IsPlayerAccount(session->GetSecurity()) && session->GetPlayer()->IsDeveloper())
SetBMMod(true);
else
SetBMMod(false);
return true;
}
if (*enableArg)
{
SetBMMod(true);
return true;
}
else
{
SetBMMod(false);
return true;
}
handler->SendErrorMessage(LANG_USE_BOL);
return false;
}
};
void AddSC_misc_commandscript()

View File

@ -290,8 +290,8 @@ public:
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
{
if (InstanceScript* instance = player->GetInstanceScript())
if (instance->GetBossState(DATA_MIRKBLOOD) != DONE)
if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD))
if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD))
if (mirkblood->IsAlive() && !mirkblood->IsInCombat())
mirkblood->AI()->Talk(SAY_APPROACH, player);
return false;
@ -306,8 +306,8 @@ public:
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
{
if (InstanceScript* instance = player->GetInstanceScript())
if (instance->GetBossState(DATA_MIRKBLOOD) != DONE)
if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD))
if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD))
if (mirkblood->IsAlive() && mirkblood->IsImmuneToPC())
mirkblood->SetImmuneToPC(false);
return false;

View File

@ -24,7 +24,7 @@
######*/
//possible creatures to be spawned
uint32 const possibleSpawns[32] = {17322, 17661, 17496, 17522, 17340, 17352, 17333, 17524, 17654, 17348, 17339, 17345, 17359, 17353, 17336, 17550, 17330, 17701, 17321, 17325, 17320, 17683, 17342, 17715, 17334, 17341, 17338, 17337, 17346, 17344, 17327};
uint32 const possibleSpawns[32] = {17322, 17661, 17496, 17522, 17340, 17352, 17333, 17524, 17654, 17348, 17339, 17345, 17353, 17336, 17550, 17330, 17701, 17321, 17325, 17320, 17683, 17342, 17715, 17334, 17341, 17338, 17337, 17346, 17344, 17327};
enum WebbedCreature
{

View File

@ -246,6 +246,12 @@ public:
events.Reset();
}
void MoveInLineOfSight(Unit* who) override
{
if (pInstance && pInstance->GetData(DATA_INSTANCE_PROGRESS) >= INSTANCE_PROGRESS_GRAND_CHAMPIONS_REACHED_DEST)
ScriptedAI::MoveInLineOfSight(who);
}
void JustEngagedWith(Unit* /*who*/) override
{
events.Reset();
@ -410,6 +416,12 @@ public:
}
}
void MoveInLineOfSight(Unit* who) override
{
if (pInstance && pInstance->GetData(DATA_INSTANCE_PROGRESS) >= INSTANCE_PROGRESS_GRAND_CHAMPIONS_REACHED_DEST)
npc_escortAI::MoveInLineOfSight(who);
}
void JustEngagedWith(Unit* /*who*/) override
{
if (pInstance && pInstance->GetData(DATA_INSTANCE_PROGRESS) == INSTANCE_PROGRESS_CHAMPIONS_UNMOUNTED )

View File

@ -5528,6 +5528,148 @@ class spell_gen_food_heart_emote : public AuraScript
}
};
// 456 - SHOWLABEL Only OFF
class spell_gen_showlabel_off : public SpellScript
{
PrepareSpellScript(spell_gen_showlabel_off)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
player->SetGMChat(false);
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_showlabel_off::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 2765 - SHOWLABEL Only ON
class spell_gen_showlabel_on : public SpellScript
{
PrepareSpellScript(spell_gen_showlabel_on)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
player->SetGMChat(true);
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_showlabel_on::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 1509 - GM Only OFF
class spell_gen_gm_off : public SpellScript
{
PrepareSpellScript(spell_gen_gm_off)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
{
player->SetGameMaster(false);
player->UpdateTriggerVisibility();
}
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_gm_off::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 18139 - GM Only ON
class spell_gen_gm_on : public SpellScript
{
PrepareSpellScript(spell_gen_gm_on)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
{
player->SetGameMaster(true);
player->UpdateTriggerVisibility();
}
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_gm_on::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 6147 - INVIS Only OFF
class spell_gen_invis_off : public SpellScript
{
PrepareSpellScript(spell_gen_invis_off)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
player->SetGMVisible(true);
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_invis_off::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 2763 - INVIS Only ON
class spell_gen_invis_on : public SpellScript
{
PrepareSpellScript(spell_gen_invis_on)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
player->SetGMVisible(false);
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_invis_on::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 20114, 24675 - BM Only OFF
class spell_gen_bm_off : public SpellScript
{
PrepareSpellScript(spell_gen_bm_off)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
player->SetBeastMaster(false);
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_bm_off::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 20115, 24676 - BM Only ON
class spell_gen_bm_on : public SpellScript
{
PrepareSpellScript(spell_gen_bm_on)
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Player* player = GetCaster()->ToPlayer())
player->SetBeastMaster(true);
}
void Register() override
{
OnEffectHit += SpellEffectFn(spell_gen_bm_on::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
void AddSC_generic_spell_scripts()
{
RegisterSpellScript(spell_silithyst);
@ -5693,4 +5835,12 @@ void AddSC_generic_spell_scripts()
RegisterSpellScriptWithArgs(spell_gen_translocate, "spell_gen_translocate_up", SPELL_TRANSLOCATION_UP);
RegisterSpellScript(spell_gen_cooldown_all);
RegisterSpellScript(spell_gen_food_heart_emote);
RegisterSpellScript(spell_gen_showlabel_off);
RegisterSpellScript(spell_gen_showlabel_on);
RegisterSpellScript(spell_gen_gm_off);
RegisterSpellScript(spell_gen_gm_on);
RegisterSpellScript(spell_gen_invis_off);
RegisterSpellScript(spell_gen_invis_on);
RegisterSpellScript(spell_gen_bm_on);
RegisterSpellScript(spell_gen_bm_off);
}

View File

@ -1503,16 +1503,20 @@ class spell_item_blessing_of_ancient_kings : public AuraScript
HealInfo* healInfo = eventInfo.GetHealInfo();
if (!healInfo)
{
return;
}
int32 absorb = int32(CalculatePct(healInfo->GetHeal(), 15.0f));
// xinef: all heals contribute to one bubble
if (AuraEffect* protEff = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PROTECTION_OF_ANCIENT_KINGS, 0/*, eventInfo.GetActor()->GetGUID()*/))
{
// The shield can grow to a maximum size of 20,000 damage absorbtion
protEff->SetAmount(std::min<int32>(protEff->GetAmount() + absorb, 20000));
// The shield is supposed to cap out at 20,000 absorption...
absorb += protEff->GetAmount();
// ...but Blizz wrote this instead. See #23152 for details
if (absorb > 20000)
absorb = 200000;
protEff->SetAmount(absorb);
// Refresh and return to prevent replacing the aura
protEff->GetBase()->RefreshDuration();