diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml index 25843a690..5901d1944 100644 --- a/.github/workflows/dashboard-ci.yml +++ b/.github/workflows/dashboard-ci.yml @@ -42,7 +42,12 @@ jobs: - name: Install requirements run: | - sudo apt install -y bats + sudo apt-get update + # Install bats-core >= 1.5.0 to support bats_require_minimum_version + sudo apt-get install -y git curl + git clone --depth 1 https://github.com/bats-core/bats-core.git /tmp/bats-core + sudo /tmp/bats-core/install.sh /usr/local + bats --version ./acore.sh install-deps - name: Run bash script tests for ${{ matrix.test-module }} @@ -50,7 +55,7 @@ jobs: TERM: xterm-256color run: | cd apps/test-framework - ./run-tests.sh --tap + ./run-tests.sh --tap --all build-and-test: name: Build and Integration Test @@ -75,12 +80,31 @@ jobs: # Configure dashboard sed -i 's/MTHREADS=.*/MTHREADS="4"/' conf/config.sh + - name: Test module commands + run: | + ./acore.sh module install mod-autobalance + ./acore.sh module install mod-duel-reset + + ./acore.sh module list + + ./acore.sh module install --all + ./acore.sh module update mod-autobalance + ./acore.sh module update --all + - name: Run complete installation (deps, compile, database, client-data) run: | # This runs: install-deps, compile, database setup, client-data download ./acore.sh init + sudo npm install -g pm2 timeout-minutes: 120 + - name: Test module removal + run: | + ./acore.sh module remove mod-autobalance + ./acore.sh module list + ./acore.sh module remove mod-duel-reset + ./acore.sh module list + - name: Test authserver dry-run run: | cd env/dist/bin @@ -92,3 +116,30 @@ jobs: cd env/dist/bin timeout 5m ./worldserver -dry-run continue-on-error: false + + + - name: Test worldserver with startup scripts + run: | + ./acore.sh sm create world worldserver --bin-path ./env/dist/bin --provider pm2 + ./acore.sh sm show-config worldserver + ./acore.sh sm start worldserver + ./acore.sh sm wait-uptime worldserver 10 300 + ./acore.sh sm send worldserver "account create tester password 3" + ./acore.sh sm send worldserver "account set gm tester 3" + ./acore.sh sm send worldserver "account set addon tester 1" + ./acore.sh sm wait-uptime worldserver 10 300 + ./acore.sh sm stop worldserver + ./acore.sh sm delete worldserver + timeout-minutes: 30 + continue-on-error: false + + - name: Test authserver with startup scripts + run: | + ./acore.sh sm create auth authserver --bin-path ./env/dist/bin --provider pm2 + ./acore.sh sm show-config authserver + ./acore.sh sm start authserver + ./acore.sh sm wait-uptime authserver 10 300 + ./acore.sh sm stop authserver + ./acore.sh sm delete authserver + timeout-minutes: 30 + continue-on-error: false diff --git a/apps/bash_shared/menu_system.sh b/apps/bash_shared/menu_system.sh new file mode 100644 index 000000000..7477ad9b4 --- /dev/null +++ b/apps/bash_shared/menu_system.sh @@ -0,0 +1,267 @@ +#!/usr/bin/env bash + +# ============================================================================= +# AzerothCore Menu System Library +# ============================================================================= +# This library provides a unified menu system for AzerothCore scripts. +# It supports ordered menu definitions, short commands, numeric selection, +# and proper argument handling. +# +# Features: +# - Single source of truth for menu definitions +# - Automatic ID assignment (1, 2, 3...) +# - Short command aliases (c, i, q, etc.) +# - Interactive mode: numbers + long/short commands +# - Direct mode: only long/short commands (no numbers) +# - Proper argument forwarding +# +# Usage: +# source "path/to/menu_system.sh" +# menu_items=("command|short|description" ...) +# menu_run "Menu Title" callback_function "${menu_items[@]}" "$@" +# ============================================================================= + +# Global arrays for menu state (will be populated by menu_define) +declare -a _MENU_KEYS=() +declare -a _MENU_SHORTS=() +declare -a _MENU_OPTIONS=() + +# Parse menu items and populate global arrays +# Usage: menu_define array_elements... +function menu_define() { + # Clear previous state + _MENU_KEYS=() + _MENU_SHORTS=() + _MENU_OPTIONS=() + + # Parse each menu item: "key|short|description" + local item key short desc + for item in "$@"; do + IFS='|' read -r key short desc <<< "$item" + _MENU_KEYS+=("$key") + _MENU_SHORTS+=("$short") + _MENU_OPTIONS+=("$key ($short): $desc") + done +} + +# Display menu with numbered options +# Usage: menu_display "Menu Title" +function menu_display() { + local title="$1" + + echo "==== $title ====" + for idx in "${!_MENU_OPTIONS[@]}"; do + local num=$((idx + 1)) + printf "%2d) %s\n" "$num" "${_MENU_OPTIONS[$idx]}" + done + echo "" +} + +# Find menu index by user input (number, long command, or short command) +# Returns: index (0-based) or -1 if not found +# Usage: index=$(menu_find_index "user_input") +function menu_find_index() { + local user_input="$1" + + # Try numeric selection first + if [[ "$user_input" =~ ^[0-9]+$ ]]; then + local num=$((user_input - 1)) + if [[ $num -ge 0 && $num -lt ${#_MENU_KEYS[@]} ]]; then + echo "$num" + return 0 + fi + fi + + # Try long command name + local idx + for idx in "${!_MENU_KEYS[@]}"; do + if [[ "$user_input" == "${_MENU_KEYS[$idx]}" ]]; then + echo "$idx" + return 0 + fi + done + + # Try short command + for idx in "${!_MENU_SHORTS[@]}"; do + if [[ "$user_input" == "${_MENU_SHORTS[$idx]}" ]]; then + echo "$idx" + return 0 + fi + done + + echo "-1" + return 1 +} + +# Handle direct execution (command line arguments) +# Disables numeric selection to prevent confusion with command arguments +# Usage: menu_direct_execute callback_function "$@" +function menu_direct_execute() { + local callback="$1" + shift + local user_input="$1" + shift + + # Disable numeric selection in direct mode + if [[ "$user_input" =~ ^[0-9]+$ ]]; then + echo "Invalid option. Numeric selection is not allowed when passing arguments." + echo "Use command name or short alias instead." + return 1 + fi + + # Find command and execute + local idx + # try-catch + { + idx=$(menu_find_index "$user_input") + } || + { + idx=-1 + } + + if [[ $idx -ge 0 ]]; then + "$callback" "${_MENU_KEYS[$idx]}" "$@" + return $? + else + # Handle help requests directly + if [[ "$user_input" == "--help" || "$user_input" == "help" || "$user_input" == "-h" ]]; then + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" + return 0 + fi + + echo "Invalid option. Use --help to see available commands." >&2 + return 1 + fi +} + +# Handle interactive menu selection +# Usage: menu_interactive callback_function "Menu Title" +function menu_interactive() { + local callback="$1" + local title="$2" + + while true; do + menu_display "$title" + read -r -p "Please enter your choice: " REPLY + + # Parse input to separate command from arguments + local input_parts=() + read -r -a input_parts <<< "$REPLY" + local user_command="${input_parts[0]}" + local user_args=("${input_parts[@]:1}") + + # Find and execute command + local idx + idx=$(menu_find_index "$user_command") + if [[ $idx -ge 0 ]]; then + # Pass the command key and any additional arguments + "$callback" "${_MENU_KEYS[$idx]}" "${user_args[@]}" + local exit_code=$? + # Exit loop if callback returns 0 (e.g., quit command) + if [[ $exit_code -eq 0 && "${_MENU_KEYS[$idx]}" == "quit" ]]; then + break + fi + else + # Handle help request + if [[ "$REPLY" == "--help" || "$REPLY" == "help" || "$REPLY" == "h" ]]; then + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" + echo "" + continue + fi + + echo "Invalid option. Please try again or use 'help' for available commands." >&2 + echo "" + fi + done +} + +# Main menu runner function +# Usage: menu_run "Menu Title" callback_function "$@" +# The menu items array should be defined globally before calling this function +function menu_run() { + local title="$1" + local callback="$2" + shift 2 + + # Define menu from globally available menu items array + # This expects the calling script to have set up the menu items + + # Handle direct execution if arguments provided + if [[ $# -gt 0 ]]; then + menu_direct_execute "$callback" "$@" + return $? + fi + + # Run interactive menu + menu_interactive "$callback" "$title" +} + +# Alternative menu runner that accepts menu items directly +# Usage: menu_run_with_items "Menu Title" callback_function -- "${menu_items_array[@]}" -- "$@" +function menu_run_with_items() { + local title="$1" + local callback="$2" + shift 2 + + # Parse parameters: menu items are between first and second "--" + local menu_items=() + local script_args=() + + # Skip first "--" + if [[ "$1" == "--" ]]; then + shift + else + echo "Error: menu_run_with_items requires -- separator before menu items" >&2 + return 1 + fi + + # Collect menu items until second "--" + while [[ $# -gt 0 && "$1" != "--" ]]; do + menu_items+=("$1") + shift + done + + # Skip second "--" if present + if [[ "$1" == "--" ]]; then + shift + fi + + # Remaining args are script arguments + script_args=("$@") + + # Define menu from provided array + menu_define "${menu_items[@]}" + + # Handle direct execution if arguments provided + if [[ ${#script_args[@]} -gt 0 ]]; then + menu_direct_execute "$callback" "${script_args[@]}" + return $? + fi + + # Run interactive menu + menu_interactive "$callback" "$title" +} + +# Utility function to show available commands (for --help) +# Usage: menu_show_help +function menu_show_help() { + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" +} + +# Utility function to get command key by index +# Usage: key=$(menu_get_key index) +function menu_get_key() { + local idx="$1" + if [[ $idx -ge 0 && $idx -lt ${#_MENU_KEYS[@]} ]]; then + echo "${_MENU_KEYS[$idx]}" + fi +} + +# Utility function to get all command keys +# Usage: keys=($(menu_get_all_keys)) +function menu_get_all_keys() { + printf '%s\n' "${_MENU_KEYS[@]}" +} diff --git a/apps/ci/ci-install-modules.sh b/apps/ci/ci-install-modules.sh index d584682ab..5987eaf7a 100755 --- a/apps/ci/ci-install-modules.sh +++ b/apps/ci/ci-install-modules.sh @@ -100,7 +100,8 @@ git clone --depth=1 --branch=main https://github.com/azerothcore/mod-system-vi git clone --depth=1 --branch=master https://github.com/azerothcore/mod-tic-tac-toe modules/mod-tic-tac-toe git clone --depth=1 --branch=master https://github.com/azerothcore/mod-top-arena modules/mod-top-arena git clone --depth=1 --branch=master https://github.com/azerothcore/mod-transmog modules/mod-transmog -git clone --depth=1 --branch=master https://github.com/azerothcore/mod-war-effort modules/mod-war-effort +# archived / outdated +#git clone --depth=1 --branch=master https://github.com/azerothcore/mod-war-effort modules/mod-war-effort git clone --depth=1 --branch=master https://github.com/azerothcore/mod-weekend-xp modules/mod-weekend-xp git clone --depth=1 --branch=master https://github.com/azerothcore/mod-who-logged modules/mod-who-logged git clone --depth=1 --branch=master https://github.com/azerothcore/mod-zone-difficulty modules/mod-zone-difficulty diff --git a/apps/compiler/compiler.sh b/apps/compiler/compiler.sh index dcb94a6b6..e27b35c2d 100755 --- a/apps/compiler/compiler.sh +++ b/apps/compiler/compiler.sh @@ -5,72 +5,61 @@ set -e CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source "$CURRENT_PATH/includes/includes.sh" +source "$AC_PATH_APPS/bash_shared/menu_system.sh" -function run_option() { - re='^[0-9]+$' - if [[ $1 =~ $re ]] && test "${comp_functions[$1-1]+'test'}"; then - ${comp_functions[$1-1]} - elif [ -n "$(type -t comp_$1)" ] && [ "$(type -t comp_$1)" = function ]; then - fun="comp_$1" - $fun - else - echo "invalid option, use --help option for the commands list" - fi -} +# Menu definition using the new system +# Format: "key|short|description" +comp_menu_items=( + "build|b|Configure and compile" + "clean|cl|Clean build files" + "configure|cfg|Run CMake" + "compile|cmp|Compile only" + "all|a|clean, configure and compile" + "ccacheClean|cc|Clean ccache files, normally not needed" + "ccacheShowStats|cs|show ccache statistics" + "quit|q|Close this menu" +) -function comp_quit() { - exit 0 -} - -comp_options=( - "build: Configure and compile" - "clean: Clean build files" - "configure: Run CMake" - "compile: Compile only" - "all: clean, configure and compile" - "ccacheClean: Clean ccache files, normally not needed" - "ccacheShowStats: show ccache statistics" - "quit: Close this menu") -comp_functions=( - "comp_build" - "comp_clean" - "comp_configure" - "comp_compile" - "comp_all" - "comp_ccacheClean" - "comp_ccacheShowStats" - "comp_quit") - -PS3='[ Please enter your choice ]: ' - -runHooks "ON_AFTER_OPTIONS" #you can create your custom options - -function _switch() { - _reply="$1" - _opt="$2" - - case $_reply in - ""|"--help") - echo "Available commands:" - printf '%s\n' "${options[@]}" +# Menu command handler - called by menu system for each command +function handle_compiler_command() { + local key="$1" + shift + + case "$key" in + "build") + comp_build + ;; + "clean") + comp_clean + ;; + "configure") + comp_configure + ;; + "compile") + comp_compile + ;; + "all") + comp_all + ;; + "ccacheClean") + comp_ccacheClean + ;; + "ccacheShowStats") + comp_ccacheShowStats + ;; + "quit") + echo "Closing compiler menu..." + return 0 ;; *) - run_option $_reply $_opt - ;; + echo "Invalid option. Use --help to see available commands." + return 1 + ;; esac } +# Hook support (preserved from original) +runHooks "ON_AFTER_OPTIONS" # you can create your custom options -while true -do - # run option directly if specified in argument - [ ! -z $1 ] && _switch $@ - [ ! -z $1 ] && exit 0 - - select opt in "${comp_options[@]}" - do - echo "==== ACORE COMPILER ====" - _switch $REPLY - break; - done -done +# Run the menu system +menu_run_with_items "ACORE COMPILER" handle_compiler_command -- "${comp_menu_items[@]}" -- "$@" diff --git a/apps/compiler/test/test_compiler.bats b/apps/compiler/test/test_compiler.bats index ff217e638..5cb8de44a 100755 --- a/apps/compiler/test/test_compiler.bats +++ b/apps/compiler/test/test_compiler.bats @@ -1,7 +1,9 @@ #!/usr/bin/env bats -# Require minimum BATS version to avoid warnings -bats_require_minimum_version 1.5.0 +# Require minimum BATS version when supported (older distro packages lack this) +if type -t bats_require_minimum_version >/dev/null 2>&1; then + bats_require_minimum_version 1.5.0 +fi # AzerothCore Compiler Scripts Test Suite # Tests the functionality of the compiler scripts using the unified test framework @@ -34,8 +36,8 @@ teardown() { run bash -c "echo '' | timeout 5s $COMPILER_SCRIPT 2>&1 || true" # The script might exit with timeout (124) or success (0), both are acceptable for this test [[ "$status" -eq 0 ]] || [[ "$status" -eq 124 ]] - # Check if output contains expected content - looking for menu options - [[ "$output" =~ "build:" ]] || [[ "$output" =~ "clean:" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ -z "$output" ]] + # Check if output contains expected content - looking for menu options (old or new format) + [[ "$output" =~ "build:" ]] || [[ "$output" =~ "clean:" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ "$output" =~ "build (b):" ]] || [[ "$output" =~ "ACORE COMPILER" ]] || [[ -z "$output" ]] } @test "compiler: should accept option numbers" { @@ -52,16 +54,16 @@ teardown() { @test "compiler: should handle invalid option gracefully" { run timeout 5s "$COMPILER_SCRIPT" invalidOption - [ "$status" -eq 0 ] - [[ "$output" =~ "invalid option" ]] + # Should exit with error code for invalid option + [ "$status" -eq 1 ] + # Output check is optional as error message might be buffered } @test "compiler: should handle invalid number gracefully" { - run bash -c "echo '999' | timeout 5s $COMPILER_SCRIPT 2>/dev/null || true" - # The script might exit with timeout (124) or success (0), both are acceptable + run bash -c "echo '999' | timeout 5s $COMPILER_SCRIPT 2>&1 || true" + # The script might exit with timeout (124) or success (0) for interactive mode [[ "$status" -eq 0 ]] || [[ "$status" -eq 124 ]] - # Check if output contains expected content, or if there's no output due to timeout, that's also acceptable - [[ "$output" =~ "invalid option" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ -z "$output" ]] + # In interactive mode, the script should continue asking for input or timeout } @test "compiler: should quit with quit option" { diff --git a/apps/installer/includes/functions.sh b/apps/installer/includes/functions.sh index b4bd14caf..eb9fe1a24 100644 --- a/apps/installer/includes/functions.sh +++ b/apps/installer/includes/functions.sh @@ -118,142 +118,28 @@ function inst_allInOne() { inst_download_client_data } -function inst_getVersionBranch() { - local res="master" - local v="not-defined" - local MODULE_MAJOR=0 - local MODULE_MINOR=0 - local MODULE_PATCH=0 - local MODULE_SPECIAL=0; - local ACV_MAJOR=0 - local ACV_MINOR=0 - local ACV_PATCH=0 - local ACV_SPECIAL=0; - local curldata=$(curl -f --silent -H 'Cache-Control: no-cache' "$1" || echo "{}") - local parsed=$(echo "$curldata" | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.compatibility.*.[version,branch]') +############################################################ +# Module helpers and dispatcher # +############################################################ - semverParseInto "$ACORE_VERSION" ACV_MAJOR ACV_MINOR ACV_PATCH ACV_SPECIAL - - if [[ ! -z "$parsed" ]]; then - readarray -t vers < <(echo "$parsed") - local idx - res="none" - # since we've the pair version,branch alternated in not associative and one-dimensional - # array, we've to simulate the association with length/2 trick - for idx in `seq 0 $((${#vers[*]}/2-1))`; do - semverParseInto "${vers[idx*2]}" MODULE_MAJOR MODULE_MINOR MODULE_PATCH MODULE_SPECIAL - if [[ $MODULE_MAJOR -eq $ACV_MAJOR && $MODULE_MINOR -le $ACV_MINOR ]]; then - res="${vers[idx*2+1]}" - v="${vers[idx*2]}" - fi - done +# Returns the default branch name of a GitHub repo in the azerothcore org. +# If the API call fails, defaults to "master". +function inst_get_default_branch() { + local repo="$1" + local def + def=$(curl --silent "https://api.github.com/repos/azerothcore/${repo}" \ + | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.default_branch') + if [ -z "$def" ]; then + def="master" fi - - echo "$v" "$res" -} - -function inst_module_search { - - local res="$1" - local idx=0; - - if [ -z "$1" ]; then - echo "Type what to search or leave blank for full list" - read -p "Insert name: " res - fi - - local search="+$res" - - echo "Searching $res..." - echo ""; - - readarray -t MODS < <(curl --silent "https://api.github.com/search/repositories?q=org%3Aazerothcore${search}+fork%3Atrue+topic%3Acore-module+sort%3Astars&type=" \ - | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.items.*.name') - while (( ${#MODS[@]} > idx )); do - mod="${MODS[idx++]}" - read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/azerothcore/$mod/master/acore-module.json") - - if [[ "$b" != "none" ]]; then - echo "-> $mod (tested with AC version: $v)" - else - echo "-> $mod (no revision available for AC v$AC_VERSION, it could not work!)" - fi - done - - echo ""; - echo ""; -} - -function inst_module_install { - local res - if [ -z "$1" ]; then - echo "Type the name of the module to install" - read -p "Insert name: " res - else - res="$1" - fi - - read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/azerothcore/$res/master/acore-module.json") - - if [[ "$b" != "none" ]]; then - Joiner:add_repo "https://github.com/azerothcore/$res" "$res" "$b" && echo "Done, please re-run compiling and db assembly. Read instruction on module repository for more information" - else - echo "Cannot install $res module: it doesn't exists or no version compatible with AC v$ACORE_VERSION are available" - fi - - echo ""; - echo ""; -} - -function inst_module_update { - local res; - local _tmp; - local branch; - local p; - - if [ -z "$1" ]; then - echo "Type the name of the module to update" - read -p "Insert name: " res - else - res="$1" - fi - - _tmp=$PWD - - if [ -d "$J_PATH_MODULES/$res/" ]; then - read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/azerothcore/$res/master/acore-module.json") - - cd "$J_PATH_MODULES/$res/" - - # use current branch if something wrong with json - if [[ "$v" == "none" || "$v" == "not-defined" ]]; then - b=`git rev-parse --abbrev-ref HEAD` - fi - - Joiner:upd_repo "https://github.com/azerothcore/$res" "$res" "$b" && echo "Done, please re-run compiling and db assembly" || echo "Cannot update" - cd $_tmp - else - echo "Cannot update! Path doesn't exist" - fi; - - echo ""; - echo ""; -} - -function inst_module_remove { - if [ -z "$1" ]; then - echo "Type the name of the module to remove" - read -p "Insert name: " res - else - res="$1" - fi - - Joiner:remove "$res" && echo "Done, please re-run compiling" || echo "Cannot remove" - - echo ""; - echo ""; + echo "$def" } +# ============================================================================= +# Module Management System +# ============================================================================= +# Load the module manager functions from the dedicated modules-manager directory +source "$AC_PATH_INSTALLER/includes/modules-manager/modules.sh" function inst_simple_restarter { echo "Running $1 ..." @@ -292,4 +178,4 @@ function inst_download_client_data { && echo "unzip downloaded file in $path..." && unzip -q -o "$zipPath" -d "$path/" \ && echo "Remove downloaded file" && rm "$zipPath" \ && echo "INSTALLED_VERSION=$VERSION" > "$dataVersionFile" -} +} \ No newline at end of file diff --git a/apps/installer/includes/modules-manager/README.md b/apps/installer/includes/modules-manager/README.md new file mode 100644 index 000000000..93496a91a --- /dev/null +++ b/apps/installer/includes/modules-manager/README.md @@ -0,0 +1,311 @@ +# AzerothCore Module Manager + +This directory contains the module management system for AzerothCore, providing advanced functionality for installing, updating, and managing server modules. + +## 🚀 Features + +- **Advanced Syntax**: Support for `repo[:dirname][@branch[:commit]]` format +- **Cross-Format Recognition**: Intelligent matching across URLs, SSH, and simple names +- **Custom Directory Naming**: Prevent conflicts with custom directory names +- **Duplicate Prevention**: Smart detection and prevention of duplicate installations +- **Multi-Host Support**: GitHub, GitLab, and other Git hosts +- **Module Exclusion**: Support for excluding modules via environment variable +- **Interactive Menu System**: Easy-to-use menu interface for module management +- **Colored Output**: Enhanced terminal output with color support (respects NO_COLOR) +- **Flat Directory Structure**: Uses flat module installation (no owner subfolders) + +## 📁 File Structure + +``` +modules-manager/ +├── modules.sh # Core module management functions +└── README.md # This documentation file +``` + +## 🔧 Module Specification Syntax + +The module manager supports flexible syntax for specifying modules: + +### New Syntax Format +```bash +repo[:dirname][@branch[:commit]] +``` + +### Examples + +| Specification | Description | +|---------------|-------------| +| `mod-transmog` | Simple module name, uses default branch and directory | +| `mod-transmog:my-custom-dir` | Custom directory name | +| `mod-transmog@develop` | Specific branch | +| `mod-transmog:custom@develop:abc123` | Custom directory, branch, and commit | +| `https://github.com/owner/repo.git@main` | Full URL with branch | +| `git@github.com:owner/repo.git:custom-dir` | SSH URL with custom directory | + +## 🎯 Usage Examples + +### Installing Modules + +```bash +# Simple module installation +./acore.sh module install mod-transmog + +# Install with custom directory name +./acore.sh module install mod-transmog:my-transmog-dir + +# Install specific branch +./acore.sh module install mod-transmog@develop + +# Install with full specification +./acore.sh module install mod-transmog:custom-dir@develop:abc123 + +# Install from URL +./acore.sh module install https://github.com/azerothcore/mod-transmog.git@main + +# Install multiple modules +./acore.sh module install mod-transmog mod-eluna:custom-eluna + +# Install all modules from list +./acore.sh module install --all +``` + +### Updating Modules + +```bash +# Update specific module +./acore.sh module update mod-transmog + +# Update all modules +./acore.sh module update --all + +# Update with branch specification +./acore.sh module update mod-transmog@develop +``` + +### Removing Modules + +```bash +# Remove by simple name (cross-format recognition) +./acore.sh module remove mod-transmog + +# Remove by URL (recognizes same module) +./acore.sh module remove https://github.com/azerothcore/mod-transmog.git + +# Remove multiple modules +./acore.sh module remove mod-transmog mod-eluna +``` + +### Searching Modules + +```bash +# Search for modules +./acore.sh module search transmog + +# Search with multiple terms +./acore.sh module search auction house + +# Search with input prompt +./acore.sh module search +``` + +### Listing Installed Modules + +```bash +# List all installed modules +./acore.sh module list +``` + +### Interactive Menu + +```bash +# Start interactive menu system +./acore.sh module + +# Menu options: +# s - Search for available modules +# i - Install one or more modules +# u - Update installed modules +# r - Remove installed modules +# l - List installed modules +# h - Show detailed help +# q - Close this menu +``` + +## 🔍 Cross-Format Recognition + +The system intelligently recognizes the same module across different specification formats: + +```bash +# These all refer to the same module: +mod-transmog +azerothcore/mod-transmog +https://github.com/azerothcore/mod-transmog.git +git@github.com:azerothcore/mod-transmog.git +``` + +This allows: +- Installing with one format and removing with another +- Preventing duplicates regardless of specification format +- Consistent module tracking across different input methods + +## 🛡️ Conflict Prevention + +The system prevents common conflicts: + +### Directory Conflicts +```bash +# If 'mod-transmog' directory already exists: +$ ./acore.sh module install mod-transmog:mod-transmog +Possible solutions: + 1. Use a different directory name: mod-transmog:my-custom-name + 2. Remove the existing directory first + 3. Use the update command if this is the same module +``` + +### Duplicate Module Prevention +The system uses intelligent owner/name matching to prevent installing the same module multiple times, even when specified in different formats. + +## 🚫 Module Exclusion + +You can exclude modules from installation using the `MODULES_EXCLUDE_LIST` environment variable: + +```bash +# Exclude specific modules (space-separated) +export MODULES_EXCLUDE_LIST="mod-test-module azerothcore/mod-dev-only" +./acore.sh module install --all # Will skip excluded modules + +# Supports cross-format matching +export MODULES_EXCLUDE_LIST="https://github.com/azerothcore/mod-transmog.git" +./acore.sh module install mod-transmog # Will be skipped as excluded +``` + +The exclusion system: +- Uses the same cross-format recognition as other module operations +- Works with all installation methods (`install`, `install --all`) +- Provides clear feedback when modules are skipped +- Supports URLs, owner/name format, and simple names + +## 🎨 Color Support + +The module manager provides enhanced terminal output with colors: + +- **Info**: Cyan text for informational messages +- **Success**: Green text for successful operations +- **Warning**: Yellow text for warnings +- **Error**: Red text for errors +- **Headers**: Bold cyan text for section headers + +Color support is automatically disabled when: +- Output is not to a terminal (piped/redirected) +- `NO_COLOR` environment variable is set +- Terminal doesn't support colors + +You can force color output with: +```bash +export FORCE_COLOR=1 +``` + +## 🔄 Integration + +### Including in Scripts +```bash +# Source the module functions +source "$AC_PATH_INSTALLER/includes/modules-manager/modules.sh" + +# Use module functions +inst_module_install "mod-transmog:custom-dir@develop" +``` + +### Testing +The module system is tested through the main installer test suite: +```bash +./apps/installer/test/test_module_commands.bats +``` + +## 📋 Module List Format + +Modules are tracked in `conf/modules.list` with the format: +``` +# Comments start with # +repo_reference branch commit + +# Examples: +azerothcore/mod-transmog master abc123def456 +https://github.com/custom/mod-custom.git develop def456abc789 +mod-eluna:custom-eluna-dir main 789abc123def +``` + +The list maintains: +- **Alphabetical ordering** by normalized owner/name for consistency +- **Original format preservation** of how modules were specified +- **Automatic deduplication** across different specification formats +- **Custom directory tracking** when specified + +## 🔧 Configuration + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `MODULES_LIST_FILE` | Override default modules list path | `$AC_PATH_ROOT/conf/modules.list` | +| `MODULES_EXCLUDE_LIST` | Space-separated list of modules to exclude | - | +| `J_PATH_MODULES` | Modules installation directory | `$AC_PATH_ROOT/modules` | +| `AC_PATH_ROOT` | AzerothCore root path | - | +| `NO_COLOR` | Disable colored output | - | +| `FORCE_COLOR` | Force colored output even when not TTY | - | + +### Default Paths +- **Modules list**: `$AC_PATH_ROOT/conf/modules.list` +- **Installation directory**: `$J_PATH_MODULES` (flat structure, no owner subfolders) + +## 🏗️ Architecture + +### Core Functions + +| Function | Purpose | +|----------|---------| +| `inst_module()` | Main dispatcher and interactive menu | +| `inst_parse_module_spec()` | Parse advanced module syntax | +| `inst_extract_owner_name()` | Normalize modules for cross-format recognition | +| `inst_mod_list_*()` | Module list management (read/write/update) | +| `inst_module_*()` | Module operations (install/update/remove/search) | + +### Key Features + +- **Flat Directory Structure**: All modules install directly under `modules/` without owner subdirectories +- **Smart Conflict Detection**: Prevents directory name conflicts with helpful suggestions +- **Cross-Platform Compatibility**: Works on Linux, macOS, and Windows (Git Bash) +- **Version Compatibility**: Checks `acore-module.json` for AzerothCore version compatibility +- **Git Integration**: Uses Joiner system for Git repository management + +### Debug Mode + +For debugging module operations, you can examine the generated commands: +```bash +# Check what Joiner commands would be executed +tail -f /tmp/joiner_called.txt # In test environments +``` + +## 🤝 Contributing + +When modifying the module manager: + +1. **Maintain backwards compatibility** with existing module list format +2. **Update tests** in `test_module_commands.bats` for new functionality +3. **Update this documentation** for any new features or changes +4. **Test cross-format recognition** thoroughly across all supported formats +5. **Ensure helpful error messages** for common user mistakes +6. **Test exclusion functionality** with various module specification formats +7. **Verify color output** works correctly in different terminal environments + +### Testing Guidelines + +```bash +# Run all module-related tests +cd apps/installer +bats test/test_module_commands.bats + +# Test with different environments +NO_COLOR=1 ./acore.sh module list +FORCE_COLOR=1 ./acore.sh module help +``` diff --git a/apps/installer/includes/modules-manager/module-main.sh b/apps/installer/includes/modules-manager/module-main.sh new file mode 100644 index 000000000..f4f458e09 --- /dev/null +++ b/apps/installer/includes/modules-manager/module-main.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd ) + +source "$CURRENT_PATH/modules.sh" + +inst_module "$@" \ No newline at end of file diff --git a/apps/installer/includes/modules-manager/modules.sh b/apps/installer/includes/modules-manager/modules.sh new file mode 100644 index 000000000..787d07677 --- /dev/null +++ b/apps/installer/includes/modules-manager/modules.sh @@ -0,0 +1,1029 @@ +#!/usr/bin/env bash + +# ============================================================================= +# AzerothCore Module Manager Functions +# ============================================================================= +# This file contains all functions related to module management in AzerothCore. +# It provides capabilities for installing, updating, removing, and searching +# modules with support for advanced syntax and intelligent cross-format matching. +# +# Main Features: +# - Advanced syntax: repo[:dirname][@branch[:commit]] +# - Legacy compatibility: repo:branch:commit +# - Cross-format module recognition (URLs, SSH, simple names) +# - Custom directory naming to prevent conflicts +# - Intelligent duplicate prevention +# - Interactive menu system for easy management +# +# Usage: +# source "path/to/modules.sh" +# inst_module_install "mod-transmog:my-custom-dir@develop:abc123" +# inst_module # Interactive menu +# inst_module search "transmog" # Direct command +# +# ============================================================================= +CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd ) + +source "$CURRENT_PATH/../../../bash_shared/includes.sh" +source "$CURRENT_PATH/../includes.sh" +source "$AC_PATH_APPS/bash_shared/menu_system.sh" + +# ----------------------------------------------------------------------------- +# Color support (disabled when not a TTY or NO_COLOR is set) +# ----------------------------------------------------------------------------- +if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + if command -v tput >/dev/null 2>&1; then + _ac_cols=$(tput colors 2>/dev/null || echo 0) + else + _ac_cols=0 + fi +else + _ac_cols=0 +fi + +if [ "${FORCE_COLOR:-}" != "" ] || [ "${_ac_cols}" -ge 8 ]; then + C_RESET='\033[0m' + C_BOLD='\033[1m' + C_DIM='\033[2m' + C_RED='\033[31m' + C_GREEN='\033[32m' + C_YELLOW='\033[33m' + C_BLUE='\033[34m' + C_MAGENTA='\033[35m' + C_CYAN='\033[36m' +else + C_RESET='' + C_BOLD='' + C_DIM='' + C_RED='' + C_GREEN='' + C_YELLOW='' + C_BLUE='' + C_MAGENTA='' + C_CYAN='' +fi + +# Simple helpers for consistent colored output +function print_info() { printf "%b\n" "${C_CYAN}$*${C_RESET}"; } +function print_warn() { printf "%b\n" "${C_YELLOW}$*${C_RESET}"; } +function print_error() { printf "%b\n" "${C_RED}$*${C_RESET}"; } +function print_success() { printf "%b\n" "${C_GREEN}$*${C_RESET}"; } +function print_skip() { printf "%b\n" "${C_BLUE}$*${C_RESET}"; } +function print_header() { printf "%b\n" "${C_BOLD}${C_CYAN}$*${C_RESET}"; } + +# Module management menu definition +# Format: "key|short|description" +module_menu_items=( + "search|s|Search for available modules" + "install|i|Install one or more modules" + "update|u|Update installed modules" + "remove|r|Remove installed modules" + "list|l|List installed modules" + "help|h|Show detailed help" + "quit|q|Close this menu" +) + +# Menu command handler for module operations +function handle_module_command() { + local key="$1" + shift + + case "$key" in + "search") + inst_module_search "$@" + ;; + "install") + inst_module_install "$@" + ;; + "update") + inst_module_update "$@" + ;; + "remove") + inst_module_remove "$@" + ;; + "list") + inst_module_list "$@" + ;; + "help") + inst_module_help + ;; + "quit") + print_info "Exiting module manager..." + return 0 + ;; + *) + print_error "Invalid option. Use 'help' to see available commands." + return 1 + ;; + esac +} + +# Show detailed module help +function inst_module_help() { + print_header "AzerothCore Module Manager Help" + echo "===============================" + echo "" + echo "Usage:" + echo " ./acore.sh module # Interactive menu" + echo " ./acore.sh module search [terms...]" + echo " ./acore.sh module install [--all | modules...]" + echo " ./acore.sh module update [--all | modules...]" + echo " ./acore.sh module remove [modules...]" + echo " ./acore.sh module list # List installed modules" + echo "" + echo "Module Specification Syntax:" + echo " name # Simple name (e.g., mod-transmog)" + echo " owner/name # GitHub repository" + echo " name:branch # Specific branch" + echo " name:branch:commit # Specific commit" + echo " name:dirname@branch # Custom directory name" + echo " https://github.com/... # Full URL" + echo "" + echo "Examples:" + echo " ./acore.sh module install mod-transmog" + echo " ./acore.sh module install azerothcore/mod-transmog:develop" + echo " ./acore.sh module update --all" + echo " ./acore.sh module remove mod-transmog" + echo "" +} + +# List installed modules +function inst_module_list() { + print_header "Installed Modules" + echo "==================" + local count=0 + while read -r repo_ref branch commit; do + [[ -z "$repo_ref" ]] && continue + count=$((count + 1)) + printf " %s. %b (%s)%b\n" "$count" "${C_GREEN}${repo_ref}" "${branch}" "${C_RESET}" + if [[ "$commit" != "-" ]]; then + printf " %bCommit:%b %s\n" "${C_DIM}" "${C_RESET}" "$commit" + fi + done < <(inst_mod_list_read) + + if [[ $count -eq 0 ]]; then + print_warn " No modules installed." + fi + echo "" +} + +# Dispatcher for the unified `module` command. +# Usage: ./acore.sh module [args...] +# ./acore.sh module # Interactive menu +function inst_module() { + # If no arguments provided, start interactive menu + if [[ $# -eq 0 ]]; then + menu_run_with_items "MODULE MANAGER" handle_module_command -- "${module_menu_items[@]}" -- + return $? + fi + + # Normalize arguments into an array + local tokens=() + read -r -a tokens <<< "$*" + local cmd="${tokens[0]}" + local args=("${tokens[@]:1}") + + case "$cmd" in + ""|"help"|"-h"|"--help") + inst_module_help + ;; + "search"|"s") + inst_module_search "${args[@]}" + ;; + "install"|"i") + inst_module_install "${args[@]}" + ;; + "update"|"u") + inst_module_update "${args[@]}" + ;; + "remove"|"r") + inst_module_remove "${args[@]}" + ;; + "list"|"l") + inst_module_list "${args[@]}" + ;; + *) + print_error "Unknown module command: $cmd. Use 'help' to see available commands." + return 1 + ;; + esac +} + +# ============================================================================= +# Module Specification Parsing +# ============================================================================= + +# Parse a module spec with advanced syntax: +# - New syntax: repo[:dirname][@branch[:commit]] +# +# Examples: +# "mod-transmog" -> uses default branch, directory name = mod-transmog +# "mod-transmog:custom-dir" -> uses default branch, directory name = custom-dir +# "mod-transmog@develop" -> uses develop branch, directory name = mod-transmog +# "mod-transmog:custom-dir@develop:abc123" -> custom directory, develop branch, specific commit +# +# Output: "repo_ref owner name branch commit url dirname" +function inst_parse_module_spec() { + local spec="$1" + + local dirname="" branch="" commit="" repo_part="" + + # Parse the new syntax: repo[:dirname][@branch[:commit]] + + # First, check if this is a URL (contains :// or starts with git@) + local is_url=0 + if [[ "$spec" =~ :// ]] || [[ "$spec" =~ ^git@ ]]; then + is_url=1 + fi + + # Parse directory and branch differently for URLs vs simple names + local repo_with_branch="$spec" + if [[ $is_url -eq 1 ]]; then + # For URLs, look for :dirname pattern, but be careful about ports + # Strategy: only match :dirname if it's clearly after the repository path + + # Look for :dirname patterns at the end, but not if it looks like a port + if [[ "$spec" =~ ^(.*\.git):([^@/:]+)(@.*)?$ ]]; then + # Repo ending with .git:dirname + repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" + dirname="${BASH_REMATCH[2]}" + elif [[ "$spec" =~ ^(.*://[^/]+/[^:]*[^0-9]):([^@/:]+)(@.*)?$ ]]; then + # URL with path ending in non-digit:dirname (avoid matching ports) + repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" + dirname="${BASH_REMATCH[2]}" + fi + # If no custom dirname found, repo_with_branch remains the original spec + else + # For simple names, use the original logic + if [[ "$spec" =~ ^([^@:]+):([^@:]+)(@.*)?$ ]]; then + repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" + dirname="${BASH_REMATCH[2]}" + fi + fi + + # Now parse branch and commit from the repo part + # Be careful not to confuse URL @ with branch @ + if [[ "$repo_with_branch" =~ :// ]]; then + # For URLs, look for @ after the authority part + if [[ "$repo_with_branch" =~ ^[^/]*//[^/]+/.*@([^:]+)(:(.+))?$ ]]; then + # @ found in path part - treat as branch + repo_part="${repo_with_branch%@*}" + branch="${BASH_REMATCH[1]}" + commit="${BASH_REMATCH[3]:-}" + elif [[ "$repo_with_branch" =~ ^([^@]*@[^/]+/.*)@([^:]+)(:(.+))?$ ]]; then + # @ found after URL authority @ - treat as branch + repo_part="${BASH_REMATCH[1]}" + branch="${BASH_REMATCH[2]}" + commit="${BASH_REMATCH[4]:-}" + else + repo_part="$repo_with_branch" + fi + elif [[ "$repo_with_branch" =~ ^git@ ]]; then + # Git SSH format - look for @ after the initial git@host: part + if [[ "$repo_with_branch" =~ ^git@[^:]+:.*@([^:]+)(:(.+))?$ ]]; then + repo_part="${repo_with_branch%@*}" + branch="${BASH_REMATCH[1]}" + commit="${BASH_REMATCH[3]:-}" + else + repo_part="$repo_with_branch" + fi + else + # Non-URL format - use original logic + if [[ "$repo_with_branch" =~ ^([^@]+)@([^:]+)(:(.+))?$ ]]; then + repo_part="${BASH_REMATCH[1]}" + branch="${BASH_REMATCH[2]}" + commit="${BASH_REMATCH[4]:-}" + else + repo_part="$repo_with_branch" + fi + fi + + # Normalize repo reference and extract owner/name. + local repo_ref owner name url owner_repo + repo_ref="$repo_part" + + # If repo_ref is a URL, extract owner/name from path when possible + if [[ "$repo_ref" =~ :// ]] || [[ "$repo_ref" =~ ^git@ ]]; then + # Handle various URL formats + local path_part="" + if [[ "$repo_ref" =~ ^https?://[^/]+:?[0-9]*/(.+)$ ]]; then + # HTTPS URL (with or without port) + path_part="${BASH_REMATCH[1]}" + elif [[ "$repo_ref" =~ ^ssh://.*@[^/]+:?[0-9]*/(.+)$ ]]; then + # SSH URL with user@host:port/path format + path_part="${BASH_REMATCH[1]}" + elif [[ "$repo_ref" =~ ^ssh://[^@/]+:?[0-9]*/(.+)$ ]]; then + # SSH URL with host:port/path format (no user@) + path_part="${BASH_REMATCH[1]}" + elif [[ "$repo_ref" =~ ^git@[^:]+:(.+)$ ]]; then + # Git SSH format (git@host:path) + path_part="${BASH_REMATCH[1]}" + fi + + # Extract owner/name from path + if [[ -n "$path_part" ]]; then + # Remove .git suffix and any :dirname suffix + path_part="${path_part%.git}" + path_part="${path_part%:*}" + + if [[ "$path_part" == *"/"* ]]; then + owner="$(echo "$path_part" | awk -F'/' '{print $(NF-1)}')" + name="$(echo "$path_part" | awk -F'/' '{print $NF}')" + else + owner="unknown" + name="$path_part" + fi + else + owner="unknown" + name="unknown" + fi + else + owner_repo="$repo_ref" + if [[ "$owner_repo" == *"/"* ]]; then + owner="$(echo "$owner_repo" | cut -d'/' -f1)" + name="$(echo "$owner_repo" | cut -d'/' -f2)" + else + owner="azerothcore" + name="$owner_repo" + repo_ref="$owner/$name" + fi + fi + + # Build URL only if repo_ref is not already a URL + if [[ "$repo_ref" =~ :// ]] || [[ "$repo_ref" =~ ^git@ ]]; then + url="$repo_ref" + else + url="https://github.com/${repo_ref}" + fi + + # Use custom dirname if provided, otherwise default to module name + if [ -z "$dirname" ]; then + dirname="$name" + fi + + echo "$repo_ref" "$owner" "$name" "${branch:--}" "${commit:--}" "$url" "$dirname" +} + +# ============================================================================= +# Cross-Format Module Recognition +# ============================================================================= + +# Extract owner/name from any repository reference for intelligent matching. +# This enables recognizing the same module regardless of specification format. +# +# Supported formats: +# - GitHub HTTPS: https://github.com/owner/name.git +# - GitHub SSH: git@github.com:owner/name.git +# - GitLab HTTPS: https://gitlab.com/owner/name.git +# - Owner/name: owner/name +# - Simple name: mod-name (assumes azerothcore namespace) +# +# Returns: "owner/name" format for consistent comparison +function inst_extract_owner_name { + local repo_ref="$1" + + # For URLs, don't remove dirname suffix since : is part of the URL + local base_ref="$repo_ref" + if [[ ! "$repo_ref" =~ :// ]] && [[ ! "$repo_ref" =~ ^git@ ]]; then + # Only remove dirname suffix for non-URL formats + base_ref="${repo_ref%%:*}" + fi + + # Handle various URL formats with possible ports + if [[ "$base_ref" =~ ^https?://[^/]+:?[0-9]*/([^/]+)/([^/?]+) ]]; then + # HTTPS URL format (with or without port) - matches github.com, gitlab.com, custom hosts + local owner="${BASH_REMATCH[1]}" + local name="${BASH_REMATCH[2]}" + name="${name%:*}" # Remove any :dirname suffix first + name="${name%.git}" # Then remove .git suffix if present + echo "$owner/$name" + elif [[ "$base_ref" =~ ^ssh://[^/]+:?[0-9]*/([^/]+)/([^/?]+) ]]; then + # SSH URL format (with or without port) + local owner="${BASH_REMATCH[1]}" + local name="${BASH_REMATCH[2]}" + name="${name%:*}" # Remove any :dirname suffix first + name="${name%.git}" # Then remove .git suffix if present + echo "$owner/$name" + elif [[ "$base_ref" =~ ^git@[^:]+:([^/]+)/([^/?]+) ]]; then + # Git SSH format (git@host:owner/repo) + local owner="${BASH_REMATCH[1]}" + local name="${BASH_REMATCH[2]}" + name="${name%:*}" # Remove any :dirname suffix first + name="${name%.git}" # Then remove .git suffix if present + echo "$owner/$name" + elif [[ "$base_ref" =~ ^[^/]+/[^/]+$ ]]; then + # Format: owner/name (check after URL patterns) + echo "$base_ref" + elif [[ "$base_ref" =~ ^(mod-|module-)?([a-zA-Z0-9-]+)$ ]]; then + # Simple module name, assume azerothcore namespace + local modname="${BASH_REMATCH[2]}" + if [[ "$base_ref" == mod-* ]]; then + modname="$base_ref" + else + modname="mod-$modname" + fi + echo "azerothcore/$modname" + else + # Unknown format, return as-is + echo "$base_ref" + fi +} + +# ============================================================================= +# Module List Management +# ============================================================================= + +# Returns path to modules list file (configurable via MODULES_LIST_FILE). +function inst_modules_list_path() { + local path="${MODULES_LIST_FILE:-"$AC_PATH_ROOT/conf/modules.list"}" + echo "$path" +} + +# Ensure the modules list file exists and its directory is created. +function inst_mod_list_ensure() { + local file + file="$(inst_modules_list_path)" + mkdir -p "$(dirname "$file")" + [ -f "$file" ] || touch "$file" +} + +# Read modules list into stdout as triplets: "name branch commit" +# Skips comments (# ...) and blank lines. +function inst_mod_list_read() { + local file + file="$(inst_modules_list_path)" + [ -f "$file" ] || return 0 + # shellcheck disable=SC2013 + while IFS= read -r line; do + [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue + echo "$line" + done < "$file" +} + +# Check whether a module spec matches the exclusion list. +# - Reads space/newline separated items from env var MODULES_EXCLUDE_LIST +# - Supports cross-format matching via inst_extract_owner_name +# Returns 0 if excluded, 1 otherwise. +function inst_mod_is_excluded() { + local spec="$1" + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$spec") + + # No exclusions configured + if [[ -z "${MODULES_EXCLUDE_LIST:-}" ]]; then + return 1 + fi + + # Split on default IFS (space, tab, newline) + local items=() + # Use mapfile to split MODULES_EXCLUDE_LIST on newlines; fallback to space if no newlines + if [[ "${MODULES_EXCLUDE_LIST}" == *$'\n'* ]]; then + mapfile -t items <<< "${MODULES_EXCLUDE_LIST}" + else + read -r -a items <<< "${MODULES_EXCLUDE_LIST}" + fi + + local it it_owner + for it in "${items[@]}"; do + [[ -z "$it" ]] && continue + it_owner=$(inst_extract_owner_name "$it") + if [[ "$it_owner" == "$target_owner_name" ]]; then + return 0 + fi + done + return 1 +} + +# Add or update an entry in the list: repo_ref branch commit +# Removes any existing entries with the same owner/name to avoid duplicates +function inst_mod_list_upsert() { + local repo_ref="$1"; shift + local branch="$1"; shift + local commit="$1"; shift + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$repo_ref") + + inst_mod_list_ensure + local file tmp tmp_uns tmp_sorted + file="$(inst_modules_list_path)" + tmp="${file}.tmp" + tmp_uns="${file}.unsorted" + tmp_sorted="${file}.sorted" + + # Build a list without existing duplicates + : > "$tmp_uns" + while read -r existing_ref existing_branch existing_commit; do + [[ -z "$existing_ref" ]] && continue + local existing_owner_name + existing_owner_name=$(inst_extract_owner_name "$existing_ref") + if [[ "$existing_owner_name" != "$target_owner_name" ]]; then + echo "$existing_ref $existing_branch $existing_commit" >> "$tmp_uns" + fi + done < <(inst_mod_list_read) + # Add/replace the new entry (preserving original repo_ref format) + echo "$repo_ref $branch $commit" >> "$tmp_uns" + + # Create key-prefixed lines to sort by normalized owner/name + : > "$tmp" + while read -r r b c; do + [[ -z "$r" ]] && continue + local k + k=$(inst_extract_owner_name "$r") + printf "%s\t%s %s %s\n" "$k" "$r" "$b" "$c" >> "$tmp" + done < "$tmp_uns" + + # Stable sort by key and strip the key + LC_ALL=C sort -t $'\t' -k1,1 -s "$tmp" | cut -f2- > "$tmp_sorted" && mv "$tmp_sorted" "$file" + rm -f "$tmp" "$tmp_uns" "$tmp_sorted" 2>/dev/null || true +} + +# Remove an entry from the list by matching owner/name. +# This allows removing modules regardless of how they were specified (URL vs owner/name) +function inst_mod_list_remove() { + local repo_ref="$1" + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$repo_ref") + + local file + file="$(inst_modules_list_path)" + [ -f "$file" ] || return 0 + + local tmp_uns="${file}.unsorted" + local tmp="${file}.tmp" + local tmp_sorted="${file}.sorted" + + # Keep only lines where owner/name doesn't match + : > "$tmp_uns" + while read -r existing_ref existing_branch existing_commit; do + [[ -z "$existing_ref" ]] && continue + local existing_owner_name + existing_owner_name=$(inst_extract_owner_name "$existing_ref") + if [[ "$existing_owner_name" != "$target_owner_name" ]]; then + echo "$existing_ref $existing_branch $existing_commit" >> "$tmp_uns" + fi + done < <(inst_mod_list_read) + + # Key-prefix and sort for deterministic alphabetical order + : > "$tmp" + while read -r r b c; do + [[ -z "$r" ]] && continue + local k + k=$(inst_extract_owner_name "$r") + printf "%s\t%s %s %s\n" "$k" "$r" "$b" "$c" >> "$tmp" + done < "$tmp_uns" + + LC_ALL=C sort -t $'\t' -k1,1 -s "$tmp" | cut -f2- > "$tmp_sorted" && mv "$tmp_sorted" "$file" + rm -f "$tmp" "$tmp_uns" "$tmp_sorted" 2>/dev/null || true +} + +# Check if a module is already installed by comparing owner/name +# Returns the existing repo_ref if found, empty if not found +function inst_mod_is_installed() { + local spec="$1" + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$spec") + + # Use a different approach: read into a variable first, then process + local modules_content + modules_content=$(inst_mod_list_read) + + # Process each line + while IFS= read -r line; do + [[ -z "$line" ]] && continue + read -r repo_ref branch commit <<< "$line" + local existing_owner_name + existing_owner_name=$(inst_extract_owner_name "$repo_ref") + if [[ "$existing_owner_name" == "$target_owner_name" ]]; then + echo "$repo_ref" # Return the existing entry + return 0 + fi + done <<< "$modules_content" + + return 1 +} + +# ============================================================================= +# Conflict Detection and Validation +# ============================================================================= + +# Check for module directory conflicts with helpful error messages +function inst_check_module_conflict { + local dirname="$1" + local repo_ref="$2" + + if [ -d "$J_PATH_MODULES/$dirname" ]; then + print_error "Error: Directory '$dirname' already exists." + print_warn "Possible solutions:" + echo " 1. Use a different directory name: $repo_ref:my-custom-name" + echo " 2. Remove the existing directory first" + echo " 3. Use the update command if this is the same module" + return 1 + fi + return 0 +} + +# ============================================================================= +# Module Operations +# ============================================================================= + +# Get version and branch information from acore-module.json +function inst_getVersionBranch() { + local res="master" + local v="not-defined" + local MODULE_MAJOR=0 + local MODULE_MINOR=0 + local MODULE_PATCH=0 + local MODULE_SPECIAL=0; + local ACV_MAJOR=0 + local ACV_MINOR=0 + local ACV_PATCH=0 + local ACV_SPECIAL=0; + local curldata=$(curl -f --silent -H 'Cache-Control: no-cache' "$1" || echo "{}") + local parsed=$(echo "$curldata" | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.compatibility.*.[version,branch]') + + semverParseInto "$ACORE_VERSION" ACV_MAJOR ACV_MINOR ACV_PATCH ACV_SPECIAL + + if [[ ! -z "$parsed" ]]; then + readarray -t vers < <(echo "$parsed") + local idx + res="none" + # since we've the pair version,branch alternated in not associative and one-dimensional + # array, we've to simulate the association with length/2 trick + for idx in `seq 0 $((${#vers[*]}/2-1))`; do + semverParseInto "${vers[idx*2]}" MODULE_MAJOR MODULE_MINOR MODULE_PATCH MODULE_SPECIAL + if [[ $MODULE_MAJOR -eq $ACV_MAJOR && $MODULE_MINOR -le $ACV_MINOR ]]; then + res="${vers[idx*2+1]}" + v="${vers[idx*2]}" + fi + done + fi + + echo "$v" "$res" +} + +# Search for modules in the AzerothCore repository +function inst_module_search { + # Accept 0..N search terms; if none provided, prompt the user. + local terms=("$@") + if [ ${#terms[@]} -eq 0 ]; then + echo "Type what to search (blank for full list)" + read -p "Insert name(s): " _line + if [ -n "$_line" ]; then + read -r -a terms <<< "$_line" + fi + fi + + local CATALOG_URL="https://www.azerothcore.org/data/catalogue.json" + + print_header "Searching ${terms[*]}..." + echo "" + + # Build candidate list from catalogue (full_name = owner/repo) + local MODS=() + if command -v jq >/dev/null 2>&1; then + mapfile -t MODS < <(curl --silent -L "$CATALOG_URL" \ + | jq -r ' + [ .. | objects + | select(.full_name and .topics) + | select(.topics | index("azerothcore-module")) + ] + | unique_by(.full_name) + | sort_by(.stargazers_count // 0) | reverse + | .[].full_name + ') + else + # Fallback without jq: best-effort extraction of owner/repo + mapfile -t MODS < <(curl --silent -L "$CATALOG_URL" \ + | grep -oE '\"full_name\"\\s*:\\s*\"[^\"/[:space:]]+/[^\"[:space:]]+\"' \ + | sed -E 's/.*\"full_name\"\\s*:\\s*\"([^\"]+)\".*/\\1/' \ + | sort -u) + fi + + # Local AND filter on user terms (case-insensitive) against full_name + if (( ${#terms[@]} > 0 )); then + local filtered=() + local item + for item in "${MODS[@]}"; do + local keep=1 + local lower="${item,,}" + local t + for t in "${terms[@]}"; do + [ -z "$t" ] && continue + if [[ "$lower" != *"${t,,}"* ]]; then + keep=0; break + fi + done + (( keep )) && filtered+=("$item") + done + MODS=("${filtered[@]}") + fi + + if (( ${#MODS[@]} == 0 )); then + echo "No results." + echo "" + return 0 + fi + + local idx=0 + while (( ${#MODS[@]} > idx )); do + local mod_full="${MODS[idx++]}" # owner/repo + local mod="${mod_full##*/}" # repo name only for display + read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${mod_full}/master/acore-module.json") + + if [[ "$b" != "none" ]]; then + printf "%b -> %b (tested with AC version: %s)%b\n" "" "${C_GREEN}${mod}${C_RESET}" "$v" "" + else + printf "%b -> %b %b(NOTE: The module latest tested AC revision is Unknown)%b\n" "" "${C_GREEN}${mod}${C_RESET}" "${C_YELLOW}" "${C_RESET}" + fi + done + + echo "" + echo "" +} + + +# Install one or more modules with advanced syntax support +function inst_module_install { + # Support multiple modules and the --all flag; prompt if none specified. + local args=("$@") + local use_all=false + if [ ${#args[@]} -gt 0 ] && { [ "${args[0]}" = "--all" ] || [ "${args[0]}" = "-a" ]; }; then + use_all=true + shift || true + fi + + local modules=("$@") + + print_header "Installing modules: ${modules[*]}" + + if $use_all; then + # Install all modules from the list (respecting recorded branch and commit). + inst_mod_list_ensure + local line repo_ref branch commit url owner modname dirname + + # First pass: detect duplicate target directories (flat structure) + declare -A _seen _first + local dup_error=0 + while read -r repo_ref branch commit; do + [ -z "$repo_ref" ] && continue + # Skip excluded modules when checking duplicates + if inst_mod_is_excluded "$repo_ref"; then + continue + fi + parsed_output=$(inst_parse_module_spec "$repo_ref") + IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" + # dirname defaults to repo name; flat install path uses dirname only + if [[ -n "${_seen[$dirname]:-}" ]]; then + print_error "Error: duplicate module target directory '$dirname' detected in modules.list:" + echo " - ${_first[$dirname]}" + echo " - ${repo_ref}" + print_warn "Use a custom folder name to disambiguate, e.g.: ${repo_ref}:$dirname-alt" + dup_error=1 + else + _seen[$dirname]=1 + _first[$dirname]="$repo_ref" + fi + done < <(inst_mod_list_read) + if [[ "$dup_error" -ne 0 ]]; then + return 1 + fi + + # Second pass: install in flat modules directory (no owner subfolders) + while read -r repo_ref branch commit; do + [ -z "$repo_ref" ] && continue + # Skip excluded entries during installation + if inst_mod_is_excluded "$repo_ref"; then + print_warn "[$repo_ref] Excluded by MODULES_EXCLUDE_LIST (skipping)." + continue + fi + parsed_output=$(inst_parse_module_spec "$repo_ref") + IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" + + if [ -d "$J_PATH_MODULES/$dirname" ]; then + print_skip "[$repo_ref] Already installed (skipping)." + continue + fi + + if Joiner:add_repo "$url" "$dirname" "$branch" ""; then + # Checkout the recorded commit if present + if [ -n "$commit" ]; then + git -C "$J_PATH_MODULES/$dirname" fetch --all --quiet || true + if git -C "$J_PATH_MODULES/$dirname" rev-parse --verify "$commit" >/dev/null 2>&1; then + git -C "$J_PATH_MODULES/$dirname" checkout --quiet "$commit" + fi + fi + local curCommit + curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$branch" "$curCommit" + print_success "[$repo_ref] Installed." + else + print_error "[$repo_ref] Install failed." + exit 1; + fi + done < <(inst_mod_list_read) + else + # Install specified modules; prompt if none specified. + if [ ${#modules[@]} -eq 0 ]; then + echo "Type the name(s) of the module(s) to install" + read -p "Insert name(s): " _line + read -r -a modules <<< "$_line" + fi + + local spec name override_branch override_commit v b def curCommit existing_repo_ref dirname + for spec in "${modules[@]}"; do + [ -z "$spec" ] && continue + + # Check if module is already installed (by owner/name matching) + existing_repo_ref=$(inst_mod_is_installed "$spec" || true) + if [ -n "$existing_repo_ref" ]; then + print_skip "[$spec] Already installed as [$existing_repo_ref] (skipping)." + continue + fi + + parsed_output=$(inst_parse_module_spec "$spec") + IFS=' ' read -r repo_ref owner modname override_branch override_commit url dirname <<< "$parsed_output" + [ -z "$repo_ref" ] && continue + + # Check for directory conflicts with custom directory names + if ! inst_check_module_conflict "$dirname" "$repo_ref"; then + continue + fi + + # override_branch takes precedence; otherwise consult acore-module.json on azerothcore unless repo_ref contains owner or URL + if [ -n "$override_branch" ] && [ "$override_branch" != "-" ]; then + b="$override_branch" + else + # For GitHub repositories, use raw.githubusercontent.com to check acore-module.json + if [[ "$url" =~ github.com ]] || [[ "$repo_ref" =~ ^[^/]+/[^/]+$ ]]; then + read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${owner}/${modname}/master/acore-module.json") + else + # Unknown host: try the repository URL as-is (may fail) + read v b < <(inst_getVersionBranch "${url}/master/acore-module.json") + fi + if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then + def="$(inst_get_default_branch "$repo_ref")" + print_warn "Warning: $repo_ref has no compatible acore-module.json; installing from branch '$def' (latest commit)." + b="$def" + fi + fi + + # Use flat directory structure with custom directory name + if [ -d "$J_PATH_MODULES/$dirname" ]; then + print_skip "[$repo_ref] Already installed (skipping)." + curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" + continue + fi + + if Joiner:add_repo "$url" "$dirname" "$b" ""; then + # If a commit was provided, try to checkout it + if [ -n "$override_commit" ] && [ "$override_commit" != "-" ]; then + git -C "$J_PATH_MODULES/$dirname" fetch --all --quiet || true + if git -C "$J_PATH_MODULES/$dirname" rev-parse --verify "$override_commit" >/dev/null 2>&1; then + git -C "$J_PATH_MODULES/$dirname" checkout --quiet "$override_commit" + else + print_warn "[$repo_ref] provided commit '$override_commit' not found; staying on branch '$b' HEAD." + fi + fi + curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" + print_success "[$repo_ref] Installed in '$dirname'. Please re-run compiling and db assembly." + else + print_error "[$repo_ref] Install failed or module not found" + exit 1; + fi + done + fi + + echo "" + echo "" +} + +# Update one or more modules +function inst_module_update { + # Handle help request + if [[ "$1" == "--help" || "$1" == "-h" ]]; then + inst_module_help + return 0 + fi + + # Support multiple modules and the --all flag; prompt if none specified. + local args=("$@") + local use_all=false + if [ ${#args[@]} -gt 0 ] && { [ "${args[0]}" = "--all" ] || [ "${args[0]}" = "-a" ]; }; then + use_all=true + shift || true + fi + + local _tmp=$PWD + + if $use_all; then + local line repo_ref branch commit newCommit owner modname url dirname + while read -r repo_ref branch commit; do + [ -z "$repo_ref" ] && continue + # Skip excluded modules during update --all + if inst_mod_is_excluded "$repo_ref"; then + print_warn "[$repo_ref] Excluded by MODULES_EXCLUDE_LIST (skipping)." + continue + fi + parsed_output=$(inst_parse_module_spec "$repo_ref") + IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" + + dirname="${dirname:-$modname}" + if [ ! -d "$J_PATH_MODULES/$dirname/" ]; then + print_skip "[$repo_ref] Not installed locally, skipping." + continue + fi + + if Joiner:upd_repo "$url" "$dirname" "$branch" ""; then + newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$branch" "$newCommit" + print_success "[$repo_ref] Updated to latest commit on '$branch'." + else + print_error "[$repo_ref] Cannot update" + fi + done < <(inst_mod_list_read) + else + local modules=("$@") + if [ ${#modules[@]} -eq 0 ]; then + echo "Type the name(s) of the module(s) to update" + read -p "Insert name(s): " _line + read -r -a modules <<< "$_line" + fi + + local spec repo_ref override_branch override_commit owner modname url dirname v b branch def newCommit + for spec in "${modules[@]}"; do + [ -z "$spec" ] && continue + parsed_output=$(inst_parse_module_spec "$spec") + IFS=' ' read -r repo_ref owner modname override_branch override_commit url dirname <<< "$parsed_output" + + dirname="${dirname:-$modname}" + if [ -d "$J_PATH_MODULES/$dirname/" ]; then + # determine preferred branch if not provided + if [ -n "$override_branch" ] && [ "$override_branch" != "-" ]; then + b="$override_branch" + else + # try reading acore-module.json for this repo + if [[ "$url" =~ github.com ]]; then + read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${owner}/${modname}/master/acore-module.json") + else + read v b < <(inst_getVersionBranch "${url}/master/acore-module.json") + fi + if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then + if branch=$(git -C "$J_PATH_MODULES/$dirname" rev-parse --abbrev-ref HEAD 2>/dev/null); then + print_warn "Warning: $repo_ref has no compatible acore-module.json; updating current branch '$branch'." + b="$branch" + else + def="$(inst_get_default_branch "$repo_ref")" + print_warn "Warning: $repo_ref has no compatible acore-module.json and no git branch detected; updating default branch '$def'." + b="$def" + fi + fi + fi + + if Joiner:upd_repo "$url" "$dirname" "$b" ""; then + newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$b" "$newCommit" + print_success "[$repo_ref] Done, please re-run compiling and db assembly" + else + print_error "[$repo_ref] Cannot update" + fi + else + print_error "[$repo_ref] Cannot update! Path doesn't exist ($J_PATH_MODULES/$dirname/)" + fi + done + fi + + echo "" + echo "" +} + +# Remove one or more modules +function inst_module_remove { + # Support multiple modules; prompt if none specified. + local modules=("$@") + if [ ${#modules[@]} -eq 0 ]; then + echo "Type the name(s) of the module(s) to remove" + read -p "Insert name(s): " _line + read -r -a modules <<< "$_line" + fi + + local spec repo_ref owner modname url override_branch override_commit dirname + for spec in "${modules[@]}"; do + [ -z "$spec" ] && continue + parsed_output=$(inst_parse_module_spec "$spec") + IFS=' ' read -r repo_ref owner modname override_branch override_commit url dirname <<< "$parsed_output" + [ -z "$repo_ref" ] && continue + + dirname="${dirname:-$modname}" + if Joiner:remove "$dirname" ""; then + inst_mod_list_remove "$repo_ref" + print_success "[$repo_ref] Done, please re-run compiling" + else + print_error "[$repo_ref] Cannot remove" + fi + done + + echo "" + echo "" +} diff --git a/apps/installer/main.sh b/apps/installer/main.sh index 396dafaad..b1cba33e8 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -1,105 +1,107 @@ #!/usr/bin/env bash +# AzerothCore Dashboard Script +# +# This script provides an interactive menu system for AzerothCore management +# using the unified menu system library. +# +# Usage: +# ./acore.sh - Interactive mode with numeric and text selection +# ./acore.sh [args] - Direct command execution (only text commands, no numbers) +# +# Interactive Mode: +# - Select options by number (1, 2, 3...), command name (init, compiler, etc.), +# or short alias (i, c, etc.) +# - All selection methods work in interactive mode +# +# Direct Command Mode: +# - Only command names and short aliases are accepted (e.g., './acore.sh compiler build', './acore.sh c build') +# - Numeric selection is disabled to prevent confusion with command arguments +# - Examples: './acore.sh init', './acore.sh compiler clean', './acore.sh module install mod-name' +# +# Menu System: +# - Uses unified menu system from bash_shared/menu_system.sh +# - Single source of truth for menu definitions +# - Consistent behavior across all AzerothCore tools + CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - source "$CURRENT_PATH/includes/includes.sh" +source "$AC_PATH_APPS/bash_shared/menu_system.sh" -PS3='[Please enter your choice]: ' -options=( - "init (i): First Installation" # 1 - "install-deps (d): Configure OS dep" # 2 - "pull (u): Update Repository" # 3 - "reset (r): Reset & Clean Repository" # 4 - "compiler (c): Run compiler tool" # 5 - "module-search (ms): Module Search by keyword" # 6 - "module-install (mi): Module Install by name" # 7 - "module-update (mu): Module Update by name" # 8 - "module-remove: (mr): Module Remove by name" # 9 - "client-data: (gd): download client data from github repository (beta)" # 10 - "run-worldserver (rw): execute a simple restarter for worldserver" # 11 - "run-authserver (ra): execute a simple restarter for authserver" # 12 - "docker (dr): Run docker tools" # 13 - "version (v): Show AzerothCore version" # 14 - "service-manager (sm): Run service manager to run authserver and worldserver in background" # 15 - "quit: Exit from this menu" # 16 - ) +# Menu: single ordered source of truth (no functions in strings) +# Format: "key|short|description" +menu_items=( + "init|i|First Installation" + "install-deps|d|Configure OS dep" + "pull|u|Update Repository" + "reset|r|Reset & Clean Repository" + "compiler|c|Run compiler tool" + "module|m|Module manager (search/install/update/remove)" + "client-data|gd|download client data from github repository (beta)" + "run-worldserver|rw|execute a simple restarter for worldserver" + "run-authserver|ra|execute a simple restarter for authserver" + "docker|dr|Run docker tools" + "version|v|Show AzerothCore version" + "service-manager|sm|Run service manager to run authserver and worldserver in background" + "quit|q|Exit from this menu" +) -function _switch() { - _reply="$1" - _opt="$2" - case $_reply in - ""|"i"|"init"|"1") - inst_allInOne +# Menu command handler - called by menu system for each command +function handle_menu_command() { + local key="$1" + shift + + case "$key" in + "init") + inst_allInOne ;; - ""|"d"|"install-deps"|"2") - inst_configureOS + "install-deps") + inst_configureOS ;; - ""|"u"|"pull"|"3") - inst_updateRepo + "pull") + inst_updateRepo ;; - ""|"r"|"reset"|"4") - inst_resetRepo + "reset") + inst_resetRepo ;; - ""|"c"|"compiler"|"5") - bash "$AC_PATH_APPS/compiler/compiler.sh" $_opt + "compiler") + bash "$AC_PATH_APPS/compiler/compiler.sh" "$@" ;; - ""|"ms"|"module-search"|"6") - inst_module_search "$_opt" + "module") + bash "$AC_PATH_APPS/installer/includes/modules-manager/module-main.sh" "$@" ;; - ""|"mi"|"module-install"|"7") - inst_module_install "$_opt" + "client-data") + inst_download_client_data ;; - ""|"mu"|"module-update"|"8") - inst_module_update "$_opt" + "run-worldserver") + inst_simple_restarter worldserver ;; - ""|"mr"|"module-remove"|"9") - inst_module_remove "$_opt" + "run-authserver") + inst_simple_restarter authserver ;; - ""|"gd"|"client-data"|"10") - inst_download_client_data + "docker") + DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "$@" + exit ;; - ""|"rw"|"run-worldserver"|"11") - inst_simple_restarter worldserver - ;; - ""|"ra"|"run-authserver"|"12") - inst_simple_restarter authserver - ;; - ""|"dr"|"docker"|"13") - DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "${@:2}" - exit - ;; - ""|"v"|"version"|"14") - # denoRunFile "$AC_PATH_APPS/installer/main.ts" "version" + "version") printf "AzerothCore Rev. %s\n" "$ACORE_VERSION" - exit + exit ;; - ""|"sm"|"service-manager"|"15") - bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "${@:2}" - exit + "service-manager") + bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@" + exit ;; - ""|"quit"|"16") + "quit") echo "Goodbye!" - exit + exit ;; - ""|"--help") - echo "Available commands:" - printf '%s\n' "${options[@]}" + *) + echo "Invalid option. Use --help to see available commands." + return 1 ;; - *) echo "invalid option, use --help option for the commands list";; esac } -while true -do - # run option directly if specified in argument - [ ! -z $1 ] && _switch $@ # old method: "${options[$cmdopt-1]}" - [ ! -z $1 ] && exit 0 - - echo "==== ACORE DASHBOARD ====" - select opt in "${options[@]}" - do - _switch $REPLY - break - done -done +# Run the menu system +menu_run_with_items "ACORE DASHBOARD" handle_menu_command -- "${menu_items[@]}" -- "$@" diff --git a/apps/installer/test/bats.conf b/apps/installer/test/bats.conf new file mode 100644 index 000000000..8034254e7 --- /dev/null +++ b/apps/installer/test/bats.conf @@ -0,0 +1,14 @@ +# BATS Test Configuration + +# Set test timeout (in seconds) +export BATS_TEST_TIMEOUT=30 + +# Enable verbose output for debugging +export BATS_VERBOSE_RUN=1 + +# Test output format +export BATS_FORMATTER=pretty + +# Enable colored output +export BATS_NO_PARALLELIZE_ACROSS_FILES=1 +export BATS_NO_PARALLELIZE_WITHIN_FILE=1 diff --git a/apps/installer/test/test_module_commands.bats b/apps/installer/test/test_module_commands.bats new file mode 100755 index 000000000..1223a80a6 --- /dev/null +++ b/apps/installer/test/test_module_commands.bats @@ -0,0 +1,755 @@ +#!/usr/bin/env bats + +# Tests for installer module commands (search/install/update/remove) +# Focused on installer:module install behavior using a mocked joiner + +load '../../test-framework/bats_libs/acore-support' +load '../../test-framework/bats_libs/acore-assert' + +setup() { + acore_test_setup + # Point to the installer src directory (not needed in this test) + + # Set installer/paths environment for the test + export AC_PATH_APPS="$TEST_DIR/apps" + export AC_PATH_ROOT="$TEST_DIR" + export AC_PATH_DEPS="$TEST_DIR/deps" + export AC_PATH_MODULES="$TEST_DIR/modules" + export MODULES_LIST_FILE="$TEST_DIR/conf/modules.list" + + # Create stubbed deps: joiner.sh (sourced by includes) and semver + mkdir -p "$TEST_DIR/deps/acore/joiner" + cat > "$TEST_DIR/deps/acore/joiner/joiner.sh" << 'EOF' +#!/usr/bin/env bash +# Stub joiner functions used by installer +Joiner:add_repo() { + # arguments: url name branch basedir + echo "ADD $@" > "$TEST_DIR/joiner_called.txt" + return 0 +} +Joiner:upd_repo() { + echo "UPD $@" > "$TEST_DIR/joiner_called.txt" + return 0 +} +Joiner:remove() { + echo "REM $@" > "$TEST_DIR/joiner_called.txt" + return 0 +} +EOF + chmod +x "$TEST_DIR/deps/acore/joiner/joiner.sh" + + mkdir -p "$TEST_DIR/deps/semver_bash" + # Minimal semver stub + cat > "$TEST_DIR/deps/semver_bash/semver.sh" << 'EOF' +#!/usr/bin/env bash +# semver stub +semver::satisfies() { return 0; } +EOF + chmod +x "$TEST_DIR/deps/semver_bash/semver.sh" + + # Provide a minimal compiler includes file expected by installer + mkdir -p "$TEST_DIR/apps/compiler/includes" + touch "$TEST_DIR/apps/compiler/includes/includes.sh" + + # Provide minimal bash_shared includes to satisfy installer include + mkdir -p "$TEST_DIR/apps/bash_shared" + cat > "$TEST_DIR/apps/bash_shared/includes.sh" << 'EOF' +#!/usr/bin/env bash +# minimal stub +EOF + + # Copy the menu system needed by modules.sh + cp "$AC_TEST_ROOT/apps/bash_shared/menu_system.sh" "$TEST_DIR/apps/bash_shared/" + + # Copy the real installer app into the test apps dir + mkdir -p "$TEST_DIR/apps" + cp -r "$(cd "$AC_TEST_ROOT/apps/installer" && pwd)" "$TEST_DIR/apps/installer" +} + +teardown() { + acore_test_teardown +} + +@test "module install should call joiner and record entry in modules list" { + cd "$TEST_DIR" + + # Source installer includes and call the install function directly to avoid menu interaction + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install example-module@main:abcd1234" + + # Check that joiner was called + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "ADD" "$TEST_DIR/joiner_called.txt" + + # Check modules list was created and contains the repo_ref and branch + [ -f "$TEST_DIR/conf/modules.list" ] + grep -q "azerothcore/example-module main" "$TEST_DIR/conf/modules.list" +} + +@test "module install with owner/name format should work" { + cd "$TEST_DIR" + + # Test with owner/name format + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install myorg/mymodule" + + # Check that joiner was called with correct URL + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "ADD https://github.com/myorg/mymodule mymodule" "$TEST_DIR/joiner_called.txt" + + # Check modules list contains the entry + [ -f "$TEST_DIR/conf/modules.list" ] + grep -q "myorg/mymodule" "$TEST_DIR/conf/modules.list" +} + +@test "module remove should call joiner remove and update modules list" { + cd "$TEST_DIR" + + # First install a module + bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install test-module" + + # Then remove it + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_remove test-module" + + # Check that joiner remove was called + [ -f "$TEST_DIR/joiner_called.txt" ] + # With flat structure, basedir is empty; ensure name is present + grep -q "REM test-module" "$TEST_DIR/joiner_called.txt" + + # Check modules list no longer contains the entry + [ -f "$TEST_DIR/conf/modules.list" ] + ! grep -q "azerothcore/test-module" "$TEST_DIR/conf/modules.list" +} + +# Tests for intelligent module management (duplicate prevention and cross-format removal) + +@test "inst_extract_owner_name should extract owner/name from various formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test simple name + run inst_extract_owner_name "mod-transmog" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test owner/name format + run inst_extract_owner_name "azerothcore/mod-transmog" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test HTTPS URL + run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog.git" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test SSH URL + run inst_extract_owner_name "git@github.com:azerothcore/mod-transmog.git" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test GitLab URL + run inst_extract_owner_name "https://gitlab.com/myorg/mymodule.git" + [ "$output" = "myorg/mymodule" ] +} + +@test "inst_extract_owner_name should handle URLs with ports correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test HTTPS URL with port + run inst_extract_owner_name "https://example.com:8080/user/repo.git" + [ "$output" = "user/repo" ] + + # Test SSH URL with port + run inst_extract_owner_name "ssh://git@example.com:2222/owner/module" + [ "$output" = "owner/module" ] + + # Test URL with port and custom directory (should ignore the directory part) + run inst_extract_owner_name "https://gitlab.internal:9443/team/project.git:custom-dir" + [ "$output" = "team/project" ] + + # Test complex URL with port (should extract owner/name correctly) + run inst_extract_owner_name "https://git.company.com:8443/department/awesome-module.git" + [ "$output" = "department/awesome-module" ] +} + +@test "duplicate module entries should be prevented across different formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Add module via simple name + inst_mod_list_upsert "mod-transmog" "master" "abc123" + + # Verify it's in the list + grep -q "mod-transmog master abc123" "$TEST_DIR/conf/modules.list" + + # Add same module via owner/name format - should replace, not duplicate + inst_mod_list_upsert "azerothcore/mod-transmog" "dev" "def456" + + # Should only have one entry (the new one) + [ "$(grep -c "azerothcore/mod-transmog" "$TEST_DIR/conf/modules.list")" -eq 1 ] + grep -q "azerothcore/mod-transmog dev def456" "$TEST_DIR/conf/modules.list" + ! grep -q "mod-transmog master abc123" "$TEST_DIR/conf/modules.list" +} + +@test "module installed via URL should be recognized when checking with different formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via HTTPS URL + inst_mod_list_upsert "https://github.com/azerothcore/mod-transmog.git" "master" "abc123" + + # Should be detected as installed using simple name + run inst_mod_is_installed "mod-transmog" + [ "$status" -eq 0 ] + + # Should be detected as installed using owner/name + run inst_mod_is_installed "azerothcore/mod-transmog" + [ "$status" -eq 0 ] + + # Should be detected as installed using SSH URL + run inst_mod_is_installed "git@github.com:azerothcore/mod-transmog.git" + [ "$status" -eq 0 ] + + # Non-existent module should not be detected + run inst_mod_is_installed "mod-nonexistent" + [ "$status" -ne 0 ] +} + +@test "module installed via URL with port should be recognized correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via URL with port + inst_mod_list_upsert "https://gitlab.internal:9443/myorg/my-module.git" "master" "abc123" + + # Should be detected as installed using normalized owner/name + run inst_mod_is_installed "myorg/my-module" + [ "$status" -eq 0 ] + + # Should be detected when checking with different URL format + run inst_mod_is_installed "ssh://git@gitlab.internal:9443/myorg/my-module" + [ "$status" -eq 0 ] + + # Should be detected when checking with custom directory syntax + run inst_mod_is_installed "myorg/my-module:custom-dir" + [ "$status" -eq 0 ] + + # Different module should not be detected + run inst_mod_is_installed "myorg/different-module" + [ "$status" -ne 0 ] +} + +@test "cross-format module removal should work" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via SSH URL + inst_mod_list_upsert "git@github.com:azerothcore/mod-transmog.git" "master" "abc123" + + # Verify it's installed + grep -q "git@github.com:azerothcore/mod-transmog.git" "$TEST_DIR/conf/modules.list" + + # Remove using simple name + inst_mod_list_remove "mod-transmog" + + # Should be completely removed + ! grep -q "azerothcore/mod-transmog" "$TEST_DIR/conf/modules.list" + ! grep -q "git@github.com:azerothcore/mod-transmog.git" "$TEST_DIR/conf/modules.list" +} + +@test "module installation should prevent duplicates when already installed" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via simple name first + inst_mod_list_upsert "mod-worldchat" "master" "abc123" + + # Try to install same module via URL - should detect it's already installed + run inst_mod_is_installed "https://github.com/azerothcore/mod-worldchat.git" + [ "$status" -eq 0 ] + + # Add via URL should replace the existing entry + inst_mod_list_upsert "https://github.com/azerothcore/mod-worldchat.git" "dev" "def456" + + # Should only have one entry + [ "$(grep -c "azerothcore/mod-worldchat" "$TEST_DIR/conf/modules.list")" -eq 1 ] + grep -q "https://github.com/azerothcore/mod-worldchat.git dev def456" "$TEST_DIR/conf/modules.list" +} + +@test "module update --all uses flat structure (no branch subfolders)" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Prepare modules.list with one entry and a matching local directory + mkdir -p "$TEST_DIR/conf" + echo "azerothcore/mod-transmog master abc123" > "$TEST_DIR/conf/modules.list" + mkdir -p "$TEST_DIR/modules/mod-transmog" + + # Run update all + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_update --all" + + # Verify Joiner:upd_repo received flat structure args (no basedir) + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "UPD https://github.com/azerothcore/mod-transmog mod-transmog master" "$TEST_DIR/joiner_called.txt" +} + +@test "module update specific uses flat structure with override branch" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Create local directory so update proceeds + mkdir -p "$TEST_DIR/modules/mymodule" + + # Run update specifying owner/name and branch + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_update myorg/mymodule@dev" + + # Should call joiner with name 'mymodule' and branch 'dev' (no basedir) + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "UPD https://github.com/myorg/mymodule mymodule dev" "$TEST_DIR/joiner_called.txt" +} + +@test "custom directory names should work with new syntax" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test parsing with custom directory name + run inst_parse_module_spec "mod-transmog:my-custom-dir@develop:abc123" + [ "$status" -eq 0 ] + # Should output: repo_ref owner name branch commit url dirname + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "azerothcore/mod-transmog" ] + [ "$owner" = "azerothcore" ] + [ "$name" = "mod-transmog" ] + [ "$branch" = "develop" ] + [ "$commit" = "abc123" ] + [ "$url" = "https://github.com/azerothcore/mod-transmog" ] + [ "$dirname" = "my-custom-dir" ] +} + +@test "directory conflict detection should work" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Create a fake existing directory + mkdir -p "$TEST_DIR/modules/existing-dir" + + # Should detect conflict + run inst_check_module_conflict "existing-dir" "mod-test" + [ "$status" -eq 1 ] + [[ "$output" =~ "Directory 'existing-dir' already exists" ]] + [[ "$output" =~ "Use a different directory name: mod-test:my-custom-name" ]] + + # Should not detect conflict for non-existing directory + run inst_check_module_conflict "non-existing-dir" "mod-test" + [ "$status" -eq 0 ] +} + +@test "module update should work with custom directories" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # First add module with custom directory to list + inst_mod_list_upsert "azerothcore/mod-transmog:custom-dir" "master" "abc123" + + # Create fake module directory structure + mkdir -p "$TEST_DIR/modules/custom-dir/.git" + echo "ref: refs/heads/master" > "$TEST_DIR/modules/custom-dir/.git/HEAD" + + # Mock git commands in the fake module directory + cat > "$TEST_DIR/modules/custom-dir/.git/config" << 'EOF' +[core] + repositoryformatversion = 0 + filemode = true + bare = false +[remote "origin"] + url = https://github.com/azerothcore/mod-transmog + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master +EOF + + # Test update with custom directory should work + # Note: This would require more complex mocking for full integration test + # For now, just test the parsing recognizes the custom directory + run inst_parse_module_spec "azerothcore/mod-transmog:custom-dir" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$dirname" = "custom-dir" ] +} + +@test "URL formats should be properly normalized" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test various URL formats produce same owner/name + run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog" + local url_format="$output" + + run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog.git" + local url_git_format="$output" + + run inst_extract_owner_name "git@github.com:azerothcore/mod-transmog.git" + local ssh_format="$output" + + run inst_extract_owner_name "azerothcore/mod-transmog" + local owner_name_format="$output" + + run inst_extract_owner_name "mod-transmog" + local simple_format="$output" + + # All should normalize to the same owner/name + [ "$url_format" = "azerothcore/mod-transmog" ] + [ "$url_git_format" = "azerothcore/mod-transmog" ] + [ "$ssh_format" = "azerothcore/mod-transmog" ] + [ "$owner_name_format" = "azerothcore/mod-transmog" ] + [ "$simple_format" = "azerothcore/mod-transmog" ] +} + +# Tests for module exclusion functionality + +@test "module exclusion should work with MODULES_EXCLUDE_LIST" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test exclusion with simple name + export MODULES_EXCLUDE_LIST="mod-test-module" + run inst_mod_is_excluded "mod-test-module" + [ "$status" -eq 0 ] + + # Test exclusion with owner/name format + export MODULES_EXCLUDE_LIST="azerothcore/mod-test" + run inst_mod_is_excluded "mod-test" + [ "$status" -eq 0 ] + + # Test exclusion with space-separated list + export MODULES_EXCLUDE_LIST="mod-one mod-two mod-three" + run inst_mod_is_excluded "mod-two" + [ "$status" -eq 0 ] + + # Test exclusion with newline-separated list + export MODULES_EXCLUDE_LIST=" +mod-alpha +mod-beta +mod-gamma +" + run inst_mod_is_excluded "mod-beta" + [ "$status" -eq 0 ] + + # Test exclusion with URL format + export MODULES_EXCLUDE_LIST="https://github.com/azerothcore/mod-transmog.git" + run inst_mod_is_excluded "mod-transmog" + [ "$status" -eq 0 ] + + # Test non-excluded module + export MODULES_EXCLUDE_LIST="mod-other" + run inst_mod_is_excluded "mod-transmog" + [ "$status" -eq 1 ] + + # Test empty exclusion list + unset MODULES_EXCLUDE_LIST + run inst_mod_is_excluded "mod-transmog" + [ "$status" -eq 1 ] +} + +@test "install --all should skip excluded modules" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Setup modules list with excluded module + mkdir -p "$TEST_DIR/conf" + cat > "$TEST_DIR/conf/modules.list" << 'EOF' +azerothcore/mod-transmog master abc123 +azerothcore/mod-excluded master def456 +EOF + + # Set exclusion list + export MODULES_EXCLUDE_LIST="mod-excluded" + + # Mock the install process to capture output + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install --all 2>&1" + + # Should show that excluded module was skipped + [[ "$output" == *"azerothcore/mod-excluded"* && "$output" == *"Excluded by MODULES_EXCLUDE_LIST"* && "$output" == *"skipping"* ]] +} + +@test "exclusion should work with multiple formats in same list" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test multiple exclusion formats + export MODULES_EXCLUDE_LIST="mod-test https://github.com/azerothcore/mod-transmog.git custom/mod-other" + + run inst_mod_is_excluded "mod-test" + [ "$status" -eq 0 ] + + run inst_mod_is_excluded "azerothcore/mod-transmog" + [ "$status" -eq 0 ] + + run inst_mod_is_excluded "custom/mod-other" + [ "$status" -eq 0 ] + + run inst_mod_is_excluded "mod-allowed" + [ "$status" -eq 1 ] +} + +# Tests for color support functionality + +@test "color functions should work correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test that print functions exist and work + run print_info "test message" + [ "$status" -eq 0 ] + + run print_warn "test warning" + [ "$status" -eq 0 ] + + run print_error "test error" + [ "$status" -eq 0 ] + + run print_success "test success" + [ "$status" -eq 0 ] + + run print_skip "test skip" + [ "$status" -eq 0 ] + + run print_header "test header" + [ "$status" -eq 0 ] +} + +@test "color support should respect NO_COLOR environment variable" { + cd "$TEST_DIR" + + # Test with NO_COLOR set + export NO_COLOR=1 + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Colors should be empty when NO_COLOR is set + [ -z "$C_RED" ] + [ -z "$C_GREEN" ] + [ -z "$C_RESET" ] +} + +# Tests for interactive menu system + +@test "module help should display comprehensive help" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + run inst_module_help + [ "$status" -eq 0 ] + + # Should contain key sections + [[ "$output" =~ "Module Manager Help" ]] + [[ "$output" =~ "Usage:" ]] + [[ "$output" =~ "Module Specification Syntax:" ]] + [[ "$output" =~ "Examples:" ]] +} + +@test "module list should show installed modules correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Setup modules list + mkdir -p "$TEST_DIR/conf" + cat > "$TEST_DIR/conf/modules.list" << 'EOF' +azerothcore/mod-transmog master abc123 +custom/mod-test develop def456 +EOF + + run inst_module_list + [ "$status" -eq 0 ] + + # Should show both modules + [[ "$output" =~ "mod-transmog" ]] + [[ "$output" =~ "custom/mod-test" ]] + [[ "$output" =~ "master" ]] + [[ "$output" =~ "develop" ]] +} + +@test "module list should handle empty list gracefully" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Ensure empty modules list + mkdir -p "$TEST_DIR/conf" + touch "$TEST_DIR/conf/modules.list" + + run inst_module_list + [ "$status" -eq 0 ] + [[ "$output" =~ "No modules installed" ]] +} + +# Tests for advanced parsing edge cases + +@test "parsing should handle complex URL formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test GitLab URL with custom directory and branch + run inst_parse_module_spec "https://gitlab.com/myorg/mymodule.git:custom-dir@develop:abc123" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://gitlab.com/myorg/mymodule.git" ] + [ "$owner" = "myorg" ] + [ "$name" = "mymodule" ] + [ "$branch" = "develop" ] + [ "$commit" = "abc123" ] + [ "$dirname" = "custom-dir" ] +} + +@test "parsing should handle URLs with ports correctly (fix for port/dirname confusion)" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test HTTPS URL with port - should NOT treat port as dirname + run inst_parse_module_spec "https://example.com:8080/user/repo.git" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://example.com:8080/user/repo.git" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$branch" = "-" ] + [ "$commit" = "-" ] + [ "$url" = "https://example.com:8080/user/repo.git" ] + [ "$dirname" = "repo" ] # Should default to repo name, NOT port number +} + +@test "parsing should handle URLs with ports and custom directory correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test URL with port AND custom directory - should parse custom directory correctly + run inst_parse_module_spec "https://example.com:8080/user/repo.git:custom-dir" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://example.com:8080/user/repo.git" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$branch" = "-" ] + [ "$commit" = "-" ] + [ "$url" = "https://example.com:8080/user/repo.git" ] + [ "$dirname" = "custom-dir" ] # Should be custom-dir, not port number +} + +@test "parsing should handle SSH URLs with ports correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test SSH URL with port + run inst_parse_module_spec "ssh://git@example.com:2222/user/repo" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "ssh://git@example.com:2222/user/repo" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$dirname" = "repo" ] # Should be repo name, not port number +} + +@test "parsing should handle SSH URLs with ports and custom directory" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test SSH URL with port and custom directory + run inst_parse_module_spec "ssh://git@example.com:2222/user/repo:my-custom-dir@develop" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "ssh://git@example.com:2222/user/repo" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$branch" = "develop" ] + [ "$dirname" = "my-custom-dir" ] +} + +@test "parsing should handle complex URLs with ports, custom dirs, and branches" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test comprehensive URL with port, custom directory, branch, and commit + run inst_parse_module_spec "https://gitlab.example.com:9443/myorg/myrepo.git:custom-name@feature-branch:abc123def" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://gitlab.example.com:9443/myorg/myrepo.git" ] + [ "$owner" = "myorg" ] + [ "$name" = "myrepo" ] + [ "$branch" = "feature-branch" ] + [ "$commit" = "abc123def" ] + [ "$url" = "https://gitlab.example.com:9443/myorg/myrepo.git" ] + [ "$dirname" = "custom-name" ] +} + +@test "URL port parsing regression test - ensure ports are not confused with directory names" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # These are the problematic cases that the fix addresses + local test_cases=( + "https://example.com:8080/repo.git" + "https://gitlab.internal:9443/group/project.git" + "ssh://git@server.com:2222/owner/repo" + "https://git.company.com:8443/team/module.git" + ) + + for spec in "${test_cases[@]}"; do + run inst_parse_module_spec "$spec" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + + # Critical: dirname should NEVER be a port number + [[ ! "$dirname" =~ ^[0-9]+$ ]] || { + echo "FAIL: Port number '$dirname' incorrectly parsed as directory name for spec: $spec" + return 1 + } + + # dirname should be the repository name by default + local expected_name + if [[ "$spec" =~ /([^/]+)(\.git)?$ ]]; then + expected_name="${BASH_REMATCH[1]}" + expected_name="${expected_name%.git}" + fi + [ "$dirname" = "$expected_name" ] || { + echo "FAIL: Expected dirname '$expected_name' but got '$dirname' for spec: $spec" + return 1 + } + done +} + +@test "parsing should handle URL with custom directory but no branch" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + run inst_parse_module_spec "https://github.com/owner/repo.git:my-dir" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://github.com/owner/repo.git" ] + [ "$dirname" = "my-dir" ] + [ "$branch" = "-" ] +} + +@test "modules list should maintain alphabetical order" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Add modules in random order + inst_mod_list_upsert "zeta/mod-z" "master" "abc" + inst_mod_list_upsert "alpha/mod-a" "master" "def" + inst_mod_list_upsert "beta/mod-b" "master" "ghi" + + # Read the list and verify alphabetical order + local entries=() + while read -r repo_ref branch commit; do + [[ -z "$repo_ref" ]] && continue + entries+=("$repo_ref") + done < <(inst_mod_list_read) + + # Should be in alphabetical order by owner/name + [ "${entries[0]}" = "alpha/mod-a" ] + [ "${entries[1]}" = "beta/mod-b" ] + [ "${entries[2]}" = "zeta/mod-z" ] +} + +@test "module dispatcher should handle unknown commands gracefully" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + run inst_module "unknown-command" + [ "$status" -eq 1 ] + [[ "$output" =~ "Unknown module command" ]] +} \ No newline at end of file diff --git a/apps/startup-scripts/README.md b/apps/startup-scripts/README.md index 7b057581d..0c7cb0940 100644 --- a/apps/startup-scripts/README.md +++ b/apps/startup-scripts/README.md @@ -305,6 +305,31 @@ Services support two restart policies: ./service-manager.sh delete auth ``` +#### Health and Console Commands + +Use these commands to programmatically check service health and interact with the console (used by CI workflows): + +```bash +# Check if service is currently running (exit 0 if running) +./service-manager.sh is-running world + +# Print current uptime in seconds (fails if not running) +./service-manager.sh uptime-seconds world + +# Wait until uptime >= 10s (optional timeout 240s) +./service-manager.sh wait-uptime world 10 240 + +# Send a console command (uses pm2 send or tmux/screen) +./service-manager.sh send world "server info" + +# Show provider, configs and run-engine settings +./service-manager.sh show-config world +``` + +Notes: +- For `send`, PM2 provider uses `pm2 send` with the process ID; systemd provider requires a session manager (tmux/screen). If no attachable session is configured, the command fails. +- `wait-uptime` fails with a non-zero exit code if the service does not reach the requested uptime within the timeout window. + #### Service Configuration ```bash # Update service settings @@ -312,6 +337,9 @@ Services support two restart policies: # Edit configuration ./service-manager.sh edit world + +# Restore missing services from registry +./service-manager.sh restore ``` ## 🌍 Multiple Realms Setup @@ -384,22 +412,72 @@ cp examples/restarter-world.sh restarter-realm2.sh ## 🛠️ Service Management +### Service Registry and Persistence + +The service manager includes a comprehensive registry system that tracks all created services and enables automatic restoration: + +#### Service Registry Features + +- **Automatic Tracking**: All services are automatically registered when created +- **Cross-Reboot Persistence**: PM2 services are configured with startup persistence +- **Service Restoration**: Missing services can be detected and restored from registry +- **Migration Support**: Legacy service configurations can be migrated to the new format + +#### Using the Registry + +```bash +# Check for missing services and restore them +./service-manager.sh restore + +# List all registered services (includes status) +./service-manager.sh list + +# Services are automatically added to registry on creation +./service-manager.sh create auth authserver --bin-path /path/to/bin +``` + +#### Custom Configuration Directories + +You can customize where service configurations and PM2/systemd files are stored: + +```bash +# Set custom directories +export AC_SERVICE_CONFIG_DIR="/path/to/your/project/services" + +# Now all service operations will use these custom directories +./service-manager.sh create auth authserver --bin-path /path/to/bin +``` + +This is particularly useful for: +- **Version Control**: Keep service configurations in your project repository +- **Multiple Projects**: Separate service configurations per project +- **Team Collaboration**: Share service setups across development teams + +#### Migration from Legacy Format + +If you have existing services in the old format, use the migration script: + +```bash +# Migrate existing registry to new format +./migrate-registry.sh + +# The script will: +# - Detect old format automatically +# - Create a backup of the old registry +# - Convert to new format with proper tracking +# - Preserve all existing service information +``` + ### PM2 Services When using PM2 as the service provider: -```bash -# PM2-specific commands -pm2 list # List all PM2 processes -pm2 logs auth # View logs -pm2 monit # Real-time monitoring -pm2 restart auth # Restart service -pm2 delete auth # Remove service +* [PM2 CLI Documentation](https://pm2.io/docs/runtime/reference/pm2-cli/) -# Save PM2 configuration -pm2 save -pm2 startup # Auto-start on boot -``` +**Automatic PM2 Persistence**: The service manager automatically configures PM2 for persistence across reboots by: +- Running `pm2 startup` to set up the startup script +- Running `pm2 save` after each service creation/modification +- This ensures your services automatically start when the system reboots NOTE: pm2 cannot run tmux/screen sessions, but you can always use the `attach` command to connect to the service console because pm2 supports interactive mode. @@ -407,6 +485,12 @@ NOTE: pm2 cannot run tmux/screen sessions, but you can always use the `attach` c The startup scripts recognize several environment variables for configuration and runtime behavior: +#### Configuration Directory Variables + +- **`AC_SERVICE_CONFIG_DIR`**: Override the default configuration directory for services registry and configurations + - Default: `${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services` + - Used for storing service registry and run-engine configurations + #### Service Detection Variables - **`AC_LAUNCHED_BY_PM2`**: Set to `1` when launched by PM2 (automatically set by service-manager) @@ -551,4 +635,17 @@ npm install -g pm2 sudo npm install -g pm2 ``` +#### 7. Registry Out of Sync +```bash +# If the service registry shows services that don't actually exist +``` +**Solution**: Use registry sync or restore +```bash +# Check and restore missing services (also cleans up orphaned entries) +./service-manager.sh restore + +# If you have a very old registry format, migrate it +./migrate-registry.sh +``` + diff --git a/apps/startup-scripts/src/migrate-registry.sh b/apps/startup-scripts/src/migrate-registry.sh new file mode 100755 index 000000000..c5898794f --- /dev/null +++ b/apps/startup-scripts/src/migrate-registry.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash + +# One-time migration script for service registry +# Converts old format to new format + +set -euo pipefail # Strict error handling + +CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}" +REGISTRY_FILE="$CONFIG_DIR/service_registry.json" +BACKUP_FILE="$CONFIG_DIR/service_registry.json.backup" + +# Colors +readonly YELLOW='\033[1;33m' +readonly GREEN='\033[0;32m' +readonly RED='\033[0;31m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' + +echo -e "${BLUE}AzerothCore Service Registry Migration Tool${NC}" +echo "==============================================" + +# Check dependencies +if ! command -v jq >/dev/null 2>&1; then + echo -e "${RED}Error: jq is required but not installed. Please install jq package.${NC}" + exit 1 +fi + +# Create config directory if it doesn't exist +mkdir -p "$CONFIG_DIR" + +# Check if registry exists +if [ ! -f "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No registry file found. Nothing to migrate.${NC}" + exit 0 +fi + +# Validate JSON format +if ! jq empty "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${RED}Error: Registry file contains invalid JSON.${NC}" + echo "Please check the file: $REGISTRY_FILE" + exit 1 +fi + +# Check if it's already new format +if jq -e 'type == "array" and (length == 0 or .[0] | has("bin_path"))' "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${GREEN}Registry is already in new format. No migration needed.${NC}" + exit 0 +fi + +# Check if it's old format +if ! jq -e 'type == "array" and (length == 0 or .[0] | has("config"))' "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${YELLOW}Registry format not recognized. Manual review needed.${NC}" + echo "Current registry content:" + cat "$REGISTRY_FILE" + exit 1 +fi + +echo -e "${YELLOW}Old format detected. Starting migration...${NC}" + +# Create backup +if ! cp "$REGISTRY_FILE" "$BACKUP_FILE"; then + echo -e "${RED}Error: Failed to create backup file.${NC}" + exit 1 +fi +echo -e "${BLUE}Backup created: $BACKUP_FILE${NC}" + +# Convert to new format +echo "[]" > "$REGISTRY_FILE.new" + +services_migrated=0 +while IFS= read -r service; do + if [ -n "$service" ] && [ "$service" != "null" ]; then + name=$(echo "$service" | jq -r '.name // ""') + provider=$(echo "$service" | jq -r '.provider // ""') + type=$(echo "$service" | jq -r '.type // ""') + config=$(echo "$service" | jq -r '.config // ""') + + # Validate required fields + if [ -z "$name" ] || [ -z "$provider" ] || [ -z "$type" ]; then + echo -e "${YELLOW}Skipping invalid service entry: $service${NC}" + continue + fi + + echo -e "${YELLOW}Migrating service: $name${NC}" + + # Create new format entry with all required fields + new_entry=$(jq -n \ + --arg name "$name" \ + --arg provider "$provider" \ + --arg type "$type" \ + --arg bin_path "unknown" \ + --arg args "" \ + --arg created "$(date -Iseconds)" \ + --arg status "migrated" \ + --arg systemd_type "--user" \ + --arg restart_policy "always" \ + --arg session_manager "none" \ + --arg gdb_enabled "0" \ + --arg pm2_opts "" \ + --arg server_config "" \ + --arg legacy_config "$config" \ + '{ + name: $name, + provider: $provider, + type: $type, + bin_path: $bin_path, + args: $args, + created: $created, + status: $status, + systemd_type: $systemd_type, + restart_policy: $restart_policy, + session_manager: $session_manager, + gdb_enabled: $gdb_enabled, + pm2_opts: $pm2_opts, + server_config: $server_config, + legacy_config: $legacy_config + }') + + # Add to new registry with error checking + if ! jq --argjson entry "$new_entry" '. += [$entry]' "$REGISTRY_FILE.new" > "$REGISTRY_FILE.new.tmp"; then + echo -e "${RED}Error: Failed to add service $name to new registry${NC}" + rm -f "$REGISTRY_FILE.new" "$REGISTRY_FILE.new.tmp" + exit 1 + fi + mv "$REGISTRY_FILE.new.tmp" "$REGISTRY_FILE.new" + + services_migrated=$((services_migrated + 1)) + fi +done < <(jq -c '.[]?' "$BACKUP_FILE" 2>/dev/null || echo "") + +# Replace old registry with new one +if ! mv "$REGISTRY_FILE.new" "$REGISTRY_FILE"; then + echo -e "${RED}Error: Failed to replace old registry with new one${NC}" + exit 1 +fi + +echo -e "${GREEN}Migration completed successfully!${NC}" +echo -e "${BLUE}Services migrated: $services_migrated${NC}" +echo -e "${BLUE}Use 'service-manager.sh restore' to review and update services.${NC}" +echo -e "${YELLOW}Note: Migrated services have bin_path='unknown' and need manual recreation.${NC}" +echo "" +echo -e "${BLUE}To recreate services, use commands like:${NC}" +echo " ./service-manager.sh create auth authserver --provider pm2 --bin-path /path/to/your/bin" +echo " ./service-manager.sh create world worldserver --provider systemd --bin-path /path/to/your/bin" diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh index 980dfbb3a..ccc4e8e35 100755 --- a/apps/startup-scripts/src/service-manager.sh +++ b/apps/startup-scripts/src/service-manager.sh @@ -4,6 +4,8 @@ # A unified interface for managing AzerothCore services with PM2 or systemd # This script provides commands to create, update, delete, and manage server instances +set -euo pipefail # Strict error handling + # Script location CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -11,16 +13,16 @@ SCRIPT_DIR="$CURRENT_PATH" ROOT_DIR="$(cd "$CURRENT_PATH/../../.." && pwd)" -# Configuration directory -CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services" +# Configuration directory (can be overridden with AC_SERVICE_CONFIG_DIR) +CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}" REGISTRY_FILE="$CONFIG_DIR/service_registry.json" # Colors for output -YELLOW='\033[1;33m' -GREEN='\033[0;32m' -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +readonly YELLOW='\033[1;33m' +readonly GREEN='\033[0;32m' +readonly RED='\033[0;31m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color # Create config directory if it doesn't exist mkdir -p "$CONFIG_DIR" @@ -38,6 +40,198 @@ check_dependencies() { } } +# Registry management functions +function add_service_to_registry() { + local service_name="$1" + local provider="$2" + local service_type="$3" + local bin_path="$4" + local args="$5" + local systemd_type="$6" + local restart_policy="$7" + local session_manager="$8" + local gdb_enabled="$9" + local pm2_opts="${10}" + local server_config="${11}" + + # Remove any existing entry with the same service name to avoid duplicates + local tmp_file + tmp_file=$(mktemp) + jq --arg name "$service_name" 'map(select(.name != $name))' "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + + # Add the new entry to the registry + tmp_file=$(mktemp) + jq --arg name "$service_name" \ + --arg provider "$provider" \ + --arg type "$service_type" \ + --arg bin_path "$bin_path" \ + --arg args "$args" \ + --arg created "$(date -Iseconds)" \ + --arg systemd_type "$systemd_type" \ + --arg restart_policy "$restart_policy" \ + --arg session_manager "$session_manager" \ + --arg gdb_enabled "$gdb_enabled" \ + --arg pm2_opts "$pm2_opts" \ + --arg server_config "$server_config" \ + '. += [{"name": $name, "provider": $provider, "type": $type, "bin_path": $bin_path, "args": $args, "created": $created, "status": "active", "systemd_type": $systemd_type, "restart_policy": $restart_policy, "session_manager": $session_manager, "gdb_enabled": $gdb_enabled, "pm2_opts": $pm2_opts, "server_config": $server_config}]' \ + "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + + echo -e "${GREEN}Service '$service_name' added to registry${NC}" +} + +function remove_service_from_registry() { + local service_name="$1" + + if [ -f "$REGISTRY_FILE" ]; then + local tmp_file + tmp_file=$(mktemp) + jq --arg name "$service_name" \ + 'map(select(.name != $name))' \ + "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + echo -e "${GREEN}Service '$service_name' removed from registry${NC}" + fi +} + +function restore_missing_services() { + echo -e "${BLUE}Checking for missing services...${NC}" + + if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No services registry found or empty${NC}" + return 0 + fi + + local missing_services=() + local services_count + services_count=$(jq length "$REGISTRY_FILE") + + if [ "$services_count" -eq 0 ]; then + echo -e "${YELLOW}No services registered${NC}" + return 0 + fi + + echo -e "${BLUE}Found $services_count registered services. Checking status...${NC}" + + # Check each service + for i in $(seq 0 $((services_count-1))); do + local service=$(jq -r ".[$i]" "$REGISTRY_FILE") + local name=$(echo "$service" | jq -r '.name') + local provider=$(echo "$service" | jq -r '.provider') + local service_type=$(echo "$service" | jq -r '.type') + local bin_path=$(echo "$service" | jq -r '.bin_path // "unknown"') + local args=$(echo "$service" | jq -r '.args // ""') + local status=$(echo "$service" | jq -r '.status // "active"') + local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"') + local restart_policy=$(echo "$service" | jq -r '.restart_policy // "always"') + local session_manager=$(echo "$service" | jq -r '.session_manager // "none"') + local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"') + local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""') + local server_config=$(echo "$service" | jq -r '.server_config // ""') + + local service_exists=false + + if [ "$provider" = "pm2" ]; then + if pm2 describe "$name" >/dev/null 2>&1; then + service_exists=true + fi + elif [ "$provider" = "systemd" ]; then + local user_unit="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/$name.service" + local system_unit="/etc/systemd/system/$name.service" + if [ -f "$user_unit" ] || [ -f "$system_unit" ]; then + # Unit file present, you can also check if it is active + service_exists=true + else + # Unit file missing: service needs to be recreated! + service_exists=false + fi + fi + + if [ "$service_exists" = false ]; then + missing_services+=("$i") + echo -e "${YELLOW}Missing service: $name ($provider)${NC}" + else + echo -e "${GREEN}✓ Service $name ($provider) exists${NC}" + fi + done + + # Handle missing services + if [ ${#missing_services[@]} -eq 0 ]; then + echo -e "${GREEN}All registered services are present${NC}" + return 0 + fi + + echo -e "${YELLOW}Found ${#missing_services[@]} missing services${NC}" + + for index in "${missing_services[@]}"; do + local service=$(jq -r ".[$index]" "$REGISTRY_FILE") + local name=$(echo "$service" | jq -r '.name') + local provider=$(echo "$service" | jq -r '.provider') + local service_type=$(echo "$service" | jq -r '.type') + local bin_path=$(echo "$service" | jq -r '.bin_path') + local args=$(echo "$service" | jq -r '.args') + local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"') + local restart_policy=$(echo "$service" | jq -r '.restart_policy // "always"') + local session_manager=$(echo "$service" | jq -r '.session_manager // "none"') + local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"') + local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""') + local server_config=$(echo "$service" | jq -r '.server_config // ""') + + echo "" + echo -e "${YELLOW}Service '$name' ($provider) is missing${NC}" + echo " Type: $service_type" + echo " Status: $status" + + if [ "$bin_path" = "unknown" ] || [ "$bin_path" = "null" ] || [ "$status" = "migrated" ]; then + echo " Binary: " + echo " Args: " + echo "" + echo -e "${YELLOW}This service needs to be recreated manually:${NC}" + echo " $0 create $service_type $name --provider $provider --bin-path /path/to/your/bin" + else + echo " Binary: $bin_path" + echo " Args: $args" + fi + echo "" + + read -p "Do you want to (r)ecreate, (d)elete from registry, or (s)kip? [r/d/s]: " choice + + case "$choice" in + r|R|recreate) + if [ "$bin_path" = "unknown" ] || [ "$status" = "migrated" ]; then + echo -e "${YELLOW}Please recreate manually with full create command${NC}" + read -p "Remove this entry from registry? [y/n]: " remove_entry + if [[ "$remove_entry" =~ ^[Yy]$ ]]; then + remove_service_from_registry "$name" + fi + else + echo -e "${BLUE}Recreating service '$name'...${NC}" + if [ "$provider" = "pm2" ]; then + if [ "$args" != "null" ] && [ -n "$args" ]; then + pm2_create_service "$name" "$bin_path $args" "$restart_policy" $pm2_opts + else + pm2_create_service "$name" "$bin_path" "$restart_policy" $pm2_opts + fi + elif [ "$provider" = "systemd" ]; then + echo -e "${BLUE}Attempting to recreate systemd service '$name' automatically...${NC}" + if systemd_create_service "$name" "$bin_path $args" "$restart_policy" "$systemd_type" "$session_manager" "$gdb_enabled" "$server_config"; then + echo -e "${GREEN}Systemd service '$name' recreated successfully${NC}" + else + echo -e "${RED}Failed to recreate systemd service '$name'. Please recreate manually.${NC}" + echo " $0 create $name $service_type --provider systemd --bin-path $bin_path" + fi + fi + fi + ;; + d|D|delete) + echo -e "${BLUE}Removing '$name' from registry...${NC}" + remove_service_from_registry "$name" + ;; + s|S|skip|*) + echo -e "${BLUE}Skipping '$name'${NC}" + ;; + esac + done +} + # Check if PM2 is installed check_pm2() { if ! command -v pm2 >/dev/null 2>&1; then @@ -81,9 +275,15 @@ function print_help() { echo " $base_name update [options]" echo " $base_name delete " echo " $base_name list [provider]" + echo " $base_name restore" echo " $base_name start|stop|restart|status " echo " $base_name logs [--follow]" echo " $base_name attach " + echo " $base_name is-running # exit 0 if running, 1 otherwise" + echo " $base_name uptime-seconds # print uptime in seconds (fails if not running)" + echo " $base_name wait-uptime [t] # wait until uptime >= seconds (timeout t, default 120)" + echo " $base_name send # send console command to service" + echo " $base_name show-config # print current service + run-engine config" echo " $base_name edit-config " echo "" echo "Providers:" @@ -139,6 +339,9 @@ function print_help() { echo " $base_name attach worldserver-realm1" echo " $base_name list pm2" echo "" + echo " # Restore missing services from registry" + echo " $base_name restore" + echo "" echo "Notes:" echo " - Configuration editing modifies run-engine settings (GDB, session manager, etc.)" echo " - Use --server-config for the actual server configuration file" @@ -150,26 +353,13 @@ function print_help() { echo " - attach command automatically detects the configured session manager and connects appropriately" echo " - attach always provides interactive access to the server console" echo " - Use 'logs' command to view service logs without interaction" + echo " - restore command checks registry and helps recreate missing services" + echo "" + echo "Environment Variables:" + echo " AC_SERVICE_CONFIG_DIR - Override default config directory for services registry" } -function register_service() { - local service_name="$1" - local provider="$2" - local service_type="$3" - local config_file="$CONFIG_DIR/$service_name.conf" - - # Add to registry - local tmp_file=$(mktemp) - jq --arg name "$service_name" \ - --arg provider "$provider" \ - --arg type "$service_type" \ - --arg config "$config_file" \ - '. += [{"name": $name, "provider": $provider, "type": $type, "config": $config}]' \ - "$REGISTRY_FILE" > "$tmp_file" - mv "$tmp_file" "$REGISTRY_FILE" - - echo -e "${GREEN}Service $service_name registered successfully${NC}" -} + function validate_service_exists() { local service_name="$1" @@ -210,47 +400,42 @@ function validate_service_exists() { function sync_registry() { echo -e "${YELLOW}Syncing service registry with actual services...${NC}" - local services=$(jq -c '.[]' "$REGISTRY_FILE") - local tmp_file=$(mktemp) + if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No services registry found or empty${NC}" + return 0 + fi - # Initialize with empty array + local services_count=$(jq length "$REGISTRY_FILE") + if [ "$services_count" -eq 0 ]; then + echo -e "${YELLOW}No services registered${NC}" + return 0 + fi + + local tmp_file=$(mktemp) echo "[]" > "$tmp_file" # Check each service in registry - while read -r service_info; do - if [ -n "$service_info" ]; then - local name=$(echo "$service_info" | jq -r '.name') - local provider=$(echo "$service_info" | jq -r '.provider') - - if validate_service_exists "$name" "$provider"; then - # Service exists, add it to the new registry - jq --argjson service "$service_info" '. += [$service]' "$tmp_file" > "$tmp_file.new" - mv "$tmp_file.new" "$tmp_file" - else - echo -e "${YELLOW}Service '$name' no longer exists. Removing from registry.${NC}" - # Don't add to new registry - fi + for i in $(seq 0 $((services_count-1))); do + local service=$(jq -r ".[$i]" "$REGISTRY_FILE") + local name=$(echo "$service" | jq -r '.name') + local provider=$(echo "$service" | jq -r '.provider') + + if validate_service_exists "$name" "$provider"; then + # Service exists, add it to the new registry + jq --argjson service "$service" '. += [$service]' "$tmp_file" > "$tmp_file.new" + mv "$tmp_file.new" "$tmp_file" + else + echo -e "${YELLOW}Service '$name' no longer exists. Removing from registry.${NC}" + # Don't add to new registry fi - done <<< "$services" + done # Replace registry with synced version mv "$tmp_file" "$REGISTRY_FILE" echo -e "${GREEN}Registry synchronized.${NC}" } -function unregister_service() { - local service_name="$1" - - # Remove from registry - local tmp_file=$(mktemp) - jq --arg name "$service_name" '. | map(select(.name != $name))' "$REGISTRY_FILE" > "$tmp_file" - mv "$tmp_file" "$REGISTRY_FILE" - - # Remove configuration file - rm -f "$CONFIG_DIR/$service_name.conf" - - echo -e "${GREEN}Service $service_name unregistered${NC}" -} + function get_service_info() { local service_name="$1" @@ -317,6 +502,15 @@ function pm2_create_service() { if eval "$pm2_cmd"; then echo -e "${GREEN}PM2 service '$service_name' created successfully${NC}" pm2 save + + # Setup PM2 startup for persistence across reboots + echo -e "${BLUE}Configuring PM2 startup for persistence...${NC}" + pm2 startup --auto >/dev/null 2>&1 || true + + # Add to registry (extract command and args from the full command) + local clean_command="$command$additional_args" + add_service_to_registry "$service_name" "pm2" "executable" "$command" "$additional_args" "" "$restart_policy" "none" "0" "$max_memory $max_restarts" "" + return 0 else echo -e "${RED}Failed to create PM2 service '$service_name'${NC}" @@ -334,8 +528,8 @@ function pm2_remove_service() { # Stop the service if it's running if pm2 describe "$service_name" >/dev/null 2>&1; then - pm2 stop "$service_name" 2>/dev/null || true - pm2 delete "$service_name" 2>/dev/null + pm2 stop "$service_name" 2>&1 || true + pm2 delete "$service_name" 2>&1 || true # Wait for PM2 to process the stop/delete command with timeout local timeout=10 @@ -357,8 +551,13 @@ function pm2_remove_service() { pm2 save echo -e "${GREEN}PM2 service '$service_name' stopped and removed${NC}" + + # Remove from registry + remove_service_from_registry "$service_name" else echo -e "${YELLOW}PM2 service '$service_name' not found or already removed${NC}" + # Still try to remove from registry in case it's orphaned + remove_service_from_registry "$service_name" fi return 0 @@ -391,6 +590,7 @@ function pm2_service_logs() { # Systemd service management functions function get_systemd_dir() { local type="$1" + if [ "$type" = "--system" ]; then echo "/etc/systemd/system" else @@ -403,17 +603,32 @@ function systemd_create_service() { local command="$2" local restart_policy="$3" local systemd_type="--user" + local bin_path="" + local gdb_enabled="0" + local server_config="" shift 3 check_systemd || return 1 - # Parse systemd type + # Parse systemd type and extract additional parameters while [[ $# -gt 0 ]]; do case "$1" in --system|--user) systemd_type="$1" shift ;; + --bin-path) + bin_path="$2" + shift 2 + ;; + --gdb-enabled) + gdb_enabled="$2" + shift 2 + ;; + --server-config) + server_config="$2" + shift 2 + ;; *) command+=" $1" shift @@ -421,6 +636,18 @@ function systemd_create_service() { esac done + # If bin_path is not provided, try to extract from command + if [ -z "$bin_path" ]; then + # Try to extract bin path from run-engine command + if [[ "$command" =~ run-engine[[:space:]]+start[[:space:]]+([^[:space:]]+) ]]; then + local binary_path="${BASH_REMATCH[1]}" + bin_path="$(dirname "$binary_path")" + else + # Fallback to current directory + bin_path="$(pwd)" + fi + fi + local systemd_dir=$(get_systemd_dir "$systemd_type") local service_file="$systemd_dir/$service_name.service" @@ -457,6 +684,11 @@ function systemd_create_service() { # Create service file echo -e "${YELLOW}Creating systemd service: $service_name${NC}" + # Ensure bin_path is absolute + if [[ ! "$bin_path" = /* ]]; then + bin_path="$(realpath "$bin_path")" + fi + if [ "$systemd_type" = "--system" ]; then # System service template (with User directive) cat > "$service_file" << EOF @@ -471,7 +703,7 @@ Restart=$restart_policy RestartSec=3 User=$(whoami) Group=$(id -gn) -WorkingDirectory=$(realpath "$bin_path") +WorkingDirectory=$bin_path StandardOutput=journal+console StandardError=journal+console @@ -490,7 +722,7 @@ Type=${service_type} ExecStart=$command Restart=$restart_policy RestartSec=3 -WorkingDirectory=$(realpath "$bin_path") +WorkingDirectory=$bin_path StandardOutput=journal+console StandardError=journal+console @@ -498,10 +730,6 @@ StandardError=journal+console WantedBy=default.target EOF fi - - if [ "$systemd_type" = "--system" ]; then - sed -i 's/WantedBy=default.target/WantedBy=multi-user.target/' "$service_file" - fi # Reload systemd and enable service if [ "$systemd_type" = "--system" ]; then @@ -512,7 +740,11 @@ EOF systemctl --user enable "$service_name.service" fi - echo -e "${GREEN}Systemd service '$service_name' created successfully${NC}" + echo -e "${GREEN}Systemd service '$service_name' created successfully with session manager '$session_manager'${NC}" + + # Add to registry + add_service_to_registry "$service_name" "systemd" "service" "$command" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config" + return 0 } @@ -572,6 +804,10 @@ function systemd_remove_service() { if [ "$removal_failed" = "true" ]; then echo -e "${YELLOW}Note: Service may still be running but configuration was removed${NC}" fi + + # Remove from registry + remove_service_from_registry "$service_name" + return 0 else echo -e "${RED}Failed to remove systemd service file '$service_file'${NC}" @@ -659,7 +895,7 @@ function create_service() { # Default values for run-engine configuration local provider="auto" - local bin_path="$BINPATH/bin" # get from config or environment + local bin_path="${BINPATH:-$ROOT_DIR/bin}" # get from config or environment local server_config="" local session_manager="none" local gdb_enabled="0" @@ -839,8 +1075,6 @@ EOF # Check if service creation was successful if [ "$service_creation_success" = "true" ]; then - # Register the service - register_service "$service_name" "$provider" "$service_type" echo -e "${GREEN}Service '$service_name' created successfully${NC}" echo -e "${BLUE}Run-engine config: $run_engine_config${NC}" @@ -880,14 +1114,20 @@ function update_service() { # Extract service information local provider=$(echo "$service_info" | jq -r '.provider') local service_type=$(echo "$service_info" | jq -r '.type') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load current configuration + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" + return 1 + fi source "$config_file" # Load current run-engine configuration if [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then source "$RUN_ENGINE_CONFIG_FILE" + else + echo -e "${YELLOW}Warning: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" fi # Parse options to update @@ -1020,11 +1260,13 @@ function delete_service() { # Extract provider and config local provider=$(echo "$service_info" | jq -r '.provider') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file if [ -f "$config_file" ]; then source "$config_file" + else + echo -e "${YELLOW}Warning: Service configuration file not found: $config_file${NC}" fi echo -e "${YELLOW}Deleting service '$service_name' (provider: $provider)...${NC}" @@ -1048,8 +1290,9 @@ function delete_service() { echo -e "${GREEN}Removed run-engine config: $RUN_ENGINE_CONFIG_FILE${NC}" fi - # Unregister service - unregister_service "$service_name" + # Remove configuration file + rm -f "$config_file" + echo -e "${GREEN}Service '$service_name' deleted successfully${NC}" else echo -e "${RED}Failed to remove service '$service_name' from $provider${NC}" @@ -1166,7 +1409,7 @@ function edit_config() { fi # Get configuration file path - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file source "$config_file" @@ -1191,7 +1434,7 @@ function attach_to_service() { # Extract provider local provider=$(echo "$service_info" | jq -r '.provider') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file if [ ! -f "$config_file" ]; then @@ -1206,6 +1449,11 @@ function attach_to_service() { echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" return 1 fi + + if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then + echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" + return 1 + fi source "$RUN_ENGINE_CONFIG_FILE" @@ -1230,6 +1478,253 @@ function attach_to_service() { fi } +######################################### +# Runtime helpers: status / send / show # +######################################### + +function service_is_running() { + local service_name="$1" + + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" >&2 + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + + if [ "$provider" = "pm2" ]; then + # pm2 jlist -> JSON array with .name and .pm2_env.status + if pm2 jlist | jq -e ".[] | select(.name==\"$service_name\" and .pm2_env.status==\"online\")" >/dev/null; then + return 0 + else + return 1 + fi + elif [ "$provider" = "systemd" ]; then + # Check user service first, then system + if systemctl --user is-active --quiet "$service_name.service" 2>/dev/null; then + return 0 + elif systemctl is-active --quiet "$service_name.service" 2>/dev/null; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + +function service_send_command() { + local service_name="$1"; shift || true + local cmd_str="$*" + if [ -z "$service_name" ] || [ -z "$cmd_str" ]; then + echo -e "${RED}Error: send requires and ${NC}" >&2 + return 1 + fi + + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" >&2 + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + local config_file="$CONFIG_DIR/$service_name.conf" + + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" >&2 + return 1 + fi + + # Load run-engine config path + # shellcheck source=/dev/null + source "$config_file" + if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then + echo -e "${RED}Error: Run-engine configuration file not found for $service_name${NC}" >&2 + return 1 + fi + + # shellcheck source=/dev/null + if ! source "$RUN_ENGINE_CONFIG_FILE"; then + echo -e "${RED}Error: Failed to source run-engine configuration file: $RUN_ENGINE_CONFIG_FILE${NC}" >&2 + return 1 + fi + + local session_manager="${SESSION_MANAGER:-auto}" + local session_name="${SESSION_NAME:-$service_name}" + + if [ "$provider" = "pm2" ]; then + # Use pm2 send (requires pm2 >= 5) + local pm2_id_json + pm2_id_json=$(pm2 id "$service_name" 2>/dev/null || true) + local numeric_id + numeric_id=$(echo "$pm2_id_json" | jq -r '.[0] // empty') + if [ -z "$numeric_id" ]; then + echo -e "${RED}Error: PM2 process '$service_name' not found${NC}" >&2 + return 1 + fi + echo -e "${YELLOW}Sending to PM2 process $service_name (ID: $numeric_id): $cmd_str${NC}" + pm2 send "$numeric_id" "$cmd_str" ENTER + return $? + fi + + # systemd provider: need a session manager to interact with the console + case "$session_manager" in + tmux|auto) + if command -v tmux >/dev/null 2>&1 && tmux has-session -t "$session_name" 2>/dev/null; then + echo -e "${YELLOW}Sending to tmux session $session_name: $cmd_str${NC}" + tmux send-keys -t "$session_name" "$cmd_str" C-m + return $? + elif [ "$session_manager" = "tmux" ]; then + echo -e "${RED}Error: tmux session '$session_name' not available${NC}" >&2 + return 1 + fi + ;;& + screen|auto) + if command -v screen >/dev/null 2>&1; then + echo -e "${YELLOW}Sending to screen session $session_name: $cmd_str${NC}" + screen -S "$session_name" -X stuff "$cmd_str\n" + return $? + elif [ "$session_manager" = "screen" ]; then + echo -e "${RED}Error: screen not installed${NC}" >&2 + return 1 + fi + ;; + none|*) + echo -e "${RED}Error: No session manager configured (SESSION_MANAGER=$session_manager). Cannot send command.${NC}" >&2 + return 1 + ;; + esac + + echo -e "${RED}Error: Unable to find usable session (tmux/screen) to send command.${NC}" >&2 + return 1 +} + +function show_config() { + local service_name="$1" + if [ -z "$service_name" ]; then + echo -e "${RED}Error: Service name required for show-config${NC}" + return 1 + fi + + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + local cfg_file="$CONFIG_DIR/$service_name.conf" + echo -e "${BLUE}Service: $service_name${NC}" + echo "Provider: $provider" + echo "Config file: $cfg_file" + if [ -f "$cfg_file" ]; then + # shellcheck source=/dev/null + source "$cfg_file" + echo "RUN_ENGINE_CONFIG_FILE: ${RUN_ENGINE_CONFIG_FILE:-}" + if [ -n "${RUN_ENGINE_CONFIG_FILE:-}" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then + # shellcheck source=/dev/null + source "$RUN_ENGINE_CONFIG_FILE" + echo "Session manager: ${SESSION_MANAGER:-}" + echo "Session name: ${SESSION_NAME:-}" + echo "BINPATH: ${BINPATH:-}" + echo "SERVERBIN: ${SERVERBIN:-}" + echo "CONFIG: ${CONFIG:-}" + echo "RESTART_POLICY: ${RESTART_POLICY:-}" + fi + else + echo "Config file not found" + fi +} + +# Return uptime in seconds for a service (echo integer), non-zero exit if not running +function service_uptime_seconds() { + local service_name="$1" + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" >&2 + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + + if [ "$provider" = "pm2" ]; then + check_pm2 || return 1 + local info_json + info_json=$(pm2 jlist 2>/dev/null) + local pm_uptime_ms + pm_uptime_ms=$(echo "$info_json" | jq -r ".[] | select(.name==\"$service_name\").pm2_env.pm_uptime // empty") + local status + status=$(echo "$info_json" | jq -r ".[] | select(.name==\"$service_name\").pm2_env.status // empty") + if [ -z "$pm_uptime_ms" ] || [ "$status" != "online" ]; then + return 1 + fi + # Current time in ms (fallback to seconds*1000 if %N unsupported) + local now_ms + if date +%s%N >/dev/null 2>&1; then + now_ms=$(( $(date +%s%N) / 1000000 )) + else + now_ms=$(( $(date +%s) * 1000 )) + fi + local diff_ms=$(( now_ms - pm_uptime_ms )) + [ "$diff_ms" -lt 0 ] && diff_ms=0 + echo $(( diff_ms / 1000 )) + return 0 + elif [ "$provider" = "systemd" ]; then + check_systemd || return 1 + local systemd_type="--user" + [ -f "/etc/systemd/system/$service_name.service" ] && systemd_type="--system" + + # Get ActiveEnterTimestampMonotonic in usec and ActiveState + local prop + if [ "$systemd_type" = "--system" ]; then + prop=$(systemctl show "$service_name.service" --property=ActiveEnterTimestampMonotonic,ActiveState 2>/dev/null) + else + prop=$(systemctl --user show "$service_name.service" --property=ActiveEnterTimestampMonotonic,ActiveState 2>/dev/null) + fi + local state + state=$(echo "$prop" | awk -F= '/^ActiveState=/{print $2}') + [ "$state" != "active" ] && return 1 + local enter_us + enter_us=$(echo "$prop" | awk -F= '/^ActiveEnterTimestampMonotonic=/{print $2}') + # Current monotonic time in seconds since boot + local now_s + now_s=$(cut -d' ' -f1 /proc/uptime) + # Compute uptime = now_monotonic - enter_monotonic + # enter_us may be empty on some systems; fallback to 0 + enter_us=${enter_us:-0} + # Convert now_s to microseconds using awk for precision, then compute diff + local diff_s + diff_s=$(awk -v now="$now_s" -v enter="$enter_us" 'BEGIN{printf "%d", (now*1000000 - enter)/1000000}') + [ "$diff_s" -lt 0 ] && diff_s=0 + echo "$diff_s" + return 0 + fi + + return 1 +} + +# Wait until service has at least uptime. Optional timeout seconds (default 120) +function wait_service_uptime() { + local service_name="$1" + local min_seconds="$2" + local timeout="${3:-120}" + local waited=0 + + while [ "$waited" -le "$timeout" ]; do + if secs=$(service_uptime_seconds "$service_name" 2>/dev/null); then + if [ "$secs" -ge "$min_seconds" ]; then + echo -e "${GREEN}Service '$service_name' has reached ${secs}s uptime (required: ${min_seconds}s)${NC}" + return 0 + fi + fi + sleep 1 + waited=$((waited + 1)) + done + echo -e "${RED}Timeout: $service_name did not reach ${min_seconds}s uptime within ${timeout}s${NC}" >&2 + return 1 +} + function attach_pm2_process() { local service_name="$1" @@ -1264,9 +1759,22 @@ function attach_interactive_shell() { # For systemd without session manager, show helpful message local service_info=$(get_service_info "$service_name") - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" + + # Check if config file exists before sourcing + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" + return 1 + fi source "$config_file" + + # Check if RUN_ENGINE_CONFIG_FILE exists before sourcing + if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then + echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" + return 1 + fi + source "$RUN_ENGINE_CONFIG_FILE" echo -e "${RED}Error: Cannot attach to systemd service '$service_name'${NC}" @@ -1373,7 +1881,10 @@ case "${1:-help}" in delete_service "$2" ;; list) - list_services "$2" + list_services "${2:-}" + ;; + restore) + restore_missing_services ;; start|stop|restart|status) if [ $# -lt 2 ]; then @@ -1411,6 +1922,52 @@ case "${1:-help}" in fi attach_to_service "$2" ;; + uptime-seconds) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for uptime-seconds command${NC}" + print_help + exit 1 + fi + service_uptime_seconds "$2" + ;; + wait-uptime) + if [ $# -lt 3 ]; then + echo -e "${RED}Error: Usage: $0 wait-uptime [timeout]${NC}" + print_help + exit 1 + fi + wait_service_uptime "$2" "$3" "${4:-120}" + ;; + is-running) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for is-running command${NC}" + print_help + exit 1 + fi + if service_is_running "$2"; then + echo -e "${GREEN}Service '$2' is running${NC}" + exit 0 + else + echo -e "${YELLOW}Service '$2' is not running${NC}" + exit 1 + fi + ;; + send) + if [ $# -lt 3 ]; then + echo -e "${RED}Error: Not enough arguments for send command${NC}" + print_help + exit 1 + fi + service_send_command "$2" "${@:3}" + ;; + show-config) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for show-config command${NC}" + print_help + exit 1 + fi + show_config "$2" + ;; help|--help|-h) print_help ;; diff --git a/apps/startup-scripts/test/test_startup_scripts.bats b/apps/startup-scripts/test/test_startup_scripts.bats index 2bbca2ffd..119a8c80c 100644 --- a/apps/startup-scripts/test/test_startup_scripts.bats +++ b/apps/startup-scripts/test/test_startup_scripts.bats @@ -143,6 +143,130 @@ teardown() { [[ "$output" =~ "on-failure|always" ]] } +@test "service-manager: help lists health and console commands" { + run "$SCRIPT_DIR/service-manager.sh" help + [ "$status" -eq 0 ] + [[ "$output" =~ "is-running " ]] + [[ "$output" =~ "uptime-seconds " ]] + [[ "$output" =~ "wait-uptime " ]] + [[ "$output" =~ "send " ]] + [[ "$output" =~ "show-config " ]] +} + +@test "service-manager: pm2 uptime and wait-uptime work with mocked pm2" { + 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" + # 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"} +] +EOF + # Create minimal service config and run-engine config files required by 'send' + echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf" + cat > "$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf" << 'EOF' +export SESSION_MANAGER="none" +export SESSION_NAME="test-world" +EOF + # Mock pm2 + cat > "$TEST_DIR/bin/pm2" << 'EOF' +#!/usr/bin/env bash +case "$1" in + jlist) + # Produce a JSON with uptime ~20 seconds + if date +%s%N >/dev/null 2>&1; then + nowms=$(( $(date +%s%N) / 1000000 )) + else + nowms=$(( $(date +%s) * 1000 )) + fi + up=$(( nowms - 20000 )) + echo "[{\"name\":\"test-world\",\"pm2_env\":{\"status\":\"online\",\"pm_uptime\":$up}}]" + ;; + id) + echo "[1]" + ;; + attach|send|list|describe|logs) + exit 0 + ;; + *) + exit 0 + ;; +esac +EOF + chmod +x "$TEST_DIR/bin/pm2" + + run "$SCRIPT_DIR/service-manager.sh" uptime-seconds test-world + debug_on_failure + [ "$status" -eq 0 ] + # Output should be a number >= 10 + [[ "$output" =~ ^[0-9]+$ ]] + [ "$output" -ge 10 ] + + run "$SCRIPT_DIR/service-manager.sh" wait-uptime test-world 10 5 + debug_on_failure + [ "$status" -eq 0 ] +} + +@test "service-manager: send works under pm2 with mocked pm2" { + 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" + # 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"} +] +EOF + echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf" + cat > "$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf" << 'EOF' +export SESSION_MANAGER="none" +export SESSION_NAME="test-world" +EOF + # pm2 mock + cat > "$TEST_DIR/bin/pm2" << 'EOF' +#!/usr/bin/env bash +case "$1" in + jlist) + if date +%s%N >/dev/null 2>&1; then + nowms=$(( $(date +%s%N) / 1000000 )) + else + nowms=$(( $(date +%s) * 1000 )) + fi + up=$(( nowms - 15000 )) + echo "[{\"name\":\"test-world\",\"pm2_env\":{\"status\":\"online\",\"pm_uptime\":$up}}]" + ;; + id) + echo "[1]" + ;; + send) + # simulate success + exit 0 + ;; + attach|list|describe|logs) + exit 0 + ;; + *) + exit 0 + ;; +esac +EOF + chmod +x "$TEST_DIR/bin/pm2" + + run "$SCRIPT_DIR/service-manager.sh" send test-world "server info" + debug_on_failure + [ "$status" -eq 0 ] +} + +@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" + mkdir -p "$AC_SERVICE_CONFIG_DIR" + echo "[]" > "$AC_SERVICE_CONFIG_DIR/service_registry.json" + run "$SCRIPT_DIR/service-manager.sh" wait-uptime unknown 2 1 + [ "$status" -ne 0 ] +} + # ===== EXAMPLE SCRIPTS TESTS ===== @test "examples: restarter-world should show configuration error" { diff --git a/conf/dist/config.sh b/conf/dist/config.sh index df7ddd3fc..1f956ca70 100644 --- a/conf/dist/config.sh +++ b/conf/dist/config.sh @@ -149,4 +149,27 @@ export CPUPROFILESIGNAL=${CPUPROFILESIGNAL:-12} # Other values for HEAPCHECK: minimal, normal (equivalent to "1"), strict, draconian #export HEAPCHECK=${HEAPCHECK:-normal} +############################################## +# +# MODULES LIST FILE (for installer `module` commands) +# +# Path to the file where the installer records installed modules +# with their branch and commit. You can override this path by +# setting the MODULES_LIST_FILE inside your config.sh or as an environment variable. +# By default it points inside the repository conf folder. +# Format of each line: +# +# Lines starting with '#' and empty lines are ignored. +export MODULES_LIST_FILE=${MODULES_LIST_FILE:-"$AC_PATH_ROOT/conf/modules.list"} + +# Space/newline separated list of modules to exclude when using +# 'module install --all' and 'module update --all'. Items can be specified +# as simple names (e.g., mod-transmog), owner/name, or full URLs. +# Example: +# export MODULES_EXCLUDE_LIST="azerothcore/mod-transmog azerothcore/mod-autobalance" +export MODULES_EXCLUDE_LIST="" + +NO_COLOR=${NO_COLOR:-} +FORCE_COLOR=${FORCE_COLOR:-} + diff --git a/data/sql/updates/db_characters/2025_09_03_00.sql b/data/sql/updates/db_characters/2025_09_03_00.sql new file mode 100644 index 000000000..c9ae6307d --- /dev/null +++ b/data/sql/updates/db_characters/2025_09_03_00.sql @@ -0,0 +1,15 @@ +-- DB update 2025_07_29_00 -> 2025_09_03_00 +-- Add petition_id column to petition table +ALTER TABLE `petition` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`; +-- Populate petition_id based on petitionguid +UPDATE `petition` SET `petition_id` = CASE WHEN `petitionguid` <= 2147483647 THEN `petitionguid` ELSE `petitionguid` - 2147483648 END WHERE `petition_id` = 0; +-- Add index on petition_id +ALTER TABLE `petition` ADD INDEX `idx_petition_id` (`petition_id`); +-- Add petition_id column to petition_sign table +ALTER TABLE `petition_sign` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`; +-- Populate petition_id in petition_sign from petition table +UPDATE `petition_sign` AS `ps` JOIN `petition` AS `p` ON `p`.`petitionguid` = `ps`.`petitionguid` SET `ps`.`petition_id` = `p`.`petition_id` WHERE `ps`.`petition_id` = 0; +-- Add index on petition_id and playerguid in petition_sign +ALTER TABLE `petition_sign` ADD INDEX `idx_petition_id_player` (`petition_id`, `playerguid`); +-- Update enchantments in item_instance with petition_id prefix +UPDATE `item_instance` AS `ii` JOIN `petition` AS `p` ON `p`.`petitionguid` = `ii`.`guid` SET `ii`.`enchantments` = CONCAT(`p`.`petition_id`, SUBSTRING(`ii`.`enchantments`, LOCATE(' ', `ii`.`enchantments`))) WHERE `ii`.`enchantments` IS NOT NULL AND `ii`.`enchantments` <> ''; diff --git a/data/sql/updates/db_world/2025_08_10_00.sql b/data/sql/updates/db_world/2025_08_10_00.sql new file mode 100644 index 000000000..cbbdd0811 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_10_00.sql @@ -0,0 +1,4 @@ +-- DB update 2025_08_08_01 -> 2025_08_10_00 + +-- Remove Creature_addon tables from some Gargoyles. +DELETE FROM `creature_addon` WHERE (`guid` IN (100016, 100017, 100018, 100032, 100033, 100034, 100035, 100056, 100057, 100058, 100059, 100060, 100061)); diff --git a/data/sql/updates/db_world/2025_08_10_01.sql b/data/sql/updates/db_world/2025_08_10_01.sql new file mode 100644 index 000000000..0629204ae --- /dev/null +++ b/data/sql/updates/db_world/2025_08_10_01.sql @@ -0,0 +1,5 @@ +-- DB update 2025_08_10_00 -> 2025_08_10_01 +-- Delete Nerubian Chitin, Borean Leather, and Arctic Fur from loot table from various creatures in WotLK +DELETE from `creature_loot_template` WHERE `item` = 33568; +DELETE from `creature_loot_template` WHERE `item` = 44128; +DELETE from `creature_loot_template` WHERE `item` = 38558; diff --git a/data/sql/updates/db_world/2025_08_12_00.sql b/data/sql/updates/db_world/2025_08_12_00.sql new file mode 100644 index 000000000..c25388c09 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_12_00.sql @@ -0,0 +1,42 @@ +-- DB update 2025_08_10_01 -> 2025_08_12_00 + +-- Set SmartAI (Wreckage A, B, C) +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE (`entry`IN (188087, 188088, 188089)); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 1) AND (`entryorguid` IN (188087, 188088, 188089)); +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 +(188087, 1, 0, 0, 38, 0, 100, 0, 0, 1, 0, 0, 0, 0, 41, 0, 60, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wreckage A - On Data Set 0 1 - Despawn Instant'), +(188088, 1, 0, 0, 38, 0, 100, 0, 0, 1, 0, 0, 0, 0, 41, 0, 60, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wreckage B - On Data Set 0 1 - Despawn Instant'), +(188089, 1, 0, 0, 38, 0, 100, 0, 0, 1, 0, 0, 0, 0, 41, 0, 60, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wreckage C - On Data Set 0 1 - Despawn Instant'); + +-- Set SmartAI (Fezzix Geartwist) +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 25849; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 25849); +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 +(25849, 0, 0, 0, 25, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Reset - Set Event Phase 1'), +(25849, 0, 1, 2, 20, 1, 100, 0, 11894, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Quest \'Patching Up\' Finished - Say Line 0 (Phase 1)'), +(25849, 0, 2, 0, 61, 1, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2584900, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Quest \'Patching Up\' Finished - Run Script (Phase 1)'), +(25849, 0, 3, 0, 40, 2, 100, 512, 11, 25849, 0, 0, 0, 0, 80, 2584901, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Point 11 of Path 25849 Reached - Run Script (Phase 2)'), +(25849, 0, 4, 5, 40, 2, 100, 512, 12, 25849, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Point 12 of Path 25849 Reached - Set Event Phase 1 (Phase 2)'), +(25849, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4.06662, 'Fezzix Geartwist - On Point 12 of Path 25849 Reached - Set Orientation 4.06662 (Phase 2)'); + +-- Set Timed Actionlist (Fezzix Geartwist) +DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (2584900, 2584901)); +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 +(2584900, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Event Phase 2'), +(2584900, 9, 1, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 12, 26040, 1, 13000, 0, 0, 0, 8, 0, 0, 0, 0, 3481.33, 4099.85, 17.839, 3.35103, 'Fezzix Geartwist - Actionlist - Summon Creature \'Fezzix\'s Flying Machine\''), +(2584900, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 14, 60069, 188087, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Data 0 1'), +(2584900, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 14, 60080, 188088, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Data 0 1'), +(2584900, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 14, 60095, 188089, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Data 0 1'), +(2584900, 9, 5, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 1'), +(2584900, 9, 6, 0, 0, 0, 100, 0, 9000, 9000, 0, 0, 0, 0, 43, 0, 22719, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Mount To Model 22719'), +(2584900, 9, 7, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 2'), +(2584900, 9, 8, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Fly On'), +(2584900, 9, 9, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 53, 0, 25849, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Start Waypoint Path 25849'), +(2584901, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 46419, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Cast \'Cosmetic - Explosion\''), +(2584901, 9, 1, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Dismount'), +(2584901, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Fly Off'), +(2584901, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 42963, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Cast \'Cosmetic - Combat Knockdown Self\''), +(2584901, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 3'), +(2584901, 9, 5, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 4'); diff --git a/data/sql/updates/db_world/2025_08_12_01.sql b/data/sql/updates/db_world/2025_08_12_01.sql new file mode 100644 index 000000000..0f712fbf3 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_12_01.sql @@ -0,0 +1,6 @@ +-- DB update 2025_08_12_00 -> 2025_08_12_01 +-- Anub'ar Guardian - Sunder Armor, Sunder Armor(H) +DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (53618, 59350); +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES +(53618, 4194304), +(59350, 4194304); diff --git a/data/sql/updates/db_world/2025_08_13_00.sql b/data/sql/updates/db_world/2025_08_13_00.sql new file mode 100644 index 000000000..719b7b844 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_13_00.sql @@ -0,0 +1,8 @@ +-- DB update 2025_08_12_01 -> 2025_08_13_00 +-- Anub'ar Venomancer - Poison Bolt +DELETE FROM `spell_custom_attr` WHERE `spell_id` = 53617; +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES (53617, 4194304); + +-- Anub'ar Venomancer - Poison Bolt(H) +DELETE FROM `spell_custom_attr` WHERE `spell_id` = 59359; +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES (59359, 4194304); diff --git a/data/sql/updates/db_world/2025_08_14_00.sql b/data/sql/updates/db_world/2025_08_14_00.sql new file mode 100644 index 000000000..0323cc323 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_14_00.sql @@ -0,0 +1,10 @@ +-- DB update 2025_08_13_00 -> 2025_08_14_00 +-- Allow Anub'ar warrior to use Strike ability. (Set the event_type 0 to "incombat update" instead of "out of combat update") +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28732; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28732) 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 +(28732, 0, 0, 0, 0, 0, 100, 0, 2000, 5000, 6000, 8000, 0, 0, 11, 52532, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "Anub'ar Warrior - In Combat - Cast Strike"); + +-- Fix misplaced spell IDs in spelldifficulty for Skittering Infector's Acid Splash +UPDATE `spelldifficulty_dbc` SET `DifficultySpellID_1` = 52446 WHERE `ID` = 59363; +UPDATE `spelldifficulty_dbc` SET `DifficultySpellID_2` = 59363 WHERE `ID` = 59363; diff --git a/data/sql/updates/db_world/2025_08_18_00.sql b/data/sql/updates/db_world/2025_08_18_00.sql new file mode 100644 index 000000000..02662cf65 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_18_00.sql @@ -0,0 +1,66 @@ +-- DB update 2025_08_14_00 -> 2025_08_18_00 +SET @CGUID := 82950; + +DELETE FROM `creature` WHERE `id1` IN (25090, 25091, 25092); +DELETE FROM `creature` WHERE `id1` IN (25090, 25091, 25092) AND `guid` BETWEEN @CGUID+0 AND @CGUID+57; +INSERT INTO `creature` (`guid`, `id1`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `VerifiedBuild`, `CreateObject`) VALUES +(@CGUID+0 , 25090, 530, 0, 0, 1, 1, 0, 13196.1201171875, -7049.33642578125, 16.22812080383300781, 0.855211317539215087, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+1 , 25090, 530, 0, 0, 1, 1, 0, 13210.173828125 , -7052.376953125 , 16.07102394104003906, 4.572762489318847656, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+2 , 25090, 530, 0, 0, 1, 1, 0, 13202.560546875 , -7051.39697265625, 16.39847373962402343, 4.188790321350097656, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+3 , 25090, 530, 0, 0, 1, 1, 0, 13201.0830078125, -7048.72509765625, 13.21125602722167968, 0.191986218094825744, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+4 , 25090, 530, 0, 0, 1, 1, 0, 13199.25 , -7050.6953125 , 14.45721721649169921, 3.700098037719726562, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+5 , 25090, 530, 0, 0, 1, 1, 0, 13207.0126953125, -7053.20068359375, 15.47437477111816406, 2.740166902542114257, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+6 , 25090, 530, 0, 0, 1, 1, 0, 13246.345703125 , -7053.97412109375, 20.62376213073730468, 4.904375076293945312, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+7 , 25090, 530, 0, 0, 1, 1, 0, 13242.1611328125, -7054.7880859375 , 17.20347023010253906, 4.764749050140380859, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+8 , 25090, 530, 0, 0, 1, 1, 0, 13240.6982421875, -7053.22998046875, 14.11119270324707031, 4.97418832778930664 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+9 , 25090, 530, 0, 0, 1, 1, 0, 13235.609375 , -7053.92626953125, 15.19749736785888671, 5.934119224548339843, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+10, 25090, 530, 0, 0, 1, 1, 0, 13247.880859375 , -7055.54150390625, 18.45570755004882812, 0.575958669185638427, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+11, 25090, 530, 0, 0, 1, 1, 0, 13212.2255859375, -7054.658203125 , 17.02962112426757812, 6.108652114868164062, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+12, 25090, 530, 0, 0, 1, 1, 0, 13237.314453125 , -7053.35498046875, 18.92669677734375 , 3.351032257080078125, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+13, 25090, 530, 0, 0, 1, 1, 0, 13253.2548828125, -7054.8837890625 , 16.24456024169921875, 0.069813169538974761, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+14, 25090, 530, 0, 0, 1, 1, 0, 13274.5966796875, -7057.69384765625, 24.88401985168457031, 0.244346097111701965, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+15, 25090, 530, 0, 0, 1, 1, 0, 13264.31640625 , -7057.705078125 , 24.02816200256347656, 1.570796370506286621, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+16, 25090, 530, 0, 0, 1, 1, 0, 13261.1611328125, -7055.92529296875, 26.55978202819824218, 3.298672199249267578, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+17, 25090, 530, 0, 0, 1, 1, 0, 13255.951171875 , -7056.603515625 , 19.514129638671875 , 1.134464025497436523, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+18, 25090, 530, 0, 0, 1, 1, 0, 13262.6103515625, -7056.1162109375 , 22.68890190124511718, 4.520402908325195312, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+19, 25090, 530, 0, 0, 1, 1, 0, 13260.7216796875, -7056.51025390625, 24.51448440551757812, 5.899212837219238281, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), + +(@CGUID+20, 25091, 530, 0, 0, 1, 1, 0, 13330.6298828125, -6993.73974609375, 18.55262374877929687, 0.453785598278045654, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+21, 25091, 530, 0, 0, 1, 1, 0, 13329.9267578125, -6994.26416015625, 15.69489192962646484, 0.279252678155899047, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+22, 25091, 530, 0, 0, 1, 1, 0, 13317.7392578125, -6990.34716796875, 17.51109886169433593, 0.03490658476948738 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+23, 25091, 530, 0, 0, 1, 1, 0, 13317.330078125 , -6988.69384765625, 15.31146907806396484, 1.413716673851013183, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+24, 25091, 530, 0, 0, 1, 1, 0, 13325.728515625 , -6992.54296875 , 17.86301040649414062, 0.314159274101257324, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+25, 25091, 530, 0, 0, 1, 1, 0, 13321.5322265625, -6991.05859375 , 18.0410003662109375 , 3.473205089569091796, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+26, 25091, 530, 0, 0, 1, 1, 0, 13312.7392578125, -6989.04150390625, 16.80069160461425781, 3.03687286376953125 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+27, 25091, 530, 0, 0, 1, 1, 0, 13326.6171875 , -6991.60400390625, 15.73497295379638671, 3.874630928039550781, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+28, 25091, 530, 0, 0, 1, 1, 0, 13321.43359375 , -6992.02294921875, 15.16357707977294921, 1.850049018859863281, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+29, 25091, 530, 0, 0, 1, 1, 0, 13315.4501953125, -6990.5986328125 , 14.17850494384765625, 5.323254108428955078, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+30, 25091, 530, 0, 0, 1, 1, 0, 13351.2919921875, -6989.8095703125 , 14.9304962158203125 , 5.619960308074951171, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+31, 25091, 530, 0, 0, 1, 1, 0, 13348.5439453125, -6990.99853515625, 17.81970596313476562, 3.525565147399902343, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+32, 25091, 530, 0, 0, 1, 1, 0, 13359.8720703125, -6990.33447265625, 11.86795330047607421, 3.892084121704101562, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+33, 25091, 530, 0, 0, 1, 1, 0, 13363.31640625 , -6990.54541015625, 17.51730155944824218, 5.044001579284667968, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+34, 25091, 530, 0, 0, 1, 1, 0, 13357.7421875 , -6991.56103515625, 18.46755599975585937, 4.729842185974121093, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+35, 25091, 530, 0, 0, 1, 1, 0, 13361.9384765625, -6990.98291015625, 21.16696739196777343, 1.308996915817260742, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+36, 25091, 530, 0, 0, 1, 1, 0, 13357.88671875 , -6991.5693359375 , 15.036224365234375 , 1.850049018859863281, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+37, 25091, 530, 0, 0, 1, 1, 0, 13364.1953125 , -6991.95556640625, 18.6686553955078125 , 4.293509960174560546, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+38, 25091, 530, 0, 0, 1, 1, 0, 13374.556640625 , -6992.58837890625, 20.41219139099121093, 0.872664630413055419, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+39, 25091, 530, 0, 0, 1, 1, 0, 13372.1640625 , -6991.0869140625 , 22.58947563171386718, 4.904375076293945312, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+40, 25091, 530, 0, 0, 1, 1, 0, 13367.83203125 , -6992.177734375 , 11.62636184692382812, 2.530727386474609375, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+41, 25091, 530, 0, 0, 1, 1, 0, 13374.2861328125, -6991.216796875 , 18.20113945007324218, 5.026548385620117187, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+42, 25091, 530, 0, 0, 1, 1, 0, 13367.4873046875, -6992.15625 , 15.75841045379638671, 4.328416347503662109, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), + +(@CGUID+43, 25092, 530, 0, 0, 1, 1, 0, 13276.2861328125, -7148.3115234375 , 18.78717231750488281, 5.25344085693359375 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+44, 25092, 530, 0, 0, 1, 1, 0, 13267.578125 , -7146.2333984375 , 17.49614906311035156, 3.089232683181762695, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+45, 25092, 530, 0, 0, 1, 1, 0, 13273.84375 , -7146.33349609375, 11.37590885162353515, 0.314159274101257324, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+46, 25092, 530, 0, 0, 1, 1, 0, 13332.458984375 , -7149.9892578125 , 25.62369537353515625, 3.455751895904541015, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+47, 25092, 530, 0, 0, 1, 1, 0, 13324.3798828125, -7148.763671875 , 12.40258979797363281, 5.393067359924316406, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+48, 25092, 530, 0, 0, 1, 1, 0, 13283.3359375 , -7150.99072265625, 16.36432838439941406, 2.49582076072692871 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+49, 25092, 530, 0, 0, 1, 1, 0, 13306.396484375 , -7148.45556640625, 19.448272705078125 , 6.126105785369873046, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+50, 25092, 530, 0, 0, 1, 1, 0, 13323.8916015625, -7149.33056640625, 23.59075736999511718, 0.331612557172775268, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+51, 25092, 530, 0, 0, 1, 1, 0, 13314.80859375 , -7148.80078125 , 21.43866920471191406, 4.834561824798583984, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+52, 25092, 530, 0, 0, 1, 1, 0, 13308.7783203125, -7147.53515625 , 14.74446582794189453, 2.652900457382202148, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+53, 25092, 530, 0, 0, 1, 1, 0, 13336.470703125 , -7149.71533203125, 24.01339530944824218, 0.942477762699127197, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+54, 25092, 530, 0, 0, 1, 1, 0, 13279.6572265625, -7149.91162109375, 16.28713226318359375, 4.747295379638671875, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+55, 25092, 530, 0, 0, 1, 1, 0, 13285.5986328125, -7150.7265625 , 20.10992622375488281, 5.113814830780029296, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+56, 25092, 530, 0, 0, 1, 1, 0, 13315.0751953125, -7149.388671875 , 15.76729774475097656, 4.066617012023925781, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+57, 25092, 530, 0, 0, 1, 1, 0, 13323.4755859375, -7150.04931640625, 19.51647567749023437, 2.251474618911743164, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1); diff --git a/data/sql/updates/db_world/2025_08_18_01.sql b/data/sql/updates/db_world/2025_08_18_01.sql new file mode 100644 index 000000000..2a0dbeedb --- /dev/null +++ b/data/sql/updates/db_world/2025_08_18_01.sql @@ -0,0 +1,4 @@ +-- DB update 2025_08_18_00 -> 2025_08_18_01 +DELETE FROM `spell_script_names` WHERE `spell_id` = 45848; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(45848, 'spell_kiljaeden_shield_of_the_blue'); diff --git a/data/sql/updates/db_world/2025_08_19_00.sql b/data/sql/updates/db_world/2025_08_19_00.sql new file mode 100644 index 000000000..a3589d86d --- /dev/null +++ b/data/sql/updates/db_world/2025_08_19_00.sql @@ -0,0 +1,66 @@ +-- DB update 2025_08_18_01 -> 2025_08_19_00 + +-- Remove Unit Flags from Roanauk Icemist (IMMUNE_TO_PC, IMMUNE_TO_NPC) +UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~(256|512) WHERE (`entry` = 26654); + +-- Remove Unit Flags from Icemist Warriors (IMMUNE_TO_PC, IMMUNE_TO_NPC, STUNNED) +UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~(256|512|262144) WHERE (`entry` = 26772); + +-- Update SmartAI (Roanauk Icemist) +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (26654, 26772)); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 26654); +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 +(26654, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Set Flags Immune To Players & Immune To NPC\'s'), +(26654, 0, 1, 2, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 11, 26656, 10, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Set Data 1 1'), +(26654, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Set Event Phase 1'), +(26654, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 47273, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Cast \'Icemist`s Prison\''), +(26654, 0, 4, 0, 1, 1, 100, 0, 5000, 30000, 120000, 150000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Out of Combat - Say Line 0 (Phase 1)'), +(26654, 0, 5, 0, 38, 0, 100, 513, 1, 1, 0, 0, 0, 0, 80, 2665400, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Data Set 1 1 - Run Script (No Repeat)'), +(26654, 0, 6, 0, 40, 0, 100, 512, 1, 0, 0, 0, 0, 0, 80, 2665401, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Point 1 of Path Any Reached - Run Script'), +(26654, 0, 7, 0, 38, 0, 100, 512, 2, 2, 0, 0, 0, 0, 80, 2665402, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Data Set 2 2 - Run Script'); + +-- Update Action List (Roanauk Icemist) +DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (2665400, 2665401)); +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 +(2665400, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 28, 47273, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Remove Aura \'Icemist`s Prison\''), +(2665400, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 2, 2, 0, 0, 0, 0, 9, 26656, 0, 200, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Data 2 2'), +(2665400, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Event Phase 2'), +(2665400, 9, 3, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 1'), +(2665400, 9, 4, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 53, 0, 26654, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Start Waypoint Path 26654'), +(2665401, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 2'), +(2665401, 9, 1, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 3'), +(2665401, 9, 2, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 19, 26608, 100, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 1'), +(2665401, 9, 3, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 11, 47378, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Cast \'Glory of the Ancestors\''), +(2665401, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 4'), +(2665401, 9, 5, 0, 0, 0, 100, 0, 7000, 7000, 0, 0, 0, 0, 19, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Remove Flags Immune To Players & Immune To NPC\'s'), +(2665401, 9, 6, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 11, 47379, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Cast \'Icemist`s Blessing\''), +(2665401, 9, 7, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 5'), +(2665401, 9, 8, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 6'), +(2665401, 9, 9, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 19, 26608, 100, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 2'), +(2665401, 9, 10, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 7'), +(2665401, 9, 11, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 19, 26608, 100, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Data 1 1'), +(2665401, 9, 12, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Home Position'), +(2665401, 9, 13, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 19, 26608, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Start Attacking'); + +-- Update SmartAI (Icemist Warriors) +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 26772); +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 +(26772, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 29266, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Respawn - Cast \'Permanent Feign Death\''), +(26772, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 262912, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Respawn - Set Flags Immune To Players & Immune To NPC\'s & Stunned'), +(26772, 0, 2, 3, 8, 0, 100, 512, 47378, 0, 0, 0, 0, 0, 28, 29266, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Spellhit \'Glory of the Ancestors\' - Remove Aura \'Permanent Feign Death\''), +(26772, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2677200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Spellhit \'Glory of the Ancestors\' - Run Script'), +(26772, 0, 4, 0, 38, 0, 100, 512, 1, 1, 0, 0, 0, 0, 41, 5000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Data Set 1 1 - Despawn In 5000 ms'); + +-- Add Action List (Icemist Warriors) +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2677200); +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 +(2677200, 9, 0, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 12, 26676, 6, 6000, 0, 0, 0, 202, 5, 1, 1, 0, 0, 0, 0, 0, 'Icemist Warrior - Actionlist - Summon Creature \'Anub\'ar Invader\''), +(2677200, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 262912, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - Actionlist - Remove Flags Immune To Players & Immune To NPC\'s & Stunned'); + +-- Set Conditions for Icemist's Blessing +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 47379; +INSERT INTO `conditions`(`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13,3,47379,0,0,31,0,3,26654,0,0,0,0,'','Icemist\'s Blessing (47379) - target is Roanauk Icemist'), +(13,3,47379,0,1,31,0,3,26772,0,0,0,0,'','Icemist\'s Blessing (47379) - target is Icemist Warrior'), +(13,3,47379,0,2,9,0,12069,0,0,0,0,0,'','Icemist\'s Blessing (47379) - player has quest 12069 active'); diff --git a/data/sql/updates/db_world/2025_08_23_00.sql b/data/sql/updates/db_world/2025_08_23_00.sql new file mode 100644 index 000000000..064b7cb64 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_23_00.sql @@ -0,0 +1,84 @@ +-- DB update 2025_08_19_00 -> 2025_08_23_00 +SET @GUID = 83113; + +DELETE FROM `creature` WHERE `id1` IN (3296, 5595, 15383, 15431, 15432, 15434, 15437, 15445, 15446, 15448, 15450, 15451, 15452, 15453, 15455, 15456, 15457, 15458, 15459, 15460, 15469, 15477, 15508, 15512, 15515, 15522, 15525, 15528, 15529, 15532, 15533, 15534, 15535, 15539, 15663, 15696, 15700, 15701, 15702, 15703, 15704, 15707, 15708, 15709, 15731, 15733, 15734, 15735, 15736, 15737, 15738, 15739, 15761, 15762, 15763, 15764, 15765, 15766, 15767, 15768) AND `guid` BETWEEN @GUID AND @GUID+69; +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +-- Ironforge +(@GUID+0, 15731, 0, 0, 0, 0, 0, 1, 1, 0, -4935.1743, -1197.6975, 501.62204, 2.460914134979248046, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Darnassus Commendation Officer +(@GUID+1, 15733, 0, 0, 0, 0, 0, 1, 1, 0, -4952.5264, -1176.9742, 501.63916, 5.393067359924316406, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Gnomeregan Commendation Officer +(@GUID+2, 15734, 0, 0, 0, 0, 0, 1, 1, 0, -4975.3374, -1196.7572, 501.74588, 1.884955525398254394, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Ironforge Commendation Officer +(@GUID+3, 15735, 0, 0, 0, 0, 0, 1, 1, 0, -4934.9854, -1214.3094, 501.7179, 3.333578824996948242, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Stormwind Commendation Officer +(@GUID+4, 5595, 0, 0, 0, 0, 0, 1, 1, 0, -4980.02, -1219.984, 501.75632, 3.822271108627319335, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Ironforge Guard +(@GUID+5, 15539, 0, 0, 0, 0, 0, 1, 1, 0, -4981.2524, -1218.3779, 501.7562, 3.804817676544189453, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- General Zog +(@GUID+6, 5595, 0, 0, 0, 0, 0, 1, 1, 0, -4982.4688, -1216.806, 501.7562, 3.874630928039550781, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Ironforge Guard +(@GUID+7, 15383, 0, 0, 0, 0, 0, 1, 1, 0, -4924.3657, -1222.7299, 501.71756, 3.926990747451782226, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sergeant Stonebrow +(@GUID+8, 15431, 0, 0, 0, 0, 0, 1, 1, 0, -4914.172, -1227.4949, 501.73282, 3.59537816047668457, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Corporal Carnes +(@GUID+9, 15432, 0, 0, 0, 0, 0, 1, 1, 0, -4930.287, -1218.7476, 501.71875, 3.752457857131958007, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Dame Twinbraid +(@GUID+10, 15434, 0, 0, 0, 0, 0, 1, 1, 0, -4952.255, -1274.4495, 501.75662, 1.797689080238342285, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Private Draxlegauge +(@GUID+11, 15437, 0, 0, 0, 0, 0, 1, 1, 0, -4945.4204, -1282.0215, 501.75787, 1.029744267463684082, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Master Nightsong +(@GUID+12, 15445, 0, 0, 0, 0, 0, 1, 1, 0, -4948.3345, -1273.7974, 501.75522, 1.064650893211364746, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sergeant Major Germaine +(@GUID+13, 15446, 0, 0, 0, 0, 0, 1, 1, 0, -4972.2017, -1169.0591, 501.72, 3.281219005584716796, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Bonnie Stoneflayer +(@GUID+14, 15448, 0, 0, 0, 0, 0, 1, 1, 0, -4966.0938, -1176.0596, 501.74265, 3.298672199249267578, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Private Porter +(@GUID+15, 15450, 0, 0, 0, 0, 0, 1, 1, 0, -4969.4565, -1180.2417, 501.7428, 3.246312379837036132, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Marta Finespindle +(@GUID+16, 15451, 0, 0, 0, 0, 0, 1, 1, 0, -4971.5747, -1151.5566, 501.73938, 3.560471534729003906, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sentinel Silversky +(@GUID+17, 15452, 0, 0, 0, 0, 0, 1, 1, 0, -4979.116, -1149.51, 501.7331, 3.368485450744628906, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Nurse Stonefield +(@GUID+18, 15453, 0, 0, 0, 0, 0, 1, 1, 0, -4979.9287, -1142.1707, 501.7428, 3.682644605636596679, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Keeper Moonshade +(@GUID+19, 15455, 0, 0, 0, 0, 0, 1, 1, 0, -4938.0024, -1275.1202, 501.75195, 2.460914134979248046, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Slicky Gastronome +(@GUID+20, 15456, 0, 0, 0, 0, 0, 1, 1, 0, -4940.392, -1277.704, 501.7544, 1.989675283432006835, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sarah Sadwhistle +(@GUID+21, 15457, 0, 0, 0, 0, 0, 1, 1, 0, -4933.8027, -1279.1617, 501.74948, 2.426007747650146484, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Huntress Swiftriver +(@GUID+22, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4917.319, -1224.8823, 501.7417, 4.327857494354248046, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+23, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4944.852, -1277.7028, 501.75586, 4.258603572845458984, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+24, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4966.2954, -1173.8835, 501.72675, 3.648972272872924804, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+25, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4975.13, -1153.5369, 501.74008, 3.013503551483154296, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+26, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4937.548, -1280.2931, 501.7544, 4.558997154235839843, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+27, 15701, 0, 0, 0, 0, 0, 1, 1, 0, -4977.4976, -1172.4156, 501.7317, 2.282009840011596679, 300, 0, 0, 30520, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- Field Marshal Snowfall +-- Orgrimmar +(@GUID+28, 15736, 0, 0, 1, 0, 0, 1, 1, 0, 1584.7704, -4112.9443, 33.37767, 5.410520553588867187, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Orgrimmar Commendation Officer +(@GUID+29, 15737, 0, 0, 1, 0, 0, 1, 1, 0, 1618.4412, -4101.7646, 32.95245, 5.235987663269042968, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Darkspear Commendation Officer +(@GUID+30, 15738, 0, 0, 1, 0, 0, 1, 1, 0, 1660.3582, -4107.4517, 34.620274, 2.059488534927368164, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Undercity Commendation Officer +(@GUID+31, 15739, 0, 0, 1, 0, 0, 1, 1, 0, 1603.886, -4142.897, 33.78176, 2.443460941314697265, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Thunder Bluff Commendation Officer +(@GUID+32, 3296, 0, 0, 1, 0, 0, 1, 1, 0, 1628.9564, -4119.1763, 31.244139, 2.094395160675048828, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Orgrimmar Grunt +(@GUID+33, 15458, 0, 0, 1, 0, 0, 1, 1, 0, 1630.693, -4118.458, 31.265778, 1.972222089767456054, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Commander Stronghammer +(@GUID+34, 3296, 0, 0, 1, 0, 0, 1, 1, 0, 1632.3059, -4117.596, 31.29346, 2.042035102844238281, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Orgrimmar Grunt +(@GUID+35, 15522, 0, 0, 1, 0, 0, 1, 1, 0, 1593.2671, -4159.4404, 36.90244, 2.94960641860961914, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sergeant Umala +(@GUID+36, 15533, 0, 0, 1, 0, 0, 1, 1, 0, 1643.4338, -4085.0864, 37.337215, 4.677482128143310546, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Bloodguard Rawtar +(@GUID+37, 15534, 0, 0, 1, 0, 0, 1, 1, 0, 1629.785, -4089.1484, 35.632874, 5.25344085693359375, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Fisherman Lin'do +(@GUID+38, 15535, 0, 0, 1, 0, 0, 1, 1, 0, 1634.1218, -4084.9915, 36.52574, 5.218534469604492187, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Chief Sharpclaw +(@GUID+39, 15459, 0, 0, 1, 0, 0, 1, 1, 0, 1650.3278, -4124.2856, 31.452269, 2.652900457382202148, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Miner Cromwell +(@GUID+40, 15460, 0, 0, 1, 0, 0, 1, 1, 0, 1665.7623, -4117.497, 34.37464, 2.443460941314697265, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Grunt Maug +(@GUID+41, 15469, 0, 0, 1, 0, 0, 1, 1, 0, 1655.7728, -4119.1626, 32.695107, 1.326450228691101074, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Senior Sergeant T'kelah +(@GUID+42, 15477, 0, 0, 1, 0, 0, 1, 1, 0, 1615.0131, -4145.532, 35.131996, 1.378810048103332519, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Herbalist Proudfeather +(@GUID+43, 15508, 0, 0, 1, 0, 0, 1, 1, 0, 1625.951, -4149.423, 36.395786, 1.902408838272094726, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Batrider Pele'keiki +(@GUID+44, 15512, 0, 0, 1, 0, 0, 1, 1, 0, 1633.2582, -4142.117, 34.70991, 2.111848354339599609, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Apothecary Jezel +(@GUID+45, 15515, 0, 0, 1, 0, 0, 1, 1, 0, 1588.1735, -4179.9014, 39.98489, 2.897246599197387695, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Skinner Jamani +(@GUID+46, 15525, 0, 0, 1, 0, 0, 1, 1, 0, 1595.7618, -4174.3965, 39.766666, 2.722713708877563476, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Doctor Serratus +(@GUID+47, 15528, 0, 0, 1, 0, 0, 1, 1, 0, 1580.1736, -4116.1064, 34.41577, 5.602506637573242187, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Healer Longrunner +(@GUID+48, 15529, 0, 0, 1, 0, 0, 1, 1, 0, 1571.1464, -4118.6587, 36.584232, 5.026548385620117187, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Lady Callow +(@GUID+49, 15532, 0, 0, 1, 0, 0, 1, 1, 0, 1565.0651, -4123.9863, 37.44075, 0, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Stoneguard Clayhoof +(@GUID+50, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1576.2423, -4118.5137, 35.26423, 4.112950801849365234, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+51, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1591.8158, -4162.673, 37.39431, 4.81041717529296875, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+52, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1639.2985, -4081.972, 37.58056, 4.678342819213867187, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+53, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1629.4264, -4142.979, 34.864754, 3.117774009704589843, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+54, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1666.3688, -4109.401, 35.089664, 1.868425965309143066, 300, 0, 0, 4775, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+55, 15700, 0, 0, 1, 0, 0, 1, 1, 0, 1581.5414, -4184.574, 39.5738, 1.563615918159484863, 300, 0, 0, 30520, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Warlord Gorchuk + +-- War Effort Recruiters +(@GUID+56, 15702, 0, 0, 1, 0, 0, 1, 1, 0, -1209.5848, 100.22011, 134.661, 3.159045934677124023, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Senior Sergeant Taiga +(@GUID+57, 15703, 0, 0, 0, 0, 0, 1, 1, 0, 1572.5758, 272.70654, -43.01935, 5.026548385620117187, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Senior Sergeant Grimsford +(@GUID+58, 15704, 0, 0, 1, 0, 0, 1, 1, 0, 1653.0684, -4403.811, 18.581886, 4.450589656829833984, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Senior Sergeant Kai'jin +(@GUID+59, 15707, 0, 0, 0, 0, 0, 1, 1, 0, -4956.0864, -931.13306, 503.3468, 5.375614166259765625, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Master Sergeant Fizzlebolt +(@GUID+60, 15708, 0, 0, 0, 0, 0, 1, 1, 0, -8813.751, 654.0678, 96.16028, 4.834561824798583984, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Master Sergeant Maclure +(@GUID+61, 15709, 0, 0, 1, 0, 0, 1, 1, 0, 9945.1455, 2494.2393, 1317.5244, 4.206243515014648437, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Master Sergeant Moonshadow +-- Commendation Officers +(@GUID+62, 15761, 0, 0, 1, 0, 0, 1, 1, 0, 1945.322, -4330.305, 22.101057, 3.50811171531677246, 300, 0, 0, 2914, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Vu'Shalay +(@GUID+63, 15762, 0, 0, 1, 0, 0, 1, 1, 0, 9965.52, 2533.7234, 1319.0049, 0.471238881349563598, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Lunalight +(@GUID+64, 15763, 0, 0, 0, 0, 0, 1, 1, 0, -4811.9756, -1264.849, 501.95117, 3.054326057434082031, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Porterhouse +(@GUID+65, 15764, 0, 0, 0, 0, 0, 1, 1, 0, -4814.475, -1055.5201, 502.26733, 6.213372230529785156, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Ironbeard +(@GUID+66, 15765, 0, 0, 1, 0, 0, 1, 1, 0, 1911.704, -4276.771, 31.655682, 4.886921882629394531, 300, 0, 0, 2914, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Redblade +(@GUID+67, 15766, 0, 0, 0, 0, 0, 1, 1, 0, -8859.14, 638.28687, 96.34692, 1.815142393112182617, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Maloof +(@GUID+68, 15767, 0, 0, 1, 0, 0, 1, 1, 0, -1246.4849, 74.262695, 128.36818, 5.026548385620117187, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Thunderstrider +(@GUID+69, 15768, 0, 0, 0, 0, 0, 1, 1, 0, 1587.8914, 279.28018, -43.019344, 4.852015495300292968, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL); -- Officer Gothena + +-- Old spawns +DELETE FROM `creature` WHERE `guid` IN (37, 2032, 6519, 6520, 25997, 32076, 46803) AND `id1` IN (14724, 15761, 15762, 15764, 15765, 15767, 15768); +DELETE FROM `creature_addon` WHERE `guid` IN (6519, 6520, 25997, 32076, 46803); diff --git a/data/sql/updates/db_world/2025_08_23_01.sql b/data/sql/updates/db_world/2025_08_23_01.sql new file mode 100644 index 000000000..d63bf23ff --- /dev/null +++ b/data/sql/updates/db_world/2025_08_23_01.sql @@ -0,0 +1,465 @@ +-- DB update 2025_08_23_00 -> 2025_08_23_01 +SET @EventID = 131, + @OGUID = 3639, + @CGUID = 83113; + +SET @AQWarAllianceBarsInitial = @EventID+0, + @AQWarAllianceBarsT1 = @EventID+1, + @AQWarAllianceBarsT2 = @EventID+2, + @AQWarAllianceBarsT3 = @EventID+3, + @AQWarAllianceBarsT4 = @EventID+4, + @AQWarAllianceBarsT5 = @EventID+5, + @AQWarAllianceHerbsInitial = @EventID+6, + @AQWarAllianceHerbsT1 = @EventID+7, + @AQWarAllianceHerbsT2 = @EventID+8, + @AQWarAllianceHerbsT3 = @EventID+9, + @AQWarAllianceHerbsT4 = @EventID+10, + @AQWarAllianceHerbsT5 = @EventID+11, + @AQWarAllianceSkinsInitial = @EventID+12, + @AQWarAllianceSkinsT1 = @EventID+13, + @AQWarAllianceSkinsT2 = @EventID+14, + @AQWarAllianceSkinsT3 = @EventID+15, + @AQWarAllianceSkinsT4 = @EventID+16, + @AQWarAllianceSkinsT5 = @EventID+17, + @AQWarAllianceBandagesInitial = @EventID+18, + @AQWarAllianceBandagesT1 = @EventID+19, + @AQWarAllianceBandagesT2 = @EventID+20, + @AQWarAllianceBandagesT3 = @EventID+21, + @AQWarAllianceBandagesT4 = @EventID+22, + @AQWarAllianceBandagesT5 = @EventID+23, + @AQWarAllianceCookedGoodsInitial = @EventID+24, + @AQWarAllianceCookedGoodsT1 = @EventID+25, + @AQWarAllianceCookedGoodsT2 = @EventID+26, + @AQWarAllianceCookedGoodsT3 = @EventID+27, + @AQWarAllianceCookedGoodsT4 = @EventID+28, + @AQWarAllianceCookedGoodsT5 = @EventID+29, + @AQWarHordeBarsInitial = @EventID+30, + @AQWarHordeBarsT1 = @EventID+31, + @AQWarHordeBarsT2 = @EventID+32, + @AQWarHordeBarsT3 = @EventID+33, + @AQWarHordeBarsT4 = @EventID+34, + @AQWarHordeBarsT5 = @EventID+35, + @AQWarHordeHerbsInitial = @EventID+36, + @AQWarHordeHerbsT1 = @EventID+37, + @AQWarHordeHerbsT2 = @EventID+38, + @AQWarHordeHerbsT3 = @EventID+39, + @AQWarHordeHerbsT4 = @EventID+40, + @AQWarHordeHerbsT5 = @EventID+41, + @AQWarHordeSkinsInitial = @EventID+42, + @AQWarHordeSkinsT1 = @EventID+43, + @AQWarHordeSkinsT2 = @EventID+44, + @AQWarHordeSkinsT3 = @EventID+45, + @AQWarHordeSkinsT4 = @EventID+46, + @AQWarHordeSkinsT5 = @EventID+47, + @AQWarHordeBandagesInitial = @EventID+48, + @AQWarHordeBandagesT1 = @EventID+49, + @AQWarHordeBandagesT2 = @EventID+50, + @AQWarHordeBandagesT3 = @EventID+51, + @AQWarHordeBandagesT4 = @EventID+52, + @AQWarHordeBandagesT5 = @EventID+53, + @AQWarHordeCookedGoodsInitial = @EventID+54, + @AQWarHordeCookedGoodsT1 = @EventID+55, + @AQWarHordeCookedGoodsT2 = @EventID+56, + @AQWarHordeCookedGoodsT3 = @EventID+57, + @AQWarHordeCookedGoodsT4 = @EventID+58, + @AQWarHordeCookedGoodsT5 = @EventID+59; + +DELETE FROM `game_event` WHERE `eventEntry` IN (@AQWarAllianceBarsInitial, @AQWarAllianceBarsT1, @AQWarAllianceBarsT2, @AQWarAllianceBarsT3, @AQWarAllianceBarsT4, @AQWarAllianceBarsT5, @AQWarAllianceHerbsInitial, @AQWarAllianceHerbsT1, @AQWarAllianceHerbsT2, @AQWarAllianceHerbsT3, @AQWarAllianceHerbsT4, @AQWarAllianceHerbsT5, @AQWarAllianceSkinsInitial, @AQWarAllianceSkinsT1, @AQWarAllianceSkinsT2, @AQWarAllianceSkinsT3, @AQWarAllianceSkinsT4, @AQWarAllianceSkinsT5, @AQWarAllianceBandagesInitial, @AQWarAllianceBandagesT1, @AQWarAllianceBandagesT2, @AQWarAllianceBandagesT3, @AQWarAllianceBandagesT4, @AQWarAllianceBandagesT5, @AQWarAllianceCookedGoodsInitial, @AQWarAllianceCookedGoodsT1, @AQWarAllianceCookedGoodsT2, @AQWarAllianceCookedGoodsT3, @AQWarAllianceCookedGoodsT4, @AQWarAllianceCookedGoodsT5, @AQWarHordeBarsInitial, @AQWarHordeBarsT1, @AQWarHordeBarsT2, @AQWarHordeBarsT3, @AQWarHordeBarsT4, @AQWarHordeBarsT5, @AQWarHordeHerbsInitial, @AQWarHordeHerbsT1, @AQWarHordeHerbsT2, @AQWarHordeHerbsT3, @AQWarHordeHerbsT4, @AQWarHordeHerbsT5, @AQWarHordeSkinsInitial, @AQWarHordeSkinsT1, @AQWarHordeSkinsT2, @AQWarHordeSkinsT3, @AQWarHordeSkinsT4, @AQWarHordeSkinsT5, @AQWarHordeBandagesInitial, @AQWarHordeBandagesT1, @AQWarHordeBandagesT2, @AQWarHordeBandagesT3, @AQWarHordeBandagesT4, @AQWarHordeBandagesT5, @AQWarHordeCookedGoodsInitial, @AQWarHordeCookedGoodsT1, @AQWarHordeCookedGoodsT2, @AQWarHordeCookedGoodsT3, @AQWarHordeCookedGoodsT4, @AQWarHordeCookedGoodsT5); +INSERT INTO `game_event` (`eventEntry`, `start_time`, `end_time`, `occurence`, `length`, `holiday`, `holidayStage`, `description`, `world_event`, `announce`) VALUES +(@AQWarAllianceBarsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Initial', 5, 2), +(@AQWarAllianceBarsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 1', 5, 2), +(@AQWarAllianceBarsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 2', 5, 2), +(@AQWarAllianceBarsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 3', 5, 2), +(@AQWarAllianceBarsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 4', 5, 2), +(@AQWarAllianceBarsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 5', 5, 2), +(@AQWarAllianceHerbsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Initial', 5, 2), +(@AQWarAllianceHerbsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 1', 5, 2), +(@AQWarAllianceHerbsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 2', 5, 2), +(@AQWarAllianceHerbsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 3', 5, 2), +(@AQWarAllianceHerbsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 4', 5, 2), +(@AQWarAllianceHerbsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 5', 5, 2), +(@AQWarAllianceSkinsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Initial', 5, 2), +(@AQWarAllianceSkinsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 1', 5, 2), +(@AQWarAllianceSkinsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 2', 5, 2), +(@AQWarAllianceSkinsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 3', 5, 2), +(@AQWarAllianceSkinsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 4', 5, 2), +(@AQWarAllianceSkinsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 5', 5, 2), +(@AQWarAllianceBandagesInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Initial', 5, 2), +(@AQWarAllianceBandagesT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 1', 5, 2), +(@AQWarAllianceBandagesT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 2', 5, 2), +(@AQWarAllianceBandagesT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 3', 5, 2), +(@AQWarAllianceBandagesT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 4', 5, 2), +(@AQWarAllianceBandagesT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 5', 5, 2), +(@AQWarAllianceCookedGoodsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Initial', 5, 2), +(@AQWarAllianceCookedGoodsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 1', 5, 2), +(@AQWarAllianceCookedGoodsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 2', 5, 2), +(@AQWarAllianceCookedGoodsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 3', 5, 2), +(@AQWarAllianceCookedGoodsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 4', 5, 2), +(@AQWarAllianceCookedGoodsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 5', 5, 2), +(@AQWarHordeBarsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Initial', 5, 2), +(@AQWarHordeBarsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 1', 5, 2), +(@AQWarHordeBarsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 2', 5, 2), +(@AQWarHordeBarsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 3', 5, 2), +(@AQWarHordeBarsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 4', 5, 2), +(@AQWarHordeBarsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 5', 5, 2), +(@AQWarHordeHerbsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Initial', 5, 2), +(@AQWarHordeHerbsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 1', 5, 2), +(@AQWarHordeHerbsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 2', 5, 2), +(@AQWarHordeHerbsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 3', 5, 2), +(@AQWarHordeHerbsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 4', 5, 2), +(@AQWarHordeHerbsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 5', 5, 2), +(@AQWarHordeSkinsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Initial', 5, 2), +(@AQWarHordeSkinsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 1', 5, 2), +(@AQWarHordeSkinsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 2', 5, 2), +(@AQWarHordeSkinsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 3', 5, 2), +(@AQWarHordeSkinsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 4', 5, 2), +(@AQWarHordeSkinsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 5', 5, 2), +(@AQWarHordeBandagesInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Initial', 5, 2), +(@AQWarHordeBandagesT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 1', 5, 2), +(@AQWarHordeBandagesT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 2', 5, 2), +(@AQWarHordeBandagesT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 3', 5, 2), +(@AQWarHordeBandagesT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 4', 5, 2), +(@AQWarHordeBandagesT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 5', 5, 2), +(@AQWarHordeCookedGoodsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Initial', 5, 2), +(@AQWarHordeCookedGoodsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 1', 5, 2), +(@AQWarHordeCookedGoodsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 2', 5, 2), +(@AQWarHordeCookedGoodsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 3', 5, 2), +(@AQWarHordeCookedGoodsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 4', 5, 2), +(@AQWarHordeCookedGoodsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 5', 5, 2); + +DELETE FROM `game_event_creature` WHERE `eventEntry` = 22 AND `guid` BETWEEN @CGUID AND @CGUID+69; +INSERT INTO `game_event_creature` (`eventEntry`, `guid`) VALUES +(22, @CGUID+0), +(22, @CGUID+1), +(22, @CGUID+2), +(22, @CGUID+3), +(22, @CGUID+4), +(22, @CGUID+5), +(22, @CGUID+6), +(22, @CGUID+7), +(22, @CGUID+8), +(22, @CGUID+9), +(22, @CGUID+10), +(22, @CGUID+11), +(22, @CGUID+12), +(22, @CGUID+13), +(22, @CGUID+14), +(22, @CGUID+15), +(22, @CGUID+16), +(22, @CGUID+17), +(22, @CGUID+18), +(22, @CGUID+19), +(22, @CGUID+20), +(22, @CGUID+21), +(22, @CGUID+22), +(22, @CGUID+23), +(22, @CGUID+24), +(22, @CGUID+25), +(22, @CGUID+26), +(22, @CGUID+27), +(22, @CGUID+28), +(22, @CGUID+29), +(22, @CGUID+30), +(22, @CGUID+31), +(22, @CGUID+32), +(22, @CGUID+33), +(22, @CGUID+34), +(22, @CGUID+35), +(22, @CGUID+36), +(22, @CGUID+37), +(22, @CGUID+38), +(22, @CGUID+39), +(22, @CGUID+40), +(22, @CGUID+41), +(22, @CGUID+42), +(22, @CGUID+43), +(22, @CGUID+44), +(22, @CGUID+45), +(22, @CGUID+46), +(22, @CGUID+47), +(22, @CGUID+48), +(22, @CGUID+49), +(22, @CGUID+50), +(22, @CGUID+51), +(22, @CGUID+52), +(22, @CGUID+53), +(22, @CGUID+54), +(22, @CGUID+55), +(22, @CGUID+56), +(22, @CGUID+57), +(22, @CGUID+58), +(22, @CGUID+59), +(22, @CGUID+60), +(22, @CGUID+61), +(22, @CGUID+62), +(22, @CGUID+63), +(22, @CGUID+64), +(22, @CGUID+65), +(22, @CGUID+66), +(22, @CGUID+67), +(22, @CGUID+68), +(22, @CGUID+69); + +DELETE FROM `game_event_creature` WHERE `eventEntry` = 22 AND `guid` IN (37, 2032, 6519, 6520, 25997, 32076, 46803); + +DELETE FROM `game_event_gameobject` WHERE `eventEntry` IN (@AQWarAllianceBarsInitial, @AQWarAllianceBarsT1, @AQWarAllianceBarsT2, @AQWarAllianceBarsT3, @AQWarAllianceBarsT4, @AQWarAllianceBarsT5, @AQWarAllianceHerbsInitial, @AQWarAllianceHerbsT1, @AQWarAllianceHerbsT2, @AQWarAllianceHerbsT3, @AQWarAllianceHerbsT4, @AQWarAllianceHerbsT5, @AQWarAllianceSkinsInitial, @AQWarAllianceSkinsT1, @AQWarAllianceSkinsT2, @AQWarAllianceSkinsT3, @AQWarAllianceSkinsT4, @AQWarAllianceSkinsT5, @AQWarAllianceBandagesInitial, @AQWarAllianceBandagesT1, @AQWarAllianceBandagesT2, @AQWarAllianceBandagesT3, @AQWarAllianceBandagesT4, @AQWarAllianceBandagesT5, @AQWarAllianceCookedGoodsInitial, @AQWarAllianceCookedGoodsT1, @AQWarAllianceCookedGoodsT2, @AQWarAllianceCookedGoodsT3, @AQWarAllianceCookedGoodsT4, @AQWarAllianceCookedGoodsT5, @AQWarHordeBarsInitial, @AQWarHordeBarsT1, @AQWarHordeBarsT2, @AQWarHordeBarsT3, @AQWarHordeBarsT4, @AQWarHordeBarsT5, @AQWarHordeHerbsInitial, @AQWarHordeHerbsT1, @AQWarHordeHerbsT2, @AQWarHordeHerbsT3, @AQWarHordeHerbsT4, @AQWarHordeHerbsT5, @AQWarHordeSkinsInitial, @AQWarHordeSkinsT1, @AQWarHordeSkinsT2, @AQWarHordeSkinsT3, @AQWarHordeSkinsT4, @AQWarHordeSkinsT5, @AQWarHordeBandagesInitial, @AQWarHordeBandagesT1, @AQWarHordeBandagesT2, @AQWarHordeBandagesT3, @AQWarHordeBandagesT4, @AQWarHordeBandagesT5, @AQWarHordeCookedGoodsInitial, @AQWarHordeCookedGoodsT1, @AQWarHordeCookedGoodsT2, @AQWarHordeCookedGoodsT3, @AQWarHordeCookedGoodsT4, @AQWarHordeCookedGoodsT5); +INSERT INTO `game_event_gameobject` (`eventEntry`, `guid`) VALUES +(@AQWarAllianceBarsInitial, @OGUID+19), +(@AQWarAllianceBarsT1, @OGUID+19), +(@AQWarAllianceBarsT1, @OGUID+20), +(@AQWarAllianceBarsT2, @OGUID+19), +(@AQWarAllianceBarsT2, @OGUID+20), +(@AQWarAllianceBarsT2, @OGUID+21), +(@AQWarAllianceBarsT3, @OGUID+19), +(@AQWarAllianceBarsT3, @OGUID+20), +(@AQWarAllianceBarsT3, @OGUID+21), +(@AQWarAllianceBarsT3, @OGUID+22), +(@AQWarAllianceBarsT4, @OGUID+19), +(@AQWarAllianceBarsT4, @OGUID+20), +(@AQWarAllianceBarsT4, @OGUID+21), +(@AQWarAllianceBarsT4, @OGUID+22), +(@AQWarAllianceBarsT4, @OGUID+23), +(@AQWarAllianceBarsT5, @OGUID+19), +(@AQWarAllianceBarsT5, @OGUID+20), +(@AQWarAllianceBarsT5, @OGUID+21), +(@AQWarAllianceBarsT5, @OGUID+22), +(@AQWarAllianceBarsT5, @OGUID+23), +(@AQWarAllianceBarsT5, @OGUID+24), +(@AQWarAllianceHerbsInitial, @OGUID+25), +(@AQWarAllianceHerbsT1, @OGUID+25), +(@AQWarAllianceHerbsT1, @OGUID+31), +(@AQWarAllianceHerbsT2, @OGUID+25), +(@AQWarAllianceHerbsT2, @OGUID+31), +(@AQWarAllianceHerbsT2, @OGUID+32), +(@AQWarAllianceHerbsT3, @OGUID+25), +(@AQWarAllianceHerbsT3, @OGUID+31), +(@AQWarAllianceHerbsT3, @OGUID+32), +(@AQWarAllianceHerbsT3, @OGUID+33), +(@AQWarAllianceHerbsT4, @OGUID+25), +(@AQWarAllianceHerbsT4, @OGUID+31), +(@AQWarAllianceHerbsT4, @OGUID+32), +(@AQWarAllianceHerbsT4, @OGUID+33), +(@AQWarAllianceHerbsT4, @OGUID+34), +(@AQWarAllianceHerbsT5, @OGUID+25), +(@AQWarAllianceHerbsT5, @OGUID+31), +(@AQWarAllianceHerbsT5, @OGUID+32), +(@AQWarAllianceHerbsT5, @OGUID+33), +(@AQWarAllianceHerbsT5, @OGUID+34), +(@AQWarAllianceHerbsT5, @OGUID+35), +(@AQWarAllianceSkinsInitial, @OGUID+36), +(@AQWarAllianceSkinsT1, @OGUID+36), +(@AQWarAllianceSkinsT1, @OGUID+37), +(@AQWarAllianceSkinsT2, @OGUID+36), +(@AQWarAllianceSkinsT2, @OGUID+37), +(@AQWarAllianceSkinsT2, @OGUID+38), +(@AQWarAllianceSkinsT3, @OGUID+36), +(@AQWarAllianceSkinsT3, @OGUID+37), +(@AQWarAllianceSkinsT3, @OGUID+38), +(@AQWarAllianceSkinsT3, @OGUID+39), +(@AQWarAllianceSkinsT4, @OGUID+36), +(@AQWarAllianceSkinsT4, @OGUID+37), +(@AQWarAllianceSkinsT4, @OGUID+38), +(@AQWarAllianceSkinsT4, @OGUID+39), +(@AQWarAllianceSkinsT4, @OGUID+40), +(@AQWarAllianceSkinsT5, @OGUID+36), +(@AQWarAllianceSkinsT5, @OGUID+37), +(@AQWarAllianceSkinsT5, @OGUID+38), +(@AQWarAllianceSkinsT5, @OGUID+39), +(@AQWarAllianceSkinsT5, @OGUID+40), +(@AQWarAllianceSkinsT5, @OGUID+41), +(@AQWarAllianceBandagesInitial, @OGUID+0), +(@AQWarAllianceBandagesT1, @OGUID+0), +(@AQWarAllianceBandagesT1, @OGUID+1), +(@AQWarAllianceBandagesT1, @OGUID+6), +(@AQWarAllianceBandagesT2, @OGUID+0), +(@AQWarAllianceBandagesT2, @OGUID+1), +(@AQWarAllianceBandagesT2, @OGUID+2), +(@AQWarAllianceBandagesT2, @OGUID+6), +(@AQWarAllianceBandagesT2, @OGUID+7), +(@AQWarAllianceBandagesT2, @OGUID+8), +(@AQWarAllianceBandagesT2, @OGUID+9), +(@AQWarAllianceBandagesT3, @OGUID+0), +(@AQWarAllianceBandagesT3, @OGUID+1), +(@AQWarAllianceBandagesT3, @OGUID+2), +(@AQWarAllianceBandagesT3, @OGUID+3), +(@AQWarAllianceBandagesT3, @OGUID+6), +(@AQWarAllianceBandagesT3, @OGUID+7), +(@AQWarAllianceBandagesT3, @OGUID+8), +(@AQWarAllianceBandagesT3, @OGUID+9), +(@AQWarAllianceBandagesT3, @OGUID+10), +(@AQWarAllianceBandagesT3, @OGUID+11), +(@AQWarAllianceBandagesT3, @OGUID+12), +(@AQWarAllianceBandagesT3, @OGUID+13), +(@AQWarAllianceBandagesT4, @OGUID+0), +(@AQWarAllianceBandagesT4, @OGUID+1), +(@AQWarAllianceBandagesT4, @OGUID+2), +(@AQWarAllianceBandagesT4, @OGUID+3), +(@AQWarAllianceBandagesT4, @OGUID+4), +(@AQWarAllianceBandagesT4, @OGUID+6), +(@AQWarAllianceBandagesT4, @OGUID+7), +(@AQWarAllianceBandagesT4, @OGUID+8), +(@AQWarAllianceBandagesT4, @OGUID+9), +(@AQWarAllianceBandagesT4, @OGUID+10), +(@AQWarAllianceBandagesT4, @OGUID+11), +(@AQWarAllianceBandagesT4, @OGUID+12), +(@AQWarAllianceBandagesT4, @OGUID+13), +(@AQWarAllianceBandagesT4, @OGUID+14), +(@AQWarAllianceBandagesT4, @OGUID+15), +(@AQWarAllianceBandagesT4, @OGUID+16), +(@AQWarAllianceBandagesT4, @OGUID+17), +(@AQWarAllianceBandagesT4, @OGUID+18), +(@AQWarAllianceBandagesT5, @OGUID+0), +(@AQWarAllianceBandagesT5, @OGUID+1), +(@AQWarAllianceBandagesT5, @OGUID+2), +(@AQWarAllianceBandagesT5, @OGUID+3), +(@AQWarAllianceBandagesT5, @OGUID+4), +(@AQWarAllianceBandagesT5, @OGUID+5), +(@AQWarAllianceBandagesT5, @OGUID+6), +(@AQWarAllianceBandagesT5, @OGUID+7), +(@AQWarAllianceBandagesT5, @OGUID+8), +(@AQWarAllianceBandagesT5, @OGUID+9), +(@AQWarAllianceBandagesT5, @OGUID+10), +(@AQWarAllianceBandagesT5, @OGUID+11), +(@AQWarAllianceBandagesT5, @OGUID+12), +(@AQWarAllianceBandagesT5, @OGUID+13), +(@AQWarAllianceBandagesT5, @OGUID+14), +(@AQWarAllianceBandagesT5, @OGUID+15), +(@AQWarAllianceBandagesT5, @OGUID+16), +(@AQWarAllianceBandagesT5, @OGUID+17), +(@AQWarAllianceBandagesT5, @OGUID+18), +(@AQWarAllianceBandagesT5, @OGUID+72), +(@AQWarAllianceBandagesT5, @OGUID+73), +(@AQWarAllianceBandagesT5, @OGUID+74), +(@AQWarAllianceBandagesT5, @OGUID+75), +(@AQWarAllianceBandagesT5, @OGUID+76), +(@AQWarAllianceBandagesT5, @OGUID+77), +(@AQWarAllianceBandagesT5, @OGUID+78), +(@AQWarAllianceBandagesT5, @OGUID+79), +(@AQWarAllianceBandagesT5, @OGUID+80), +(@AQWarAllianceBandagesT5, @OGUID+81), +(@AQWarAllianceCookedGoodsInitial, @OGUID+25), +(@AQWarAllianceCookedGoodsT1, @OGUID+25), +(@AQWarAllianceCookedGoodsT1, @OGUID+26), +(@AQWarAllianceCookedGoodsT2, @OGUID+25), +(@AQWarAllianceCookedGoodsT2, @OGUID+26), +(@AQWarAllianceCookedGoodsT2, @OGUID+27), +(@AQWarAllianceCookedGoodsT3, @OGUID+25), +(@AQWarAllianceCookedGoodsT3, @OGUID+26), +(@AQWarAllianceCookedGoodsT3, @OGUID+27), +(@AQWarAllianceCookedGoodsT3, @OGUID+28), +(@AQWarAllianceCookedGoodsT4, @OGUID+25), +(@AQWarAllianceCookedGoodsT4, @OGUID+26), +(@AQWarAllianceCookedGoodsT4, @OGUID+27), +(@AQWarAllianceCookedGoodsT4, @OGUID+28), +(@AQWarAllianceCookedGoodsT4, @OGUID+29), +(@AQWarAllianceCookedGoodsT5, @OGUID+25), +(@AQWarAllianceCookedGoodsT5, @OGUID+26), +(@AQWarAllianceCookedGoodsT5, @OGUID+27), +(@AQWarAllianceCookedGoodsT5, @OGUID+28), +(@AQWarAllianceCookedGoodsT5, @OGUID+29), +(@AQWarAllianceCookedGoodsT5, @OGUID+30), +(@AQWarHordeBarsInitial, @OGUID+48), +(@AQWarHordeBarsT1, @OGUID+48), +(@AQWarHordeBarsT1, @OGUID+49), +(@AQWarHordeBarsT2, @OGUID+48), +(@AQWarHordeBarsT2, @OGUID+49), +(@AQWarHordeBarsT2, @OGUID+50), +(@AQWarHordeBarsT3, @OGUID+48), +(@AQWarHordeBarsT3, @OGUID+49), +(@AQWarHordeBarsT3, @OGUID+50), +(@AQWarHordeBarsT3, @OGUID+51), +(@AQWarHordeBarsT4, @OGUID+48), +(@AQWarHordeBarsT4, @OGUID+49), +(@AQWarHordeBarsT4, @OGUID+50), +(@AQWarHordeBarsT4, @OGUID+51), +(@AQWarHordeBarsT4, @OGUID+52), +(@AQWarHordeBarsT5, @OGUID+48), +(@AQWarHordeBarsT5, @OGUID+49), +(@AQWarHordeBarsT5, @OGUID+50), +(@AQWarHordeBarsT5, @OGUID+51), +(@AQWarHordeBarsT5, @OGUID+52), +(@AQWarHordeBarsT5, @OGUID+53), +(@AQWarHordeHerbsInitial, @OGUID+60), +(@AQWarHordeHerbsT1, @OGUID+60), +(@AQWarHordeHerbsT1, @OGUID+61), +(@AQWarHordeHerbsT2, @OGUID+60), +(@AQWarHordeHerbsT2, @OGUID+61), +(@AQWarHordeHerbsT2, @OGUID+62), +(@AQWarHordeHerbsT3, @OGUID+60), +(@AQWarHordeHerbsT3, @OGUID+61), +(@AQWarHordeHerbsT3, @OGUID+62), +(@AQWarHordeHerbsT3, @OGUID+63), +(@AQWarHordeHerbsT4, @OGUID+60), +(@AQWarHordeHerbsT4, @OGUID+61), +(@AQWarHordeHerbsT4, @OGUID+62), +(@AQWarHordeHerbsT4, @OGUID+63), +(@AQWarHordeHerbsT4, @OGUID+64), +(@AQWarHordeHerbsT5, @OGUID+60), +(@AQWarHordeHerbsT5, @OGUID+61), +(@AQWarHordeHerbsT5, @OGUID+62), +(@AQWarHordeHerbsT5, @OGUID+63), +(@AQWarHordeHerbsT5, @OGUID+64), +(@AQWarHordeHerbsT5, @OGUID+65), +(@AQWarHordeSkinsInitial, @OGUID+66), +(@AQWarHordeSkinsT1, @OGUID+66), +(@AQWarHordeSkinsT1, @OGUID+67), +(@AQWarHordeSkinsT2, @OGUID+66), +(@AQWarHordeSkinsT2, @OGUID+67), +(@AQWarHordeSkinsT2, @OGUID+68), +(@AQWarHordeSkinsT3, @OGUID+66), +(@AQWarHordeSkinsT3, @OGUID+67), +(@AQWarHordeSkinsT3, @OGUID+68), +(@AQWarHordeSkinsT3, @OGUID+69), +(@AQWarHordeSkinsT4, @OGUID+66), +(@AQWarHordeSkinsT4, @OGUID+67), +(@AQWarHordeSkinsT4, @OGUID+68), +(@AQWarHordeSkinsT4, @OGUID+69), +(@AQWarHordeSkinsT4, @OGUID+70), +(@AQWarHordeSkinsT5, @OGUID+66), +(@AQWarHordeSkinsT5, @OGUID+67), +(@AQWarHordeSkinsT5, @OGUID+68), +(@AQWarHordeSkinsT5, @OGUID+69), +(@AQWarHordeSkinsT5, @OGUID+70), +(@AQWarHordeSkinsT5, @OGUID+71), +(@AQWarHordeBandagesInitial, @OGUID+42), +(@AQWarHordeBandagesT1, @OGUID+42), +(@AQWarHordeBandagesT1, @OGUID+43), +(@AQWarHordeBandagesT2, @OGUID+42), +(@AQWarHordeBandagesT2, @OGUID+43), +(@AQWarHordeBandagesT2, @OGUID+44), +(@AQWarHordeBandagesT3, @OGUID+42), +(@AQWarHordeBandagesT3, @OGUID+43), +(@AQWarHordeBandagesT3, @OGUID+44), +(@AQWarHordeBandagesT3, @OGUID+45), +(@AQWarHordeBandagesT4, @OGUID+42), +(@AQWarHordeBandagesT4, @OGUID+43), +(@AQWarHordeBandagesT4, @OGUID+44), +(@AQWarHordeBandagesT4, @OGUID+45), +(@AQWarHordeBandagesT4, @OGUID+46), +(@AQWarHordeBandagesT5, @OGUID+42), +(@AQWarHordeBandagesT5, @OGUID+43), +(@AQWarHordeBandagesT5, @OGUID+44), +(@AQWarHordeBandagesT5, @OGUID+45), +(@AQWarHordeBandagesT5, @OGUID+46), +(@AQWarHordeBandagesT5, @OGUID+47), +(@AQWarHordeCookedGoodsInitial, @OGUID+54), +(@AQWarHordeCookedGoodsT1, @OGUID+54), +(@AQWarHordeCookedGoodsT1, @OGUID+55), +(@AQWarHordeCookedGoodsT2, @OGUID+54), +(@AQWarHordeCookedGoodsT2, @OGUID+55), +(@AQWarHordeCookedGoodsT2, @OGUID+56), +(@AQWarHordeCookedGoodsT3, @OGUID+54), +(@AQWarHordeCookedGoodsT3, @OGUID+55), +(@AQWarHordeCookedGoodsT3, @OGUID+56), +(@AQWarHordeCookedGoodsT3, @OGUID+57), +(@AQWarHordeCookedGoodsT4, @OGUID+54), +(@AQWarHordeCookedGoodsT4, @OGUID+55), +(@AQWarHordeCookedGoodsT4, @OGUID+56), +(@AQWarHordeCookedGoodsT4, @OGUID+57), +(@AQWarHordeCookedGoodsT4, @OGUID+58), +(@AQWarHordeCookedGoodsT5, @OGUID+54), +(@AQWarHordeCookedGoodsT5, @OGUID+55), +(@AQWarHordeCookedGoodsT5, @OGUID+56), +(@AQWarHordeCookedGoodsT5, @OGUID+57), +(@AQWarHordeCookedGoodsT5, @OGUID+58), +(@AQWarHordeCookedGoodsT5, @OGUID+59); diff --git a/data/sql/updates/db_world/2025_08_23_02.sql b/data/sql/updates/db_world/2025_08_23_02.sql new file mode 100644 index 000000000..acc01795c --- /dev/null +++ b/data/sql/updates/db_world/2025_08_23_02.sql @@ -0,0 +1,103 @@ +-- DB update 2025_08_23_01 -> 2025_08_23_02 +SET @GUID = 3639; +-- Note: Some of these are CO2s, especially since there's some weird destroy/recreate thing going on on official, but I just noted them as CO1s for the time being. +DELETE FROM `gameobject` WHERE `id` IN (180598, 180674, 180675, 180676, 180677, 180678, 180679, 180680, 180681, 180692, 180693, 180694, 180695, 180696, 180714, 180780, 180781, 180782, 180783, 180784, 180800, 180801, 180802, 180803, 180804, 180805, 180806, 180807, 180808, 180809, 180812, 180813, 180814, 180815, 180816, 180817, 180818, 180819, 180820, 180821, 180822, 180823, 180826, 180827, 180828, 180829, 180830, 180831, 180832, 180833, 180834, 180835, 180836, 180837, 180838, 180839, 180840, 180841, 180842, 180843) AND `guid` BETWEEN @GUID AND @GUID+81; +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +-- Ironforge +-- Bandages +(@GUID+0, 180598, 0, 0, 0, 1, 1, -4971.5483, -1148.5706, 501.648, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+1, 180674, 0, 0, 0, 1, 1, -4968.3267, -1152.889, 501.9254, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+2, 180675, 0, 0, 0, 1, 1, -4969.2095, -1143.8433, 509.2506, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+3, 180676, 0, 0, 0, 1, 1, -4983.004, -1136.2194, 501.6594, 2.3038306, 0, 0, 0.91354465, 0.40673843, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+4, 180677, 0, 0, 0, 1, 1, -4975.6016, -1147.3348, 509.2504, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+5, 180678, 0, 0, 0, 1, 1, -4974.111, -1148.3993, 510.8475, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T5 +-- CrateAllianceFirstAid01 +(@GUID+6, 180714, 0, 0, 0, 1, 1, -4972.807, -1145.8307, 501.64996, 3.6128378, 0, 0, -0.9723692, 0.23344836, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+7, 180714, 0, 0, 0, 1, 1, -4976.2925, -1158.7479, 501.64252, 0.85521054, 0, 0, 0.41469288, 0.90996146, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+8, 180714, 0, 0, 0, 1, 1, -4979.9673, -1146.8904, 501.65506, 4.2062464, 0, 0, -0.86162853, 0.5075394, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+9, 180714, 0, 0, 0, 1, 1, -4972.857, -1145.8458, 502.8929, 2.1991146, 0, 0, 0.89100647, 0.45399064, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+10, 180714, 0, 0, 0, 1, 1, -4977.4546, -1157.944, 501.64926, 3.9269955, 0, 0, -0.92387867, 0.3826855, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+11, 180714, 0, 0, 0, 1, 1, -4985.743, -1137.5488, 501.6594, 3.5430236, 0, 0, -0.9799242, 0.19937038, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+12, 180714, 0, 0, 0, 1, 1, -4979.9263, -1139.9106, 501.65945, 1.3613561, 0, 0, 0.62932014, 0.77714616, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+13, 180714, 0, 0, 0, 1, 1, -4972.919, -1145.8206, 504.1289, 1.7627825, 0, 0, 0.77162457, 0.63607824, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+14, 180714, 0, 0, 0, 1, 1, -4976.9517, -1158.2965, 502.88937, 4.153885, 0, 0, -0.8746195, 0.48481005, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+15, 180714, 0, 0, 0, 1, 1, -4972.6714, -1152.6599, 509.25027, 0.3316107, 0, 0, 0.16504669, 0.98628575, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+16, 180714, 0, 0, 0, 1, 1, -4968.471, -1149.9625, 501.9254, 5.0265493, 0, 0, -0.58778477, 0.80901736, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+17, 180714, 0, 0, 0, 1, 1, -4979.914, -1139.9069, 502.9025, 1.4835281, 0, 0, 0.67558956, 0.7372779, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+18, 180714, 0, 0, 0, 1, 1, -4985.936, -1135.9319, 501.6594, 3.1590624, 0, 0, -0.99996185, 0.008734641, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+72, 180714, 0, 0, 0, 1, 1, -4972.6826, -1136.9497, 509.77707, 4.3807764, 0, 0, -0.8141155, 0.58070296, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+73, 180714, 0, 0, 0, 1, 1, -4979.8037, -1146.875, 502.89078, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+74, 180714, 0, 0, 0, 1, 1, -4971.91, -1153.9872, 509.25027, 5.2359877, 0, 0, -0.5, 0.8660254, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+75, 180714, 0, 0, 0, 1, 1, -4978.415, -1159.3356, 501.64444, 3.0368383, 0, 0, 0.9986286, 0.052353222, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+76, 180714, 0, 0, 0, 1, 1, -4974.837, -1156.8785, 503.5236, 6.003934, 0, 0, -0.13917255, 0.9902682, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+77, 180714, 0, 0, 0, 1, 1, -4986.0234, -1136.7771, 502.90247, 3.1940022, 0, 0, -0.9996567, 0.026201647, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+78, 180714, 0, 0, 0, 1, 1, -4979.7725, -1139.9098, 504.1386, 5.98648, 0, 0, -0.14780903, 0.98901594, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+79, 180714, 0, 0, 0, 1, 1, -4968.3887, -1150.027, 503.16846, 0.36651757, 0, 0, 0.18223476, 0.983255, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+80, 180714, 0, 0, 0, 1, 1, -4972.4443, -1153.132, 510.49332, 5.2534423, 0, 0, -0.49242306, 0.87035596, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+81, 180714, 0, 0, 0, 1, 1, -4984.3228, -1135.7563, 504.87466, 3.6302915, 0, 0, -0.97029495, 0.241925, 300, 0, 1, '', 61582, NULL), -- T5 +-- Bars +(@GUID+19, 180680, 0, 0, 0, 1, 1, -4913.854, -1225.9967, 501.6508, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+20, 180780, 0, 0, 0, 1, 1, -4913.729, -1225.9507, 501.6506, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+21, 180781, 0, 0, 0, 1, 1, -4913.737, -1225.9277, 501.65067, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+22, 180782, 0, 0, 0, 1, 1, -4913.723, -1225.9167, 501.65067, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+23, 180783, 0, 0, 0, 1, 1, -4913.71, -1225.9053, 501.65067, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+24, 180784, 0, 0, 0, 1, 1, -4913.7773, -1225.8594, 501.65082, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T5 +-- Cooking +(@GUID+25, 180679, 0, 0, 0, 1, 1, -4937.2886, -1282.7358, 501.67215, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- Cooking/Herbs Initial +(@GUID+26, 180800, 0, 0, 0, 1, 1, -4937.282, -1282.8739, 501.67227, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+27, 180806, 0, 0, 0, 1, 1, -4937.136, -1282.895, 501.6721, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+28, 180807, 0, 0, 0, 1, 1, -4937.2856, -1282.869, 501.67227, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+29, 180808, 0, 0, 0, 1, 1, -4937.336, -1282.7858, 501.67227, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+30, 180809, 0, 0, 0, 1, 1, -4937.2197, -1282.8075, 501.67215, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T5 +-- Herbs +(@GUID+31, 180801, 0, 0, 0, 1, 1, -4935.5796, -1284.82, 501.67105, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+32, 180802, 0, 0, 0, 1, 1, -4935.6045, -1284.8381, 501.6711, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+33, 180803, 0, 0, 0, 1, 1, -4935.594, -1284.833, 501.6711, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+34, 180804, 0, 0, 0, 1, 1, -4935.621, -1284.8282, 501.6711, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+35, 180805, 0, 0, 0, 1, 1, -4935.5576, -1284.8864, 501.67105, 2.2340178, 0, 0, 0.8987932, 0.43837282, 300, 0, 1, '', 61582, NULL), -- T5 +-- Skins +(@GUID+36, 180681, 0, 0, 0, 1, 1, -4958.5093, -1179.3196, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+37, 180692, 0, 0, 0, 1, 1, -4958.517, -1179.3344, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+38, 180693, 0, 0, 0, 1, 1, -4958.528, -1179.335, 501.65945, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+39, 180694, 0, 0, 0, 1, 1, -4958.526, -1179.3273, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+40, 180695, 0, 0, 0, 1, 1, -4958.5156, -1179.3286, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+41, 180696, 0, 0, 0, 1, 1, -4958.5293, -1179.3357, 501.65945, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T5 +-- Orgrimmar +-- Bandages +(@GUID+42, 180826, 1, 0, 0, 1, 1, 1579.3531, -4109.2544, 34.541737, 3.7524624, 0, 0, -0.9537163, 0.3007079, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+43, 180827, 1, 0, 0, 1, 1, 1579.3342, -4109.2476, 34.54871, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+44, 180828, 1, 0, 0, 1, 1, 1579.321, -4109.278, 34.551514, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+45, 180829, 1, 0, 0, 1, 1, 1579.333, -4109.2837, 34.547028, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+46, 180830, 1, 0, 0, 1, 1, 1579.3325, -4109.2783, 34.547504, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+47, 180831, 1, 0, 0, 1, 1, 1579.331, -4109.2827, 34.54776, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T5 +-- Bars +(@GUID+48, 180838, 1, 0, 0, 1, 1, 1683.1149, -4134.354, 39.541912, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+49, 180839, 1, 0, 0, 1, 1, 1683.0984, -4134.31, 39.539, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+50, 180840, 1, 0, 0, 1, 1, 1683.097, -4134.3, 39.538742, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+51, 180841, 1, 0, 0, 1, 1, 1683.1057, -4134.3135, 39.54028, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+52, 180842, 1, 0, 0, 1, 1, 1683.0347, -4134.3135, 39.52796, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+53, 180843, 1, 0, 0, 1, 1, 1683.1418, -4134.3403, 39.54657, 3.7524624, 0, 0, -0.9537163, 0.3007079, 300, 0, 1, '', 61582, NULL), -- T5 +-- Cooking +(@GUID+54, 180832, 1, 0, 0, 1, 1, 1619.8307, -4092.4302, 34.51068, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+55, 180833, 1, 0, 0, 1, 1, 1619.8004, -4092.5334, 34.488815, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+56, 180834, 1, 0, 0, 1, 1, 1619.8002, -4092.5317, 34.489204, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+57, 180835, 1, 0, 0, 1, 1, 1619.8073, -4092.5269, 34.490135, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+58, 180836, 1, 0, 0, 1, 1, 1619.8062, -4092.5305, 34.48937, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+59, 180837, 1, 0, 0, 1, 1, 1619.8094, -4092.5217, 34.491196, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T5 +-- Herbs +(@GUID+60, 180818, 1, 0, 0, 1, 1, 1637.1053, -4147.2134, 36.04144, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+61, 180819, 1, 0, 0, 1, 1, 1637.1001, -4147.2534, 36.053123, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+62, 180820, 1, 0, 0, 1, 1, 1637.111, -4147.2534, 36.05357, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+63, 180821, 1, 0, 0, 1, 1, 1637.1099, -4147.2573, 36.054718, 3.7524624, 0, 0, -0.9537163, 0.3007079, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+64, 180822, 1, 0, 0, 1, 1, 1637.079, -4147.228, 36.04483, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+65, 180823, 1, 0, 0, 1, 1, 1637.1013, -4147.226, 36.045063, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T5 +-- Skins +(@GUID+66, 180812, 1, 0, 0, 1, 1, 1590.8248, -4155.328, 36.292576, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+67, 180813, 1, 0, 0, 1, 1, 1590.8755, -4155.335, 36.298023, 3.6826503, 0, 0, -0.9636297, 0.267241, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+68, 180814, 1, 0, 0, 1, 1, 1590.8511, -4155.342, 36.299625, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+69, 180815, 1, 0, 0, 1, 1, 1590.8533, -4155.339, 36.299156, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+70, 180816, 1, 0, 0, 1, 1, 1590.8516, -4155.341, 36.299488, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+71, 180817, 1, 0, 0, 1, 1, 1590.8517, -4155.3438, 36.299927, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL); -- T5 + +-- Old spawns +DELETE FROM `gameobject` WHERE `guid` IN (29294, 29299, 29300, 29301) AND `id` IN (180598, 180679, 180680, 180681); diff --git a/data/sql/updates/db_world/2025_08_24_00.sql b/data/sql/updates/db_world/2025_08_24_00.sql new file mode 100644 index 000000000..0f3d678d9 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_24_00.sql @@ -0,0 +1,3 @@ +-- DB update 2025_08_23_02 -> 2025_08_24_00 +-- +UPDATE `smart_scripts` SET `event_type` = 61 WHERE `entryorguid` = 18945 AND `source_type` = 0 AND `id` = 4; diff --git a/data/sql/updates/db_world/2025_08_25_00.sql b/data/sql/updates/db_world/2025_08_25_00.sql new file mode 100644 index 000000000..c5793e675 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_25_00.sql @@ -0,0 +1,5 @@ +-- DB update 2025_08_24_00 -> 2025_08_25_00 +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 4) AND (`SourceGroup` = 193997) AND (`SourceEntry` = 44725) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 5) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 1119) AND (`ConditionValue2` = 16) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(4, 26782, 44725, 0, 0, 5, 0, 1119, 240, 0, 0, 0, 0, '', 'Everfrost Chip requires Sons of Hodir friendly'); diff --git a/data/sql/updates/db_world/2025_08_25_01.sql b/data/sql/updates/db_world/2025_08_25_01.sql new file mode 100644 index 000000000..196057350 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_25_01.sql @@ -0,0 +1,60 @@ +-- DB update 2025_08_25_00 -> 2025_08_25_01 +-- set dark portal creatures active +DELETE FROM `smart_scripts` WHERE (`entryorguid` IN (18944, 18946, 18948, 18949, 18950, 18965, 18966, 18969, 18970, 18971, 18972, 18986, -68744, -68745, -74081, -74082)) AND (`source_type` = 0) AND (`event_type` IN (11, 36)) AND (`id` IN (42, 43)); +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 +-- 18944 Fel Soldier +(18944, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fel Soldier - On Respawn - Set Active On'), +(18944, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fel Soldier - On Corpse Removed - Set Active On'), +-- 18945 Pit Commander, already set active +-- 18946 Infernal Siegebreaker +(18946, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Siegebreaker - On Respawn - Set Active On'), +(18946, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Siegebreaker - On Corpse Removed - Set Active On'), +-- 18948 Stormwind Soldier +(18948, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Soldier - On Respawn - Set Active On'), +(18948, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Soldier - On Corpse Removed - Set Active On'), +-- 18949 Stormwind Mage +(18949, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Mage - On Respawn - Set Active On'), +(18949, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Mage - On Corpse Removed - Set Active On'), +-- 18950 Orgrimmar Grunt +(18950, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Grunt - On Respawn - Set Active On'), +(18950, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Grunt - On Corpse Removed - Set Active On'), +-- 18965 Darnassian Archer +(18965, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Archer - On Respawn - Set Active On'), +(18965, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Archer - On Corpse Removed - Set Active On'), +-- 18966 Justinius the Harbinger +(18966, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Justinius the Harbinger - On Respawn - Set Active On'), +(18966, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Justinius the Harbinger - On Corpse Removed - Set Active On'), +-- 18969 Melgromm Highmountain +(18969, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Melgromm Highmountain - On Respawn - Set Active On'), +(18969, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Melgromm Highmountain - On Corpse Removed - Set Active On'), +-- 18970 Darkspear Axe Thrower +(18970, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darkspear Axe Thrower - On Respawn - Set Active On'), +(18970, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darkspear Axe Thrower - On Corpse Removed - Set Active On'), +-- 18971 Undercity Mage +(18971, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Undercity Mage - On Respawn - Set Active On'), +(18971, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Undercity Mage - On Corpse Removed - Set Active On'), +-- 18972 Orgrimmar Shaman +(18972, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Shaman - On Respawn - Set Active On'), +(18972, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Shaman - On Corpse Removed - Set Active On'), +-- 18986 Ironforge Paladin +(18986, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ironforge Paladin - On Respawn - Set Active On'), +(18986, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ironforge Paladin - On Corpse Removed - Set Active On'), +-- 19005 Wrath Master, GUID SAI -68311, -68312, -68313, -68314, already set active +-- 19215 Infernal Relay (Hellfire) +(-68744, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Respawn - Set Active On'), +(-68744, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Corpse Removed - Set Active On'), +(-68745, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Respawn - Set Active On'), +(-68745, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Corpse Removed - Set Active On'), +-- 21075 Infernal Target (Hyjal) +(-74081, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal) - On Respawn - Set Active On'), +(-74081, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal)- On Corpse Removed - Set Active On'), +(-74082, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal) - On Respawn - Set Active On'), +(-74082, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal) - On Corpse Removed - Set Active On'); + +-- cleanup +-- Infernal Relay (Hellfire) used to set nearby 19005 Wrath Master active +DELETE FROM `smart_scripts` WHERE (`entryorguid` = -68744) AND (`source_type` = 0) AND (`id` IN (10)); +UPDATE `smart_scripts` SET `link` = 0 WHERE (`entryorguid` = -68744) AND (`source_type` = 0) AND (`id` IN (9)); + +-- update spawn comment for GUID SAI +UPDATE `creature` SET `Comment` = 'GUID SAI, SAI Target' WHERE (`id1` = 19215) AND (`guid` = 68745); diff --git a/data/sql/updates/db_world/2025_08_25_02.sql b/data/sql/updates/db_world/2025_08_25_02.sql new file mode 100644 index 000000000..47a79017c --- /dev/null +++ b/data/sql/updates/db_world/2025_08_25_02.sql @@ -0,0 +1,6 @@ +-- DB update 2025_08_25_01 -> 2025_08_25_02 +-- fix dark portal creatures calling for help on aggro +-- 18948 Stormwind Soldier +UPDATE `smart_scripts` SET `event_type` = 4 WHERE (`entryorguid` = 18948) AND (`source_type` = 0) AND (`id` IN (7)); +-- 18950 Orgrimmar Grunt +UPDATE `smart_scripts` SET `event_type` = 4 WHERE (`entryorguid` = 18950) AND (`source_type` = 0) AND (`id` IN (7)); diff --git a/data/sql/updates/db_world/2025_08_27_00.sql b/data/sql/updates/db_world/2025_08_27_00.sql new file mode 100644 index 000000000..13cac6c78 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_27_00.sql @@ -0,0 +1,6 @@ +-- DB update 2025_08_25_02 -> 2025_08_27_00 +UPDATE `item_template` SET `armor` = 1983, `MaxDurability` = 165 WHERE `entry` = 40440; +UPDATE `item_template` SET `armor` = 1239, `MaxDurability` = 55 WHERE `entry` = 40441; +UPDATE `item_template` SET `armor` = 1611, `MaxDurability` = 100 WHERE `entry` = 40442; +UPDATE `item_template` SET `armor` = 1735, `MaxDurability` = 120 WHERE `entry` = 40443; +UPDATE `item_template` SET `armor` = 1487, `MaxDurability` = 100 WHERE `entry` = 40444; diff --git a/data/sql/updates/db_world/2025_08_27_01.sql b/data/sql/updates/db_world/2025_08_27_01.sql new file mode 100644 index 000000000..eab3c1735 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_27_01.sql @@ -0,0 +1,83 @@ +-- DB update 2025_08_27_00 -> 2025_08_27_01 +SET @SAY_APPROACH = 0, +@SAY_AGGRO = 1, +@SAY_SUMMON = 2, +@GUID = 12748; + +DELETE FROM `areatrigger_scripts` WHERE `entry` IN (5014, 5015); +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(5014, 'at_karazhan_mirkblood_approach'), +(5015, 'at_karazhan_mirkblood_entrance'); + +UPDATE `creature_template` SET `minlevel` = 73, `maxlevel` = 73, `speed_run` = 1.85714285714, `ScriptName` = 'boss_tenris_mirkblood' WHERE `entry` = 28194; +UPDATE `creature_template` SET `speed_walk` = 0.4, `speed_run` = 0.14285714285, `ScriptName` = 'npc_sanguine_spirit' WHERE `entry` = 28232; +UPDATE `creature_template` SET `unit_flags` = 33554432, `AIName` = 'SmartAI' WHERE `entry` = 28485; +UPDATE `creature_template` SET `unit_flags` = 33555200 WHERE `entry` = 28493; + +UPDATE `creature_model_info` SET `BoundingRadius` = 0.200000002980232238, `CombatReach` = 0.400000005960464477 WHERE `DisplayID` = 25296; +UPDATE `creature_model_info` SET `BoundingRadius` = 0.465000003576278686, `CombatReach` = 1.5 WHERE `DisplayID` = 25541; + +DELETE FROM `creature_text` WHERE `CreatureID` = 28194; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(28194, @SAY_APPROACH, 0, 'I smell... $r. Delicious!', 14, 0, 100, 0, 0, 0, 27780, 0, 'Prince Tenris Mirkblood - SAY_APPROACH'), +(28194, @SAY_AGGRO, 0, 'I shall consume you!', 14, 0, 100, 0, 0, 0, 27781, 0, 'Prince Tenris Mirkblood - SAY_AGGRO'), +(28194, @SAY_SUMMON, 0, 'Drink, mortals! Taste my blood! Taste your death!', 12, 0, 100, 0, 0, 0, 27712, 0, 'Prince Tenris Mirkblood - SAY_SUMMON'); + +UPDATE `gameobject_template` SET `ScriptName` = 'go_blood_drenched_door' WHERE `entry` = 181032; + +DELETE FROM `spell_script_names` WHERE `spell_id` IN (50883, 50925, 51013); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(50883, 'spell_mirkblood_blood_mirror_target_picker'), +(50925, 'spell_mirkblood_dash_gash_return_to_tank_pre_spell'), +(51013, 'spell_mirkblood_exsanguinate'); + +DELETE FROM `spell_linked_spell` WHERE `spell_trigger` = -50845 AND `spell_effect` = -50844; +INSERT INTO `spell_linked_spell` (`spell_trigger`, `spell_effect`, `type`, `comment`) VALUES +(-50845, -50844, 0, 'Tenris Mirkblood Blood Mirror'); + +DELETE FROM `creature_template_addon` WHERE `entry` IN (28232, 28485, 28493); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(28232, 0, 0, 0, 0, 0, 0, '51282'), +(28485, 0, 0, 0, 0, 0, 0, '30987'), +(28493, 0, 0, 0, 0, 383, 0, ''); + +DELETE FROM `creature_template_movement` WHERE `CreatureId` IN (28485, 28493); +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES +(28485, 0, 0, 1, 0, 0, 0, NULL), +(28493, 0, 0, 1, 0, 0, 0, NULL); + +DELETE FROM `creature` WHERE `guid` BETWEEN @GUID+0 AND @GUID+9 AND `id1` IN (28485, 28493); +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(@GUID+0, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11087.619, -1996.4193, 82.59072, 0.453785598278045654, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+1, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11104.703, -1973.5052, 82.73294, 0.05235987901687622, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+2, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11084.556, -1981.4388, 82.4658, 0.575958669185638427, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+3, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11091.643, -1961.8134, 82.77006, 0.104719758033752441, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+4, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11097.971, -1982.734, 82.39082, 0.418879032135009765, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+5, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11097.721, -1982.62, 77.43985, 5.113814830780029296, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+6, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11104.523, -1973.4592, 78.07421, 1.396263360977172851, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+7, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11084.696, -1981.4202, 77.87848, 4.904375076293945312, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+8, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11091.594, -1962.1276, 78.054115, 2.146754980087280273, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+9, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11087.617, -1996.2291, 77.72322, 0.436332315206527709, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL); + +DELETE FROM `game_event_creature` WHERE `eventEntry` = 120 AND `guid` BETWEEN @GUID+0 AND @GUID+9; +INSERT INTO `game_event_creature` (`eventEntry`, `guid`) VALUES +(120, @GUID+0), +(120, @GUID+1), +(120, @GUID+2), +(120, @GUID+3), +(120, @GUID+4), +(120, @GUID+5), +(120, @GUID+6), +(120, @GUID+7), +(120, @GUID+8), +(120, @GUID+9); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28485) 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`, `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 +(28485, 0, 0, 0, 37, 0, 100, 0, 0, 0, 0, 0, 0, 11, 51773, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blood Vat Bunny - On Initialize - Cast \'Scourge Invasion Blood Vat Bunny\''); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 51773 AND `ConditionTypeOrReference` = 31 AND `ConditionValue2` = 28485; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 51773, 0, 0, 31, 0, 3, 28485, 0, 0, 0, 0, '', 'Target must be unit Blood Vat Bunny'); + +UPDATE `spell_dbc` SET `Effect_1` = 28, `EffectMiscValue_1` = 28232, `EffectMiscValueB_1` = 64 WHERE `ID` = 50996; diff --git a/data/sql/updates/db_world/2025_08_27_02.sql b/data/sql/updates/db_world/2025_08_27_02.sql new file mode 100644 index 000000000..ee7d79294 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_27_02.sql @@ -0,0 +1,14 @@ +-- DB update 2025_08_27_01 -> 2025_08_27_02 +-- +UPDATE `creature_template` SET `ScriptName` = '', `AIName` = 'SmartAI' WHERE (`entry` = 16143); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 16143) AND (`source_type` = 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 +(16143, 0, 0, 1, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 5000, 1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Summoned - Say Line 0'), +(16143, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 10389, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Summoned - Cast \'Spawn Smoke\''), +(16143, 0, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 256, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Summoned - Set Flags Immune To Players'), +(16143, 0, 3, 0, 52, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 256, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Text 0 Over - Remove Flags Immune To Players'), +(16143, 0, 4, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 28056, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Died - Cast \'Zap Crystal Corpse\''), +(16143, 0, 5, 0, 8, 0, 100, 0, 17680, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Spellhit \'Spirit Spawn-out\' - Despawn In 3000 ms'), +(16143, 0, 6, 0, 0, 0, 100, 0, 2000, 2000, 6500, 13000, 0, 0, 11, 16568, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - In Combat - Cast \'Mind Flay\''), +(16143, 0, 7, 0, 0, 0, 100, 0, 2000, 2000, 14500, 14500, 0, 0, 11, 12542, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - In Combat - Cast \'Fear\''); diff --git a/data/sql/updates/db_world/2025_08_28_00.sql b/data/sql/updates/db_world/2025_08_28_00.sql new file mode 100644 index 000000000..befc80e5b --- /dev/null +++ b/data/sql/updates/db_world/2025_08_28_00.sql @@ -0,0 +1,2 @@ +-- DB update 2025_08_27_02 -> 2025_08_28_00 +UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 1 WHERE `entry` = 28194; diff --git a/data/sql/updates/db_world/2025_08_29_00.sql b/data/sql/updates/db_world/2025_08_29_00.sql new file mode 100644 index 000000000..0d320af88 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_29_00.sql @@ -0,0 +1,43 @@ +-- DB update 2025_08_28_00 -> 2025_08_29_00 +-- Necrotic Runes +DELETE FROM `creature_loot_template` WHERE `Item` = 22484; +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(14697, 22484, 0, 100, 0, 1, 0, 2, 3, 'Lumbering Horror - Necrotic Rune'), +(16379, 22484, 0, 100, 0, 1, 0, 2, 3, 'Spirit of the Damned - Necrotic Rune'), +(16380, 22484, 0, 100, 0, 1, 0, 2, 3, 'Bone Witch - Necrotic Rune'), +(16143, 22484, 0, 100, 0, 1, 0, 30, 30, 'Shadow of Doom - Necrotic Rune'), +(16141, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Ghoul Berserker - Necrotic Rune'), +(16298, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Spectral Soldier - Necrotic Rune'), +(16299, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Skeletal Shocktrooper - Necrotic Rune'), +(16383, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Flameshocker - Necrotic Rune'); + +-- Sealed Research Report items +DELETE FROM `creature_loot_template` WHERE `Item` IN (22970, 22972, 22973, 22974, 22975, 22977); +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(16141, 22970, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Bloodstained Envelope'), +(16141, 22972, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Careworn Note'), +(16141, 22973, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Crumpled Missive'), +(16141, 22974, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Ragged Page'), +(16141, 22975, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Smudged Document'), +(16141, 22977, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Torn Letter'), +(16298, 22970, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Bloodstained Envelope'), +(16298, 22972, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Careworn Note'), +(16298, 22973, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Crumpled Missive'), +(16298, 22974, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Ragged Page'), +(16298, 22975, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Smudged Document'), +(16298, 22977, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Torn Letter'), +(16299, 22970, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Bloodstained Envelope'), +(16299, 22972, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Careworn Note'), +(16299, 22973, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Crumpled Missive'), +(16299, 22974, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Ragged Page'), +(16299, 22975, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Smudged Document'), +(16299, 22977, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Torn Letter'), +(16383, 22970, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Bloodstained Envelope'), +(16383, 22972, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Careworn Note'), +(16383, 22973, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Crumpled Missive'), +(16383, 22974, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Ragged Page'), +(16383, 22975, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Smudged Document'), +(16383, 22977, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Torn Letter'); + +-- Dim Necrotic Stone +UPDATE `creature_loot_template` SET `Chance` = 25 WHERE `Item` = 22892; diff --git a/data/sql/updates/db_world/2025_08_29_01.sql b/data/sql/updates/db_world/2025_08_29_01.sql new file mode 100644 index 000000000..0abfb0727 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_29_01.sql @@ -0,0 +1,4 @@ +-- DB update 2025_08_29_00 -> 2025_08_29_01 +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 202) 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 +(202, 0, 0, 0, 101, 0, 100, 0, 2, 5, 5000, 9000, 13000, 0, 11, 7399, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Horror - On 2 or More Players in Range - Cast \'Terrify\''); diff --git a/data/sql/updates/db_world/2025_08_30_00.sql b/data/sql/updates/db_world/2025_08_30_00.sql new file mode 100644 index 000000000..884506500 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_30_00.sql @@ -0,0 +1,2 @@ +-- DB update 2025_08_29_01 -> 2025_08_30_00 +UPDATE `creature` SET `spawntimesecs` = 604800 WHERE `id1` = 28194; diff --git a/data/sql/updates/db_world/2025_08_30_01.sql b/data/sql/updates/db_world/2025_08_30_01.sql new file mode 100644 index 000000000..184fa73ce --- /dev/null +++ b/data/sql/updates/db_world/2025_08_30_01.sql @@ -0,0 +1,6 @@ +-- DB update 2025_08_30_00 -> 2025_08_30_01 +-- Update creature 'Tahu Sagewind' with sniffed values +-- new spawns +DELETE FROM `creature` WHERE (`id1` IN (34528)) AND (`guid` IN (37)); +INSERT INTO `creature` (`guid`, `id1`, `map`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(37, 34528, 1, 1, 1, 0, -1047.1771240234375, -287.98785400390625, 159.113677978515625, 2.728425025939941406, 120, 0, 0, 0, 0, 0, "", 45435, 1, NULL); diff --git a/data/sql/updates/db_world/2025_08_30_02.sql b/data/sql/updates/db_world/2025_08_30_02.sql new file mode 100644 index 000000000..89b84f42c --- /dev/null +++ b/data/sql/updates/db_world/2025_08_30_02.sql @@ -0,0 +1,13 @@ +-- DB update 2025_08_30_01 -> 2025_08_30_02 +-- Update gameobject 'Mag'har Rug' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (182257)) AND (`guid` IN (22684, 22685, 22686)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(22684, 182257, 530, 0, 0, 1, 1, -1235.0460205078125, 7247.54248046875, 57.33856201171875, 4.97418975830078125, 0, 0, -0.60876083374023437, 0.793353796005249023, 120, 255, 1, "", 45704, NULL), +(22685, 182257, 530, 0, 0, 1, 1, -1242.7056884765625, 7246.685546875, 57.29010009765625, 4.97418975830078125, 0, 0, -0.60876083374023437, 0.793353796005249023, 120, 255, 1, "", 45704, NULL), +(22686, 182257, 530, 0, 0, 1, 1, -1238.972412109375, 7247.02001953125, 57.3078765869140625, 4.97418975830078125, 0, 0, -0.60876083374023437, 0.793353796005249023, 120, 255, 1, "", 45704, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (182257)) AND (`guid` IN (38)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(38, 182257, 1, 0, 0, 1, 1, -1049.82470703125, -286.196197509765625, 159.0303497314453125, 2.548179388046264648, 0, 0, 0.956304550170898437, 0.292372345924377441, 120, 255, 1, "", 45435, NULL); diff --git a/data/sql/updates/db_world/2025_08_30_03.sql b/data/sql/updates/db_world/2025_08_30_03.sql new file mode 100644 index 000000000..c2927a889 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_30_03.sql @@ -0,0 +1,12 @@ +-- DB update 2025_08_30_02 -> 2025_08_30_03 +-- Update gameobject 'Stranglekelp Sack' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (185004)) AND (`guid` IN (25928)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(25928, 185004, 530, 0, 0, 1, 1, 728.25927734375, 6844.61083984375, -66.3580474853515625, 4.433136463165283203, 0, 0, -0.79863548278808593, 0.60181504487991333, 120, 255, 1, "", 45942, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (185004)) AND (`guid` IN (84, 85)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(84, 185004, 1, 0, 0, 1, 1, -1049.5347900390625, -290.34722900390625, 159.0303497314453125, 0.209439441561698913, 0, 0, 0.104528427124023437, 0.994521915912628173, 120, 255, 1, "", 45435, NULL), +(85, 185004, 1, 0, 0, 1, 1, -1050.2257080078125, -290.552093505859375, 159.0303497314453125, 2.495818138122558593, 0, 0, 0.948323249816894531, 0.317305892705917358, 120, 255, 1, "", 45435, NULL); diff --git a/data/sql/updates/db_world/2025_09_02_00.sql b/data/sql/updates/db_world/2025_09_02_00.sql new file mode 100644 index 000000000..08ea0a4fe --- /dev/null +++ b/data/sql/updates/db_world/2025_09_02_00.sql @@ -0,0 +1,14 @@ +-- DB update 2025_08_30_03 -> 2025_09_02_00 + +-- Morbid Carcass, Vault Geist, Rabid Cannibal, Death Knight Master +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (29719, 29720, 29722, 29738)); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` IN (29719, 29720, 29722, 29738)); +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 +(29719, 0, 0, 0, 0, 0, 100, 0, 8000, 12000, 8000, 12000, 0, 0, 11, 40504, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Morbid Carcass - In Combat - Cast \'Cleave\''), +(29719, 0, 1, 0, 9, 0, 100, 0, 8000, 12000, 8000, 12000, 8, 40, 11, 50335, 0, 0, 0, 0, 0, 5, 40, 0, 0, 0, 0, 0, 0, 0, 'Morbid Carcass - Within 8-40 Range - Cast \'Scourge Hook\''), +(29720, 0, 0, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 97, 20, 10, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Vault Geist - On Aggro - Jump To Pos'), +(29720, 0, 1, 0, 0, 0, 100, 0, 4000, 6000, 18000, 24000, 0, 0, 11, 36590, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Vault Geist - In Combat - Cast \'Rip\''), +(29738, 0, 0, 0, 0, 0, 100, 0, 0, 0, 20000, 24000, 0, 0, 11, 50688, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Death Knight Master - In Combat - Cast \'Plague Strike\''), +(29738, 0, 1, 0, 60, 0, 100, 0, 0, 0, 30000, 30000, 0, 0, 11, 50689, 32, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Death Knight Master - On Update - Cast \'Blood Presence\''), +(29722, 0, 0, 0, 0, 0, 100, 0, 2000, 4000, 18000, 22000, 0, 0, 11, 30639, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Rabid Cannibal - In Combat - Cast \'Carnivorous Bite\''); diff --git a/data/sql/updates/db_world/2025_09_02_01.sql b/data/sql/updates/db_world/2025_09_02_01.sql new file mode 100644 index 000000000..58a544d75 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_02_01.sql @@ -0,0 +1,4 @@ +-- DB update 2025_09_02_00 -> 2025_09_02_01 +DELETE FROM `creature` WHERE `guid` = 1741 AND `id1` = 14724; +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(1741, 14724, 0, 0, 0, 0, 0, 1, 1, 0, -4823.649, -1299.3605, 501.95117, 0.959931075572967529, 300, 0, 0, 1220, 0, 0, 0, 0, 0, '', 45613, 1, NULL); diff --git a/data/sql/updates/db_world/2025_09_02_02.sql b/data/sql/updates/db_world/2025_09_02_02.sql new file mode 100644 index 000000000..922f9cb8d --- /dev/null +++ b/data/sql/updates/db_world/2025_09_02_02.sql @@ -0,0 +1,5 @@ +-- DB update 2025_09_02_01 -> 2025_09_02_02 +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28851) AND (`source_type` = 0) AND (`id` IN (2)); +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 +(28851, 0, 2, 0, 28, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Enraged Mammoth - On Passenger Removed - Despawn In 3000 ms'); diff --git a/data/sql/updates/db_world/2025_09_03_00.sql b/data/sql/updates/db_world/2025_09_03_00.sql new file mode 100644 index 000000000..0630c0561 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_03_00.sql @@ -0,0 +1,151 @@ +-- DB update 2025_09_02_02 -> 2025_09_03_00 +-- Update gameobject 'various doodad' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (193692, 193766, 193765, 193764, 193763, 193762, 193761, 193760, 193759, 193758, 193757, 193756, 193755, 193754, 193753, 193752, 193751, 193750, 193749, 193748, 193747, 193746, 193745, 193744, 193743, 193742, 193741, 193740, 193739, 193738, 193737, 193736, 193735, 193734, 193733, 193732, 193731, 193730, 193729, 193728, 193727, 193726, 193725, 193724, 193723, 193722, 193721, 193720, 193719, 193718, 193717, 193716, 193715, 193714, 193713, 193712, 193711, 193710, 193709, 193708, 193707, 193706, 193705, 193704, 193703, 193702, 193701, 193700, 193699, 193698, 193697, 193696, 193693, 193691, 193678, 193677, 193676, 193674, 193669, 193668, 193666, 193665, 193664, 193663, 193659, 193658, 193657, 193662, 193660, 193661, 193654, 193652, 193651, 193649, 193650, 193671, 193670, 193645, 193644, 193643, 193642, 193637, 193632, 193631, 193630, 193653, 193636, 193635, 193634, 193633, 193681, 193680)) AND (`guid` IN (255513, 268696, 268697, 268698, 268699, 268700, 268701, 268702, 268703, 268704, 268705, 268706, 268707, 268708, 268709, 268710, 268711, 268712, 268713, 268714, 268715, 268716, 268717, 268718, 268719, 268720, 268721, 268722, 268723, 268724, 268725, 268726, 268727, 268728, 268729, 268730, 268731, 268732, 268733, 268734, 268735, 268736, 268737, 268738, 268739, 268740, 268741, 268742, 268743, 268744, 268745, 268746, 268747, 268748, 268749, 268750, 268751, 268752, 268753, 268754, 268755, 268756, 268757, 268758, 268759, 268760, 268761, 268762, 268763, 268764, 268765, 268766, 268772, 268773, 268774, 268775, 268779, 268780, 268781, 268782, 268783, 268784, 268785, 268786, 268790, 268791, 268792, 268793, 268794, 268795, 268799, 268800, 268807, 268808, 268809, 268815, 268816, 268818, 268819, 268820, 268821, 268822, 268825, 268826, 268827, 268829, 268832, 268833, 268834, 268835, 268839, 268840)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(255513, 193692, 571, 0, 0, 1, 1, 7882.8623046875, 2043.8250732421875, 600.73297119140625, 4.665524959564208984, 0.187406539916992187, -0.31766891479492187, -0.64971733093261718, 0.66470491886138916, 120, 255, 1, "", 46368, NULL), +(268696, 193766, 571, 0, 0, 1, 1, 7637.41064453125, 2072.35791015625, 600.27178955078125, 1.248236894607543945, 0.51220560073852539, 0.357756614685058593, 0.46212005615234375, 0.629365265369415283, 120, 255, 1, "", 50664, NULL), +(268697, 193765, 571, 0, 0, 1, 1, 7635.556640625, 2046.7196044921875, 601.6680908203125, 1.269469738006591796, -0.03150653839111328, 0.077538490295410156, 0.589531898498535156, 0.803397297859191894, 120, 255, 1, "", 50664, NULL), +(268698, 193764, 571, 0, 0, 1, 1, 7625.869140625, 2060.052978515625, 604.26983642578125, 0.078540004789829254, 0, 0, 0.039259910583496093, 0.999229073524475097, 120, 255, 1, "", 50664, NULL), +(268699, 193763, 571, 0, 0, 1, 1, 7625.7724609375, 2060.06494140625, 600.8868408203125, 0.063891783356666564, 0, 0, 0.031940460205078125, 0.999489784240722656, 120, 255, 1, "", 50664, NULL), +(268700, 193762, 571, 0, 0, 1, 1, 7625.6640625, 2060.03564453125, 604.1954345703125, 3.22885894775390625, 0, 0, -0.99904823303222656, 0.043619260191917419, 120, 255, 1, "", 50664, NULL), +(268701, 193761, 571, 0, 0, 1, 1, 7629.734375, 2062.71484375, 600.2579345703125, 2.95832991600036621, 0, 0, 0.995804786682128906, 0.091503240168094635, 120, 255, 1, "", 50664, NULL), +(268702, 193760, 571, 0, 0, 1, 1, 7630.5419921875, 2062.549560546875, 600.24835205078125, 4.249876976013183593, 0, 0, -0.85035228729248046, 0.526213824748992919, 120, 255, 1, "", 50664, NULL), +(268703, 193759, 571, 0, 0, 1, 1, 7629.98193359375, 2061.771240234375, 600.24383544921875, 3.351046562194824218, 0, 0, -0.99452114105224609, 0.104535527527332305, 120, 255, 1, "", 50664, NULL), +(268704, 193758, 571, 0, 0, 1, 1, 7628.8798828125, 2060.107666015625, 600.49481201171875, 4.721115589141845703, 0, 0, -0.70401477813720703, 0.71018528938293457, 120, 255, 1, "", 50664, NULL), +(268705, 193757, 571, 0, 0, 1, 1, 7628.60400390625, 2060.201171875, 599.6319580078125, 4.616395950317382812, 0, 0, -0.74021816253662109, 0.672366797924041748, 120, 255, 1, "", 50664, NULL), +(268706, 193756, 571, 0, 0, 1, 1, 7628.60400390625, 2060.201171875, 598.53387451171875, 6.143558979034423828, 0, 0, -0.06975650787353515, 0.997564077377319335, 120, 255, 1, "", 50664, NULL), +(268707, 193755, 571, 0, 0, 1, 1, 7617.7353515625, 2050.1357421875, 600.6690673828125, 5.480334281921386718, 0, 0, -0.39073085784912109, 0.920504987239837646, 120, 255, 1, "", 50664, NULL), +(268708, 193754, 571, 0, 0, 1, 1, 7630.58837890625, 2060.197998046875, 600.16461181640625, 0.017452461645007133, 0, 0, 0.008726119995117187, 0.999961912631988525, 120, 255, 1, "", 50664, NULL), +(268709, 193753, 571, 0, 0, 1, 1, 7615.00048828125, 2053.274658203125, 601.31890869140625, 4.834563255310058593, -0.07124805450439453, -0.11837959289550781, -0.65368366241455078, 0.744048118591308593, 120, 255, 1, "", 50664, NULL), +(268710, 193752, 571, 0, 0, 1, 1, 7614.63818359375, 2053.234130859375, 601.3212890625, 4.1538848876953125, 0, 0, -0.8746194839477539, 0.484810054302215576, 120, 255, 1, "", 50664, NULL), +(268711, 193751, 571, 0, 0, 1, 1, 7614.73583984375, 2052.607177734375, 601.3494873046875, 5.52397012710571289, 0.025708198547363281, 0.1858978271484375, -0.35620498657226562, 0.915368258953094482, 120, 255, 1, "", 50664, NULL), +(268712, 193750, 571, 0, 0, 1, 1, 7614.7763671875, 2052.47705078125, 600.12860107421875, 0.759216904640197753, 0, 0, 0.370556831359863281, 0.928809821605682373, 120, 255, 1, "", 50664, NULL), +(268713, 193749, 571, 0, 0, 1, 1, 7621.33642578125, 2045.822998046875, 600.0079345703125, 5.864306926727294921, 0, 0, -0.20791149139404296, 0.978147625923156738, 120, 255, 1, "", 50664, NULL), +(268714, 193748, 571, 0, 0, 1, 1, 7621.1689453125, 2048.1103515625, 600.03460693359375, 3.194002151489257812, 0, 0, -0.99965667724609375, 0.026201646775007247, 120, 255, 1, "", 50664, NULL), +(268715, 193747, 571, 0, 0, 1, 1, 7619.73583984375, 2046.9991455078125, 600.139892578125, 4.127707481384277343, 0, 0, -0.880889892578125, 0.473321229219436645, 120, 255, 1, "", 50664, NULL), +(268716, 193746, 571, 0, 0, 1, 1, 7611.34130859375, 2059.42138671875, 600.2303466796875, 0.680676698684692382, 0, 0, 0.333806037902832031, 0.942641794681549072, 120, 255, 1, "", 50664, NULL), +(268717, 193745, 571, 0, 0, 1, 1, 7611.443359375, 2061.458740234375, 600.2303466796875, 2.076939344406127929, 0, 0, 0.861628532409667968, 0.50753939151763916, 120, 255, 1, "", 50664, NULL), +(268718, 193744, 571, 0, 0, 1, 1, 7611.46630859375, 2060.24267578125, 601.098876953125, 1.474833250045776367, 0.740206718444824218, 0.672366142272949218, 0.004041671752929687, 0.001196039956994354, 120, 255, 1, "", 50664, NULL), +(268719, 193743, 571, 0, 0, 1, 1, 7629.513671875, 2043.40283203125, 600.21649169921875, 2.303830623626708984, 0, 0, 0.913544654846191406, 0.406738430261611938, 120, 255, 1, "", 50664, NULL), +(268720, 193742, 571, 0, 0, 1, 1, 7627.474609375, 2043.3980712890625, 600.215576171875, 3.70009779930114746, 0, 0, -0.96126174926757812, 0.275637149810791015, 120, 255, 1, "", 50664, NULL), +(268721, 193741, 571, 0, 0, 1, 1, 7628.4716796875, 2043.3489990234375, 601.08172607421875, 3.097992658615112304, -0.02180719375610351, -0.99975299835205078, -0.00331974029541015, 0.002718634903430938, 120, 255, 1, "", 50664, NULL), +(268722, 193740, 571, 0, 0, 1, 1, 7620.10205078125, 2074.37548828125, 601.4208984375, 6.012660503387451171, 0, 0, -0.13485050201416015, 0.990865945816040039, 120, 255, 1, "", 50664, NULL), +(268723, 193739, 571, 0, 0, 1, 1, 7621.29052734375, 2074.291748046875, 601.4190673828125, 0.157077088952064514, 0, 0, 0.078457832336425781, 0.996917426586151123, 120, 255, 1, "", 50664, NULL), +(268724, 193738, 571, 0, 0, 1, 1, 7621.4072265625, 2072.64208984375, 601.4334716796875, 1.954769015312194824, 0, 0, 0.829037666320800781, 0.559192776679992675, 120, 255, 1, "", 50664, NULL), +(268725, 193737, 571, 0, 0, 1, 1, 7620.9501953125, 2073.94580078125, 600.21722412109375, 5.253442287445068359, 0, 0, -0.49242305755615234, 0.870355963706970214, 120, 255, 1, "", 50664, NULL), +(268726, 193736, 571, 0, 0, 1, 1, 7618.22607421875, 2070.371337890625, 600.55718994140625, 3.892086982727050781, 0, 0, -0.93041706085205078, 0.366502493619918823, 120, 255, 1, "", 50664, NULL), +(268727, 193735, 571, 0, 0, 1, 1, 7613.96484375, 2068.400634765625, 600.26336669921875, 5.410520076751708984, 0.18164825439453125, -0.05159950256347656, -0.42017078399658203, 0.887579798698425292, 120, 255, 1, "", 50664, NULL), +(268728, 193734, 571, 0, 0, 1, 1, 7614.1796875, 2069.150146484375, 600.2694091796875, 2.277705669403076171, 0.4829254150390625, 0.523255348205566406, -0.48152923583984375, 0.510995566844940185, 120, 255, 1, "", 50664, NULL), +(268729, 193733, 571, 0, 0, 1, 1, 7614.11865234375, 2068.62841796875, 600.2442626953125, 2.862335443496704101, 0, 0, 0.990267753601074218, 0.139175355434417724, 120, 255, 1, "", 50664, NULL), +(268730, 193732, 571, 0, 0, 1, 1, 7613.57958984375, 2067.849365234375, 600.361083984375, 0.758936166763305664, 0.710488319396972656, 0.047131538391113281, -0.70103359222412109, 0.039202630519866943, 120, 255, 1, "", 50664, NULL), +(268731, 193731, 571, 0, 0, 1, 1, 7613.70166015625, 2066.873779296875, 600.338623046875, 5.733424663543701171, -0.62461566925048828, 0.353822708129882812, 0.599942207336425781, 0.353177070617675781, 120, 255, 1, "", 50664, NULL), +(268732, 193730, 571, 0, 0, 1, 1, 7613.63330078125, 2067.5244140625, 600.229736328125, 2.809975385665893554, 0, 0, 0.986285209655761718, 0.165049895644187927, 120, 255, 1, "", 50664, NULL), +(268733, 193729, 571, 0, 0, 1, 1, 7627.837890625, 2076.871337890625, 600.24664306640625, 5.471607208251953125, 0, 0, -0.39474391937255859, 0.918791174888610839, 120, 255, 1, "", 50664, NULL), +(268734, 193728, 571, 0, 0, 1, 1, 7628.87841796875, 2076.95263671875, 601.11578369140625, 6.265766620635986328, 0.999953269958496093, -0.00871753692626953, 0.00246429443359375, 0.00337409065105021, 120, 255, 1, "", 50664, NULL), +(268735, 193727, 571, 0, 0, 1, 1, 7629.87646484375, 2076.929931640625, 600.24664306640625, 0.584683895111083984, 0, 0, 0.288195610046386718, 0.957571566104888916, 120, 255, 1, "", 50664, NULL), +(268736, 193726, 571, 0, 0, 1, 1, 7644.1845703125, 2052.05908203125, 600.23858642578125, 2.085667610168457031, 0, 0, 0.863835334777832031, 0.503774285316467285, 120, 255, 1, "", 50664, NULL), +(268737, 193725, 571, 0, 0, 1, 1, 7644.37548828125, 2052.462158203125, 600.2384033203125, 4.598945140838623046, 0, 0, -0.74605655670166015, 0.665882587432861328, 120, 255, 1, "", 50664, NULL), +(268738, 193724, 571, 0, 0, 1, 1, 7644.04248046875, 2052.38671875, 600.23297119140625, 0.706856131553649902, 0.038586616516113281, -0.14088153839111328, 0.340669631958007812, 0.928766727447509765, 120, 255, 1, "", 50664, NULL), +(268739, 193723, 571, 0, 0, 1, 1, 7636.345703125, 2046.3046875, 601.35015869140625, 0.575957715511322021, -0.09489917755126953, 0.161904335021972656, 0.286963462829589843, 0.939379096031188964, 120, 255, 1, "", 50664, NULL), +(268740, 193722, 571, 0, 0, 1, 1, 7636.70751953125, 2047.2916259765625, 601.309326171875, 3.010666131973266601, 0, 0, 0.997858047485351562, 0.065416477620601654, 120, 255, 1, "", 50664, NULL), +(268741, 193721, 571, 0, 0, 1, 1, 7636.48095703125, 2046.31298828125, 600.129638671875, 2.094393253326416015, 0, 0, 0.866024971008300781, 0.50000077486038208, 120, 255, 1, "", 50664, NULL), +(268742, 193720, 571, 0, 0, 1, 1, 7639.40576171875, 2049.9814453125, 600.68109130859375, 0.750488698482513427, 0, 0, 0.366499900817871093, 0.930418074131011962, 120, 255, 1, "", 50664, NULL), +(268743, 193719, 571, 0, 0, 1, 1, 7642.1875, 2066.251953125, 601.51141357421875, 4.319693565368652343, 0.329238414764404296, -0.62578010559082031, -0.54365253448486328, 0.452154040336608886, 120, 255, 1, "", 50664, NULL), +(268744, 193718, 571, 0, 0, 1, 1, 7641.9541015625, 2051.512939453125, 600.240234375, 2.923415660858154296, 0, 0, 0.994055747985839843, 0.108872212469577789, 120, 255, 1, "", 50664, NULL), +(268745, 193717, 571, 0, 0, 1, 1, 7642.1845703125, 2051.5986328125, 600.46063232421875, 5.174901962280273437, 0, 0, -0.52621364593505859, 0.850352406501770019, 120, 255, 1, "", 50664, NULL), +(268746, 193716, 571, 0, 0, 1, 1, 7642.3935546875, 2051.588134765625, 600.239990234375, 3.63901376724243164, 0, 0, -0.96923065185546875, 0.246154293417930603, 120, 255, 1, "", 50664, NULL), +(268747, 193715, 571, 0, 0, 1, 1, 7642.1396484375, 2051.818115234375, 600.2618408203125, 5.349435329437255859, 0, 0, -0.45009803771972656, 0.892979145050048828, 120, 255, 1, "", 50664, NULL), +(268748, 193714, 571, 0, 0, 1, 1, 7642.29345703125, 2051.72412109375, 600.26397705078125, 4.860742568969726562, 0, 0, -0.65275955200195312, 0.757565200328826904, 120, 255, 1, "", 50664, NULL), +(268749, 193713, 571, 0, 0, 1, 1, 7642.54052734375, 2051.8837890625, 600.25, 1.844759345054626464, 0.050289154052734375, 0.201838493347167968, 0.769841194152832031, 0.603387713432312011, 120, 255, 1, "", 50664, NULL), +(268750, 193712, 571, 0, 0, 1, 1, 7642.7177734375, 2051.58984375, 600.26141357421875, 1.710421562194824218, 0, 0, 0.754709243774414062, 0.656059443950653076, 120, 255, 1, "", 50664, NULL), +(268751, 193711, 571, 0, 0, 1, 1, 7645.83544921875, 2061.108642578125, 600.2529296875, 3.848450660705566406, 0, 0, -0.93819141387939453, 0.346116840839385986, 120, 255, 1, "", 50664, NULL), +(268752, 193710, 571, 0, 0, 1, 1, 7645.78662109375, 2059.0693359375, 600.252685546875, 5.244716167449951171, 0, 0, -0.4962158203125, 0.86819922924041748, 120, 255, 1, "", 50664, NULL), +(268753, 193709, 571, 0, 0, 1, 1, 7645.86181640625, 2060.064697265625, 601.121826171875, 4.642610549926757812, 0.681998252868652343, -0.73134136199951171, -0.00068855285644531, 0.004207443445920944, 120, 255, 1, "", 50664, NULL), +(268754, 193708, 571, 0, 0, 1, 1, 7636.177734375, 2073.49462890625, 601.4434814453125, 3.420847892761230468, -0.21741914749145507, -0.08422660827636718, -0.96116828918457031, 0.1476154625415802, 120, 255, 1, "", 50664, NULL), +(268755, 193707, 571, 0, 0, 1, 1, 7639.47998046875, 2069.831787109375, 600.65679931640625, 2.347463846206665039, 0, 0, 0.922200202941894531, 0.386712819337844848, 120, 255, 1, "", 50664, NULL), +(268756, 193706, 571, 0, 0, 1, 1, 7642.2158203125, 2066.698486328125, 601.4727783203125, 5.715955257415771484, 0, 0, -0.27982807159423828, 0.960050106048583984, 120, 255, 1, "", 50664, NULL), +(268757, 193705, 571, 0, 0, 1, 1, 7641.5390625, 2066.837890625, 601.4727783203125, 0.401424884796142578, 0, 0, 0.199367523193359375, 0.979924798011779785, 120, 255, 1, "", 50664, NULL), +(268758, 193704, 571, 0, 0, 1, 1, 7642.36376953125, 2067.4638671875, 601.4591064453125, 3.395873546600341796, 0.056763648986816406, 0.014508247375488281, -0.99017143249511718, 0.126995846629142761, 120, 255, 1, "", 50664, NULL), +(268759, 193703, 571, 0, 0, 1, 1, 7642.8466796875, 2067.182861328125, 601.47882080078125, 2.1816558837890625, -0.18238639831542968, 0.044202804565429687, 0.875073432922363281, 0.446125298738479614, 120, 255, 1, "", 50664, NULL), +(268760, 193702, 571, 0, 0, 1, 1, 7643.08251953125, 2067.653564453125, 601.462158203125, 3.307400941848754882, -0.08287525177001953, 0.042493820190429687, -0.99254989624023437, 0.078553743660449981, 120, 255, 1, "", 50664, NULL), +(268761, 193701, 571, 0, 0, 1, 1, 7642.833984375, 2067.318359375, 600.256591796875, 3.70009779930114746, 0, 0, -0.96126174926757812, 0.275637149810791015, 120, 255, 1, "", 50664, NULL), +(268762, 193700, 571, 0, 0, 1, 1, 7638.7451171875, 2073.489501953125, 600.251220703125, 1.247907638549804687, 0, 0, 0.584248542785644531, 0.811574757099151611, 120, 255, 1, "", 50664, NULL), +(268763, 193699, 571, 0, 0, 1, 1, 7637.20556640625, 2073.30859375, 601.41949462890625, 4.347476005554199218, 0.06531381607055664, 0.67337799072265625, -0.33799266815185546, 0.654260754585266113, 120, 255, 1, "", 50664, NULL), +(268764, 193698, 571, 0, 0, 1, 1, 7636.033203125, 2073.449951171875, 600.2333984375, 4.354224681854248046, -0.00527000427246093, -0.02503776550292968, -0.82129764556884765, 0.569925904273986816, 120, 255, 1, "", 50664, NULL), +(268765, 193697, 571, 0, 0, 1, 1, 7635.75830078125, 2074.88037109375, 600.2703857421875, 3.333590030670166015, 0, 0, -0.99539566040039062, 0.095851235091686248, 120, 255, 1, "", 50664, NULL), +(268766, 193696, 571, 0, 0, 1, 1, 7637.640625, 2074.039306640625, 600.2724609375, 2.609261274337768554, 0, 0, 0.964786529541015625, 0.263034075498580932, 120, 255, 1, "", 50664, NULL), +(268772, 193693, 571, 0, 0, 1, 1, 7892.458984375, 2073.525146484375, 601.7738037109375, 5.521906852722167968, -0.67214488983154296, 0.21199798583984375, -0.31161308288574218, 0.637318909168243408, 120, 255, 1, "", 46368, NULL), +(268773, 193691, 571, 0, 0, 1, 1, 7891.21435546875, 2057.974853515625, 604.2218017578125, 3.124123096466064453, 0, 0, 0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 46368, NULL), +(268774, 193678, 571, 0, 0, 1, 1, 7891.45458984375, 2058.0087890625, 600.86529541015625, 0.011531894095242023, 0, 0, 0.005765914916992187, 0.999983370304107666, 120, 255, 1, "", 46368, NULL), +(268775, 193677, 571, 0, 0, 1, 1, 7891.65234375, 2058.012939453125, 604.251953125, 6.265733242034912109, 0, 0, -0.00872611999511718, 0.999961912631988525, 120, 255, 1, "", 46368, NULL), +(268779, 193676, 571, 0, 0, 1, 1, 7888.271484375, 2058.06640625, 600.46380615234375, 1.579522013664245605, 0, 0, 0.710185050964355468, 0.704015016555786132, 120, 255, 1, "", 46368, NULL), +(268780, 193674, 571, 0, 0, 1, 1, 7888.546875, 2057.972900390625, 598.50286865234375, 3.001946926116943359, 0, 0, 0.997563362121582031, 0.069766148924827575, 120, 255, 1, "", 46368, NULL), +(268781, 193669, 571, 0, 0, 1, 1, 7877.67138671875, 2048.341796875, 600.62579345703125, 5.489060401916503906, 0, 0, -0.38671112060546875, 0.922200918197631835, 120, 255, 1, "", 46368, NULL), +(268782, 193668, 571, 0, 0, 1, 1, 7886.5634765625, 2057.9765625, 600.13427734375, 3.159062385559082031, 0, 0, -0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 46368, NULL), +(268783, 193666, 571, 0, 0, 1, 1, 7874.21435546875, 2051.378173828125, 601.40032958984375, 5.759586811065673828, 0.030743122100830078, 0.030928611755371093, -0.25936603546142578, 0.964794039726257324, 120, 255, 1, "", 46368, NULL), +(268784, 193665, 571, 0, 0, 1, 1, 7874.3037109375, 2050.991455078125, 601.44683837890625, 5.32325601577758789, 0.044202804565429687, 0.182386398315429687, -0.4461221694946289, 0.875075042247772216, 120, 255, 1, "", 46368, NULL), +(268785, 193664, 571, 0, 0, 1, 1, 7874.00244140625, 2050.78515625, 601.4483642578125, 4.642575740814208984, -0.07862043380737304, -0.12896251678466796, -0.71886634826660156, 0.678541600704193115, 120, 255, 1, "", 46368, NULL), +(268786, 193663, 571, 0, 0, 1, 1, 7874.31689453125, 2050.85595703125, 600.2255859375, 0.558503925800323486, 0, 0, 0.275636672973632812, 0.961261868476867675, 120, 255, 1, "", 46368, NULL), +(268790, 193659, 571, 0, 0, 1, 1, 7871.31591796875, 2057.065185546875, 600.2220458984375, 0.706856250762939453, 0, 0, 0.346116065979003906, 0.938191711902618408, 120, 255, 1, "", 46368, NULL), +(268791, 193658, 571, 0, 0, 1, 1, 7871.3642578125, 2059.104248046875, 600.22412109375, 2.103119850158691406, 0, 0, 0.868198394775390625, 0.496217250823974609, 120, 255, 1, "", 46368, NULL), +(268792, 193657, 571, 0, 0, 1, 1, 7871.28857421875, 2058.10986328125, 601.09088134765625, 1.501015067100524902, 0.731341838836669921, 0.681998252868652343, 0.00405120849609375, 0.001035800902172923, 120, 255, 1, "", 46368, NULL), +(268793, 193662, 571, 0, 0, 1, 1, 7889.31298828125, 2041.3023681640625, 600.216796875, 2.33001255989074707, 0, 0, 0.918790817260742187, 0.394744753837585449, 120, 255, 1, "", 46368, NULL), +(268794, 193660, 571, 0, 0, 1, 1, 7887.27392578125, 2041.244140625, 600.21728515625, 3.72628021240234375, 0, 0, -0.95757102966308593, 0.288197338581085205, 120, 255, 1, "", 46368, NULL), +(268795, 193661, 571, 0, 0, 1, 1, 7888.27294921875, 2041.2213134765625, 601.0849609375, 3.12417149543762207, -0.00871896743774414, -0.99995326995849609, -0.00328731536865234, 0.002583741443231701, 120, 255, 1, "", 46368, NULL), +(268799, 193654, 571, 0, 0, 1, 1, 7880.66943359375, 2071.861328125, 600.09869384765625, 5.235987663269042968, 0, 0, -0.5, 0.866025388240814208, 120, 255, 1, "", 46368, NULL), +(268800, 193652, 571, 0, 0, 1, 1, 7877.7451171875, 2068.192626953125, 600.65087890625, 3.892086982727050781, 0, 0, -0.93041706085205078, 0.366502493619918823, 120, 255, 1, "", 46368, NULL), +(268807, 193651, 571, 0, 0, 1, 1, 7887.63720703125, 2074.77099609375, 600.18682861328125, 5.445429801940917968, 0, 0, -0.40673542022705078, 0.91354602575302124, 120, 255, 1, "", 46368, NULL), +(268808, 193649, 571, 0, 0, 1, 1, 7888.68017578125, 2074.82470703125, 601.05072021484375, 6.239586830139160156, 0.999753475189208984, -0.0218057632446289, 0.002421379089355468, 0.00341796875, 120, 255, 1, "", 46368, NULL), +(268809, 193650, 571, 0, 0, 1, 1, 7889.67724609375, 2074.776123046875, 600.18499755859375, 0.558503925800323486, 0, 0, 0.275636672973632812, 0.961261868476867675, 120, 255, 1, "", 46368, NULL), +(268815, 193671, 571, 0, 0, 1, 1, 7896.20068359375, 2044.22802734375, 600.18603515625, 2.111847877502441406, 0, 0, 0.870355606079101562, 0.492423713207244873, 120, 255, 1, "", 46368, NULL), +(268816, 193670, 571, 0, 0, 1, 1, 7898.9248046875, 2047.802734375, 600.52520751953125, 0.750488698482513427, 0, 0, 0.366499900817871093, 0.930418074131011962, 120, 255, 1, "", 46368, NULL), +(268818, 193645, 571, 0, 0, 1, 1, 7903.21923828125, 2050.816650390625, 600.10931396484375, 1.256635904312133789, 0, 0, 0.587784767150878906, 0.809017360210418701, 120, 255, 1, "", 46368, NULL), +(268819, 193644, 571, 0, 0, 1, 1, 7903.2822265625, 2050.578857421875, 600.28997802734375, 3.508116960525512695, 0, 0, -0.98325443267822265, 0.182238012552261352, 120, 255, 1, "", 46368, NULL), +(268820, 193643, 571, 0, 0, 1, 1, 7903.251953125, 2050.372314453125, 599.96185302734375, 1.972219824790954589, 0, 0, 0.83388519287109375, 0.55193793773651123, 120, 255, 1, "", 46368, NULL), +(268821, 193642, 571, 0, 0, 1, 1, 7903.5048828125, 2050.60205078125, 600.26007080078125, 3.682650327682495117, 0, 0, -0.96362972259521484, 0.26724100112915039, 120, 255, 1, "", 46368, NULL), +(268822, 193637, 571, 0, 0, 1, 1, 7902.48046875, 2048.71728515625, 600.2122802734375, 5.166173934936523437, 0, 0, -0.52991962432861328, 0.848047912120819091, 120, 255, 1, "", 46368, NULL), +(268825, 193632, 571, 0, 0, 1, 1, 7905.810546875, 2058.752685546875, 600.1993408203125, 3.822272777557373046, 0, 0, -0.94264125823974609, 0.333807557821273803, 120, 255, 1, "", 46368, NULL), +(268826, 193631, 571, 0, 0, 1, 1, 7905.70849609375, 2056.715576171875, 600.1993408203125, 5.218533515930175781, 0, 0, -0.5075387954711914, 0.861628890037536621, 120, 255, 1, "", 46368, NULL), +(268827, 193630, 571, 0, 0, 1, 1, 7905.68505859375, 2057.931640625, 601.06793212890625, 4.616430282592773437, 0.672366619110107421, -0.74020576477050781, -0.00074100494384765, 0.00424973014742136, 120, 255, 1, "", 46368, NULL), +(268829, 193653, 571, 0, 0, 1, 1, 7899.41552734375, 2068.0380859375, 600.639404296875, 2.338739633560180664, 0, 0, 0.920504570007324218, 0.3907318115234375, 120, 255, 1, "", 46368, NULL), +(268832, 193636, 571, 0, 0, 1, 1, 7902.55126953125, 2066.074951171875, 601.30126953125, 3.508113622665405273, -0.08671522140502929, 0.033976554870605468, -0.97968578338623046, 0.177600175142288208, 120, 255, 1, "", 46368, NULL), +(268833, 193635, 571, 0, 0, 1, 1, 7902.6494140625, 2066.373046875, 601.30133056640625, 4.389505863189697265, -0.07650041580200195, -0.06483268737792968, -0.80701732635498046, 0.581951439380645751, 120, 255, 1, "", 46368, NULL), +(268834, 193634, 571, 0, 0, 1, 1, 7902.3046875, 2066.493408203125, 601.3028564453125, 3.708826541900634765, 0, 0, -0.96004962921142578, 0.279829770326614379, 120, 255, 1, "", 46368, NULL), +(268835, 193633, 571, 0, 0, 1, 1, 7902.375, 2065.69677734375, 600.09716796875, 3.900813102722167968, 0, 0, -0.92880916595458984, 0.370558410882949829, 120, 255, 1, "", 46368, NULL), +(268839, 193681, 571, 0, 0, 1, 1, 7895.1708984375, 2072.284423828125, 600.2469482421875, 1.517560839653015136, 0, 0, 0.688036918640136718, 0.725675702095031738, 120, 255, 1, "", 46368, NULL), +(268840, 193680, 571, 0, 0, 1, 1, 7893.27880859375, 2073.20849609375, 600.249755859375, 0.280996710062026977, 0, 0, 0.140036582946777343, 0.990146338939666748, 120, 255, 1, "", 46368, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (193638, 193639, 193640, 193641, 193646, 193647, 193648, 193655, 193656, 193667, 193672, 193673, 193675, 193679, 193682, 193683, 193684, 193685, 193686, 193687, 193688, 193689, 193690, 193694, 193695)) AND (`guid` IN (2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(2077, 193638, 571, 0, 0, 1, 1, 7902.05078125, 2048.413330078125, 600.2374267578125, 4.581534385681152343, -0.28159475326538085, 0.654002189636230468, 0.270960807800292968, 0.647738933563232421, 120, 255, 1, "", 46368, NULL), +(2078, 193639, 571, 0, 0, 1, 1, 7903.22216796875, 2050.048828125, 599.98638916015625, 0.043632153421640396, 0, 0, 0.021814346313476562, 0.99976205825805664, 120, 255, 1, "", 46368, NULL), +(2079, 193640, 571, 0, 0, 1, 1, 7903.53271484375, 2050.19677734375, 600.09759521484375, 0.177969858050346374, 0.183217525482177734, 0.098484039306640625, 0.070977210998535156, 0.975548326969146728, 120, 255, 1, "", 46368, NULL), +(2080, 193641, 571, 0, 0, 1, 1, 7903.39697265625, 2050.458740234375, 600.12481689453125, 3.194002151489257812, 0, 0, -0.99965667724609375, 0.026201646775007247, 120, 255, 1, "", 46368, NULL), +(2081, 193646, 571, 0, 0, 1, 1, 7904.517578125, 2052.545166015625, 600.2119140625, 6.065019607543945312, 0, 0, -0.10886669158935546, 0.994056344032287597, 120, 255, 1, "", 46368, NULL), +(2082, 193647, 571, 0, 0, 1, 1, 7903.68505859375, 2052.069091796875, 600.21636962890625, 0.680676698684692382, 0, 0, 0.333806037902832031, 0.942641794681549072, 120, 255, 1, "", 46368, NULL), +(2083, 193648, 571, 0, 0, 1, 1, 7904.35107421875, 2051.583740234375, 600.22607421875, 5.672319889068603515, 0, 0, -0.3007059097290039, 0.953716933727264404, 120, 255, 1, "", 46368, NULL), +(2084, 193655, 571, 0, 0, 1, 1, 7880.44384765625, 2070.882568359375, 601.27783203125, 6.152286052703857421, 0, 0, -0.06540298461914062, 0.997858941555023193, 120, 255, 1, "", 46368, NULL), +(2085, 193656, 571, 0, 0, 1, 1, 7880.8056640625, 2071.86962890625, 601.318603515625, 3.717553138732910156, 0.161904335021972656, 0.094899177551269531, -0.93937873840332031, 0.286964625120162963, 120, 255, 1, "", 46368, NULL), +(2086, 193667, 571, 0, 0, 1, 1, 7874.93603515625, 2051.4755859375, 601.44091796875, 2.574358940124511718, 0, 0, 0.960049629211425781, 0.279829770326614379, 120, 255, 1, "", 46368, NULL), +(2087, 193672, 571, 0, 0, 1, 1, 7895.59130859375, 2044.85791015625, 601.402587890625, 4.127707481384277343, 0, 0, -0.880889892578125, 0.473321229219436645, 120, 255, 1, "", 46368, NULL), +(2088, 193673, 571, 0, 0, 1, 1, 7895.86083984375, 2043.8819580078125, 601.3876953125, 3.298687219619750976, 0, 0, -0.99691677093505859, 0.078466430306434631, 120, 255, 1, "", 46368, NULL), +(2089, 193675, 571, 0, 0, 1, 1, 7888.546875, 2057.972900390625, 599.6009521484375, 1.474801421165466308, 0, 0, 0.672366142272949218, 0.740218758583068847, 120, 255, 1, "", 46368, NULL), +(2090, 193679, 571, 0, 0, 1, 1, 7893.048828125, 2071.62255859375, 600.365478515625, 3.813161611557006835, -0.69073057174682617, -0.15002155303955078, -0.69123363494873046, 0.150269299745559692, 120, 255, 1, "", 46368, NULL), +(2091, 193682, 571, 0, 0, 1, 1, 7895.12890625, 2072.302978515625, 601.992919921875, 0.365644693374633789, 0, 0, 0.181805610656738281, 0.983334481716156005, 120, 255, 1, "", 46368, NULL), +(2092, 193683, 571, 0, 0, 1, 1, 7875.3193359375, 2053.161376953125, 600.2493896484375, 1.278458118438720703, 0.212913990020751953, -0.32244300842285156, 0.543003082275390625, 0.745550692081451416, 120, 255, 1, "", 46368, NULL), +(2093, 193684, 571, 0, 0, 1, 1, 7898.14697265625, 2066.417236328125, 600.31329345703125, 0.75534135103225708, 0, 0, 0.368756294250488281, 0.929526090621948242, 120, 255, 1, "", 46368, NULL), +(2094, 193685, 571, 0, 0, 1, 1, 7897.3955078125, 2049.253173828125, 600.3131103515625, 5.459003925323486328, 0, 0, -0.40052604675292968, 0.916285336017608642, 120, 255, 1, "", 46368, NULL), +(2095, 193686, 571, 0, 0, 1, 1, 7878.8671875, 2049.250244140625, 600.31298828125, 0.75534135103225708, 0, 0, 0.368756294250488281, 0.929526090621948242, 120, 255, 1, "", 46368, NULL), +(2096, 193687, 571, 0, 0, 1, 1, 7878.82861328125, 2066.62109375, 600.31317138671875, 5.432824611663818359, 0, 0, -0.41248512268066406, 0.910964369773864746, 120, 255, 1, "", 46368, NULL), +(2097, 193688, 571, 0, 0, 1, 1, 7881.25048828125, 2043.955078125, 600.2493896484375, 1.4564744234085083, 0, 0, 0.665555000305175781, 0.746348798274993896, 120, 255, 1, "", 46368, NULL), +(2098, 193689, 571, 0, 0, 1, 1, 7879.41748046875, 2044.993408203125, 600.2493896484375, 0.219910025596618652, 0, 0, 0.10973358154296875, 0.993961036205291748, 120, 255, 1, "", 46368, NULL), +(2099, 193690, 571, 0, 0, 1, 1, 7881.208984375, 2043.9761962890625, 600.2493896484375, 0.304556638002395629, 0, 0, 0.151690483093261718, 0.988428056240081787, 120, 255, 1, "", 46368, NULL), +(2100, 193694, 571, 0, 0, 1, 1, 7886.49609375, 2059.90869140625, 600.2626953125, 5.452062606811523437, 0, 0, -0.40370368957519531, 0.914889812469482421, 120, 255, 1, "", 46368, NULL), +(2101, 193695, 571, 0, 0, 1, 1, 7885.58935546875, 2059.190185546875, 600.76806640625, 2.337379932403564453, 0.248764514923095703, 0.376603126525878906, 0.836214065551757812, 0.311500102281570434, 120, 255, 1, "", 46368, NULL); + +-- remove duplicate spawns +DELETE FROM `gameobject` WHERE (`id` IN (193706, 193712, 193713, 193719, 193698, 193699, 193700, 193705, 193708, 193722, 193723, 193724, 193725, 193726, 193730, 193731, 193732, 193733, 193734, 193735, 193738, 193739, 193740, 193759, 193760, 193761, 193749, 193747, 193748, 193692)) AND (`guid` IN (268830, 268824, 268823, 268817, 268838, 268837, 268836, 268831, 268828, 268814, 268813, 268812, 268811, 268810, 268806, 268805, 268804, 268803, 268802, 268801, 268798, 268797, 268796, 268778, 268777, 268776, 268787, 268789, 268788, 256036, 257787, 258233, 260066, 260579, 261410, 261970, 262680, 263207, 263653, 265211, 266056, 267263, 267510, 267905, 268771)); +DELETE FROM `gameobject_addon` WHERE (`guid` IN (268830, 268824, 268823, 268817, 268838, 268837, 268836, 268831, 268828, 268814, 268813, 268812, 268811, 268810, 268806, 268805, 268804, 268803, 268802, 268801, 268798, 268797, 268796, 268778, 268777, 268776, 268787, 268789, 268788, 256036, 257787, 258233, 260066, 260579, 261410, 261970, 262680, 263207, 263653, 265211, 266056, 267263, 267510, 267905, 268771)); +DELETE FROM `game_event_gameobject` WHERE (`eventEntry` = 0) AND (`guid` IN (268830, 268824, 268823, 268817, 268838, 268837, 268836, 268831, 268828, 268814, 268813, 268812, 268811, 268810, 268806, 268805, 268804, 268803, 268802, 268801, 268798, 268797, 268796, 268778, 268777, 268776, 268787, 268789, 268788, 256036, 257787, 258233, 260066, 260579, 261410, 261970, 262680, 263207, 263653, 265211, 266056, 267263, 267510, 267905, 268771)); diff --git a/data/sql/updates/db_world/2025_09_03_01.sql b/data/sql/updates/db_world/2025_09_03_01.sql new file mode 100644 index 000000000..692c4337e --- /dev/null +++ b/data/sql/updates/db_world/2025_09_03_01.sql @@ -0,0 +1,12 @@ +-- DB update 2025_09_03_00 -> 2025_09_03_01 +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 17) AND (`SourceGroup` = 0) AND (`SourceEntry` = 44407); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17, 0, 44407, 0, 0, 31, 1, 3, 24747, 0, 0, 0, 0, '', 'Hawk Hunting must target Fjord Hawk'); + +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 24747; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 24747) AND (`source_type` = 0); + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_hawk_hunting'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(44407, 'spell_hawk_hunting'); diff --git a/data/sql/updates/db_world/2025_09_03_02.sql b/data/sql/updates/db_world/2025_09_03_02.sql new file mode 100644 index 000000000..98cfb21bd --- /dev/null +++ b/data/sql/updates/db_world/2025_09_03_02.sql @@ -0,0 +1,9 @@ +-- DB update 2025_09_03_01 -> 2025_09_03_02 +-- +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` IN (16423, 16437, 16438, 16422); +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (16423, 16437, 16438, 16422); +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 +(16423, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Spectral Apparition - In Combat - Cast \'Scourge Strike\''), +(16437, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Spectral Spirit - In Combat - Cast \'Scourge Strike\''), +(16422, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Soldier - In Combat - Cast \'Scourge Strike\''), +(16438, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Trooper - In Combat - Cast \'Scourge Strike\''); diff --git a/data/sql/updates/db_world/2025_09_04_00.sql b/data/sql/updates/db_world/2025_09_04_00.sql new file mode 100644 index 000000000..fc9d4a9a2 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_04_00.sql @@ -0,0 +1,7 @@ +-- DB update 2025_09_03_02 -> 2025_09_04_00 +-- +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 188141; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 1 AND `entryorguid` = 188141); +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 +(188141, 1, 0, 0, 64, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Frozen Phylactery - On Gossip Hello - Despawn In 3000 ms'); diff --git a/data/sql/updates/db_world/2025_09_04_01.sql b/data/sql/updates/db_world/2025_09_04_01.sql new file mode 100644 index 000000000..9a885fd6d --- /dev/null +++ b/data/sql/updates/db_world/2025_09_04_01.sql @@ -0,0 +1,2 @@ +-- DB update 2025_09_04_00 -> 2025_09_04_01 +UPDATE `creature_model_info` SET `DisplayID_Other_Gender` = 0 WHERE `DisplayID` IN (16292, 16294); diff --git a/data/sql/updates/db_world/2025_09_04_02.sql b/data/sql/updates/db_world/2025_09_04_02.sql new file mode 100644 index 000000000..91873ddd1 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_04_02.sql @@ -0,0 +1,18 @@ +-- DB update 2025_09_04_01 -> 2025_09_04_02 +-- Update gameobject 'Gravestone' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (192256)) AND (`guid` IN (76993)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(76993, 192256, 571, 0, 0, 1, 1, 9025.6845703125, -1178.6163330078125, 1058.107666015625, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 46368, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (192257, 192258, 192260, 192265, 192380)) AND (`guid` BETWEEN 265 AND 269); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(265, 192257, 571, 0, 0, 1, 1, 8094.673828125, -995.29864501953125, 936.18206787109375, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 53788, NULL), +(266, 192258, 571, 0, 0, 1, 1, 7832.95556640625, -2018.984375, 1224.683349609375, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 52237, NULL), +(267, 192260, 571, 0, 0, 1, 1, 7463.06689453125, -3326.37841796875, 897.74884033203125, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 47720, NULL), +(268, 192265, 571, 0, 0, 1, 1, 6942.8603515625, -552.515625, 914.403564453125, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 50664, NULL), +(269, 192380, 571, 0, 0, 1, 1, 6431.64404296875, -1186.592041015625, 446.2081298828125, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 47720, NULL); + +-- remaining spawns (no sniffed values available) +-- (`guid` IN (77187)) diff --git a/data/sql/updates/db_world/2025_09_04_03.sql b/data/sql/updates/db_world/2025_09_04_03.sql new file mode 100644 index 000000000..07265fb35 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_04_03.sql @@ -0,0 +1,23 @@ +-- DB update 2025_09_04_02 -> 2025_09_04_03 +-- +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24170); +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 +(24170, 0, 0, 3, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Summoned - Store Targetlist'), +(24170, 0, 1, 4, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 33, 24170, 0, 0, 0, 0, 0, 12, 1, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Died - Quest Credit \'null\''), +(24170, 0, 2, 0, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Summoned - Set Visibility Off'), +(24170, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 50, 186598, 45, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Summoned - Summon Gameobject \'Tillinghast\'s Plagued Meat\''), +(24170, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 1, 0, 0, 0, 20, 186598, 10, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Died - Despawn Instant'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23689); +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 +(23689, 0, 1, 4, 65, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 36809, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - On Follow Complete - Cast \'Overpowering Sickness\''), +(23689, 0, 3, 5, 1, 0, 100, 512, 10000, 10000, 10000, 10000, 0, 0, 29, 0, 0, 24170, 0, 0, 0, 19, 24170, 75, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Out of Combat - Start Follow Closest Creature \'Draconis Gastritis Bunny\''), +(23689, 0, 4, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 19, 24170, 10, 0, 0, 0, 0, 0, 0, 'Proto-Drake - On Follow Complete - Kill Target'), +(23689, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Out of Combat - Set Event Phase 1'), +(23689, 0, 6, 0, 1, 1, 100, 512, 45000, 45000, 45000, 45000, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Out of Combat - Despawn Instant (Phase 1)'), +-- update comments with Keira +(23689, 0, 8, 0, 8, 0, 100, 0, 40969, 0, 120000, 120000, 0, 0, 69, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - On Spellhit \'Malister`s Frost Wand\' - Move To Invoker'), +(23689, 0, 9, 0, 9, 0, 100, 513, 0, 0, 0, 0, 0, 20, 101, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Within 0-20 Range - Set Home Position (No Repeat)'), +(23689, 0, 10, 0, 9, 0, 100, 0, 0, 0, 2000, 3500, 0, 5, 11, 51219, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Within 0-5 Range - Cast \'Flame Breath\''), +(23689, 0, 11, 0, 0, 0, 100, 0, 3000, 9000, 30000, 45000, 0, 0, 11, 42362, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - In Combat - Cast \'Flames of Birth\''), +(23689, 0, 12, 0, 9, 0, 100, 0, 0, 0, 10000, 15000, 0, 20, 11, 41572, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Within 0-20 Range - Cast \'Wing Buffet\''); diff --git a/data/sql/updates/db_world/2025_09_05_00.sql b/data/sql/updates/db_world/2025_09_05_00.sql new file mode 100644 index 000000000..d5bb66322 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_05_00.sql @@ -0,0 +1,64 @@ +-- DB update 2025_09_04_03 -> 2025_09_05_00 +-- Prevent removal on evade +DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (50665, 50681, 50695); +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES +(50665, 2048), +(50681, 2048), +(50695, 2048); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28148); +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 +(28148, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Reset - Set Event Phase 1'), +(28148, 0, 1, 2, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50695, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Just Summoned - Cast \'Bleeding Out\''), +(28148, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Just Summoned - Start Follow Invoker'), +(28148, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Just Summoned - Remove FlagStandstate Sit Down'), +(28148, 0, 4, 0, 23, 1, 100, 513, 50695, 0, 0, 0, 0, 0, 80, 2814800, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Aura \'Bleeding Out\' - Run Script (Phase 1) (No Repeat)'), +(28148, 0, 5, 6, 40, 0, 100, 513, 4, 0, 0, 0, 0, 0, 90, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Point 4 of Path Any Reached - Set Flag Standstate Sit Down (No Repeat)'), +(28148, 0, 6, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 20000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Point 4 of Path Any Reached - Despawn In 20000 ms (No Repeat)'), +(28148, 0, 7, 8, 8, 1, 100, 512, 50669, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Set Event Phase 2 (Phase 1)'), +(28148, 0, 8, 9, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50698, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cast \'Kill Credit Jospehine 01\' (Phase 1)'), +(28148, 0, 9, 10, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50711, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cast \'Strip Aura Josephine 01\' (Phase 1)'), +(28148, 0, 10, 11, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50699, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cross Cast \'Josephine Kill Credit\' (Phase 1)'), +(28148, 0, 11, 12, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50712, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cross Cast \'Strip Aura Josephine\' (Phase 1)'), +(28148, 0, 12, 13, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Stop Follow (Phase 1)'), +(28148, 0, 13, 14, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Say Line 0 (Phase 1)'), +(28148, 0, 14, 15, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 0, 28148, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Start Waypoint Path 28148 (Phase 1)'), +(28148, 0, 15, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Remove Npc Flags Gossip (Phase 1)'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28142) AND (`source_type` = 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 +(28142, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Reset - Set Event Phase 1'), +(28142, 0, 1, 2, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50681, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Just Summoned - Cast \'Bleeding Out\''), +(28142, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Just Summoned - Start Follow Invoker'), +(28142, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Just Summoned - Remove FlagStandstate Sit Down'), +(28142, 0, 4, 0, 23, 1, 100, 513, 50681, 0, 0, 0, 0, 0, 80, 2814200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Aura \'Bleeding Out\' - Run Script (Phase 1) (No Repeat)'), +(28142, 0, 5, 6, 40, 0, 100, 513, 5, 0, 0, 0, 0, 0, 90, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Point 5 of Path Any Reached - Set Flag Standstate Sit Down (No Repeat)'), +(28142, 0, 6, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 20000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Point 5 of Path Any Reached - Despawn In 20000 ms (No Repeat)'), +(28142, 0, 7, 8, 8, 1, 100, 512, 50669, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Set Event Phase 2 (Phase 1)'), +(28142, 0, 8, 9, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50683, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cast \'Kill Credit Lamoof 01\' (Phase 1)'), +(28142, 0, 9, 10, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50723, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cast \'Strip Aura Lamoof 01\' (Phase 1)'), +(28142, 0, 10, 11, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50684, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cross Cast \'Lamoof Kill Credit\' (Phase 1)'), +(28142, 0, 11, 12, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50722, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cross Cast \'Strip Aura Lamoof\' (Phase 1)'), +(28142, 0, 12, 13, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Stop Follow (Phase 1)'), +(28142, 0, 13, 14, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Say Line 0 (Phase 1)'), +(28142, 0, 14, 15, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 0, 28142, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Start Waypoint Path 28142 (Phase 1)'), +(28142, 0, 15, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Remove Npc Flags Gossip (Phase 1)'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28136); +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 +(28136, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Reset - Set Event Phase 1'), +(28136, 0, 1, 2, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50665, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Just Summoned - Cast \'Bleeding Out\''), +(28136, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Just Summoned - Start Follow Invoker'), +(28136, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Just Summoned - Remove FlagStandstate Sit Down'), +(28136, 0, 4, 0, 23, 1, 100, 513, 50665, 0, 0, 0, 0, 0, 80, 2813600, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Aura \'Bleeding Out\' - Run Script (Phase 1) (No Repeat)'), +(28136, 0, 5, 6, 40, 0, 100, 513, 5, 0, 0, 0, 0, 0, 90, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Point 5 of Path Any Reached - Set Flag Standstate Sit Down (No Repeat)'), +(28136, 0, 6, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 20000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Point 5 of Path Any Reached - Despawn In 20000 ms (No Repeat)'), +(28136, 0, 7, 8, 8, 1, 100, 512, 50669, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Set Event Phase 2 (Phase 1)'), +(28136, 0, 8, 9, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50671, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cast \'Kill Credit Jonathan 01\' (Phase 1)'), +(28136, 0, 9, 10, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50709, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cast \'Strip Aura Jonathan 01\' (Phase 1)'), +(28136, 0, 10, 11, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50680, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cross Cast \'Jonathan Kill Credit\' (Phase 1)'), +(28136, 0, 11, 12, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50710, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cross Cast \'Strip Aura Jonanthan\' (Phase 1)'), +(28136, 0, 12, 13, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Stop Follow (Phase 1)'), +(28136, 0, 13, 14, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Say Line 0 (Phase 1)'), +(28136, 0, 14, 15, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 0, 28136, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Start Waypoint Path 28136 (Phase 1)'), +(28136, 0, 15, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Remove Npc Flags Gossip (Phase 1)'); diff --git a/data/sql/updates/db_world/2025_09_05_01.sql b/data/sql/updates/db_world/2025_09_05_01.sql new file mode 100644 index 000000000..0e87f6295 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_05_01.sql @@ -0,0 +1,5 @@ +-- DB update 2025_09_05_00 -> 2025_09_05_01 +DELETE FROM `creature_template_spell` WHERE `CreatureID` = 21750 AND `Index` IN (2, 3); +INSERT INTO `creature_template_spell` (`CreatureID`, `Index`, `Spell`, `VerifiedBuild`) VALUES +(21750, 2, 37463, 0), +(21750, 3, 37469, 0); diff --git a/data/sql/updates/db_world/2025_09_06_00.sql b/data/sql/updates/db_world/2025_09_06_00.sql new file mode 100644 index 000000000..04d9dde00 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_06_00.sql @@ -0,0 +1,9 @@ +-- DB update 2025_09_05_01 -> 2025_09_06_00 +-- +SET @CGUID:=12891; +DELETE FROM `creature` WHERE (`id1` = 16786) AND (`guid` = (@CGUID)); +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `Comment`, `VerifiedBuild`) VALUES +(@CGUID, 16786, 0, 0, 0, 0, 0, 1, 1, 0, -4926.95, -981.718, 501.55, 2.0071299076080322, 120, 0, 0, 1, 0, 0, 0, 0, 0, '', '', 0); + +DELETE FROM `game_event_creature` WHERE `guid` = @CGUID; +INSERT INTO `game_event_creature` (`eventEntry`, `guid`) VALUES(17, @CGUID); diff --git a/data/sql/updates/db_world/2025_09_06_01.sql b/data/sql/updates/db_world/2025_09_06_01.sql new file mode 100644 index 000000000..6830e36d2 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_06_01.sql @@ -0,0 +1,3 @@ +-- DB update 2025_09_06_00 -> 2025_09_06_01 +-- +UPDATE `creature_template` SET `RegenHealth` = 0 WHERE (`entry` IN (16136, 16172)); diff --git a/data/sql/updates/db_world/2025_09_06_02.sql b/data/sql/updates/db_world/2025_09_06_02.sql new file mode 100644 index 000000000..372e87753 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_06_02.sql @@ -0,0 +1,80 @@ +-- DB update 2025_09_06_01 -> 2025_09_06_02 +-- +-- Changes Scorn's spawn from Event 17 (Scourge Invasion) to 120 (Scourge Invasion - Boss in instance activation) +UPDATE `game_event_creature` SET `eventEntry` = 120 WHERE `guid` = 248652; + +-- Adds "FORCE_GOSSIP" for Sever +UPDATE `creature_template` SET `type_flags` = 134217728 WHERE `entry` = 14682; +-- Adds "FORCE_GOSSIP" and changes from "Warrior" to "Paladin" for Balzaphon, Lady Falther'ess and Scorn +UPDATE `creature_template` SET `unit_class` = 2, `type_flags` = 134217728 WHERE `entry` IN (14684, 14686, 14690, 14693); +-- Makes Balzaphon and Revanchion immune to Charge +UPDATE `creature_template` SET `mechanic_immune_mask` = (`mechanic_immune_mask` | 2048) WHERE `entry` IN (14684, 14690); +-- Scorn immune to root +UPDATE `creature_template` SET `mechanic_immune_mask` = (`mechanic_immune_mask` | 64) WHERE `entry` IN (14693); + +-- Adds SAI to Sever, Balzaphon, Lady Falther'ess, Revanchion and Lord Blackwood +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` IN (14682, 14684, 14686, 14695); + +-- Adds Spirit Particles (purple) to Lord Blackwood +UPDATE `creature_template_addon` SET `auras` = '28126' WHERE `entry` = 14695; + +-- Adds Spirit Particles (purple) and Frost Armor to Revanchion +UPDATE `creature_template_addon` SET `auras` = '28126 12556' WHERE `entry` = 14690 ; + +-- Adds SAI to Holding Pen (157819) +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 157819; + +-- Adds Spirit Particles (purple) to Sever, Balzaphon, Revanchion and Scorn +DELETE FROM `creature_template_addon` WHERE `entry` IN (14684, 14686, 14690, 14693, 14682); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(14682, 0, 0, 0, 1, 0, 0, '28126'), -- Sever +(14684, 0, 0, 0, 1, 0, 0, '28126'), -- Balzaphon +(14690, 0, 0, 0, 1, 0, 0, '28126'), -- Revanchion +(14693, 0, 0, 0, 1, 0, 0, '28126'); -- Scorn + +-- Adds Server and Lady Falther'ess texts. +DELETE FROM `creature_text` WHERE `CreatureID` IN (14682, 14686); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(14682, 0, 0, '%s goes into a frenzy!', 16, 0, 100, 0, 0, 0, 1191, 0, '[Sever] Sever goes into a frenzy! / %s goes into a frenzy!'), +(14686, 0, 0, 'Thank you for becoming my next victim!', 14, 0, 100, 0, 0, 0, 12429, 0, '[Lady Falther\'ess] Thank you for becoming my next victim!'); + +-- SmartGameObjectAI +-- 157819 (Holding Pen), when the "holding pen" is opened, saves data variable and sends to lady father'ess so it can be used after as a initatior of encounter with the player. +DELETE FROM `smart_scripts` WHERE `source_type` = 1 AND `entryorguid` = 157819; +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 +(157819, 1, 0, 1, 70, 0, 100, 0, 2, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Holding Pen - On Gameobject State Changed - Store Targetlist to Lady Falther\'ess'), +(157819, 1, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 100, 1, 0, 0, 0, 0, 0, 19, 14686, 0, 0, 0, 0, 0, 0, 0, 'Holding Pen - On Gameobject State Changed - Send Target 1 to Lady Falther\'ess'), +(157819, 1, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 19, 14686, 0, 0, 0, 0, 0, 0, 0, 'Holding Pen - On Gameobject State Changed - Set Data 1 1 to Lady Falther\'ess'); + +-- SmartAI +-- Adds SAI logic to Sever, Balzaphon, Lady Falther'ess, Revanchion and Lord Blackwood +DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `entryorguid` IN (14682, 14684, 14686, 14690, 14695); +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 +-- Sever +(14682, 0, 0, 0, 0, 0, 100, 0, 12000, 31000, 8000, 30000, 0, 0, 11, 17745, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Sever - In Combat - Cast \'Diseased Spit\''), +(14682, 0, 1, 2, 2, 0, 100, 0, 1, 50, 0, 0, 0, 0, 11, 8269, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sever - Between 1-50% Health - Cast \'Frenzy\''), +(14682, 0, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sever - Between 1-50% Health - Say Line 0 "Sever goes into a frenzy!"'), +(14682, 0, 3, 0, 101, 0, 100, 0, 2, 10, 12500, 10000, 15000, 0, 11, 16508, 0, 0, 0, 0, 0, 17, 0, 10, 5, 0, 0, 0, 0, 0, 'Sever - On 2 or More Players in Range - Cast \'Intimidating Roar\''), +-- Balzaphon +(14684, 0, 0, 0, 0, 0, 100, 0, 2000, 7000, 2000, 5000, 0, 0, 11, 16799, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Balzaphon - In Combat - Cast \'Frostbolt\''), +(14684, 0, 1, 0, 0, 0, 100, 0, 6000, 12000, 7000, 15000, 0, 0, 11, 15244, 0, 0, 0, 0, 0, 5, 10, 0, 2, 0, 0, 0, 0, 0, 'Balzaphon - In Combat - Cast \'Cone of Cold\''), +(14684, 0, 2, 0, 0, 0, 100, 0, 10000, 20000, 12000, 20000, 0, 0, 11, 8398, 0, 0, 0, 0, 0, 5, 20, 0, 2, 0, 0, 0, 0, 0, 'Balzaphon - In Combat - Cast \'Frostbolt Volley\''), +-- Lady Falther'ess +(14686, 0, 0, 1, 37, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 28533, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Initialize - Cast \'Transform\' (Salma Saldean)'), +(14686, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 2, 35, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Initialize - Set Faction 35 (Friendly)'), +(14686, 0, 2, 3, 38, 0, 100, 0, 1, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Demorph'), +(14686, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Say Line 0 - Thank you for becoming my next victim! '), +(14686, 0, 4, 5, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 2, 21, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Set Faction 21 (Undead, Scourge)'), +(14686, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 21, 30, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Start Attacking (Closest Player within 30 yards)'), +(14686, 0, 6, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 75, 28126, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Add Aura \'Spirit Particles (purple)\''), +(14686, 0, 7, 0, 0, 0, 100, 0, 2500, 8000, 10000, 18000, 0, 0, 11, 22743, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - In Combat - Cast \'Ribbon of Souls\''), +(14686, 0, 8, 0, 0, 0, 100, 0, 17500, 20000, 19000, 22000, 0, 0, 11, 17105, 0, 0, 0, 0, 0, 5, 30, 1, 2, 17105, 0, 0, 0, 0, 'Lady Falther\'ess - In Combat - Cast \'Banshee Curse\''), +(14686, 0, 9, 0, 101, 0, 100, 0, 2, 10, 7500, 5000, 6000, 0, 11, 16838, 0, 0, 5, 0, 0, 17, 0, 5, 5, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On 2 or More Players in Range - Cast \'Banshee Shriek\''), +-- Revanchion +(14690, 0, 0, 0, 0, 0, 100, 0, 10000, 15000, 12500, 14000, 0, 0, 11, 15245, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Revanchion - In Combat - Cast \'Shadow Bolt Volley\''), +(14690, 0, 1, 0, 0, 0, 100, 0, 13000, 16000, 14000, 18000, 0, 0, 11, 14907, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Revanchion - In Combat - Cast \'Frost Nova\''), +-- Lord Blackwood +(14695, 0, 0, 0, 0, 0, 100, 0, 8000, 16000, 20000, 20000, 0, 0, 11, 7964, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - In Combat - Cast \'Smoke Bomb\''), +(14695, 0, 1, 0, 105, 0, 100, 0, 10000, 12000, 10000, 12000, 0, 5, 11, 11972, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - On Hostile Casting in Range - Cast \'Shield Bash\''), +(14695, 0, 2, 0, 110, 0, 100, 0, 2000, 20000, 20000, 20000, 0, 1, 11, 20733, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - On Melee Range Target - Cast \'Black Arrow\''), +(14695, 0, 3, 0, 110, 0, 100, 0, 0, 0, 2400, 2400, 0, 1, 11, 16496, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - On Melee Range Target - Cast \'Shoot\''); diff --git a/data/sql/updates/db_world/2025_09_06_03.sql b/data/sql/updates/db_world/2025_09_06_03.sql new file mode 100644 index 000000000..3bac2621c --- /dev/null +++ b/data/sql/updates/db_world/2025_09_06_03.sql @@ -0,0 +1,30 @@ +-- DB update 2025_09_06_02 -> 2025_09_06_03 +-- Remove heroic casts as it is already handled by spelldifficulty_dbc and add on aggro engage +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28729); +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 +(28729, 0, 0, 0, 0, 0, 100, 0, 2000, 6000, 15000, 20000, 0, 0, 11, 52524, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Cast \'Blinding Webs\''), +(28729, 0, 2, 0, 0, 0, 100, 0, 6000, 15000, 20000, 25000, 0, 0, 11, 52086, 0, 0, 0, 0, 0, 5, 30, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Cast \'Web Wrap\''), +(28729, 0, 3, 0, 0, 0, 100, 0, 4000, 12000, 9000, 15000, 0, 0, 11, 52469, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Cast \'Infected Bite\''), +(28729, 0, 5, 0, 8, 0, 100, 0, 52343, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - On Spellhit \'Krik`Thir Subboss Aggro Trigger\' - Set In Combat With Zone'), +(28729, 0, 6, 0, 0, 0, 100, 1, 500, 500, 0, 0, 0, 0, 39, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Call For Help (No Repeat)'), +(28729, 0, 1, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 205, 0, 1, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - On Aggro - Do Action ID 1'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28730); +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 +(28730, 0, 0, 0, 0, 0, 100, 0, 2000, 6000, 15000, 20000, 0, 0, 11, 52470, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Cast \'Enrage\''), +(28730, 0, 1, 0, 0, 0, 100, 0, 6000, 15000, 20000, 25000, 0, 0, 11, 52086, 0, 0, 0, 0, 0, 5, 30, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Cast \'Web Wrap\''), +(28730, 0, 2, 0, 0, 0, 100, 0, 4000, 12000, 9000, 15000, 0, 0, 11, 52469, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Cast \'Infected Bite\''), +(28730, 0, 4, 0, 8, 0, 100, 0, 52343, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - On Spellhit \'Krik`Thir Subboss Aggro Trigger\' - Set In Combat With Zone'), +(28730, 0, 5, 0, 0, 0, 100, 1, 500, 500, 0, 0, 0, 0, 39, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Call For Help (No Repeat)'), +(28730, 0, 3, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 205, 0, 1, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - On Aggro - Do Action ID 1'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28731); +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 +(28731, 0, 0, 0, 0, 0, 100, 0, 2000, 6000, 15000, 20000, 0, 0, 11, 52493, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Cast \'Poison Spray\''), +(28731, 0, 2, 0, 0, 0, 100, 0, 6000, 15000, 20000, 25000, 0, 0, 11, 52086, 0, 0, 0, 0, 0, 5, 30, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Cast \'Web Wrap\''), +(28731, 0, 3, 0, 0, 0, 100, 0, 4000, 12000, 9000, 15000, 0, 0, 11, 52469, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Cast \'Infected Bite\''), +(28731, 0, 5, 0, 8, 0, 100, 0, 52343, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - On Spellhit \'Krik`Thir Subboss Aggro Trigger\' - Set In Combat With Zone'), +(28731, 0, 6, 0, 0, 0, 100, 1, 500, 500, 0, 0, 0, 0, 39, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Call For Help (No Repeat)'), +(28731, 0, 1, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 205, 0, 1, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - On Aggro - Do Action ID 1'); + +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` &~ 33554432 WHERE `entry` IN (28684, 31612); diff --git a/data/sql/updates/db_world/2025_09_06_04.sql b/data/sql/updates/db_world/2025_09_06_04.sql new file mode 100644 index 000000000..aef37356c --- /dev/null +++ b/data/sql/updates/db_world/2025_09_06_04.sql @@ -0,0 +1,13 @@ +-- DB update 2025_09_06_03 -> 2025_09_06_04 + +-- Arzeth the Merciless (Charm, Fear, Root, Snare, Banish, Horror) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|16|64|1024|131072|8388608 WHERE (`entry` = 19354); + +-- Illidari Dreadlord (Charm, Fear, Snare) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|16|1024 WHERE (`entry` = 21166); + +-- Wrath Master (Charm, Snare) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|1024 WHERE (`entry` = 19005); + +-- Arazzius the Cruel (Charm, Fear, Root, Snare, Stun, Freeze, Polymorph, Banish) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|16|64|1024|2048|4096|65536|131072 WHERE (`entry` = 19191); diff --git a/data/sql/updates/db_world/2025_09_06_05.sql b/data/sql/updates/db_world/2025_09_06_05.sql new file mode 100644 index 000000000..fea71f267 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_06_05.sql @@ -0,0 +1,13 @@ +-- DB update 2025_09_06_04 -> 2025_09_06_05 +-- Update gameobject 'Doodad_FrostGiantIceShard' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (192193, 192194, 192195)) AND (`guid` IN (20924, 20925, 20926)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(20924, 192193, 571, 0, 0, 1, 4, 7325.5732421875, -2044.4727783203125, 760.7373046875, 5.478795528411865234, 0.056999683380126953, 0.307971954345703125, -0.35146713256835937, 0.882255733013153076, 120, 255, 1, "", 46158, NULL), +(20925, 192194, 571, 0, 0, 1, 4, 7320.685546875, -2053.649169921875, 761.33929443359375, 5.478795528411865234, 0.056999683380126953, 0.307971954345703125, -0.35146713256835937, 0.882255733013153076, 120, 255, 1, "", 46158, NULL), +(20926, 192195, 571, 0, 0, 1, 4, 7321.001953125, -2054.294677734375, 760.8995361328125, 4.88195037841796875, 0.128789901733398437, -0.01092052459716796, -0.64533519744873046, 0.752885341644287109, 120, 255, 1, "", 46158, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (192186)) AND (`guid` IN (40)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(40, 192186, 571, 0, 0, 1, 4, 7299.98193359375, -2056.776123046875, 760.91754150390625, 3.129810810089111328, 0.336331367492675781, 0.04759979248046875, -0.9404611587524414, 0.012177699245512485, 120, 255, 1, "", 46158, NULL); diff --git a/data/sql/updates/db_world/2025_09_07_00.sql b/data/sql/updates/db_world/2025_09_07_00.sql new file mode 100644 index 000000000..2162949fb --- /dev/null +++ b/data/sql/updates/db_world/2025_09_07_00.sql @@ -0,0 +1,16 @@ +-- DB update 2025_09_06_05 -> 2025_09_07_00 +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 1) AND (`SourceEntry` = 27409) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 32) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 16) 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 +(22, 1, 27409, 0, 0, 32, 0, 16, 0, 0, 0, 0, 0, '', 'Ducal\'s horse only run sai if boarding passenger is player'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 2740900) AND (`source_type` = 9) 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 +(2740900, 9, 0, 0, 0, 0, 100, 512, 3000, 3000, 0, 0, 0, 0, 53, 1, 27409, 0, 12308, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Script - Start Waypoint'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 27409) AND (`source_type` = 0) AND (`id` IN (1, 8, 9, 10)); +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 +(27409, 0, 1, 10, 61, 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, 'Ducal\'s Horse - On Passenger Boarded - Set Reactstate Passive'), +(27409, 0, 8, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 67, 1, 10000, 10000, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Reset - Create Timed Event'), +(27409, 0, 9, 0, 59, 0, 100, 0, 1, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Timed Event 1 Triggered - Despawn Instant'), +(27409, 0, 10, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 74, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Passenger Boarded - Remove Timed Event 1'); diff --git a/data/sql/updates/db_world/2025_09_09_00.sql b/data/sql/updates/db_world/2025_09_09_00.sql new file mode 100644 index 000000000..a16856bd5 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_09_00.sql @@ -0,0 +1,4 @@ +-- DB update 2025_09_07_00 -> 2025_09_09_00 +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=45612; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (45612, 'spell_necropolis_beam'); diff --git a/data/sql/updates/db_world/2025_09_09_01.sql b/data/sql/updates/db_world/2025_09_09_01.sql new file mode 100644 index 000000000..38d8b7a40 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_09_01.sql @@ -0,0 +1,36 @@ +-- DB update 2025_09_09_00 -> 2025_09_09_01 +-- Update gameobject 'Meat Wagon' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (193616, 193618, 193620)) AND (`guid` IN (62425, 62426, 62430, 62435, 62436, 62437)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(62425, 193616, 571, 0, 0, 1, 1, 6879.06787109375, 1607.7725830078125, 388.887359619140625, 3.612837791442871093, 0, 0, -0.97236919403076171, 0.233448356389999389, 120, 255, 1, "", 46368, NULL), +(62426, 193616, 571, 0, 0, 1, 1, 6883.10888671875, 1600.4949951171875, 389.033172607421875, 0.506144583225250244, 0, 0, 0.250379562377929687, 0.968147754669189453, 120, 255, 1, "", 46368, NULL), +(62430, 193618, 571, 0, 0, 1, 1, 6878.4189453125, 1602.437744140625, 389.033172607421875, 0.471238493919372558, 0, 0, 0.233445167541503906, 0.972369968891143798, 120, 255, 1, "", 46368, NULL), +(62435, 193620, 571, 0, 0, 1, 1, 6881.7578125, 1604.779541015625, 388.380401611328125, 3.612837791442871093, 0, 0, -0.97236919403076171, 0.233448356389999389, 120, 255, 1, "", 46368, NULL), +(62436, 193620, 571, 0, 0, 1, 1, 6896.3095703125, 1609.5350341796875, 388.3387451171875, 4.049167633056640625, 0, 0, -0.89879322052001953, 0.438372820615768432, 120, 255, 1, "", 46368, NULL), +(62437, 193620, 571, 0, 0, 1, 1, 6886.94091796875, 1619.73095703125, 388.269287109375, 2.007128477096557617, 0, 0, 0.84339141845703125, 0.537299633026123046, 120, 255, 1, "", 46368, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (193616, 193617, 193618, 193619, 193620)) AND (`guid` BETWEEN 2211 AND 2231); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(2211, 193616, 571, 0, 0, 1, 1, 6876.56005859375, 1539.6214599609375, 389.033172607421875, 6.213373661041259765, 0, 0, -0.03489875793457031, 0.999390840530395507, 120, 255, 1, "", 46368, NULL), +(2212, 193616, 571, 0, 0, 1, 1, 6880.126953125, 1540.478759765625, 389.033172607421875, 3.45575571060180664, 0, 0, -0.98768806457519531, 0.156436234712600708, 120, 255, 1, "", 46368, NULL), +(2213, 193616, 571, 0, 0, 1, 1, 6883.654296875, 1540.2552490234375, 389.033172607421875, 1.134462952613830566, 0, 0, 0.537299156188964843, 0.843391716480255126, 120, 255, 1, "", 46368, NULL), +(2214, 193616, 571, 0, 0, 1, 1, 6884.5859375, 1573.8497314453125, 389.033172607421875, 0.052358884364366531, 0, 0, 0.02617645263671875, 0.999657332897186279, 120, 255, 1, "", 46368, NULL), +(2215, 193616, 571, 0, 0, 1, 1, 6884.59228515625, 1582.0037841796875, 389.033172607421875, 3.159062385559082031, 0, 0, -0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 46368, NULL), +(2216, 193616, 571, 0, 0, 1, 1, 6888.81982421875, 1541.5426025390625, 389.033172607421875, 3.036838293075561523, 0, 0, 0.998628616333007812, 0.052353221923112869, 120, 255, 1, "", 46368, NULL), +(2217, 193617, 571, 0, 0, 1, 1, 6875.7060546875, 1601.95068359375, 389.033172607421875, 3.612837791442871093, 0, 0, -0.97236919403076171, 0.233448356389999389, 120, 255, 1, "", 46368, NULL), +(2218, 193617, 571, 0, 0, 1, 1, 6879.490234375, 1578.3424072265625, 389.033172607421875, 3.176533222198486328, 0, 0, -0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 46368, NULL), +(2219, 193617, 571, 0, 0, 1, 1, 6903.123046875, 1554.924072265625, 389.033172607421875, 2.286378860473632812, 0, 0, 0.909960746765136718, 0.414694398641586303, 120, 255, 1, "", 46368, NULL), +(2220, 193617, 571, 0, 0, 1, 1, 6907.595703125, 1567.492919921875, 389.033172607421875, 6.161012649536132812, 0, 0, -0.06104850769042968, 0.998134791851043701, 120, 255, 1, "", 46368, NULL), +(2221, 193617, 571, 0, 0, 1, 1, 6912.046875, 1560.1182861328125, 389.033203125, 1.291541695594787597, 0, 0, 0.60181427001953125, 0.798636078834533691, 120, 255, 1, "", 46368, NULL), +(2222, 193618, 571, 0, 0, 1, 1, 6881.86181640625, 1578.113037109375, 389.033172607421875, 0.052358884364366531, 0, 0, 0.02617645263671875, 0.999657332897186279, 120, 255, 1, "", 46368, NULL), +(2223, 193618, 571, 0, 0, 1, 1, 6906.19384765625, 1579.285400390625, 389.033172607421875, 0.610863447189331054, 0, 0, 0.3007049560546875, 0.953717231750488281, 120, 255, 1, "", 46368, NULL), +(2224, 193618, 571, 0, 0, 1, 1, 6906.30126953125, 1589.4271240234375, 389.033172607421875, 1.151916384696960449, 0, 0, 0.544638633728027343, 0.838670849800109863, 120, 255, 1, "", 46368, NULL), +(2225, 193619, 571, 0, 0, 1, 1, 6872.22021484375, 1602.7569580078125, 389.033172607421875, 3.682650327682495117, 0, 0, -0.96362972259521484, 0.26724100112915039, 120, 255, 1, "", 46368, NULL), +(2226, 193619, 571, 0, 0, 1, 1, 6872.970703125, 1600.5621337890625, 389.033172607421875, 3.665196180343627929, 0, 0, -0.96592521667480468, 0.258821308612823486, 120, 255, 1, "", 46368, NULL), +(2227, 193619, 571, 0, 0, 1, 1, 6874.19140625, 1598.2149658203125, 389.033172607421875, 3.665196180343627929, 0, 0, -0.96592521667480468, 0.258821308612823486, 120, 255, 1, "", 46368, NULL), +(2228, 193619, 571, 0, 0, 1, 1, 6875.76220703125, 1577.950927734375, 389.033172607421875, 3.22885894775390625, 0, 0, -0.99904823303222656, 0.043619260191917419, 120, 255, 1, "", 46368, NULL), +(2229, 193619, 571, 0, 0, 1, 1, 6875.81689453125, 1580.9786376953125, 389.033172607421875, 3.106652259826660156, 0, 0, 0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 46368, NULL), +(2230, 193619, 571, 0, 0, 1, 1, 6876.02099609375, 1575.36083984375, 389.033172607421875, 3.333590030670166015, 0, 0, -0.99539566040039062, 0.095851235091686248, 120, 255, 1, "", 46368, NULL), +(2231, 193620, 571, 0, 0, 1, 1, 6885.62060546875, 1578.173583984375, 388.435943603515625, 3.176533222198486328, 0, 0, -0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 46368, NULL); diff --git a/data/sql/updates/db_world/2025_09_09_02.sql b/data/sql/updates/db_world/2025_09_09_02.sql new file mode 100644 index 000000000..3ddeed53e --- /dev/null +++ b/data/sql/updates/db_world/2025_09_09_02.sql @@ -0,0 +1,8 @@ +-- DB update 2025_09_09_01 -> 2025_09_09_02 +-- Update gameobject 'Death's Gaze Orb' with sniffed values +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (192917)) AND (`guid` IN (174, 175, 176)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(174, 192917, 571, 0, 0, 1, 1, 6475.67822265625, 3399.67529296875, 599.08349609375, 2.059488296508789062, 0, 0, 0.857167243957519531, 0.515038192272186279, 120, 255, 1, "", 46368, NULL), +(175, 192917, 571, 0, 0, 1, 1, 6514.7314453125, 3273.22216796875, 667.54388427734375, 3.926995515823364257, 0, 0, -0.92387866973876953, 0.38268551230430603, 120, 255, 1, "", 46368, NULL), +(176, 192917, 571, 0, 0, 1, 1, 6705.81982421875, 3528.986328125, 673.74957275390625, 0.715584874153137207, 0, 0, 0.350207328796386718, 0.936672210693359375, 120, 255, 1, "", 46368, NULL); diff --git a/data/sql/updates/db_world/2025_09_09_03.sql b/data/sql/updates/db_world/2025_09_09_03.sql new file mode 100644 index 000000000..e1be42dfc --- /dev/null +++ b/data/sql/updates/db_world/2025_09_09_03.sql @@ -0,0 +1,25 @@ +-- DB update 2025_09_09_02 -> 2025_09_09_03 +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=48297; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (48297, 'spell_handover_reins'); + +DELETE FROM `vehicle_seat_addon` WHERE `SeatEntry`=742; +INSERT INTO `vehicle_seat_addon` (`SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue`) VALUES +(742, 0, 0, -2, 0, 0, 1); + +-- Set Phase from 5 to 1 for id 2 +-- Generate comments with Keira +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 27213); +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 +(27213, 0, 0, 1, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 211, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Respawn - Flag reset 0'), +(27213, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Respawn - Set Event Phase 1'), +(27213, 0, 2, 3, 28, 1, 100, 512, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Event Phase 2 (Phase 1)'), +(27213, 0, 3, 4, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Home Position (Phase 1)'), +(27213, 0, 4, 5, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 2, 35, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Faction 35 (Phase 1)'), +(27213, 0, 5, 6, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 18, 131072, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Flags Pacified (Phase 1)'), +(27213, 0, 6, 0, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2721300, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Run Script (Phase 1)'), +(27213, 0, 7, 0, 59, 2, 100, 512, 1, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Timed Event 1 Triggered - Despawn Instant (Phase 2)'), +(27213, 0, 8, 9, 23, 2, 100, 512, 48290, 1, 500, 500, 0, 0, 74, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Aura \'Onslaught Riding Crop\' - Remove Timed Event 1 (Phase 2)'), +(27213, 0, 9, 0, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 22, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Aura \'Onslaught Riding Crop\' - Set Event Phase 3 (Phase 2)'), +(27213, 0, 10, 11, 31, 4, 100, 513, 48297, 0, 0, 0, 0, 0, 22, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Target Spellhit \'Hand Over Reins\' - Set Event Phase 4 (Phase 3) (No Repeat)'), +(27213, 0, 11, 0, 61, 8, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2721301, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Target Spellhit \'Hand Over Reins\' - Run Script (Phase 3) (No Repeat)'); diff --git a/data/sql/updates/db_world/2025_09_09_04.sql b/data/sql/updates/db_world/2025_09_09_04.sql new file mode 100644 index 000000000..9b1aba172 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_09_04.sql @@ -0,0 +1,3 @@ +-- DB update 2025_09_09_03 -> 2025_09_09_04 +UPDATE `creature_template_addon` SET `auras` = '19818 34623' WHERE `entry` = 18733; +UPDATE `creature_addon` SET `visibilityDistanceType` = 5, `auras` = '19818 34623' WHERE `guid` IN (67001, 203341); diff --git a/data/sql/updates/db_world/2025_09_09_05.sql b/data/sql/updates/db_world/2025_09_09_05.sql new file mode 100644 index 000000000..ff68f7f4b --- /dev/null +++ b/data/sql/updates/db_world/2025_09_09_05.sql @@ -0,0 +1,7 @@ +-- DB update 2025_09_09_04 -> 2025_09_09_05 +-- +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 29978; + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 29984) AND (`source_type` = 0) AND (`id` = 0); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 29978) AND (`source_type` = 0); diff --git a/data/sql/updates/db_world/2025_09_10_00.sql b/data/sql/updates/db_world/2025_09_10_00.sql new file mode 100644 index 000000000..ca05e35c1 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_10_00.sql @@ -0,0 +1,6 @@ +-- DB update 2025_09_09_05 -> 2025_09_10_00 +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 18) AND (`SourceGroup` = 25334) AND (`SourceEntry` IN (47917, 46598)) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 9) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 11652) 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 +(18, 25334, 47917, 0, 0, 9, 0, 11652, 0, 0, 0, 0, 0, '', 'Horde Siege Tank requires player to be on quest The Plains of Nasam'), +(18, 25334, 46598, 0, 0, 9, 0, 11652, 0, 0, 0, 0, 0, '', 'Horde Siege Tank requires player to be on quest The Plains of Nasam'); diff --git a/data/sql/updates/db_world/2025_09_10_01.sql b/data/sql/updates/db_world/2025_09_10_01.sql new file mode 100644 index 000000000..60a729439 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_10_01.sql @@ -0,0 +1,51 @@ +-- DB update 2025_09_10_00 -> 2025_09_10_01 +-- Update gameobject 'Weapon Rack' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (181627, 183269, 183991, 105172, 105171, 105170, 105169, 188659)) AND (`guid` IN (11420, 24123, 24845, 24846, 45165, 45166, 45167, 45168, 61365, 61366, 61367, 61368, 61369, 61370, 61371, 61372, 61373, 61374, 61375, 61376, 61377, 61378, 61379, 61380, 61381, 61382, 61383, 61384, 61385, 61386)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(11420, 181627, 0, 0, 0, 1, 1, -6335.33544921875, -3115.09375, 299.723052978515625, 0.907570242881774902, 0, 0, 0.438370704650878906, 0.898794233798980712, 120, 255, 1, "", 48120, NULL), +(24123, 183269, 530, 0, 0, 1, 1, 2228.34716796875, 2312.203125, 89.11359405517578125, 2.321285009384155273, -0.00742387771606445, -0.15768909454345703, 0.902186393737792968, 0.401420950889587402, 120, 255, 1, "", 45704, NULL), +(24845, 183991, 530, 0, 0, 1, 1, -596.56427001953125, 2914.64013671875, 59.21495437622070312, 3.543023586273193359, 0, 0, -0.97992420196533203, 0.199370384216308593, 120, 255, 1, "", 45854, NULL), +(24846, 183991, 530, 0, 0, 1, 1, -584.88446044921875, 2896.530029296875, 59.20400619506835937, 3.700104713439941406, 0, 0, -0.96126079559326171, 0.275640487670898437, 120, 255, 1, "", 45854, NULL), +(45165, 105172, 0, 0, 0, 1, 1, 3023.028564453125, 653.81402587890625, 75.34989166259765625, 1.553341388702392578, 0, 0, 0.700908660888671875, 0.713251054286956787, 120, 255, 1, "", 46779, NULL), +(45166, 105171, 0, 0, 0, 1, 1, 3019.9033203125, 688.79742431640625, 66.45070648193359375, 4.747295856475830078, 0, 0, -0.69465827941894531, 0.719339847564697265, 120, 255, 1, "", 46779, NULL), +(45167, 105170, 0, 0, 0, 1, 1, 3063.673583984375, 697.9215087890625, 66.45069122314453125, 3.211419343948364257, 0, 0, -0.9993906021118164, 0.034906134009361267, 120, 255, 1, "", 46779, NULL), +(45168, 105169, 0, 0, 0, 1, 1, 3058.616455078125, 653.5849609375, 58.1085205078125, 3.996806621551513671, 0, 0, -0.90996074676513671, 0.414694398641586303, 120, 255, 1, "", 46779, NULL), +(61365, 188659, 571, 0, 0, 1, 1, 2953.047119140625, -451.505218505859375, 140.7652130126953125, 5.794494152069091796, 0, 0, -0.24192142486572265, 0.970295846462249755, 120, 255, 1, "", 46158, NULL), +(61366, 188659, 571, 0, 0, 1, 1, 2832.775390625, -279.4259033203125, 136.0123291015625, 2.426007747650146484, 0, 0, 0.936672210693359375, 0.350207358598709106, 120, 255, 1, "", 45942, NULL), +(61367, 188659, 571, 0, 0, 1, 1, 2741.550537109375, -114.055442810058593, 115.7203216552734375, 6.195919513702392578, 0, 0, -0.04361915588378906, 0.999048233032226562, 120, 255, 1, "", 46158, NULL), +(61368, 188659, 571, 0, 0, 1, 1, 2834.82958984375, -531.0296630859375, 121.3561477661132812, 4.48549652099609375, 0, 0, -0.7826080322265625, 0.622514784336090087, 120, 255, 1, "", 45942, NULL), +(61369, 188659, 571, 0, 0, 1, 1, 2782.08203125, -188.334426879882812, 139.139404296875, 3.577930212020874023, 0, 0, -0.97629547119140625, 0.216442063450813293, 120, 255, 1, "", 46158, NULL), +(61370, 188659, 571, 0, 0, 1, 1, 2809.780029296875, -324.545013427734375, 130.2048797607421875, 3.78736734390258789, 0, 0, -0.94832324981689453, 0.317305892705917358, 120, 255, 1, "", 46158, NULL), +(61371, 188659, 571, 0, 0, 1, 1, 2928.7626953125, -353.397918701171875, 112.4615936279296875, 2.932138919830322265, 0, 0, 0.994521141052246093, 0.104535527527332305, 120, 255, 1, "", 45942, NULL), +(61372, 188659, 571, 0, 0, 1, 1, 2878.55908203125, -431.913116455078125, 118.3675765991210937, 3.9793548583984375, 0, 0, -0.9135446548461914, 0.406738430261611938, 120, 255, 1, "", 45942, NULL), +(61373, 188659, 571, 0, 0, 1, 1, 2883.435546875, -373.7969970703125, 112.4618301391601562, 3.9793548583984375, 0, 0, -0.9135446548461914, 0.406738430261611938, 120, 255, 1, "", 46158, NULL), +(61374, 188659, 571, 0, 0, 1, 1, 2858.78125, -276.4066162109375, 114.0344467163085937, 1.553341388702392578, 0, 0, 0.700908660888671875, 0.713251054286956787, 120, 255, 1, "", 45942, NULL), +(61375, 188659, 571, 0, 0, 1, 1, 2864.294189453125, -278.00445556640625, 122.8568801879882812, 1.570795774459838867, 0, 0, 0.707106590270996093, 0.707106947898864746, 120, 255, 1, "", 45942, NULL), +(61376, 188659, 571, 0, 0, 1, 1, 2883.530029296875, -296.61376953125, 114.0345230102539062, 3.071766138076782226, 0, 0, 0.999390602111816406, 0.034906134009361267, 120, 255, 1, "", 46158, NULL), +(61377, 188659, 571, 0, 0, 1, 1, 2891.976318359375, -294.088714599609375, 122.85626220703125, 3.106652259826660156, 0, 0, 0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 45942, NULL), +(61378, 188659, 571, 0, 0, 1, 1, 2889.322998046875, -290.53131103515625, 106.8801422119140625, 3.106652259826660156, 0, 0, 0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 45942, NULL), +(61379, 188659, 571, 0, 0, 1, 1, 2901.346923828125, -320.123565673828125, 114.0344924926757812, 1.535889506340026855, 0, 0, 0.694658279418945312, 0.719339847564697265, 120, 255, 1, "", 45942, NULL), +(61380, 188659, 571, 0, 0, 1, 1, 2912.576416015625, -281.989471435546875, 138.0604248046875, 3.124123096466064453, 0, 0, 0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 45942, NULL), +(61381, 188659, 571, 0, 0, 1, 1, 2968.82080078125, -444.152923583984375, 125.9091415405273437, 1.780233979225158691, 0, 0, 0.7771453857421875, 0.629321098327636718, 120, 255, 1, "", 45942, NULL), +(61382, 188659, 571, 0, 0, 1, 1, 2942.835205078125, -348.2596435546875, 114.6573333740234375, 4.555310726165771484, 0, 0, -0.76040554046630859, 0.649448513984680175, 120, 255, 1, "", 45942, NULL), +(61383, 188659, 571, 0, 0, 1, 1, 2929.404052734375, -332.448272705078125, 113.43121337890625, 4.572763919830322265, 0, 0, -0.75470924377441406, 0.656059443950653076, 120, 255, 1, "", 45942, NULL), +(61384, 188659, 571, 0, 0, 1, 1, 2757.970458984375, -180.418655395507812, 138.998748779296875, 0.959929943084716796, 0, 0, 0.461748123168945312, 0.887011110782623291, 120, 255, 1, "", 46158, NULL), +(61385, 188659, 571, 0, 0, 1, 1, 2697.780517578125, -200.24945068359375, 140.154632568359375, 1.570795774459838867, 0, 0, 0.707106590270996093, 0.707106947898864746, 120, 255, 1, "", 46158, NULL), +(61386, 188659, 571, 0, 0, 1, 1, 2731.54833984375, -241.892257690429687, 141.5568389892578125, 2.199114561080932617, 0, 0, 0.8910064697265625, 0.453990638256072998, 120, 255, 1, "", 46158, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (188426, 188659)) AND (`guid` BETWEEN 939 AND 947); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(939, 188426, 571, 0, 0, 1, 1, 2608.928955078125, 5263.6396484375, 39.4164886474609375, 1.239183306694030761, 0, 0, 0.580702781677246093, 0.814115643501281738, 120, 255, 1, "", 49345, NULL), +(940, 188426, 571, 0, 0, 1, 1, 2611.096923828125, 5260.75732421875, 39.40889358520507812, 2.164205789566040039, 0, 0, 0.882946968078613281, 0.469472706317901611, 120, 255, 1, "", 49345, NULL), +(941, 188426, 571, 0, 0, 1, 1, 2614.411376953125, 5258.751953125, 39.40840911865234375, 3.665196180343627929, 0, 0, -0.96592521667480468, 0.258821308612823486, 120, 255, 1, "", 49345, NULL), +(942, 188426, 571, 0, 0, 1, 1, 2615.94189453125, 5267.6845703125, 39.4207000732421875, 5.462882041931152343, 0, 0, -0.39874839782714843, 0.917060375213623046, 120, 255, 1, "", 49345, NULL), +(943, 188426, 571, 0, 0, 1, 1, 2623.84375, 5255.21875, 38.28279876708984375, 0.750490784645080566, 0, 0, 0.3665008544921875, 0.93041771650314331, 120, 255, 1, "", 49345, NULL), +(944, 188659, 571, 0, 0, 1, 1, 2444.703125, -394.670135498046875, 7.966825008392333984, 0.034906249493360519, 0, 0, 0.017452239990234375, 0.999847710132598876, 120, 255, 1, "", 46158, NULL), +(945, 188659, 571, 0, 0, 1, 1, 2477.215576171875, -350.638916015625, 1.422237038612365722, 0.820303261280059814, 0, 0, 0.398748397827148437, 0.917060375213623046, 120, 255, 1, "", 46158, NULL), +(946, 188659, 571, 0, 0, 1, 1, 2622.64111328125, -264.823455810546875, 2.252971887588500976, 4.066620349884033203, 0, 0, -0.89493370056152343, 0.44619917869567871, 120, 255, 1, "", 45854, NULL), +(947, 188659, 571, 0, 0, 1, 1, 2684.013427734375, -485.452423095703125, 50.89562606811523437, 3.595378875732421875, 0, 0, -0.97437000274658203, 0.224951311945915222, 120, 255, 1, "", 45854, NULL); + +-- remaining spawns (no sniffed values available) +-- (`guid` IN (99937)) diff --git a/data/sql/updates/db_world/2025_09_10_02.sql b/data/sql/updates/db_world/2025_09_10_02.sql new file mode 100644 index 000000000..04d4b34c3 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_10_02.sql @@ -0,0 +1,14 @@ +-- DB update 2025_09_10_01 -> 2025_09_10_02 +-- +DELETE FROM `creature_template_addon` WHERE `entry`=29854; +INSERT INTO `creature_template_addon` (`entry`,`path_id`,`bytes1`,`mount`,`auras`) VALUES +(29854,0,1,0, ''); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 56393); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 56393, 0, 0, 31, 0, 3, 29854, 0, 0, 0, 0, '', 'Feed Stormcrest Eagle target Stormcrest Eagle'), +(13, 1, 56393, 0, 0, 1, 0, 56393, 0, 0, 1, 0, 0, '', 'Feed Stormcrest Eagle target must not have Feed Stormcrest Eagle aura'); + +DELETE FROM `spell_script_names` WHERE `spell_id` = 56393; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(56393, 'spell_feed_stormcrest_eagle'); diff --git a/deps/acore/joiner/joiner.sh b/deps/acore/joiner/joiner.sh index be95b673a..1b1300716 100755 --- a/deps/acore/joiner/joiner.sh +++ b/deps/acore/joiner/joiner.sh @@ -94,11 +94,11 @@ function Joiner:add_repo() ( basedir="${4:-""}" [[ -z $url ]] && hasReq=false || hasReq=true - Joiner:_help $hasReq "$1" "Syntax: joiner.sh add-repo [-d] [-e] url name branch [basedir]" + Joiner:_help "$hasReq" "$1" "Syntax: joiner.sh add-repo [-d] [-e] url name branch [basedir]" # retrieving info from url if not set if [[ -z $name ]]; then - basename=$(basename $url) + basename=$(basename "$url") name=${basename%%.*} if [[ -z "$basedir" ]]; then @@ -115,10 +115,12 @@ function Joiner:add_repo() ( if [ -e "$path/.git/" ]; then # if exists , update - git --git-dir="$path/.git/" rev-parse && git --git-dir="$path/.git/" pull origin $branch | grep 'Already up-to-date.' && changed="no" || true + echo "Updating $name on branch $branch..." + git --git-dir="$path/.git/" --work-tree="$path" rev-parse && git --git-dir="$path/.git/" --work-tree="$path" pull origin "$branch" | grep 'Already up-to-date.' && changed="no" || true else # otherwise clone - git clone $url -c advice.detachedHead=0 -b $branch "$path" + echo "Cloning $name on branch $branch..." + git clone "$url" -c advice.detachedHead=0 -b "$branch" "$path" fi if [ "$?" -ne "0" ]; then @@ -140,16 +142,16 @@ function Joiner:add_git_submodule() ( basedir=${4:-""} [[ -z $url ]] && hasReq=false || hasReq=true - Joiner:_help $hasReq "$1" "Syntax: joiner.sh add-git-submodule [-d] [-e] url name branch [basedir]" + Joiner:_help "$hasReq" "$1" "Syntax: joiner.sh add-git-submodule [-d] [-e] url name branch [basedir]" # retrieving info from url if not set if [[ -z $name ]]; then - basename=$(basename $url) + basename=$(basename "$url") name=${basename%%.*} if [[ -z $basedir ]]; then - dir=$(dirname $url) - basedir=$(basename $dir) + dir=$(dirname "$url") + basedir=$(basename "$dir") fi name="${name,,}" #to lowercase @@ -158,17 +160,17 @@ function Joiner:add_git_submodule() ( path="$J_PATH_MODULES/$basedir/$name" valid_path=`Joiner:_searchFirstValiPath "$path"` - rel_path=${path#$valid_path} + rel_path=${path#"$valid_path"} rel_path=${rel_path#/} - if [ -e $path/ ]; then + if [ -e "$path/" ]; then # if exists , update - (cd "$path" && git pull origin $branch) - (cd "$valid_path" && git submodule update -f --init $rel_path) + (cd "$path" && git pull origin "$branch") + (cd "$valid_path" && git submodule update -f --init "$rel_path") else # otherwise add - (cd "$valid_path" && git submodule add -f -b $branch $url $rel_path) - (cd "$valid_path" && git submodule update -f --init $rel_path) + (cd "$valid_path" && git submodule add -f -b "$branch" "$url" "$rel_path") + (cd "$valid_path" && git submodule update -f --init "$rel_path") fi if [ "$?" -ne "0" ]; then @@ -324,7 +326,7 @@ function Joiner:self_update() { if [ ! -z "$J_VER_REQ" ]; then # if J_VER_REQ is defined then update only if tag is different _cur_branch=`git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" rev-parse --abbrev-ref HEAD` - _cur_ver=`git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" name-rev --tags --name-only $_cur_branch` + _cur_ver=`git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" name-rev --tags --name-only "$_cur_branch"` if [ "$_cur_ver" != "$J_VER_REQ" ]; then git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" rev-parse && git --git-dir="$J_PATH/.git/" fetch --tags origin "$_cur_branch" --quiet git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" checkout "tags/$J_VER_REQ" -b "$_cur_branch" @@ -416,8 +418,8 @@ function Joiner:menu() { while true do # run option directly if specified in argument - [ ! -z $1 ] && _switch $@ - [ ! -z $1 ] && exit 0 + [ ! -z "$1" ] && _switch $@ + [ ! -z "$1" ] && exit 0 echo "" echo "==== JOINER MENU ====" diff --git a/deps/boost/CMakeLists.txt b/deps/boost/CMakeLists.txt index b6fd4ec1b..783356283 100644 --- a/deps/boost/CMakeLists.txt +++ b/deps/boost/CMakeLists.txt @@ -31,7 +31,8 @@ else() set(BOOST_REQUIRED_VERSION 1.74) endif() -find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED system filesystem program_options iostreams regex thread) +# Boost.System is header-only since 1.69; do not require it explicitly. +find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED COMPONENTS filesystem program_options iostreams regex thread) if(NOT Boost_FOUND) if(NOT DEFINED ENV{Boost_ROOT} AND NOT DEFINED Boost_DIR AND NOT DEFINED BOOST_ROOT AND NOT DEFINED BOOSTROOT) diff --git a/src/common/Dynamic/TypeContainer.h b/src/common/Dynamic/TypeContainer.h index 0c338de5d..6240eb48d 100644 --- a/src/common/Dynamic/TypeContainer.h +++ b/src/common/Dynamic/TypeContainer.h @@ -26,6 +26,7 @@ #include "Dynamic/TypeList.h" #include "GridRefMgr.h" #include +#include /* * @class ContainerMapList is a mulit-type container for map elements @@ -50,6 +51,24 @@ struct ContainerMapList> ContainerMapList _TailElements; }; +template +struct ContainerVector +{ + std::vector _element; +}; + +template<> +struct ContainerVector +{ +}; + +template +struct ContainerVector> +{ + ContainerVector _elements; + ContainerVector _TailElements; +}; + template struct ContainerUnorderedMap { @@ -123,6 +142,33 @@ private: ContainerMapList i_elements; }; +template +class TypeVectorContainer +{ +public: + template [[nodiscard]] std::size_t Count() const { return Acore::Count(i_elements, (SPECIFIC_TYPE*)nullptr); } + + template + bool Insert(SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Acore::Insert(i_elements, obj); + return (t != nullptr); + } + + template + bool Remove(SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Acore::Remove(i_elements, obj); + return (t != nullptr); + } + + ContainerVector& GetElements() { return i_elements; } + [[nodiscard]] const ContainerVector& GetElements() const { return i_elements; } + +private: + ContainerVector i_elements; +}; + template class TypeUnorderedMapContainer { diff --git a/src/common/Dynamic/TypeContainerFunctions.h b/src/common/Dynamic/TypeContainerFunctions.h index b517f4529..61735d7d8 100644 --- a/src/common/Dynamic/TypeContainerFunctions.h +++ b/src/common/Dynamic/TypeContainerFunctions.h @@ -239,5 +239,101 @@ namespace Acore // SPECIFIC_TYPE* t = Remove(elements._elements, obj); // return ( t != nullptr ? t : Remove(elements._TailElements, obj)); //} + + /* ContainerVector Helpers */ + // count functions + template + std::size_t Count(const ContainerVector& elements, SPECIFIC_TYPE* /*fake*/) + { + return elements._element.getSize(); + } + + template + std::size_t Count(const ContainerVector& /*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template + std::size_t Count(const ContainerVector& /*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template + std::size_t Count(const ContainerVector>& elements, SPECIFIC_TYPE* fake) + { + return Count(elements._elements, fake); + } + + template + std::size_t Count(const ContainerVector>& elements, SPECIFIC_TYPE* fake) + { + return Count(elements._TailElements, fake); + } + + // non-const insert functions + template + SPECIFIC_TYPE* Insert(ContainerVector& elements, SPECIFIC_TYPE* obj) + { + elements._element.push_back(obj); + return obj; + } + + template + SPECIFIC_TYPE* Insert(ContainerVector& /*elements*/, SPECIFIC_TYPE* /*obj*/) + { + return nullptr; + } + + // this is a missed + template + SPECIFIC_TYPE* Insert(ContainerVector& /*elements*/, SPECIFIC_TYPE* /*obj*/) + { + return nullptr; // a missed + } + + // Recursion + template + SPECIFIC_TYPE* Insert(ContainerVector>& elements, SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Insert(elements._elements, obj); + return (t != nullptr ? t : Insert(elements._TailElements, obj)); + } + + // non-const remove method + template SPECIFIC_TYPE* Remove(ContainerVector& elements, SPECIFIC_TYPE *obj) + { + // Simple vector find/swap/pop, this container should be very lightly used + // so I don't suspect the linear search complexity to be an issue + auto itr = std::find(elements._element.begin(), elements._element.end(), obj); + if (itr != elements._element.end()) + { + // Swap the element to be removed with the last element + std::swap(*itr, elements._element.back()); + + // Remove the last element (which is now the element we wanted to remove) + elements._element.pop_back(); + } + return obj; + } + + template SPECIFIC_TYPE* Remove(ContainerVector &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return nullptr; + } + + // this is a missed + template SPECIFIC_TYPE* Remove(ContainerVector &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return nullptr; // a missed + } + + template SPECIFIC_TYPE* Remove(ContainerVector > &elements, SPECIFIC_TYPE *obj) + { + // The head element is bad + SPECIFIC_TYPE* t = Remove(elements._elements, obj); + return ( t != nullptr ? t : Remove(elements._TailElements, obj)); + } } #endif diff --git a/src/common/Dynamic/TypeContainerVisitor.h b/src/common/Dynamic/TypeContainerVisitor.h index 1553d918a..066f6f308 100644 --- a/src/common/Dynamic/TypeContainerVisitor.h +++ b/src/common/Dynamic/TypeContainerVisitor.h @@ -56,6 +56,27 @@ template void VisitorHelper(VISITOR& v, TypeM VisitorHelper(v, c.GetElements()); } +// VectorContainer +template void VisitorHelper(VISITOR& /*v*/, ContainerVector& /*c*/) {} + +template void VisitorHelper(VISITOR& v, ContainerVector& c) +{ + v.Visit(c._element); +} + +// recursion container map list +template void VisitorHelper(VISITOR& v, ContainerVector>& c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// for TypeMapContainer +template void VisitorHelper(VISITOR& v, TypeVectorContainer& c) +{ + VisitorHelper(v, c.GetElements()); +} + // TypeUnorderedMapContainer template void VisitorHelper(VISITOR& /*v*/, ContainerUnorderedMap& /*c*/) { } diff --git a/src/server/apps/authserver/Main.cpp b/src/server/apps/authserver/Main.cpp index e9ed0119f..b5fbb319a 100644 --- a/src/server/apps/authserver/Main.cpp +++ b/src/server/apps/authserver/Main.cpp @@ -29,6 +29,7 @@ #include "Config.h" #include "DatabaseEnv.h" #include "DatabaseLoader.h" +#include "GitRevision.h" #include "IPLocation.h" #include "IoContext.h" #include "Log.h" @@ -75,7 +76,7 @@ int main(int argc, char** argv) auto vm = GetConsoleArguments(argc, argv, configFile); // exit if help or version is enabled - if (vm.count("help")) + if (vm.count("help") || vm.count("version")) return 0; // Add file and args in config @@ -292,13 +293,11 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile) } if (variablesMap.count("help")) - { std::cout << all << "\n"; - } + else if (variablesMap.count("version")) + std::cout << GitRevision::GetFullVersion() << "\n"; else if (variablesMap.count("dry-run")) - { sConfigMgr->setDryRun(true); - } return variablesMap; } diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp index d259f0220..31fa1b16e 100644 --- a/src/server/apps/worldserver/Main.cpp +++ b/src/server/apps/worldserver/Main.cpp @@ -127,7 +127,7 @@ int main(int argc, char** argv) auto vm = GetConsoleArguments(argc, argv, configFile, configService); // exit if help or version is enabled - if (vm.count("help")) + if (vm.count("help") || vm.count("version")) return 0; #if AC_PLATFORM == AC_PLATFORM_WINDOWS @@ -744,13 +744,11 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [ } if (vm.count("help")) - { std::cout << all << "\n"; - } + else if (vm.count("version")) + std::cout << GitRevision::GetFullVersion() << "\n"; else if (vm.count("dry-run")) - { sConfigMgr->setDryRun(true); - } return vm; } diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index db0936a04..634df5262 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -657,7 +657,7 @@ Appender.Console=1,4,0,"1 9 3 6 5 8" Appender.Server=2,5,0,Server.log,w Appender.Playerbots=2,5,0,Playerbots.log,w # Appender.GM=2,5,15,gm_%s.log -Appender.Errors=2,5,0,Errors.log,w +Appender.Errors=2,2,0,Errors.log,w # Appender.DB=3,5,0 # Logger config values: Given a logger "name" @@ -686,6 +686,7 @@ Logger.mmaps=4,Server Logger.scripts.hotswap=4,Console Server Logger.server=4,Console Server Logger.sql.sql=2,Console Errors +Logger.sql.updates=4,Console Server Errors Logger.sql=4,Console Server Logger.time.update=4,Console Server Logger.module=4,Console Server @@ -785,7 +786,6 @@ Logger.playerbots=5,Console Playerbots #Logger.spells=4,Console Server #Logger.sql.dev=4,Console Server #Logger.sql.driver=4,Console Server -#Logger.sql.updates=4,Console Server #Logger.vehicles=4,Console Server #Logger.warden=4,Console Server #Logger.weather=4,Console Server @@ -3143,6 +3143,16 @@ LeaveGroupOnLogout.Enabled = 0 Group.Raid.LevelRestriction = 10 +# +# Group.RandomRollMaximum +# +# The maximum value for use with the client '/roll' command. +# Blizzlike and maximum value is 1000000. (Based on Classic and 3.3.5a client testing respectively) +# Default: 1000000 +# + +Group.RandomRollMaximum = 1000000 + # ################################################################################################### diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 80ebac719..ab31d3cd7 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -353,8 +353,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS, "UPDATE characters SET at_login = at_login | ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_BUG_REPORT, "INSERT INTO bugreport (type, content) VALUES(?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petitionguid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petitionguid, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petition_id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petition_id, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_OFFLINE, "UPDATE characters SET online = 0 WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_GROUP, "INSERT INTO `groups` (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raidDifficulty, masterLooterGuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); @@ -457,9 +457,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_CHAR_GIFT, "INSERT INTO character_gifts (guid, item_guid, entry, flags) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INSTANCE_BY_INSTANCE, "DELETE FROM instance WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID, "DELETE FROM petition_sign WHERE petitionguid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (petition_id, ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PETITION_BY_ID, "DELETE FROM petition WHERE petition_id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_ID, "DELETE FROM petition_sign WHERE petition_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_DECLINED_NAME, "INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_RACE, "UPDATE characters SET race = ? WHERE guid = ?", CONNECTION_ASYNC); @@ -607,7 +607,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_PROFANITY_PLAYER_NAME, "INSERT IGNORE INTO profanity_name (name) VALUES (?)", CONNECTION_ASYNC); // Character settings - PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 801826b48..20c9d9892 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -382,8 +382,8 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_INSTANCE_BY_INSTANCE, CHAR_DEL_MAIL_ITEM_BY_ID, CHAR_INS_PETITION, - CHAR_DEL_PETITION_BY_GUID, - CHAR_DEL_PETITION_SIGNATURE_BY_GUID, + CHAR_DEL_PETITION_BY_ID, + CHAR_DEL_PETITION_SIGNATURE_BY_ID, CHAR_DEL_CHAR_DECLINED_NAME, CHAR_INS_CHAR_DECLINED_NAME, CHAR_UPD_CHAR_RACE, diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index d48cf2681..f7e30d339 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -72,6 +72,8 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c) mcanSpawn = true; + _chaseOnInterrupt = false; + // Xinef: Vehicle conditions m_ConditionsTimer = 0; if (me->GetVehicleKit()) @@ -658,6 +660,7 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/) if (me->GetCharmerGUID().IsPlayer() || me->HasUnitFlag(UNIT_FLAG_POSSESSED)) { me->AttackStop(); + me->RemoveUnitFlag(UNIT_FLAG_IN_COMBAT); return; } diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index ede658a47..1cc940df4 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -212,6 +212,9 @@ public: // Xinef void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; } + void SetChaseOnInterrupt(bool apply) { _chaseOnInterrupt = apply; } + [[nodiscard]] bool CanChaseOnInterrupt() const { return _chaseOnInterrupt; } + private: bool mIsCharmed; uint32 mFollowCreditType; @@ -257,6 +260,8 @@ private: void CheckConditions(const uint32 diff); ConditionList conditions; uint32 m_ConditionsTimer; + + bool _chaseOnInterrupt; }; class SmartGameObjectAI : public GameObjectAI diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 24a3d6901..c980f4f70 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -742,6 +742,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) { + CAST_AI(SmartAI, me->AI())->SetChaseOnInterrupt(true); + if (!me->isMoving()) // Don't try to reposition while we are moving { // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS. @@ -893,7 +895,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u go->SetLootState(GO_READY); } - go->UseDoorOrButton(0, !!e.action.activateObject.alternative, unit); + go->UseDoorOrButton(0, e.action.activateObject.alternative, unit); LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ACTIVATE_GOBJECT. Gameobject {} activated", go->GetGUID().ToString()); } } @@ -1483,14 +1485,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { for (WorldObject* target : targets) if (IsUnit(target)) - target->ToUnit()->SetVisible(!!e.action.visibility.state); + target->ToUnit()->SetVisible(e.action.visibility.state); break; } case SMART_ACTION_SET_ACTIVE: { for (WorldObject* target : targets) - target->setActive(!!e.action.setActive.state); + target->setActive(e.action.setActive.state); break; } case SMART_ACTION_ATTACK_START: @@ -3832,19 +3834,20 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e, { targets.clear(); - if (owner->ToCreature()) + if (IsCreature(owner)) { if (Unit* base = ObjectAccessor::GetUnit(*owner, owner->ToCreature()->GetCharmerOrOwnerGUID())) - { targets.push_back(base); - } } - else + else if (IsGameObject(owner)) { if (Unit* base = ObjectAccessor::GetUnit(*owner, owner->ToGameObject()->GetOwnerGUID())) - { targets.push_back(base); - } + } + else if (IsPlayer(owner)) + { + if (Unit* base = owner->ToPlayer()->GetCharmerOrOwner()) + targets.push_back(base); } } } diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 0040e0bd3..9185f77ac 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -202,10 +202,29 @@ void SmartAIMgr::LoadSmartAIFromDB() } else { - if (!sObjectMgr->GetCreatureData(uint32(std::abs(temp.entryOrGuid)))) + switch (source_type) { - LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Creature guid ({}) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid))); - continue; + case SMART_SCRIPT_TYPE_CREATURE: + { + if (!sObjectMgr->GetCreatureData(uint32(std::abs(temp.entryOrGuid)))) + { + LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Creature guid ({}) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid))); + continue; + } + break; + } + case SMART_SCRIPT_TYPE_GAMEOBJECT: + { + if (!sObjectMgr->GetGameObjectData(uint32(std::abs(temp.entryOrGuid)))) + { + LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid ({}) does not exist, skipped loading.", uint32(temp.entryOrGuid)); + continue; + } + break; + } + default: + LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: not yet implemented source_type {}", (uint32)source_type); + continue; } } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 736fd46e1..dcc1b7662 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -488,7 +488,7 @@ void AuctionHouseObject::AddAuction(AuctionEntry* auction) bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction) { - bool wasInMap = !!_auctionsMap.erase(auction->Id); + bool wasInMap = _auctionsMap.erase(auction->Id); sAuctionMgr->GetAuctionHouseSearcher()->RemoveAuction(auction); sScriptMgr->OnAuctionRemove(this, auction); diff --git a/src/server/game/Chat/HyperlinkTags.cpp b/src/server/game/Chat/HyperlinkTags.cpp index 342ba3035..2cb36b698 100644 --- a/src/server/game/Chat/HyperlinkTags.cpp +++ b/src/server/game/Chat/HyperlinkTags.cpp @@ -206,7 +206,7 @@ bool Acore::Hyperlinks::LinkTags::spell::StoreTo(SpellInfo const*& val, std::str if (!(t.TryConsumeTo(spellId) && t.IsEmpty())) return false; - return !!(val = sSpellMgr->GetSpellInfo(spellId)); + return (val = sSpellMgr->GetSpellInfo(spellId)); } bool Acore::Hyperlinks::LinkTags::talent::StoreTo(TalentLinkData& val, std::string_view text) diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 93bdc0c8a..09edfab28 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -68,7 +68,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) { // don't allow 0 items (it's checked during table load) ASSERT(ConditionValue2); - bool checkBank = !!ConditionValue3; + bool checkBank = ConditionValue3; condMeets = player->HasItemCount(ConditionValue1, ConditionValue2, checkBank); } } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index dbaea048f..2cc46d3e5 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1579,7 +1579,7 @@ void Creature::SelectLevel(bool changelevel) SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower); SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower); - sScriptMgr->Creature_SelectLevel(cInfo, this); + sScriptMgr->OnCreatureSelectLevel(cInfo, this); } float Creature::_GetHealthMod(int32 Rank) @@ -1987,8 +1987,6 @@ void Creature::setDeathState(DeathState state, bool despawn) Dismount(); // if creature is mounted on a virtual mount, remove it at death - setActive(false); - if (HasSearchedAssistance()) { SetNoSearchAssistance(false); @@ -3904,7 +3902,7 @@ bool Creature::IsUpdateNeeded() if (IsInCombat()) return true; - if (IsVisibilityOverridden()) + if (!GetObjectVisibilityContainer().GetVisiblePlayersMap().empty()) return true; if (ToTempSummon()) diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp index c4ef53ae6..c603b51da 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.cpp +++ b/src/server/game/Entities/Creature/CreatureGroups.cpp @@ -361,8 +361,9 @@ void CreatureGroup::LeaderMoveTo(float x, float y, float z, uint32 move_type) if (member == m_leader || !member->IsAlive() || member->GetVictim() || !pFormationInfo.HasGroupFlag(std::underlying_type_t(GroupAIFlags::GROUP_AI_FLAG_FOLLOW_LEADER))) continue; - // Xinef: If member is stunned / rooted etc don't allow to move him - if (member->HasUnitState(UNIT_STATE_NOT_MOVE)) + // If member is stunned / rooted etc don't allow to move him + // Or if charmed/controlled + if (member->HasUnitState(UNIT_STATE_NOT_MOVE) || member->isPossessed() || member->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) continue; // Xinef: this should be automatized, if turn angle is greater than PI/2 (90�) we should swap formation angle diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h index 2cb6e9ae8..54b01c08c 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.h +++ b/src/server/game/Entities/Creature/CreatureGroups.h @@ -67,7 +67,7 @@ struct FormationInfo uint32 point_1; uint32 point_2; - bool HasGroupFlag(uint16 flag) const { return !!(groupAI & flag); } + bool HasGroupFlag(uint16 flag) const { return (groupAI & flag); } }; typedef std::unordered_map CreatureGroupInfoType; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 827dc38dd..f258db1ca 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -431,15 +431,11 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u // Check if GameObject is Large if (goinfo->IsLargeGameObject()) - { SetVisibilityDistanceOverride(VisibilityDistanceType::Large); - } // Check if GameObject is Infinite if (goinfo->IsInfiniteGameObject()) - { SetVisibilityDistanceOverride(VisibilityDistanceType::Infinite); - } return true; } @@ -3086,7 +3082,7 @@ bool GameObject::IsUpdateNeeded() if (GetMap()->isCellMarked(GetCurrentCell().GetCellCoord().GetId())) return true; - if (IsVisibilityOverridden()) + if (!GetObjectVisibilityContainer().GetVisiblePlayersMap().empty()) return true; if (IsTransport()) diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 37605de21..1b8ccd047 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -156,8 +156,8 @@ public: void SaveToDB(bool saveAddon = false); void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool saveAddon = false); - bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); } - bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true); + virtual bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); } + virtual bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true); void DeleteFromDB(); void SetOwnerGUID(ObjectGuid owner) diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h index fac8fa9d2..fd95aa514 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -629,6 +629,8 @@ struct GameObjectTemplate return true; case GAMEOBJECT_TYPE_TRAPDOOR: return true; + case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: + return true; default: return false; } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a9fcd7199..a183d1467 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1037,7 +1037,7 @@ void MovementInfo::OutDebug() } WorldObject::WorldObject() : WorldLocation(), - LastUsedScriptID(0), m_name(""), m_isActive(false), m_visibilityDistanceOverride(), m_zoneScript(nullptr), + LastUsedScriptID(0), m_name(""), m_isActive(false), _visibilityDistanceOverrideType(VisibilityDistanceType::Normal), m_zoneScript(nullptr), _zoneId(0), _areaId(0), _floorZ(INVALID_HEIGHT), _outdoors(false), _liquidData(), _updatePositionData(false), m_transport(nullptr), m_currMap(nullptr), _heartbeatTimer(HEARTBEAT_INTERVAL), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0), _objectVisibilityContainer(this) @@ -1082,15 +1082,36 @@ void WorldObject::setActive(bool on) map->AddObjectToPendingUpdateList(this); } +float WorldObject::GetVisibilityOverrideDistance() const +{ + ASSERT(_visibilityDistanceOverrideType < VisibilityDistanceType::Max); + return VisibilityDistances[AsUnderlyingType(_visibilityDistanceOverrideType)]; +} + void WorldObject::SetVisibilityDistanceOverride(VisibilityDistanceType type) { ASSERT(type < VisibilityDistanceType::Max); - if (IsPlayer()) - { + + if (type == GetVisibilityOverrideType()) return; + + if (IsPlayer()) + return; + + if (IsVisibilityOverridden()) + { + if (IsFarVisible()) + GetMap()->RemoveWorldObjectFromFarVisibleMap(this); + else if (IsZoneWideVisible()) + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(GetZoneId(), this); } - m_visibilityDistanceOverride = VisibilityDistances[AsUnderlyingType(type)]; + if (type == VisibilityDistanceType::Large || type == VisibilityDistanceType::Gigantic) + GetMap()->AddWorldObjectToFarVisibleMap(this); + else if (type == VisibilityDistanceType::Infinite) + GetMap()->AddWorldObjectToZoneWideVisibleMap(GetZoneId(), this); + + _visibilityDistanceOverrideType = type; } void WorldObject::CleanupsBeforeDelete(bool /*finalCleanup*/) @@ -1127,6 +1148,8 @@ void WorldObject::UpdatePositionData() void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { + uint32 const oldZoneId = _zoneId; + _zoneId = _areaId = data.areaId; if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(_areaId)) @@ -1136,6 +1159,17 @@ void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& da _outdoors = data.outdoors; _floorZ = data.floorZ; _liquidData = data.liquidInfo; + + // Has zone ID changed? + if (oldZoneId != _zoneId) + { + // If so, check if we are far visibility overridden object and refresh maps if needed. + if (IsZoneWideVisible()) + { + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(oldZoneId, this); + GetMap()->AddWorldObjectToZoneWideVisibleMap(_zoneId, this); + } + } } void WorldObject::AddToWorld() @@ -1150,6 +1184,9 @@ void WorldObject::RemoveFromWorld() if (!IsInWorld()) return; + if (IsZoneWideVisible()) + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(GetZoneId(), this); + DestroyForVisiblePlayers(); GetObjectVisibilityContainer().CleanVisibilityReferences(); @@ -1612,26 +1649,16 @@ float WorldObject::GetGridActivationRange() const float WorldObject::GetVisibilityRange() const { - if (IsVisibilityOverridden() && IsCreature()) - { - return *m_visibilityDistanceOverride; - } + if (IsCreature() && IsVisibilityOverridden()) + return GetVisibilityOverrideDistance(); else if (IsGameObject()) { - { - if (IsInWintergrasp()) - { - return VISIBILITY_DIST_WINTERGRASP + VISIBILITY_INC_FOR_GOBJECTS; - } - else if (IsVisibilityOverridden()) - { - return *m_visibilityDistanceOverride; - } - else - { - return GetMap()->GetVisibilityRange() + VISIBILITY_INC_FOR_GOBJECTS; - } - } + if (IsInWintergrasp()) + return VISIBILITY_DIST_WINTERGRASP; + else if (IsVisibilityOverridden()) + return GetVisibilityOverrideDistance(); + else + return GetMap()->GetVisibilityRange(); } else return IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); @@ -1645,28 +1672,18 @@ float WorldObject::GetSightRange(WorldObject const* target) const { if (target) { - if (target->IsVisibilityOverridden() && target->IsCreature()) - { - return *target->m_visibilityDistanceOverride; - } + if (target->IsCreature() && target->IsVisibilityOverridden()) + return target->GetVisibilityOverrideDistance(); else if (target->IsGameObject()) { if (IsInWintergrasp() && target->IsInWintergrasp()) - { - return VISIBILITY_DIST_WINTERGRASP + VISIBILITY_INC_FOR_GOBJECTS; - } + return VISIBILITY_DIST_WINTERGRASP; else if (target->IsVisibilityOverridden()) - { - return *target->m_visibilityDistanceOverride; - } + return target->GetVisibilityOverrideDistance(); else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) - { return DEFAULT_VISIBILITY_INSTANCE; - } else - { - return GetMap()->GetVisibilityRange() + VISIBILITY_INC_FOR_GOBJECTS; - } + return GetMap()->GetVisibilityRange(); } return IsInWintergrasp() && target->IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); @@ -1674,19 +1691,13 @@ float WorldObject::GetSightRange(WorldObject const* target) const return IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); } else if (ToCreature()) - { return ToCreature()->m_SightDistance; - } else - { return SIGHT_RANGE_UNIT; - } } if (ToDynObject() && isActiveObject()) - { return GetMap()->GetVisibilityRange(); - } return 0.0f; } @@ -1696,7 +1707,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo if (this == obj) return true; - if (obj->IsNeverVisible() || CanNeverSee(obj)) + if (CanNeverSee(obj)) return false; if (obj->IsAlwaysVisibleFor(this) || CanAlwaysSee(obj)) @@ -1790,7 +1801,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo } // Xinef: check reversely obj vs viewpoint, object could be a gameObject which overrides _IsWithinDist function to include gameobject size - if (!corpseCheck && !viewpoint->IsWithinDist(obj, GetSightRange(obj), true)) + if (!corpseCheck && !viewpoint->IsWithinDist(obj, GetSightRange(obj), false)) return false; } @@ -1840,6 +1851,12 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo bool WorldObject::CanNeverSee(WorldObject const* obj) const { + if (!IsInWorld()) + return true; + + if (obj->IsNeverVisible()) + return true; + if (IsCreature() && obj->IsCreature()) return GetMap() != obj->GetMap() || (!InSamePhase(obj) && ToUnit()->GetVehicleBase() != obj && this != obj->ToUnit()->GetVehicleBase()); return GetMap() != obj->GetMap() || !InSamePhase(obj); diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 1a2cdf384..23784c9c6 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -358,9 +358,20 @@ template class GridObject { public: - [[nodiscard]] bool IsInGrid() const { return _gridRef.isValid(); } - void AddToGrid(GridRefMgr& m) { ASSERT(!IsInGrid()); _gridRef.link(&m, (T*)this); } - void RemoveFromGrid() { ASSERT(IsInGrid()); _gridRef.unlink(); } + bool IsInGrid() const + { + return _gridRef.isValid(); + } + void AddToGrid(GridRefMgr& m) + { + ASSERT(!IsInGrid()); + _gridRef.link(&m, (T*)this); + } + void RemoveFromGrid() + { + ASSERT(IsInGrid()); + _gridRef.unlink(); + } private: GridReference _gridRef; }; @@ -654,8 +665,11 @@ public: [[nodiscard]] bool isActiveObject() const { return m_isActive; } void setActive(bool isActiveObject); - [[nodiscard]] bool IsFarVisible() const { return m_isFarVisible; } - [[nodiscard]] bool IsVisibilityOverridden() const { return m_visibilityDistanceOverride.has_value(); } + VisibilityDistanceType GetVisibilityOverrideType() const { return _visibilityDistanceOverrideType; } + bool IsVisibilityOverridden() const { return _visibilityDistanceOverrideType > VisibilityDistanceType::Normal; } + bool IsZoneWideVisible() const { return _visibilityDistanceOverrideType == VisibilityDistanceType::Infinite; } + bool IsFarVisible() const { return _visibilityDistanceOverrideType == VisibilityDistanceType::Large || _visibilityDistanceOverrideType == VisibilityDistanceType::Gigantic; } + float GetVisibilityOverrideDistance() const; void SetVisibilityDistanceOverride(VisibilityDistanceType type); [[nodiscard]] bool IsInWintergrasp() const @@ -719,8 +733,7 @@ public: protected: std::string m_name; bool m_isActive; - bool m_isFarVisible; - Optional m_visibilityDistanceOverride; + VisibilityDistanceType _visibilityDistanceOverrideType; ZoneScript* m_zoneScript; virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data); diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h index bfae02203..fb0daa500 100644 --- a/src/server/game/Entities/Object/ObjectDefines.h +++ b/src/server/game/Entities/Object/ObjectDefines.h @@ -25,7 +25,6 @@ #define ATTACK_DISTANCE 5.0f #define VISIBILITY_COMPENSATION 15.0f // increase searchers #define INSPECT_DISTANCE 28.0f -#define VISIBILITY_INC_FOR_GOBJECTS 30.0f // pussywizard #define SPELL_SEARCHER_COMPENSATION 30.0f // increase searchers size in case we have large npc near cell border #define TRADE_DISTANCE 11.11f #define MAX_VISIBILITY_DISTANCE 250.0f // max distance for visible objects, experimental diff --git a/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp b/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp index 9831adf5a..358626180 100644 --- a/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp +++ b/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp @@ -66,6 +66,10 @@ void ObjectVisibilityContainer::LinkWorldObjectVisibility(WorldObject* worldObje if (worldObject == _selfObject) return; + // Transports are special and should not be added to our visibility map + if (worldObject->IsGameObject() && worldObject->ToGameObject()->IsTransport()) + return; + // Only players can link visibility if (!_visibleWorldObjectsMap) return; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 21b2a8e02..a58df5939 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4552,6 +4552,9 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) // update visibility UpdateObjectVisibility(); + // recast lost by death auras of any items held in the inventory + CastAllObtainSpells(); + sScriptMgr->OnPlayerResurrect(this, restore_percent, applySickness); if (!applySickness) @@ -6019,6 +6022,7 @@ void Player::RewardReputation(Unit* victim) if (Rep->RepFaction1 && (!Rep->TeamDependent || teamId == TEAM_ALLIANCE)) { float donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast(Rep->RepValue1), ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); + sScriptMgr->OnPlayerGiveReputation(this, Rep->RepFaction1, donerep1, REPUTATION_SOURCE_KILL); FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); if (factionEntry1) @@ -6030,6 +6034,7 @@ void Player::RewardReputation(Unit* victim) if (Rep->RepFaction2 && (!Rep->TeamDependent || teamId == TEAM_HORDE)) { float donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast(Rep->RepValue2), ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); + sScriptMgr->OnPlayerGiveReputation(this, Rep->RepFaction2, donerep2, REPUTATION_SOURCE_KILL); FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); if (factionEntry2) @@ -6069,22 +6074,27 @@ void Player::RewardReputation(Quest const* quest) if (quest->IsDaily()) { rep = CalculateReputationGain(REPUTATION_SOURCE_DAILY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_DAILY_QUEST); } else if (quest->IsWeekly()) { rep = CalculateReputationGain(REPUTATION_SOURCE_WEEKLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_WEEKLY_QUEST); } else if (quest->IsMonthly()) { rep = CalculateReputationGain(REPUTATION_SOURCE_MONTHLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_MONTHLY_QUEST); } else if (quest->IsRepeatable()) { rep = CalculateReputationGain(REPUTATION_SOURCE_REPEATABLE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_REPEATABLE_QUEST); } else { rep = CalculateReputationGain(REPUTATION_SOURCE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_QUEST); } if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->RewardFactionId[i])) @@ -7028,6 +7038,54 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt UpdateDamagePhysical(WeaponAttackType(attType)); } +void Player::CastAllObtainSpells() +{ + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot) + if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + ApplyItemObtainSpells(item, true); + + for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + Bag* bag = GetBagByPos(i); + if (!bag) + continue; + + for (uint32 slot = 0; slot < bag->GetBagSize(); ++slot) + if (Item* item = bag->GetItemByPos(slot)) + ApplyItemObtainSpells(item, true); + } +} + +void Player::ApplyItemObtainSpells(Item* item, bool apply) +{ + ItemTemplate const* itemTemplate = item->GetTemplate(); + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (itemTemplate->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger + continue; + + int32 const spellId = itemTemplate->Spells[i].SpellId; + if (spellId <= 0) + continue; + + if (apply) + { + if (!HasAura(spellId)) + CastSpell(this, spellId, true, item); + } + else + RemoveAurasDueToSpell(spellId); + } +} + +void Player::UpdateItemObtainSpells(Item* item, uint8 bag, uint8 slot) +{ + if (IsBankPos(bag, slot)) + ApplyItemObtainSpells(item, false); + else if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) + ApplyItemObtainSpells(item, true); +} + SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const { if (Item const* weapon = GetWeaponForAttack(attackType, true)) @@ -16264,13 +16322,25 @@ float Player::GetSightRange(WorldObject const* target) const { float sightRange = WorldObject::GetSightRange(target); if (_farSightDistance) - { sightRange += *_farSightDistance; - } return sightRange; } +bool Player::IsWorldObjectOutOfSightRange(WorldObject const* target) const +{ + // Special handling for Infinite visibility override objects -> they are zone wide visible + if (target->GetVisibilityOverrideType() == VisibilityDistanceType::Infinite) + { + // Same zone, always visible + if (target->GetZoneId() == GetZoneId()) + return false; + } + + // Check if out of range + return !m_seer->IsWithinDist(target, GetSightRange(target), false); +} + std::string Player::GetPlayerName() { std::string name = GetName(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 933f30148..9dd87b39b 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -235,17 +235,6 @@ enum ActionButtonType ACTION_BUTTON_ITEM = 0x80 }; -enum ReputationSource -{ - REPUTATION_SOURCE_KILL, - REPUTATION_SOURCE_QUEST, - REPUTATION_SOURCE_DAILY_QUEST, - REPUTATION_SOURCE_WEEKLY_QUEST, - REPUTATION_SOURCE_MONTHLY_QUEST, - REPUTATION_SOURCE_REPEATABLE_QUEST, - REPUTATION_SOURCE_SPELL -}; - enum QuestSound { QUEST_SOUND_FAILURE = 847 @@ -1682,7 +1671,7 @@ public: bool RemoveMItem(ObjectGuid::LowType itemLowGuid) { - return !!mMitems.erase(itemLowGuid); + return mMitems.erase(itemLowGuid); } void PetSpellInitialize(); @@ -2208,6 +2197,10 @@ public: void ResetAllPowers(); + void CastAllObtainSpells(); + void ApplyItemObtainSpells(Item* item, bool apply); + void UpdateItemObtainSpells(Item* item, uint8 bag, uint8 slot); + SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const override; void _ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply); @@ -2628,12 +2621,13 @@ public: [[nodiscard]] Optional GetFarSightDistance() const; float GetSightRange(WorldObject const* target = nullptr) const override; + bool IsWorldObjectOutOfSightRange(WorldObject const* target) const; std::string GetPlayerName(); // Settings - [[nodiscard]] PlayerSetting GetPlayerSetting(std::string const& source, uint8 index); - void UpdatePlayerSetting(std::string const& source, uint8 index, uint32 value); + [[nodiscard]] PlayerSetting GetPlayerSetting(std::string const& source, uint32 index); + void UpdatePlayerSetting(std::string const& source, uint32 index, uint32 value); void SendSystemMessage(std::string_view msg, bool escapeCharacters = false); diff --git a/src/server/game/Entities/Player/PlayerSettings.cpp b/src/server/game/Entities/Player/PlayerSettings.cpp index dba4afaa5..1fd4aae3a 100644 --- a/src/server/game/Entities/Player/PlayerSettings.cpp +++ b/src/server/game/Entities/Player/PlayerSettings.cpp @@ -18,11 +18,95 @@ #include "Player.h" #include "StringConvert.h" #include "Tokenize.h" +#include "CharacterDatabase.h" /*********************************************************/ /*** PLAYER SETTINGS SYSTEM ***/ /*********************************************************/ +namespace PlayerSettingsStore +{ + // Common helper: parse space-separated data string into PlayerSettingVector + PlayerSettingVector ParseSettingsData(std::string const& data) + { + PlayerSettingVector result; + std::vector tokens = Acore::Tokenize(data, ' ', false); + result.reserve(tokens.size()); + for (auto const& token : tokens) + { + if (token.empty()) + continue; + if (auto parsed = Acore::StringTo(token)) + result.emplace_back(*parsed); + } + return result; + } + + // Common helper: serialize PlayerSettingVector to space-separated string + std::string SerializeSettingsData(PlayerSettingVector const& settings) + { + if (settings.empty()) + return ""; + + std::ostringstream data; + data << settings[0].value; + for (size_t i = 1; i < settings.size(); ++i) + data << ' ' << settings[i].value; + return data.str(); + } + + // helper: load a single source row for a player and parse to vector + static PlayerSettingVector LoadPlayerSettings(uint32 playerLowGuid, std::string const& source) + { + PlayerSettingVector result; + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS); + stmt->SetData(0, playerLowGuid); + PreparedQueryResult dbRes = CharacterDatabase.Query(stmt); + if (!dbRes) + return result; + + do + { + Field* fields = dbRes->Fetch(); + std::string rowSource = fields[0].Get(); + if (rowSource != source) + continue; + + std::string data = fields[1].Get(); + return ParseSettingsData(data); + } while (dbRes->NextRow()); + + return result; + } + + void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value) + { + if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED)) + return; + + PlayerSettingVector settings = LoadPlayerSettings(playerLowGuid, source); + size_t const requiredSize = static_cast(index) + 1; + if (settings.size() < requiredSize) + settings.resize(requiredSize); // zero-initialized PlayerSetting::value + + settings[index].value = value; + + CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(playerLowGuid, source, settings); + CharacterDatabase.Execute(stmt); + } +} + +// Implementation of PrepareReplaceStatement +CharacterDatabasePreparedStatement* PlayerSettingsStore::PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings) +{ + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS); + stmt->SetData(0, playerLowGuid); + stmt->SetData(1, source); + stmt->SetData(2, SerializeSettingsData(settings)); + return stmt; +} + void Player::_LoadCharacterSettings(PreparedQueryResult result) { m_charSettingsMap.clear(); @@ -39,27 +123,13 @@ void Player::_LoadCharacterSettings(PreparedQueryResult result) std::string source = fields[0].Get(); std::string data = fields[1].Get(); - std::vector tokens = Acore::Tokenize(data, ' ', false); - - PlayerSettingVector settings; - settings.reserve(tokens.size()); // reserve capacity but don't resize - - for (auto const& token : tokens) - { - if (token.empty()) - continue; - - // Try to parse the value safely - if (auto parsed = Acore::StringTo(token)) - settings.emplace_back(*parsed); - } - + PlayerSettingVector settings = PlayerSettingsStore::ParseSettingsData(data); m_charSettingsMap.emplace(std::move(source), std::move(settings)); } while (result->NextRow()); } -PlayerSetting Player::GetPlayerSetting(std::string const& source, uint8 index) +PlayerSetting Player::GetPlayerSetting(std::string const& source, uint32 index) { auto it = m_charSettingsMap.find(source); if (it == m_charSettingsMap.end() || static_cast(index) >= it->second.size()) @@ -81,21 +151,12 @@ void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans) if (settings.empty()) continue; - std::ostringstream data; - data << settings[0].value; - - for (size_t i = 1; i < settings.size(); ++i) - data << ' ' << settings[i].value; - - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS); - stmt->SetData(0, GetGUID().GetCounter()); - stmt->SetData(1, source); - stmt->SetData(2, data.str()); + CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(GetGUID().GetCounter(), source, settings); trans->Append(stmt); } } -void Player::UpdatePlayerSetting(std::string const& source, uint8 index, uint32 value) +void Player::UpdatePlayerSetting(std::string const& source, uint32 index, uint32 value) { auto it = m_charSettingsMap.find(source); size_t const requiredSize = static_cast(index) + 1; diff --git a/src/server/game/Entities/Player/PlayerSettings.h b/src/server/game/Entities/Player/PlayerSettings.h index 027062e3c..4e0c96135 100644 --- a/src/server/game/Entities/Player/PlayerSettings.h +++ b/src/server/game/Entities/Player/PlayerSettings.h @@ -17,6 +17,7 @@ #ifndef _PLAYER_SETTINGS_H #define _PLAYER_SETTINGS_H +#include "DatabaseEnvFwd.h" class Player; @@ -51,4 +52,18 @@ struct PlayerSetting typedef std::vector PlayerSettingVector; typedef std::map PlayerSettingMap; +// Standalone API: update a player's setting directly on DB by GUID (low part) without requiring a Player instance +namespace PlayerSettingsStore +{ + // Update a single setting value for any player by GUID (works for online or offline players). + // This reads the existing "source" row from character_settings, adjusts the index, and REPLACE's it back. + void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value); + + // Common helpers for parsing and serializing settings data + PlayerSettingVector ParseSettingsData(std::string const& data); + std::string SerializeSettingsData(PlayerSettingVector const& settings); + // Prepare a REPLACE statement populated with given settings data. Caller may execute or append to a transaction. + CharacterDatabasePreparedStatement* PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings); +} + #endif diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 01595faa5..1423afc98 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -2577,8 +2577,6 @@ Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update) return nullptr; Item* lastItem = pItem; - ItemTemplate const* proto = pItem->GetTemplate(); - for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end();) { uint16 pos = itr->pos; @@ -2595,13 +2593,6 @@ Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update) lastItem = _StoreItem(pos, pItem, count, true, update); } - // cast after item storing - some checks in checkcast requires item to be present!! - if (lastItem) - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger - if (!HasAura(proto->Spells[i].SpellId)) - CastSpell(this, proto->Spells[i].SpellId, true, lastItem); - return lastItem; } @@ -2663,6 +2654,7 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool AddEnchantmentDurations(pItem); AddItemDurations(pItem); + UpdateItemObtainSpells(pItem, bag, slot); return pItem; } @@ -2700,6 +2692,8 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool pItem2->SetState(ITEM_CHANGED, this); + UpdateItemObtainSpells(pItem2, bag, slot); + return pItem2; } } @@ -3046,10 +3040,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) pItem->ClearSoulboundTradeable(this); RemoveTradeableItem(pItem); - ItemTemplate const* proto = pItem->GetTemplate(); - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger - RemoveAurasDueToSpell(proto->Spells[i].SpellId); + ApplyItemObtainSpells(pItem, false); ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount()); @@ -3102,8 +3093,9 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) pBag->RemoveItem(slot, update); // Xinef: item is removed, remove loot from storage if any - if (proto->HasFlag(ITEM_FLAG_HAS_LOOT)) - sLootItemStorage->RemoveStoredLoot(pItem->GetGUID()); + if (ItemTemplate const* proto = pItem->GetTemplate()) + if (proto->HasFlag(ITEM_FLAG_HAS_LOOT)) + sLootItemStorage->RemoveStoredLoot(pItem->GetGUID()); if (IsInWorld() && update) { diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index bdd95a4f7..9b4925541 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1595,21 +1595,12 @@ void Player::UpdateVisibilityForPlayer(bool mapChange) // After added to map seer must be a player - there is no possibility to // still have different seer (all charm auras must be already removed) if (mapChange && m_seer != this) - { m_seer = this; - } - Acore::VisibleNotifier notifierNoLarge( - *this, mapChange, - false); // visit only objects which are not large; default distance - Cell::VisitObjects(m_seer, notifierNoLarge, - GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - notifierNoLarge.SendToSelf(); - - Acore::VisibleNotifier notifierLarge( - *this, mapChange, true); // visit only large objects; maximum distance - Cell::VisitObjects(m_seer, notifierLarge, GetSightRange()); - notifierLarge.SendToSelf(); + Acore::VisibleNotifier notifier(*this, mapChange); + Cell::VisitObjects(m_seer, notifier, GetSightRange()); + Cell::VisitFarVisibleObjects(m_seer, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); if (mapChange) m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); @@ -1646,10 +1637,7 @@ template <> inline void UpdateVisibilityOf_helper(Player* player, GameObject* target, std::vector& /*v*/) { - // @HACK: This is to prevent objects like deeprun tram from disappearing - // when player moves far from its spawn point while riding it - if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT)) - player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); + player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); } template <> diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 251cdb936..ee87af41d 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -607,12 +607,12 @@ void MotionTransport::DelayedTeleportTransport() } Map* newMap = sMapMgr->CreateBaseMap(newMapId); - GetMap()->RemoveFromMap(this, false); + GetMap()->RemoveFromMap(this, false); newMap->LoadGrid(x, y); // xinef: load before adding passengers to new map SetMap(newMap); Relocate(x, y, z, o); - GetMap()->AddToMap(this); + GetMap()->AddToMap(this); LoadStaticPassengers(); } @@ -690,6 +690,40 @@ StaticTransport::~StaticTransport() ASSERT(_passengers.empty()); } +bool StaticTransport::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap) +{ + GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId); + + if (!data) + { + LOG_ERROR("sql.sql", "Gameobject (GUID: {}) not found in table `gameobject`, can't load. ", spawnId); + return false; + } + + uint32 entry = data->id; + //uint32 map_id = data->mapid; // already used before call + uint32 phaseMask = data->phaseMask; + float x = data->posX; + float y = data->posY; + float z = data->posZ; + float ang = data->orientation; + + uint32 animprogress = data->animprogress; + GOState go_state = data->go_state; + uint32 artKit = data->artKit; + + m_goData = data; + m_spawnId = spawnId; + + if (!Create(map->GenerateLowGuid(), entry, map, phaseMask, x, y, z, ang, data->rotation, animprogress, go_state, artKit)) + return false; + + if (addToMap && !GetMap()->AddToMap(this)) + return false; + + return true; +} + bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit) { ASSERT(map); @@ -794,7 +828,6 @@ bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* m LastUsedScriptID = GetGOInfo()->ScriptId; AIM_Initialize(); - this->setActive(true); return true; } @@ -929,7 +962,9 @@ void StaticTransport::UpdatePosition(float x, float y, float z, float o) if (!GetMap()->IsGridLoaded(x, y)) // pussywizard: should not happen, but just in case GetMap()->LoadGrid(x, y); - GetMap()->GameObjectRelocation(this, x, y, z, o); // this also relocates the model + Relocate(x, y, z, o); + UpdateModelPosition(); + UpdatePassengerPositions(); } diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 005e9d789..9b50775af 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -38,6 +38,8 @@ public: virtual void RemovePassenger(WorldObject* passenger, bool withAll = false) = 0; PassengerSet const& GetPassengers() const { return _passengers; } + virtual void DelayedUpdate(uint32 /*diff*/) {} + uint32 GetPathProgress() const { return GetGOValue()->Transport.PathProgress; } void SetPathProgress(uint32 val) { m_goValue.Transport.PathProgress = val; } @@ -57,7 +59,7 @@ public: void BuildUpdate(UpdateDataMapType& data_map) override; void Update(uint32 diff) override; - void DelayedUpdate(uint32 diff); + void DelayedUpdate(uint32 diff) override; void UpdatePosition(float x, float y, float z, float o); void AddPassenger(WorldObject* passenger, bool withAll = false) override; @@ -115,6 +117,8 @@ public: StaticTransport(); ~StaticTransport() override; + bool LoadFromDB(ObjectGuid::LowType guid, Map* map) override { return LoadGameObjectFromDB(guid, map, false); } + bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true) override; bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0) override; void CleanupsBeforeDelete(bool finalCleanup = true) override; void BuildUpdate(UpdateDataMapType& data_map) override; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 131663680..62d148312 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -53,6 +53,7 @@ #include "Player.h" #include "ReputationMgr.h" #include "ScriptMgr.h" +#include "SmartAI.h" #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" @@ -4109,8 +4110,8 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi //LOG_DEBUG("entities.unit", "Interrupt spell for unit {}.", GetEntry()); Spell* spell = m_currentSpells[spellType]; if (spell - && (withDelayed || spell->getState() != SPELL_STATE_DELAYED) - && (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING)) // xinef: or spell is in casting state (channeled spells only) + && (withDelayed || spell->getState() != SPELL_STATE_DELAYED) + && (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING)) // xinef: or spell is in casting state (channeled spells only) { // for example, do not let self-stun aura interrupt itself if (!spell->IsInterruptable()) @@ -4128,6 +4129,15 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi m_currentSpells[spellType] = nullptr; spell->SetReferencedFromCurrent(false); } + + // SAI creatures only + // Start chasing victim if they are spell casters (at least one SMC spell) if interrupted/silenced. + if (IsCreature()) + { + if (SmartAI* ai = dynamic_cast(ToCreature()->AI())) + if (ai->CanChaseOnInterrupt()) + ai->SetCombatMove(true); + } } } @@ -20305,12 +20315,10 @@ void Unit::ExecuteDelayedUnitRelocationEvent() //active->m_last_notify_position.Relocate(active->GetPositionX(), active->GetPositionY(), active->GetPositionZ()); } - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); + Acore::PlayerRelocationNotifier notifier(*player); + Cell::VisitObjects(viewPoint, notifier, player->GetSightRange()); + Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); } if (Player* player = this->ToPlayer()) @@ -20344,16 +20352,10 @@ void Unit::ExecuteDelayedUnitRelocationEvent() GetMap()->LoadGridsInRange(*player, MAX_VISIBILITY_DISTANCE); - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - - if (!player->GetFarSightDistance()) - { - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); - } + Acore::PlayerRelocationNotifier notifier(*player); + Cell::VisitObjects(viewPoint, notifier, player->GetSightRange()); + Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); this->AddToNotify(NOTIFY_AI_RELOCATION); } @@ -20373,7 +20375,7 @@ void Unit::ExecuteDelayedUnitRelocationEvent() unit->m_last_notify_position.Relocate(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); Acore::CreatureRelocationNotifier relocate(*unit); - Cell::VisitObjects(unit, relocate, unit->GetVisibilityRange() + VISIBILITY_COMPENSATION); + Cell::VisitObjects(unit, relocate, unit->GetVisibilityRange()); this->AddToNotify(NOTIFY_AI_RELOCATION); } diff --git a/src/server/game/Grids/Cells/Cell.h b/src/server/game/Grids/Cells/Cell.h index 89676468d..a54f7f4cd 100644 --- a/src/server/game/Grids/Cells/Cell.h +++ b/src/server/game/Grids/Cells/Cell.h @@ -106,6 +106,8 @@ struct Cell template static void VisitObjects(WorldObject const* obj, T& visitor, float radius); template static void VisitObjects(float x, float y, Map* map, T& visitor, float radius); + template static void VisitFarVisibleObjects(WorldObject const* obj, T& visitor, float radius); + private: template void VisitCircle(TypeContainerVisitor&, Map&, CellCoord const&, CellCoord const&) const; }; diff --git a/src/server/game/Grids/Cells/CellImpl.h b/src/server/game/Grids/Cells/CellImpl.h index d3f97a09d..997840367 100644 --- a/src/server/game/Grids/Cells/CellImpl.h +++ b/src/server/game/Grids/Cells/CellImpl.h @@ -181,4 +181,14 @@ inline void Cell::VisitObjects(float x, float y, Map* map, T& visitor, float rad cell.Visit(p, gnotifier, *map, x, y, radius); } +template +inline void Cell::VisitFarVisibleObjects(WorldObject const* center_obj, T& visitor, float radius) +{ + CellCoord p(Acore::ComputeCellCoord(center_obj->GetPositionX(), center_obj->GetPositionY())); + Cell cell(p); + + TypeContainerVisitor gnotifier(visitor); + cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius); +} + #endif diff --git a/src/server/game/Grids/GridCell.h b/src/server/game/Grids/GridCell.h index 62ff76545..65e3a0315 100644 --- a/src/server/game/Grids/GridCell.h +++ b/src/server/game/Grids/GridCell.h @@ -33,16 +33,20 @@ #include "TypeContainer.h" #include "TypeContainerVisitor.h" +class WorldObject; + template < - class GRID_OBJECT_TYPES + class GRID_OBJECT_TYPES, + class FAR_VISIBLE_OBJECT_TYPES > class GridCell { public: ~GridCell() = default; - template void AddGridObject(SPECIFIC_OBJECT* obj) + template + void AddGridObject(SPECIFIC_OBJECT* obj) { _gridObjects.template insert(obj); ASSERT(obj->IsInGrid()); @@ -50,12 +54,32 @@ public: // Visit grid objects template - void Visit(TypeContainerVisitor >& visitor) + void Visit(TypeContainerVisitor>& visitor) { visitor.Visit(_gridObjects); } + template + void AddFarVisibleObject(SPECIFIC_OBJECT* obj) + { + _farVisibleObjects.template Insert(obj); + } + + template + void RemoveFarVisibleObject(SPECIFIC_OBJECT* obj) + { + _farVisibleObjects.template Remove(obj); + } + + // Visit far objects + template + void Visit(TypeContainerVisitor>& visitor) + { + visitor.Visit(_farVisibleObjects); + } + private: TypeMapContainer _gridObjects; + TypeVectorContainer _farVisibleObjects; }; #endif diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index 6918d2b1a..5c1d09c63 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -57,6 +57,9 @@ typedef TYPELIST_5(GameObject, Player, Creature, Corpse, DynamicObject) AllMapGr // List of object types stored on map level typedef TYPELIST_4(Creature, GameObject, DynamicObject, Corpse) AllMapStoredObjectTypes; +// List of object types that can have far visible range +typedef TYPELIST_2(Creature, GameObject) AllFarVisibleObjectTypes; + typedef GridRefMgr CorpseMapType; typedef GridRefMgr CreatureMapType; typedef GridRefMgr DynamicObjectMapType; @@ -73,10 +76,11 @@ enum GridMapTypeMask GRID_MAP_TYPE_MASK_ALL = 0x1F }; -typedef GridCell GridCellType; -typedef MapGrid MapGridType; +typedef GridCell GridCellType; +typedef MapGrid MapGridType; typedef TypeMapContainer GridTypeMapContainer; +typedef TypeVectorContainer FarVisibleGridContainer; typedef TypeUnorderedMapContainer MapStoredObjectTypesContainer; template diff --git a/src/server/game/Grids/GridObjectLoader.cpp b/src/server/game/Grids/GridObjectLoader.cpp index 6a5b4b736..d8b39ebd6 100644 --- a/src/server/game/Grids/GridObjectLoader.cpp +++ b/src/server/game/Grids/GridObjectLoader.cpp @@ -64,15 +64,28 @@ void GridObjectLoader::LoadGameObjects(CellGuidSet const& guid_set, Map* map) for (ObjectGuid::LowType const& guid : guid_set) { GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); - GameObject* obj = data && sObjectMgr->IsGameObjectStaticTransport(data->id) ? new StaticTransport() : new GameObject(); - if (!obj->LoadFromDB(guid, map)) + if (data && sObjectMgr->IsGameObjectStaticTransport(data->id)) { - delete obj; - continue; - } + StaticTransport* transport = new StaticTransport(); - AddObjectHelper(map, obj); + // Special case for static transports - we are loaded via grids + // but we do not want to actually be stored in the grid + if (!transport->LoadGameObjectFromDB(guid, map, true)) + delete transport; + } + else + { + GameObject* obj = new GameObject(); + + if (!obj->LoadFromDB(guid, map)) + { + delete obj; + continue; + } + + AddObjectHelper(map, obj); + } } } diff --git a/src/server/game/Grids/MapGrid.h b/src/server/game/Grids/MapGrid.h index b6dda486c..c80c3b4d3 100644 --- a/src/server/game/Grids/MapGrid.h +++ b/src/server/game/Grids/MapGrid.h @@ -25,12 +25,13 @@ class GridTerrainData; template < - class GRID_OBJECT_TYPES + class GRID_OBJECT_TYPES, + class FAR_VISIBLE_OBJECT_TYPES > class MapGrid { public: - typedef GridCell GridCellType; + typedef GridCell GridCellType; MapGrid(uint16 const x, uint16 const y) : _x(x), _y(y), _objectDataLoaded(false), _terrainData(nullptr) { } @@ -54,9 +55,19 @@ public: GetOrCreateCell(x, y).RemoveGridObject(obj); } + template void AddFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj) + { + GetOrCreateCell(x, y).AddFarVisibleObject(obj); + } + + template void RemoveFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj) + { + GetOrCreateCell(x, y).RemoveFarVisibleObject(obj); + } + // Visit all cells template - void VisitAllCells(TypeContainerVisitor >& visitor) + void VisitAllCells(TypeContainerVisitor& visitor) { for (auto& cellX : _cells) { @@ -72,7 +83,7 @@ public: // Visit single cell template - void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor >& visitor) + void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor& visitor) { GridCellType* gridCell = GetCell(x, y); if (!gridCell) @@ -81,7 +92,7 @@ public: gridCell->Visit(visitor); } - void link(GridRefMgr>* pTo) + void link(GridRefMgr>* pTo) { _gridReference.link(pTo, this); } @@ -134,7 +145,7 @@ private: bool _objectDataLoaded; std::array, MAX_NUMBER_OF_CELLS>, MAX_NUMBER_OF_CELLS> _cells; // N * N array - GridReference> _gridReference; + GridReference> _gridReference; // Instances will share a copy of the parent maps terrainData std::shared_ptr _terrainData; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 563634929..5dd717f32 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -29,38 +29,31 @@ void VisibleNotifier::Visit(GameObjectMapType& m) for (GameObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { GameObject* go = iter->GetSource(); - if (i_largeOnly != go->IsVisibilityOverridden()) - continue; - i_player.UpdateVisibilityOf(go, i_data, i_visibleNow); } } void VisibleNotifier::SendToSelf() { - // at this moment i_clientGUIDs have guids that not iterate at grid level checks - // but exist one case when this possible and object not out of range: transports - if (Transport* transport = i_player.GetTransport()) + // Update far visible objects + ZoneWideVisibleWorldObjectsSet const* zoneWideVisibleObjects = i_player.GetMap()->GetZoneWideVisibleWorldObjectsForZone(i_player.GetZoneId()); + if (zoneWideVisibleObjects) { - for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) + for (WorldObject* obj : *zoneWideVisibleObjects) { - if (i_largeOnly != (*itr)->IsVisibilityOverridden()) - continue; - - switch ((*itr)->GetTypeId()) + switch (obj->GetTypeId()) { - case TYPEID_GAMEOBJECT: - i_player.UpdateVisibilityOf((*itr)->ToGameObject(), i_data, i_visibleNow); - break; - case TYPEID_PLAYER: - i_player.UpdateVisibilityOf((*itr)->ToPlayer(), i_data, i_visibleNow); - (*itr)->ToPlayer()->UpdateVisibilityOf(&i_player); - break; - case TYPEID_UNIT: - i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow); - break; - default: - break; + case TYPEID_GAMEOBJECT: + i_player.UpdateVisibilityOf(obj->ToGameObject(), i_data, i_visibleNow); + break; + case TYPEID_UNIT: + i_player.UpdateVisibilityOf(obj->ToCreature(), i_data, i_visibleNow); + break; + case TYPEID_DYNAMICOBJECT: + i_player.UpdateVisibilityOf(obj->ToDynObject(), i_data, i_visibleNow); + break; + default: + break; } } } @@ -69,26 +62,8 @@ void VisibleNotifier::SendToSelf() for (VisibleWorldObjectsMap::iterator itr = visibleWorldObjects->begin(); itr != visibleWorldObjects->end();) { WorldObject* obj = itr->second; - if (i_largeOnly != obj->IsVisibilityOverridden()) - { - ++itr; - continue; - } - - // pussywizard: static transports are removed only in RemovePlayerFromMap and here if can no longer detect (eg. phase changed) - if (itr->first.IsTransport()) - { - if (GameObject* staticTrans = obj->ToGameObject()) - { - if (i_player.CanSeeOrDetect(staticTrans, false, true)) - { - ++itr; - continue; - } - } - } - - if (i_player.m_seer->IsWithinDist(obj, i_player.GetSightRange(obj), true)) + if (!i_player.IsWorldObjectOutOfSightRange(obj) + || i_player.CanSeeOrDetect(obj, false, true)) { ++itr; continue; @@ -111,12 +86,7 @@ void VisibleNotifier::SendToSelf() i_player.GetSession()->SendPacket(&packet); for (std::vector::const_iterator it = i_visibleNow.begin(); it != i_visibleNow.end(); ++it) - { - if (i_largeOnly != (*it)->IsVisibilityOverridden()) - continue; - i_player.GetInitialVisiblePackets(*it); - } } void VisibleChangesNotifier::Visit(PlayerMapType& m) diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 040c54244..d9556c77f 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -45,16 +45,16 @@ namespace Acore Player& i_player; std::vector& i_visibleNow; bool i_gobjOnly; - bool i_largeOnly; UpdateData i_data; - VisibleNotifier(Player& player, bool gobjOnly, bool largeOnly) : - i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly), i_largeOnly(largeOnly) + VisibleNotifier(Player& player, bool gobjOnly) : + i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly) { i_visibleNow.clear(); } void Visit(GameObjectMapType&); + template void Visit(std::vector& m); template void Visit(GridRefMgr& m); void SendToSelf(void); }; @@ -72,8 +72,9 @@ namespace Acore struct PlayerRelocationNotifier : public VisibleNotifier { - PlayerRelocationNotifier(Player& player, bool largeOnly): VisibleNotifier(player, false, largeOnly) { } + PlayerRelocationNotifier(Player& player): VisibleNotifier(player, false) { } + template void Visit(std::vector& m) { VisibleNotifier::Visit(m); } template void Visit(GridRefMgr& m) { VisibleNotifier::Visit(m); } void Visit(PlayerMapType&); }; diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index 592e584ea..a24e87374 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -25,6 +25,13 @@ #include "WorldPacket.h" #include "WorldSession.h" +template +inline void Acore::VisibleNotifier::Visit(std::vector& m) +{ + for (typename std::vector::iterator iter = m.begin(); iter != m.end(); ++iter) + i_player.UpdateVisibilityOf((*iter), i_data, i_visibleNow); +} + template inline void Acore::VisibleNotifier::Visit(GridRefMgr& m) { @@ -33,12 +40,7 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr& m) return; for (typename GridRefMgr::iterator iter = m.begin(); iter != m.end(); ++iter) - { - if (i_largeOnly != iter->GetSource()->IsVisibilityOverridden()) - continue; - i_player.UpdateVisibilityOf(iter->GetSource(), i_data, i_visibleNow); - } } // SEARCHERS & LIST SEARCHERS & WORKERS diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 3fede6ffc..b4b2331e1 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -28,6 +28,7 @@ #include "LFGMgr.h" #include "Log.h" #include "MapMgr.h" +#include "MiscPackets.h" #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" @@ -2059,6 +2060,16 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* return GroupJoinBattlegroundResult(bgTemplate->GetBgTypeID()); } +void Group::DoMinimapPing(ObjectGuid sourceGuid, float mapX, float mapY) +{ + WorldPackets::Misc::MinimapPing minimapPing; + minimapPing.SourceGuid = sourceGuid; + minimapPing.MapX = mapX; + minimapPing.MapY = mapY; + + BroadcastPacket(minimapPing.Write(), true, -1, sourceGuid); +} + //=================================================== //============== Roll =============================== //=================================================== diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index e07360342..ff51131bf 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -255,6 +255,8 @@ public: void SetBattlefieldGroup(Battlefield* bf); GroupJoinBattlegroundResult CanJoinBattlegroundQueue(Battleground const* bgTemplate, BattlegroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot); + void DoMinimapPing(ObjectGuid sourceGuid, float mapX, float mapY); + void ChangeMembersGroup(ObjectGuid guid, uint8 group); void SetTargetIcon(uint8 id, ObjectGuid whoGuid, ObjectGuid targetGuid); void SetGroupMemberFlag(ObjectGuid guid, bool apply, GroupMemberFlags flag); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 0ac318319..ec5ca110f 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1175,6 +1175,10 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar) pCurrChar->GetMap()->SendInitTransports(pCurrChar); pCurrChar->GetMap()->SendInitSelf(pCurrChar); pCurrChar->GetMap()->SendZoneDynamicInfo(pCurrChar); + + // If we are logging into an existing player, simply clear visibility references + // so player will receive a fresh list of new objects on the next vis update. + pCurrChar->GetObjectVisibilityContainer().CleanVisibilityReferences(); pCurrChar->UpdateObjectVisibility(false); pCurrChar->CleanupChannels(); diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index ee4e0676a..c8f14779b 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -22,6 +22,7 @@ #include "LFGMgr.h" #include "Language.h" #include "Log.h" +#include "MapMgr.h" #include "MiscPackets.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -522,24 +523,17 @@ void WorldSession::HandleLootRoll(WorldPacket& recvData) } } -void WorldSession::HandleMinimapPingOpcode(WorldPacket& recvData) +void WorldSession::HandleMinimapPingOpcode(WorldPackets::Misc::MinimapPingClient& packet) { - if (!GetPlayer()->GetGroup()) + if (!sMapMgr->IsValidMapCoord(GetPlayer()->GetMap()->GetId(), packet.MapX, packet.MapY)) return; - float x, y; - recvData >> x; - recvData >> y; + Group* group = GetPlayer()->GetGroup(); - /** error handling **/ - /********************/ + if (!group) + return; - // everything's fine, do it - WorldPacket data(MSG_MINIMAP_PING, (8 + 4 + 4)); - data << GetPlayer()->GetGUID(); - data << float(x); - data << float(y); - GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID()); + group->DoMinimapPing(GetPlayer()->GetGUID(), packet.MapX, packet.MapY); } void WorldSession::HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& packet) @@ -549,10 +543,8 @@ void WorldSession::HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& maximum = packet.Max; /** error handling **/ - if (minimum > maximum || maximum > 10000) // < 32768 for urand call - { + if (minimum > maximum || maximum > sWorld->getIntConfig(CONFIG_RANDOM_ROLL_MAXIMUM)) return; - } GetPlayer()->DoRandomRoll(minimum, maximum); } diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 4ce4ceb95..ce5ca1081 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -29,91 +29,82 @@ #include "WorldSession.h" #include -void WorldSession::HandleSplitItemOpcode(WorldPacket& recvData) +#include "ItemPackets.h" + +void WorldSession::HandleSplitItemOpcode(WorldPackets::Item::SplitItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_SPLIT_ITEM"); - uint8 srcbag, srcslot, dstbag, dstslot; - uint32 count; - recvData >> srcbag >> srcslot >> dstbag >> dstslot >> count; - - uint16 src = ((srcbag << 8) | srcslot); - uint16 dst = ((dstbag << 8) | dstslot); + uint16 src = ((packet.SourceBag << 8) | packet.SourceSlot); + uint16 dst = ((packet.DestinationBag << 8) | packet.DestinationSlot); if (src == dst) return; - if (count == 0) - return; //check count - if zero it's fake packet + if (packet.Count == 0) + return; //check count - if zero it's fake packet - if (!_player->IsValidPos(srcbag, srcslot, true)) + if (!_player->IsValidPos(packet.SourceBag, packet.SourceSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } - if (!_player->IsValidPos(dstbag, dstslot, false)) // can be autostore pos + if (!_player->IsValidPos(packet.DestinationBag, packet.DestinationSlot, false)) // can be autostore pos { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; } - _player->SplitItem(src, dst, count); + _player->SplitItem(src, dst, packet.Count); } -void WorldSession::HandleSwapInvItemOpcode(WorldPacket& recvData) +void WorldSession::HandleSwapInvItemOpcode(WorldPackets::Item::SwapInventoryItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_SWAP_INV_ITEM"); - uint8 srcslot, dstslot; - recvData >> dstslot >> srcslot; - - // prevent attempt swap same item to current position generated by client at special checting sequence - if (srcslot == dstslot) + // prevent attempt swap same item to current position generated by client at special cheating sequence + if (packet.SourceSlot == packet.DestinationSlot) return; - if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, srcslot, true)) + if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, packet.SourceSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } - if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, true)) + if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, packet.DestinationSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; } - if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, srcslot) && !CanUseBank()) + if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, packet.SourceSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapInvItemOpcode - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; } - if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, dstslot) && !CanUseBank()) + if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, packet.DestinationSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapInvItemOpcode - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; } - uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot); - uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot); + uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | packet.SourceSlot); + uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | packet.DestinationSlot); _player->SwapItem(src, dst); } -void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPacket& recvData) +void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPackets::Item::AutoEquipItemSlot& packet) { - ObjectGuid itemguid; - uint8 dstslot; - recvData >> itemguid >> dstslot; - // cheating attempt, client should never send opcode in that case - if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot)) + if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, packet.DestinationSlot)) return; - Item* item = _player->GetItemByGuid(itemguid); - uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8); + Item* item = _player->GetItemByGuid(packet.ItemGuid); + uint16 dstpos = packet.DestinationSlot | (INVENTORY_SLOT_BAG_0 << 8); if (!item || item->GetPos() == dstpos) return; @@ -121,39 +112,36 @@ void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPacket& recvData) _player->SwapItem(item->GetPos(), dstpos); } -void WorldSession::HandleSwapItem(WorldPacket& recvData) +void WorldSession::HandleSwapItem(WorldPackets::Item::SwapItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_SWAP_ITEM"); - uint8 dstbag, dstslot, srcbag, srcslot; - recvData >> dstbag >> dstslot >> srcbag >> srcslot; + uint16 src = ((packet.SourceBag << 8) | packet.SourceSlot); + uint16 dst = ((packet.DestinationBag << 8) | packet.DestinationSlot); - uint16 src = ((srcbag << 8) | srcslot); - uint16 dst = ((dstbag << 8) | dstslot); - - // prevent attempt swap same item to current position generated by client at special checting sequence + // prevent attempt swap same item to current position generated by client at special cheating sequence if (src == dst) return; - if (!_player->IsValidPos(srcbag, srcslot, true)) + if (!_player->IsValidPos(packet.SourceBag, packet.SourceSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } - if (!_player->IsValidPos(dstbag, dstslot, true)) + if (!_player->IsValidPos(packet.DestinationBag, packet.DestinationSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; } - if (_player->IsBankPos(srcbag, srcslot) && !CanUseBank()) + if (_player->IsBankPos(packet.SourceBag, packet.SourceSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapItem - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; } - if (_player->IsBankPos(dstbag, dstslot) && !CanUseBank()) + if (_player->IsBankPos(packet.DestinationBag, packet.DestinationSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapItem - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; @@ -162,14 +150,11 @@ void WorldSession::HandleSwapItem(WorldPacket& recvData) _player->SwapItem(src, dst); } -void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) +void WorldSession::HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_AUTOEQUIP_ITEM"); - uint8 srcbag, srcslot; - recvData >> srcbag >> srcslot; - - Item* pSrcItem = _player->GetItemByPos(srcbag, srcslot); + Item* pSrcItem = _player->GetItemByPos(packet.SourceBag, packet.SourceSlot); if (!pSrcItem) return; // only at cheat @@ -219,7 +204,7 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) if (!pDstItem) // empty slot, simple case { - _player->RemoveItem(srcbag, srcslot, true); + _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true); _player->EquipItem(dest, pSrcItem, true); _player->AutoUnequipOffhandIfNeed(); } @@ -243,23 +228,23 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) uint16 eSrc = 0; if (_player->IsInventoryPos(src)) { - msg = _player->CanStoreItem(srcbag, srcslot, sSrc, pDstItem, true); + msg = _player->CanStoreItem(packet.SourceBag, packet.SourceSlot, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) - msg = _player->CanStoreItem(srcbag, NULL_SLOT, sSrc, pDstItem, true); + msg = _player->CanStoreItem(packet.SourceBag, NULL_SLOT, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true); } else if (_player->IsBankPos(src)) { - msg = _player->CanBankItem(srcbag, srcslot, sSrc, pDstItem, true); + msg = _player->CanBankItem(packet.SourceBag, packet.SourceSlot, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) - msg = _player->CanBankItem(srcbag, NULL_SLOT, sSrc, pDstItem, true); + msg = _player->CanBankItem(packet.SourceBag, NULL_SLOT, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true); } else if (_player->IsEquipmentPos(src)) { - msg = _player->CanEquipItem(srcslot, eSrc, pDstItem, true); + msg = _player->CanEquipItem(packet.SourceSlot, eSrc, pDstItem, true); if (msg == EQUIP_ERR_OK) msg = _player->CanUnequipItem(eSrc, true); } @@ -272,7 +257,7 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) // now do moves, remove... _player->RemoveItem(dstbag, dstslot, true, true); - _player->RemoveItem(srcbag, srcslot, true, true); + _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true, true); // add to dest _player->EquipItem(dest, pSrcItem, true); @@ -292,14 +277,11 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) } } -void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) +void WorldSession::HandleDestroyItemOpcode(WorldPackets::Item::DestroyItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_DESTROYITEM"); - uint8 bag, slot, count, data1, data2, data3; - recvData >> bag >> slot >> count >> data1 >> data2 >> data3; - - uint16 pos = (bag << 8) | slot; + uint16 pos = (packet.Bag << 8) | packet.Slot; // prevent drop unequipable items (in combat, for example) and non-empty bags if (_player->IsEquipmentPos(pos) || _player->IsBagPos(pos)) @@ -312,7 +294,7 @@ void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) } } - Item* pItem = _player->GetItemByPos(bag, slot); + Item* pItem = _player->GetItemByPos(packet.Bag, packet.Slot); if (!pItem) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); @@ -327,14 +309,14 @@ void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) recoveryItem(pItem); - if (count) + if (packet.Count) { - uint32 i_count = count; + uint32 i_count = packet.Count; _player->DestroyItemCount(pItem, i_count, true); } else { - _player->DestroyItem(bag, slot, true); + _player->DestroyItem(packet.Bag, packet.Slot, true); } _player->SendQuestGiverStatusMultiple(); } @@ -692,15 +674,12 @@ void WorldSession::HandleItemQuerySingleOpcode(WorldPacket& recvData) } } -void WorldSession::HandleReadItem(WorldPacket& recvData) +void WorldSession::HandleReadItem(WorldPackets::Item::ReadItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_READ_ITEM"); - uint8 bag, slot; - recvData >> bag >> slot; - //LOG_DEBUG("network.opcode", "STORAGE: Read bag = {}, slot = {}", bag, slot); - Item* pItem = _player->GetItemByPos(bag, slot); + Item* pItem = _player->GetItemByPos(packet.Bag, packet.Slot); if (pItem && pItem->GetTemplate()->PageText) { @@ -725,27 +704,22 @@ void WorldSession::HandleReadItem(WorldPacket& recvData) _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); } -void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) +void WorldSession::HandleSellItemOpcode(WorldPackets::Item::SellItem& packet) { - ObjectGuid vendorguid, itemguid; - uint32 count; - - recvData >> vendorguid >> itemguid >> count; - - if (!itemguid) + if (!packet.ItemGuid) return; - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.VendorGuid, UNIT_NPC_FLAG_VENDOR); if (!creature) { - LOG_DEBUG("network", "WORLD: HandleSellItemOpcode - Unit ({}) not found or you can not interact with him.", vendorguid.ToString()); - _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, itemguid, 0); + LOG_DEBUG("network", "WORLD: HandleSellItemOpcode - Unit ({}) not found or you can not interact with him.", packet.VendorGuid.ToString()); + _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, packet.ItemGuid, 0); return; } if (creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_NO_SELL_VENDOR)) { - _player->SendSellError(SELL_ERR_CANT_SELL_TO_THIS_MERCHANT, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_TO_THIS_MERCHANT, creature, packet.ItemGuid, 0); return; } @@ -753,7 +727,7 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - Item* pItem = _player->GetItemByGuid(itemguid); + Item* pItem = _player->GetItemByGuid(packet.ItemGuid); if (pItem) { if (!sScriptMgr->OnPlayerCanSellItem(_player, pItem, creature)) @@ -762,21 +736,21 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) // prevent sell not owner item if (_player->GetGUID() != pItem->GetOwnerGUID()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } // prevent sell non empty bag by drag-and-drop at vendor's item list if (pItem->IsNotEmptyBag()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } // prevent sell currently looted item if (_player->GetLootGUID() == pItem->GetGUID()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } @@ -787,16 +761,14 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) return; // Therefore, no feedback to client // special case at auto sell (sell all) - if (count == 0) - { - count = pItem->GetCount(); - } + if (packet.Count == 0) + packet.Count = pItem->GetCount(); else { // prevent sell more items that exist in stack (possible only not from client) - if (count > pItem->GetCount()) + if (packet.Count > pItem->GetCount()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } } @@ -806,11 +778,11 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) { if (pProto->SellPrice > 0) { - uint32 money = pProto->SellPrice * count; + uint32 money = pProto->SellPrice * packet.Count; if (_player->GetMoney() >= MAX_MONEY_AMOUNT - money) // prevent exceeding gold limit { _player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, nullptr, nullptr); - _player->SendSellError(SELL_ERR_UNK, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_UNK, creature, packet.ItemGuid, 0); return; } @@ -828,8 +800,8 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(pProto->ItemLevel); if (!dcost) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); - LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong item lvl {} for item {} count = {}", pProto->ItemLevel, pItem->GetEntry(), count); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); + LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong item lvl {} for item {} count = {}", pProto->ItemLevel, pItem->GetEntry(), packet.Count); return; } @@ -837,8 +809,8 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) DurabilityQualityEntry const* dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId); if (!dQualitymodEntry) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); - LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong dQualityModEntry {} for item {} count = {}", dQualitymodEntryId, pItem->GetEntry(), count); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); + LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong dQualityModEntry {} for item {} count = {}", dQualitymodEntryId, pItem->GetEntry(), packet.Count); return; } @@ -846,36 +818,30 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) uint32 refund = uint32(std::ceil(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod))); if (!refund) - { refund = 1; - } //starter items can cost more to refund than vendorprice if (refund > money) - { money = 1; - } else - { money -= refund; - } } } - if (count < pItem->GetCount()) // need split items + if (packet.Count < pItem->GetCount()) // need split items { - Item* pNewItem = pItem->CloneItem(count, _player); + Item* pNewItem = pItem->CloneItem(packet.Count, _player); if (!pNewItem) { - LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - could not create clone of item {}; count = {}", pItem->GetEntry(), count); - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - could not create clone of item {}; count = {}", pItem->GetEntry(), packet.Count); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } pNewItem->SetUInt32Value(ITEM_FIELD_DURABILITY, pItem->GetUInt32Value(ITEM_FIELD_DURABILITY)); - pItem->SetCount(pItem->GetCount() - count); - _player->ItemRemovedQuestCheck(pItem->GetEntry(), count); + pItem->SetCount(pItem->GetCount() - packet.Count); + _player->ItemRemovedQuestCheck(pItem->GetEntry(), packet.Count); if (_player->IsInWorld()) pItem->SendUpdateToPlayer(_player); pItem->SetState(ITEM_CHANGED, _player); @@ -897,25 +863,20 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money); } else - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } } - _player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, packet.ItemGuid, 0); return; } -void WorldSession::HandleBuybackItem(WorldPacket& recvData) +void WorldSession::HandleBuybackItem(WorldPackets::Item::BuybackItem& packet) { - ObjectGuid vendorguid; - uint32 slot; - - recvData >> vendorguid >> slot; - - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.VendorGuid, UNIT_NPC_FLAG_VENDOR); if (!creature) { - LOG_DEBUG("network", "WORLD: HandleBuybackItem - Unit ({}) not found or you can not interact with him.", vendorguid.ToString()); + LOG_DEBUG("network", "WORLD: HandleBuybackItem - Unit ({}) not found or you can not interact with him.", packet.VendorGuid.ToString()); _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, ObjectGuid::Empty, 0); return; } @@ -924,10 +885,10 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - Item* pItem = _player->GetItemFromBuyBackSlot(slot); + Item* pItem = _player->GetItemFromBuyBackSlot(packet.Slot); if (pItem) { - uint32 price = _player->GetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START); + uint32 price = _player->GetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + packet.Slot - BUYBACK_SLOT_START); if (!_player->HasEnoughMoney(price)) { _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, pItem->GetEntry(), 0); @@ -948,7 +909,7 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) } _player->ModifyMoney(-(int32)price); - _player->RemoveItemFromBuyBackSlot(slot, false); + _player->RemoveItemFromBuyBackSlot(packet.Slot, false); _player->ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount()); _player->StoreItem(dest, pItem, true); } @@ -960,24 +921,18 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, 0, 0); } -void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) +void WorldSession::HandleBuyItemInSlotOpcode(WorldPackets::Item::BuyItemInSlot& packet) { - ObjectGuid vendorguid, bagguid; - uint32 item, slot, count; - uint8 bagslot; - - recvData >> vendorguid >> item >> slot >> bagguid >> bagslot >> count; - // client expects count starting at 1, and we send vendorslot+1 to client already - if (slot > 0) - --slot; + if (packet.Slot > 0) + --packet.Slot; else - return; // cheating + return; // cheating - uint8 bag = NULL_BAG; // init for case invalid bagGUID + uint8 bag = NULL_BAG; // init for case invalid bagGUID // find bag slot by bag guid - if (bagguid == _player->GetGUID()) + if (packet.BagGuid == _player->GetGUID()) bag = INVENTORY_SLOT_BAG_0; else { @@ -985,7 +940,7 @@ void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) { if (Bag* pBag = _player->GetBagByPos(i)) { - if (bagguid == pBag->GetGUID()) + if (packet.BagGuid == pBag->GetGUID()) { bag = i; break; @@ -998,38 +953,28 @@ void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) if (bag == NULL_BAG) return; - GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, bag, bagslot); + GetPlayer()->BuyItemFromVendorSlot(packet.VendorGuid, packet.Slot, packet.Item, packet.Count, bag, packet.BagSlot); } -void WorldSession::HandleBuyItemOpcode(WorldPacket& recvData) +void WorldSession::HandleBuyItemOpcode(WorldPackets::Item::BuyItem& packet) { - ObjectGuid vendorguid; - uint32 item, slot, count; - uint8 unk1; - - recvData >> vendorguid >> item >> slot >> count >> unk1; - // client expects count starting at 1, and we send vendorslot+1 to client already - if (slot > 0) - --slot; + if (packet.Slot > 0) + --packet.Slot; else return; // cheating - GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, NULL_BAG, NULL_SLOT); + GetPlayer()->BuyItemFromVendorSlot(packet.VendorGuid, packet.Slot, packet.Item, packet.Count, NULL_BAG, NULL_SLOT); } -void WorldSession::HandleListInventoryOpcode(WorldPacket& recvData) +void WorldSession::HandleListInventoryOpcode(WorldPackets::Item::ListInventory& packet) { - ObjectGuid guid; - - recvData >> guid; - if (!GetPlayer()->IsAlive()) return; LOG_DEBUG("network", "WORLD: Recvd CMSG_LIST_INVENTORY"); - SendListInventory(guid); + SendListInventory(packet.VendorGuid); } void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry) @@ -1143,18 +1088,14 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry) SendPacket(&data); } -void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) +void WorldSession::HandleAutoStoreBagItemOpcode(WorldPackets::Item::AutoStoreBagItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_AUTOSTORE_BAG_ITEM"); - uint8 srcbag, srcslot, dstbag; - - recvData >> srcbag >> srcslot >> dstbag; - - Item* pItem = _player->GetItemByPos(srcbag, srcslot); + Item* pItem = _player->GetItemByPos(packet.SourceBag, packet.SourceSlot); if (!pItem) return; - if (!_player->IsValidPos(dstbag, NULL_SLOT, false)) // can be autostore pos + if (!_player->IsValidPos(packet.DestinationBag, NULL_SLOT, false)) // can be autostore pos { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; @@ -1174,7 +1115,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) } ItemPosCountVec dest; - InventoryResult msg = _player->CanStoreItem(dstbag, NULL_SLOT, dest, pItem, false); + InventoryResult msg = _player->CanStoreItem(packet.DestinationBag, NULL_SLOT, dest, pItem, false); if (msg != EQUIP_ERR_OK) { _player->SendEquipError(msg, pItem, nullptr); @@ -1189,7 +1130,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) return; } - _player->RemoveItem(srcbag, srcslot, true); + _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true); _player->StoreItem(dest, pItem, true); _player->UpdateTitansGrip(); } @@ -1223,23 +1164,22 @@ void WorldSession::HandleSetAmmoOpcode(WorldPacket& recvData) void WorldSession::SendEnchantmentLog(ObjectGuid target, ObjectGuid caster, uint32 itemId, uint32 enchantId) { - WorldPacket data(SMSG_ENCHANTMENTLOG, (8 + 8 + 4 + 4)); // last check 2.0.10 - data << target.WriteAsPacked(); - data << caster.WriteAsPacked(); - data << uint32(itemId); - data << uint32(enchantId); - GetPlayer()->SendMessageToSet(&data, true); + WorldPackets::Item::EnchantmentLog enchantmentLog; + enchantmentLog.Target = target.WriteAsPacked(); + enchantmentLog.Caster = caster.WriteAsPacked(); + enchantmentLog.ItemId = itemId; + enchantmentLog.EnchantId = enchantId; + GetPlayer()->SendMessageToSet(enchantmentLog.Write(), true); } void WorldSession::SendItemEnchantTimeUpdate(ObjectGuid Playerguid, ObjectGuid Itemguid, uint32 slot, uint32 Duration) { - // last check 2.0.10 - WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8 + 4 + 4 + 8)); - data << Itemguid; - data << uint32(slot); - data << uint32(Duration); - data << Playerguid; - SendPacket(&data); + WorldPackets::Item::ItemEnchantTimeUpdate itemEnchantTimeUpdate; + itemEnchantTimeUpdate.ItemGuid = Itemguid; + itemEnchantTimeUpdate.Slot = slot; + itemEnchantTimeUpdate.Duration = Duration; + itemEnchantTimeUpdate.PlayerGuid = Playerguid; + SendPacket(itemEnchantTimeUpdate.Write()); } void WorldSession::HandleItemNameQueryOpcode(WorldPacket& recvData) @@ -1266,18 +1206,13 @@ void WorldSession::HandleItemNameQueryOpcode(WorldPacket& recvData) } } -void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) +void WorldSession::HandleWrapItemOpcode(WorldPackets::Item::WrapItem& packet) { LOG_DEBUG("network", "Received opcode CMSG_WRAP_ITEM"); - uint8 gift_bag, gift_slot, item_bag, item_slot; + LOG_DEBUG("network", "WRAP: receive GiftBag = {}, GiftSlot = {}, ItemBag = {}, ItemSlot = {}", packet.GiftBag, packet.GiftSlot, packet.ItemBag, packet.ItemSlot); - recvData >> gift_bag >> gift_slot; // paper - recvData >> item_bag >> item_slot; // item - - LOG_DEBUG("network", "WRAP: receive gift_bag = {}, gift_slot = {}, item_bag = {}, item_slot = {}", gift_bag, gift_slot, item_bag, item_slot); - - Item* gift = _player->GetItemByPos(gift_bag, gift_slot); + Item* gift = _player->GetItemByPos(packet.GiftBag, packet.GiftSlot); if (!gift) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, nullptr); @@ -1290,7 +1225,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) return; } - Item* item = _player->GetItemByPos(item_bag, item_slot); + Item* item = _player->GetItemByPos(packet.ItemBag, packet.ItemSlot); if (!item) { @@ -1305,7 +1240,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) return; } - if (item == gift) // not possable with pacjket from real client + if (item == gift) // not possible with packet from real client { _player->SendEquipError(EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, nullptr); return; @@ -1399,26 +1334,19 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) _player->DestroyItemCount(gift, count, true); } -void WorldSession::HandleSocketOpcode(WorldPacket& recvData) +void WorldSession::HandleSocketOpcode(WorldPackets::Item::SocketGems& packet) { LOG_DEBUG("network", "WORLD: CMSG_SOCKET_GEMS"); - ObjectGuid item_guid; - ObjectGuid gem_guids[MAX_GEM_SOCKETS]; - - recvData >> item_guid; - if (!item_guid) + if (!packet.ItemGuid) return; - for (int i = 0; i < MAX_GEM_SOCKETS; ++i) - recvData >> gem_guids[i]; - //cheat -> tried to socket same gem multiple times - if ((gem_guids[0] && (gem_guids[0] == gem_guids[1] || gem_guids[0] == gem_guids[2])) || - (gem_guids[1] && (gem_guids[1] == gem_guids[2]))) + if ((packet.GemGuids[0] && (packet.GemGuids[0] == packet.GemGuids[1] || packet.GemGuids[0] == packet.GemGuids[2])) || + (packet.GemGuids[1] && (packet.GemGuids[1] == packet.GemGuids[2]))) return; - Item* itemTarget = _player->GetItemByGuid(item_guid); + Item* itemTarget = _player->GetItemByGuid(packet.ItemGuid); if (!itemTarget) //missing item to socket return; @@ -1431,7 +1359,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) Item* Gems[MAX_GEM_SOCKETS]; for (int i = 0; i < MAX_GEM_SOCKETS; ++i) - Gems[i] = gem_guids[i] ? _player->GetItemByGuid(gem_guids[i]) : nullptr; + Gems[i] = packet.GemGuids[i] ? _player->GetItemByGuid(packet.GemGuids[i]) : nullptr; GemPropertiesEntry const* GemProps[MAX_GEM_SOCKETS]; for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage @@ -1572,7 +1500,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) if (GemEnchants[i]) { itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + i), GemEnchants[i], 0, 0, _player->GetGUID()); - if (Item* guidItem = _player->GetItemByGuid(gem_guids[i])) + if (Item* guidItem = _player->GetItemByGuid(packet.GemGuids[i])) _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true); } } @@ -1597,19 +1525,15 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) itemTarget->SendUpdateSockets(); } -void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recvData) +void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPackets::Item::CancelTempEnchantment& packet) { LOG_DEBUG("network", "WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT"); - uint32 eslot; - - recvData >> eslot; - // apply only to equipped item - if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, eslot)) + if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, packet.EquipmentSlot)) return; - Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot); + Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, packet.EquipmentSlot); if (!item) return; @@ -1621,14 +1545,11 @@ void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recvData) item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT); } -void WorldSession::HandleItemRefundInfoRequest(WorldPacket& recvData) +void WorldSession::HandleItemRefundInfoRequest(WorldPackets::Item::ItemRefundInfo& packet) { LOG_DEBUG("network", "WORLD: CMSG_ITEM_REFUND_INFO"); - ObjectGuid guid; - recvData >> guid; // item guid - - Item* item = _player->GetItemByGuid(guid); + Item* item = _player->GetItemByGuid(packet.ItemGuid); if (!item) { LOG_DEBUG("network", "Item refund: item not found!"); @@ -1638,13 +1559,11 @@ void WorldSession::HandleItemRefundInfoRequest(WorldPacket& recvData) GetPlayer()->SendRefundInfo(item); } -void WorldSession::HandleItemRefund(WorldPacket& recvData) +void WorldSession::HandleItemRefund(WorldPackets::Item::ItemRefund& packet) { LOG_DEBUG("network", "WORLD: CMSG_ITEM_REFUND"); - ObjectGuid guid; - recvData >> guid; // item guid - Item* item = _player->GetItemByGuid(guid); + Item* item = _player->GetItemByGuid(packet.ItemGuid); if (!item) { LOG_DEBUG("network", "Item refund: item not found!"); @@ -1652,7 +1571,7 @@ void WorldSession::HandleItemRefund(WorldPacket& recvData) } // Don't try to refund item currently being disenchanted - if (_player->GetLootGUID() == guid) + if (_player->GetLootGUID() == packet.ItemGuid) return; GetPlayer()->RefundItem(item); diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index 571390966..5a7e31ca1 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -183,7 +183,9 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData) if (!charter) return; - charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, charter->GetGUID().GetCounter()); + // Use a 31-bit safe petition id instead of the raw item guid + uint32 petitionId = sPetitionMgr->GeneratePetitionId(); + charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, petitionId); // ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id // ITEM_FIELD_ENCHANTMENT_1_1+1 is current signatures count (showed on item) charter->SetState(ITEM_CHANGED, _player); @@ -211,16 +213,18 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData) // xinef: petition pointer is invalid from now on CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION); - stmt->SetData(0, _player->GetGUID().GetCounter()); - stmt->SetData(1, charter->GetGUID().GetCounter()); - stmt->SetData(2, name); - stmt->SetData(3, uint8(type)); + // petition_id, ownerguid, petitionguid(item guid), name, type + stmt->SetData(0, petitionId); + stmt->SetData(1, _player->GetGUID().GetCounter()); + stmt->SetData(2, charter->GetGUID().GetCounter()); + stmt->SetData(3, name); + stmt->SetData(4, uint8(type)); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); - // xinef: fill petition store - sPetitionMgr->AddPetition(charter->GetGUID(), _player->GetGUID(), name, uint8(type)); + // xinef: fill petition store (include petitionId) + sPetitionMgr->AddPetition(charter->GetGUID(), _player->GetGUID(), name, uint8(type), petitionId); } void WorldSession::HandlePetitionShowSignOpcode(WorldPacket& recvData) @@ -249,7 +253,7 @@ void WorldSession::HandlePetitionShowSignOpcode(WorldPacket& recvData) WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8 + 8 + 4 + 1 + signs * 12)); data << petitionguid; // petition guid data << _player->GetGUID(); // owner guid - data << uint32(petitionguid.GetCounter()); // guild guid + data << uint32(petition->petitionId); // guild/team id (31-bit safe) data << uint8(signs); // sign's count if (signs) @@ -286,7 +290,7 @@ void WorldSession::SendPetitionQueryOpcode(ObjectGuid petitionguid) uint8 type = petition->petitionType; WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4 + 8 + petition->petitionName.size() + 1 + 1 + 4 * 12 + 2 + 10)); - data << uint32(petitionguid.GetCounter()); // guild/team guid (in Trinity always same as petition low guid + data << uint32(petition->petitionId); // guild/team id (was item low guid) data << petition->ownerGuid; // charter owner guid data << petition->petitionName; // name (guild/arena team) data << uint8(0); // some string @@ -373,7 +377,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket& recvData) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME); stmt->SetData(0, newName); - stmt->SetData(1, petitionGuid.GetCounter()); + stmt->SetData(1, petition->petitionId); CharacterDatabase.Execute(stmt); @@ -499,7 +503,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE); stmt->SetData(0, petition->ownerGuid.GetCounter()); - stmt->SetData(1, petitionGuid.GetCounter()); + stmt->SetData(1, petition->petitionId); stmt->SetData(2, playerGuid.GetCounter()); stmt->SetData(3, GetAccountId()); @@ -627,7 +631,7 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData) WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8 + 8 + 4 + signs + signs * 12)); data << petitionguid; // petition guid data << _player->GetGUID(); // owner guid - data << uint32(petitionguid.GetCounter()); // guild guid + data << uint32(petition->petitionId); // guild/team id (31-bit safe) data << uint8(signs); // sign's count if (signs) @@ -792,12 +796,12 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket& recvData) CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID); - stmt->SetData(0, petitionGuid.GetCounter()); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_ID); + stmt->SetData(0, petition->petitionId); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID); - stmt->SetData(0, petitionGuid.GetCounter()); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_ID); + stmt->SetData(0, petition->petitionId); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 0666af71c..604977301 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -24,6 +24,7 @@ #include "Opcodes.h" #include "Pet.h" #include "Player.h" +#include "QueryPackets.h" #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" @@ -32,62 +33,55 @@ void WorldSession::SendNameQueryOpcode(ObjectGuid guid) { CharacterCacheEntry const* playerData = sCharacterCache->GetCharacterCacheByGuid(guid); - WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8 + 1 + 1 + 1 + 1 + 1 + 10)); - data << guid.WriteAsPacked(); + WorldPackets::Query::NameQueryResponse nameQueryResponse; + nameQueryResponse.Guid = guid.WriteAsPacked(); if (!playerData) { - data << uint8(1); // name unknown - SendPacket(&data); + nameQueryResponse.NameUnknown = true; + SendPacket(nameQueryResponse.Write()); return; } Player* player = ObjectAccessor::FindConnectedPlayer(guid); - data << uint8(0); // name known - data << playerData->Name; // played name - data << uint8(0); // realm name - only set for cross realm interaction (such as Battlegrounds) - data << uint8(player ? player->getRace() : playerData->Race); - data << uint8(playerData->Sex); - data << uint8(playerData->Class); + nameQueryResponse.NameUnknown = false; + nameQueryResponse.Name = playerData->Name; + nameQueryResponse.Race = player ? player->getRace() : playerData->Race; + nameQueryResponse.Sex = player ? player->getGender() : playerData->Sex; + nameQueryResponse.Class = player ? player->getClass() : playerData->Class; - // pussywizard: optimization - /*Player* player = ObjectAccessor::FindConnectedPlayer(guid); if (DeclinedName const* names = (player ? player->GetDeclinedNames() : nullptr)) { - data << uint8(1); // Name is declined - for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - data << names->name[i]; + nameQueryResponse.Declined = true; + nameQueryResponse.DeclinedNames = *names; } - else*/ - data << uint8(0); // Name is not declined + else + nameQueryResponse.Declined = false; - SendPacket(&data); + SendPacket(nameQueryResponse.Write()); } -void WorldSession::HandleNameQueryOpcode(WorldPacket& recvData) +void WorldSession::HandleNameQueryOpcode(WorldPackets::Query::NameQuery& packet) { - ObjectGuid guid; - recvData >> guid; - // This is disable by default to prevent lots of console spam // LOG_INFO("network.opcode", "HandleNameQueryOpcode {}", guid); - SendNameQueryOpcode(guid); + SendNameQueryOpcode(packet.Guid); } -void WorldSession::HandleQueryTimeOpcode(WorldPacket& /*recvData*/) +void WorldSession::HandleTimeQueryOpcode(WorldPackets::Query::TimeQuery& /*packet*/) { - SendQueryTimeResponse(); + SendTimeQueryResponse(); } -void WorldSession::SendQueryTimeResponse() +void WorldSession::SendTimeQueryResponse() { auto timeResponse = sWorld->GetNextDailyQuestsResetTime() - GameTime::GetGameTime(); - WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4 + 4); - data << uint32(GameTime::GetGameTime().count()); - data << uint32(timeResponse.count()); - SendPacket(&data); + WorldPackets::Query::TimeQueryResponse timeQueryResponse; + timeQueryResponse.ServerTime = GameTime::GetGameTime().count(); + timeQueryResponse.TimeResponse = timeResponse.count(); + SendPacket(timeQueryResponse.Write()); } /// Only _static_ data is sent in this packet !!! @@ -402,13 +396,10 @@ void WorldSession::HandlePageTextQueryOpcode(WorldPacket& recvData) } } -void WorldSession::HandleCorpseMapPositionQuery(WorldPacket& recvData) +void WorldSession::HandleCorpseMapPositionQuery(WorldPackets::Query::CorpseMapPositionQuery& /*packet*/) { LOG_DEBUG("network", "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY"); - uint32 unk; - recvData >> unk; - WorldPacket data(SMSG_CORPSE_MAP_POSITION_QUERY_RESPONSE, 4 + 4 + 4 + 4); data << float(0); data << float(0); diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index eb6243b21..089546aa5 100644 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -172,7 +172,7 @@ void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket& /*recv_data*/) void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket& /*recv_data*/) { - SendQueryTimeResponse(); + SendTimeQueryResponse(); if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) { diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 4c05d182b..919a7f487 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -73,6 +73,8 @@ Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : //lets initialize visibility distance for map Map::InitVisibilityDistance(); + + _corpseUpdateTimer.SetInterval(20 * MINUTE * IN_MILLISECONDS); } // Hook called after map is created AND after added to map list @@ -116,6 +118,8 @@ void Map::AddToGrid(Creature* obj, Cell const& cell) { MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); grid->AddGridObject(cell.CellX(), cell.CellY(), obj); + if (obj->IsFarVisible()) + grid->AddFarVisibleObject(cell.CellX(), cell.CellY(), obj); obj->SetCurrentCell(cell); } @@ -125,6 +129,8 @@ void Map::AddToGrid(GameObject* obj, Cell const& cell) { MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); grid->AddGridObject(cell.CellX(), cell.CellY(), obj); + if (obj->IsFarVisible()) + grid->AddFarVisibleObject(cell.CellX(), cell.CellY(), obj); obj->SetCurrentCell(cell); } @@ -345,7 +351,7 @@ bool Map::AddToMap(T* obj, bool checkTransport) } template<> -bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/) +bool Map::AddToMap(Transport* obj, bool /*checkTransport*/) { //TODO: Needs clean up. An object should not be added to map twice. if (obj->IsInWorld()) @@ -360,26 +366,22 @@ bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/) } Cell cell(cellCoord); - if (obj->isActiveObject()) - EnsureGridLoaded(cell); + EnsureGridLoaded(cell); obj->AddToWorld(); _transports.insert(obj); // Broadcast creation to players - if (!GetPlayers().IsEmpty()) + for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { - for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (itr->GetSource()->GetTransport() != obj) { - if (itr->GetSource()->GetTransport() != obj) - { - UpdateData data; - obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); - WorldPacket packet; - data.BuildPacket(packet); - itr->GetSource()->SendDirectMessage(&packet); - } + UpdateData data; + obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); + WorldPacket packet; + data.BuildPacket(packet); + itr->GetSource()->SendDirectMessage(&packet); } } @@ -498,6 +500,8 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) HandleDelayedVisibility(); + UpdateExpiredCorpses(t_diff); + sScriptMgr->OnMapUpdate(this, t_diff); METRIC_VALUE("map_creatures", uint64(GetObjectsStore().Size()), @@ -604,6 +608,69 @@ void Map::RemoveObjectFromMapUpdateList(WorldObject* obj) _RemoveObjectFromUpdateList(obj); } +// Used in VisibilityDistanceType::Large and VisibilityDistanceType::Gigantic +void Map::AddWorldObjectToFarVisibleMap(WorldObject* obj) +{ + if (Creature* creature = obj->ToCreature()) + { + if (!creature->IsInGrid()) + return; + + Cell curr_cell = creature->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), creature); + } + else if (GameObject* go = obj->ToGameObject()) + { + if (!go->IsInGrid()) + return; + + Cell curr_cell = go->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), go); + } +} + +void Map::RemoveWorldObjectFromFarVisibleMap(WorldObject* obj) +{ + if (Creature* creature = obj->ToCreature()) + { + Cell curr_cell = creature->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), creature); + } + else if (GameObject* go = obj->ToGameObject()) + { + Cell curr_cell = go->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), go); + } +} + +// Used in VisibilityDistanceType::Infinite +void Map::AddWorldObjectToZoneWideVisibleMap(uint32 zoneId, WorldObject* obj) +{ + _zoneWideVisibleWorldObjectsMap[zoneId].insert(obj); +} + +void Map::RemoveWorldObjectFromZoneWideVisibleMap(uint32 zoneId, WorldObject* obj) +{ + ZoneWideVisibleWorldObjectsMap::iterator itr = _zoneWideVisibleWorldObjectsMap.find(zoneId); + if (itr == _zoneWideVisibleWorldObjectsMap.end()) + return; + + itr->second.erase(obj); +} + +ZoneWideVisibleWorldObjectsSet const* Map::GetZoneWideVisibleWorldObjectsForZone(uint32 zoneId) const +{ + ZoneWideVisibleWorldObjectsMap::const_iterator itr = _zoneWideVisibleWorldObjectsMap.find(zoneId); + if (itr == _zoneWideVisibleWorldObjectsMap.end()) + return nullptr; + + return &itr->second; +} + void Map::HandleDelayedVisibility() { if (i_objectsForDelayedVisibility.empty()) @@ -656,6 +723,8 @@ void Map::RemoveFromMap(T* obj, bool remove) obj->RemoveFromWorld(); obj->RemoveFromGrid(); + if (obj->IsFarVisible()) + RemoveWorldObjectFromFarVisibleMap(obj); obj->ResetMap(); @@ -667,7 +736,7 @@ void Map::RemoveFromMap(T* obj, bool remove) } template<> -void Map::RemoveFromMap(MotionTransport* obj, bool remove) +void Map::RemoveFromMap(Transport* obj, bool remove) { obj->RemoveFromWorld(); @@ -697,8 +766,6 @@ void Map::RemoveFromMap(MotionTransport* obj, bool remove) obj->ResetMap(); - // Transports are never actually deleted, but it *should* be safe to clear - // from update list when removing from world RemoveObjectFromMapUpdateList(obj); if (remove) @@ -854,6 +921,11 @@ void Map::MoveAllCreaturesInMoveList() Cell const& old_cell = c->GetCurrentCell(); Cell new_cell(c->GetPositionX(), c->GetPositionY()); + if (c->IsFarVisible()) + { + // Removes via GetCurrentCell, added back in AddToGrid + RemoveWorldObjectFromFarVisibleMap(c); + } c->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) @@ -884,6 +956,12 @@ void Map::MoveAllGameObjectsInMoveList() Cell const& old_cell = go->GetCurrentCell(); Cell new_cell(go->GetPositionX(), go->GetPositionY()); + if (go->IsFarVisible()) + { + // Removes via GetCurrentCell, added back in AddToGrid + RemoveWorldObjectFromFarVisibleMap(go); + } + go->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); @@ -966,11 +1044,10 @@ void Map::UnloadAll() for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end();) { - MotionTransport* transport = *itr; + Transport* transport = *itr; ++itr; - transport->RemoveFromWorld(); - delete transport; + RemoveFromMap(transport, true); } _transports.clear(); @@ -1587,8 +1664,22 @@ void Map::SendInitSelf(Player* player) player->SendDirectMessage(&packet); } +void Map::UpdateExpiredCorpses(uint32 const diff) +{ + _corpseUpdateTimer.Update(diff); + if (!_corpseUpdateTimer.Passed()) + return; + + RemoveOldCorpses(); + + _corpseUpdateTimer.Reset(); +} + void Map::SendInitTransports(Player* player) { + if (_transports.empty()) + return; + // Hack to send out transports UpdateData transData; for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) @@ -1605,24 +1696,15 @@ void Map::SendInitTransports(Player* player) void Map::SendRemoveTransports(Player* player) { + if (_transports.empty()) + return; + // Hack to send out transports UpdateData transData; for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) if (*itr != player->GetTransport()) (*itr)->BuildOutOfRangeUpdateBlock(&transData); - // pussywizard: remove static transports from client - /*for (GuidUnorderedSet::const_iterator it = player->m_clientGUIDs.begin(); it != player->m_clientGUIDs.end(); ) - { - if ((*it).IsTransport()) - { - transData.AddOutOfRangeGUID(*it); - it = player->m_clientGUIDs.erase(it); - } - else - ++it; - }*/ - if (!transData.HasData()) return; @@ -1699,7 +1781,7 @@ void Map::DelayedUpdate(const uint32 t_diff) { for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) { - MotionTransport* transport = *_transportsUpdateIter; + Transport* transport = *_transportsUpdateIter; ++_transportsUpdateIter; if (!transport->IsInWorld()) @@ -1744,7 +1826,7 @@ void Map::RemoveAllObjectsInRemoveList() RemoveFromMap((DynamicObject*)obj, true); break; case TYPEID_GAMEOBJECT: - if (MotionTransport* transport = obj->ToGameObject()->ToMotionTransport()) + if (Transport* transport = obj->ToGameObject()->ToTransport()) RemoveFromMap(transport, true); else RemoveFromMap(obj->ToGameObject(), true); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index fb24b0924..c875e4eaa 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -146,7 +146,9 @@ struct ZoneDynamicInfo typedef std::map CreatureGroupHolderType; typedef std::unordered_map ZoneDynamicInfoMap; -typedef std::set TransportsContainer; +typedef std::unordered_set TransportsContainer; +typedef std::unordered_set ZoneWideVisibleWorldObjectsSet; +typedef std::unordered_map ZoneWideVisibleWorldObjectsMap; enum EncounterCreditType : uint8 { @@ -443,6 +445,8 @@ public: void SendZoneDynamicInfo(Player* player); void SendInitSelf(Player* player); + void UpdateExpiredCorpses(uint32 const diff); + void PlayDirectSoundToMap(uint32 soundId, uint32 zoneId = 0); void SetZoneMusic(uint32 zoneId, uint32 musicId); void SetZoneWeather(uint32 zoneId, WeatherState weatherId, float weatherGrade); @@ -479,7 +483,7 @@ public: _updateObjects.erase(obj); } - size_t GetUpdateObjectsCount() const { return _updateObjects.size(); } + size_t GetUpdatableObjectsCount() const { return _updatableObjectList.size(); } virtual std::string GetDebugInfo() const; @@ -494,6 +498,12 @@ public: typedef std::vector UpdatableObjectList; typedef std::unordered_set PendingAddUpdatableObjectList; + void AddWorldObjectToFarVisibleMap(WorldObject* obj); + void RemoveWorldObjectFromFarVisibleMap(WorldObject* obj); + void AddWorldObjectToZoneWideVisibleMap(uint32 zoneId, WorldObject* obj); + void RemoveWorldObjectFromZoneWideVisibleMap(uint32 zoneId, WorldObject* obj); + ZoneWideVisibleWorldObjectsSet const* GetZoneWideVisibleWorldObjectsForZone(uint32 zoneId) const; + private: template void InitializeObject(T* obj); @@ -535,7 +545,6 @@ protected: MapRefMgr m_mapRefMgr; MapRefMgr::iterator m_mapRefIter; - // Objects that must update even in inactive grids without activating them TransportsContainer _transports; TransportsContainer::iterator _transportsUpdateIter; @@ -577,6 +586,8 @@ private: ZoneDynamicInfoMap _zoneDynamicInfo; uint32 _defaultLight; + IntervalTimer _corpseUpdateTimer; + template inline ObjectGuidGeneratorBase& GetGuidSequenceGenerator() { @@ -600,6 +611,7 @@ private: UpdatableObjectList _updatableObjectList; PendingAddUpdatableObjectList _pendingAddUpdatableObjectList; IntervalTimer _updatableObjectListRecheckTimer; + ZoneWideVisibleWorldObjectsMap _zoneWideVisibleWorldObjectsMap; }; enum InstanceResetMethod diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp index ad319e943..f065ccf56 100644 --- a/src/server/game/Maps/TransportMgr.cpp +++ b/src/server/game/Maps/TransportMgr.cpp @@ -412,10 +412,8 @@ MotionTransport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType if (map && map->IsDungeon()) trans->m_zoneScript = map->ToInstanceMap()->GetInstanceScript(); - // xinef: transports are active so passengers can be relocated (grids must be loaded) - trans->setActive(true); HashMapHolder::Insert(trans); - trans->GetMap()->AddToMap(trans); + trans->GetMap()->AddToMap(trans); return trans; } diff --git a/src/server/game/Petitions/PetitionMgr.cpp b/src/server/game/Petitions/PetitionMgr.cpp index a3c5c69ff..7e946923e 100644 --- a/src/server/game/Petitions/PetitionMgr.cpp +++ b/src/server/game/Petitions/PetitionMgr.cpp @@ -41,8 +41,9 @@ void PetitionMgr::LoadPetitions() { uint32 oldMSTime = getMSTime(); PetitionStore.clear(); + PetitionIdToItemGuid.clear(); - QueryResult result = CharacterDatabase.Query("SELECT ownerguid, petitionguid, name, type FROM petition"); + QueryResult result = CharacterDatabase.Query("SELECT ownerguid, petitionguid, name, type, petition_id FROM petition"); if (!result) { LOG_WARN("server.loading", ">> Loaded 0 Petitions!"); @@ -51,13 +52,24 @@ void PetitionMgr::LoadPetitions() } uint32 count = 0; + uint32 maxId = 0; do { Field* fields = result->Fetch(); - AddPetition(ObjectGuid::Create(fields[1].Get()), ObjectGuid::Create(fields[0].Get()), fields[2].Get(), fields[3].Get()); + uint32 itemLow = fields[1].Get(); + uint32 petitionId = fields[4].Get(); + ObjectGuid itemGuid = ObjectGuid::Create(itemLow); + ObjectGuid ownerGuid = ObjectGuid::Create(fields[0].Get()); + AddPetition(itemGuid, ownerGuid, fields[2].Get(), fields[3].Get(), petitionId); + PetitionIdToItemGuid[petitionId] = itemGuid; + if (petitionId > maxId) + maxId = petitionId; ++count; } while (result->NextRow()); + // initialize next id (keep within 31-bit safe range) + _nextPetitionId = std::min(std::max(maxId + 1, 1), 0x7FFFFFFFu); + LOG_INFO("server.loading", ">> Loaded {} Petitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } @@ -67,7 +79,7 @@ void PetitionMgr::LoadSignatures() uint32 oldMSTime = getMSTime(); SignatureStore.clear(); - QueryResult result = CharacterDatabase.Query("SELECT petitionguid, playerguid, player_account FROM petition_sign"); + QueryResult result = CharacterDatabase.Query("SELECT petition_id, playerguid, player_account FROM petition_sign"); if (!result) { LOG_WARN("server.loading", ">> Loaded 0 Petition signs!"); @@ -79,7 +91,11 @@ void PetitionMgr::LoadSignatures() do { Field* fields = result->Fetch(); - AddSignature(ObjectGuid::Create(fields[0].Get()), fields[2].Get(), ObjectGuid::Create(fields[1].Get())); + uint32 petitionId = fields[0].Get(); + auto it = PetitionIdToItemGuid.find(petitionId); + if (it == PetitionIdToItemGuid.end()) + continue; // orphan signature, skip + AddSignature(it->second, fields[2].Get(), ObjectGuid::Create(fields[1].Get())); ++count; } while (result->NextRow()); @@ -87,10 +103,11 @@ void PetitionMgr::LoadSignatures() LOG_INFO("server.loading", " "); } -void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type) +void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type, uint32 petitionId) { Petition& p = PetitionStore[petitionGUID]; p.petitionGuid = petitionGUID; + p.petitionId = petitionId; p.ownerGuid = ownerGuid; p.petitionName = name; p.petitionType = type; @@ -102,7 +119,12 @@ void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std void PetitionMgr::RemovePetition(ObjectGuid petitionGUID) { - PetitionStore.erase(petitionGUID); + auto it = PetitionStore.find(petitionGUID); + if (it != PetitionStore.end()) + { + PetitionIdToItemGuid.erase(it->second.petitionId); + PetitionStore.erase(it); + } // remove signatures SignatureStore.erase(petitionGUID); @@ -143,6 +165,15 @@ Petition const* PetitionMgr::GetPetition(ObjectGuid petitionGUID) const return nullptr; } +Petition const* PetitionMgr::GetPetitionById(uint32 petitionId) const +{ + auto it = PetitionIdToItemGuid.find(petitionId); + if (it == PetitionIdToItemGuid.end()) + return nullptr; + + return GetPetition(it->second); +} + Petition const* PetitionMgr::GetPetitionByOwnerWithType(ObjectGuid ownerGuid, uint8 type) const { for (PetitionContainer::const_iterator itr = PetitionStore.begin(); itr != PetitionStore.end(); ++itr) @@ -189,3 +220,37 @@ void PetitionMgr::RemoveSignaturesByPlayerAndType(ObjectGuid playerGuid, uint8 t itr->second.signatureMap.erase(signItr); } } + +uint32 PetitionMgr::GeneratePetitionId() +{ + // ensure 31-bit range and avoid collisions with already loaded petitions + if (_nextPetitionId == 0 || _nextPetitionId >= 0x7FFFFFFF) + _nextPetitionId = 1; + + // find first free id + while (PetitionIdToItemGuid.count(_nextPetitionId)) + { + ++_nextPetitionId; + if (_nextPetitionId >= 0x7FFFFFFF) + _nextPetitionId = 1; + } + + uint32 id = _nextPetitionId++; + if (_nextPetitionId >= 0x7FFFFFFF) + _nextPetitionId = 1; + return id; +} + +uint32 PetitionMgr::GetPetitionIdByItemGuid(ObjectGuid petitionItemGuid) const +{ + Petition const* p = GetPetition(petitionItemGuid); + return p ? p->petitionId : 0; +} + +ObjectGuid PetitionMgr::GetItemGuidByPetitionId(uint32 petitionId) const +{ + auto it = PetitionIdToItemGuid.find(petitionId); + if (it == PetitionIdToItemGuid.end()) + return ObjectGuid::Empty; + return it->second; +} diff --git a/src/server/game/Petitions/PetitionMgr.h b/src/server/game/Petitions/PetitionMgr.h index 97491fb3a..cd7d47ead 100644 --- a/src/server/game/Petitions/PetitionMgr.h +++ b/src/server/game/Petitions/PetitionMgr.h @@ -20,6 +20,7 @@ #include "ObjectGuid.h" +#include #include #define CHARTER_DISPLAY_ID 16161 @@ -37,14 +38,21 @@ typedef std::map SignatureMap; struct Petition { + // Item GUID of the charter item (used to find the item in inventory) ObjectGuid petitionGuid; + // New 31-bit safe petition identifier used in packets/DB relations + uint32 petitionId; + // Owner character GUID ObjectGuid ownerGuid; + // Petition type (guild / arena) uint8 petitionType; + // Name associated with the petition (guild/arena name) std::string petitionName; }; struct Signatures { + // Keep keying by item-guid for backward compatibility in code paths ObjectGuid petitionGuid; SignatureMap signatureMap; }; @@ -65,10 +73,11 @@ public: void LoadSignatures(); // Petitions - void AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type); + void AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type, uint32 petitionId); void RemovePetition(ObjectGuid petitionGUID); void RemovePetitionByOwnerAndType(ObjectGuid ownerGuid, uint8 type); Petition const* GetPetition(ObjectGuid petitionGUID) const; + Petition const* GetPetitionById(uint32 petitionId) const; Petition const* GetPetitionByOwnerWithType(ObjectGuid ownerGuid, uint8 type) const; PetitionContainer* GetPetitionStore() { return &PetitionStore; } @@ -79,9 +88,17 @@ public: Signatures const* GetSignature(ObjectGuid petitionGUID) const; SignatureContainer* GetSignatureStore() { return &SignatureStore; } + uint32 GeneratePetitionId(); + uint32 GetPetitionIdByItemGuid(ObjectGuid petitionItemGuid) const; + ObjectGuid GetItemGuidByPetitionId(uint32 petitionId) const; + protected: PetitionContainer PetitionStore; SignatureContainer SignatureStore; + // Mapping id -> item-guid to support DB-id lookups + std::unordered_map PetitionIdToItemGuid; + // Next petition id (kept < 2^31) + uint32 _nextPetitionId = 1; }; #define sPetitionMgr PetitionMgr::instance() diff --git a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp index 45d90cf10..f34599d81 100644 --- a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp @@ -57,11 +57,11 @@ void ScriptMgr::OnBeforeCreatureSelectLevel(const CreatureTemplate* cinfo, Creat }); } -void ScriptMgr::Creature_SelectLevel(const CreatureTemplate* cinfo, Creature* creature) +void ScriptMgr::OnCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature) { ExecuteScript([&](AllCreatureScript* script) { - script->Creature_SelectLevel(cinfo, creature); + script->OnCreatureSelectLevel(cinfo, creature); }); } diff --git a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h index a4f83dd2b..71e230e8b 100644 --- a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h +++ b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h @@ -33,7 +33,7 @@ public: virtual void OnBeforeCreatureSelectLevel(const CreatureTemplate* /*cinfo*/, Creature* /*creature*/, uint8& /*level*/) { } // Called from End of Creature SelectLevel. - virtual void Creature_SelectLevel(const CreatureTemplate* /*cinfo*/, Creature* /*creature*/) { } + virtual void OnCreatureSelectLevel(const CreatureTemplate* /*cinfo*/, Creature* /*creature*/) { } /** * @brief This hook runs after add creature in world diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index be3bdb843..925e2c71a 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -139,6 +139,11 @@ void ScriptMgr::OnPlayerReputationRankChange(Player* player, uint32 factionID, R CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_REPUTATION_RANK_CHANGE, script->OnPlayerReputationRankChange(player, factionID, newRank, oldRank, increased)); } +void ScriptMgr::OnPlayerGiveReputation(Player* player, int32 factionID, float& amount, ReputationSource repSource) +{ + CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_GIVE_REPUTATION, script->OnPlayerGiveReputation(player, factionID, amount, repSource)); +} + void ScriptMgr::OnPlayerLearnSpell(Player* player, uint32 spellID) { CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LEARN_SPELL, script->OnPlayerLearnSpell(player, spellID)); diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index ca1a1445e..1686144fc 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -211,6 +211,7 @@ enum PlayerHook PLAYERHOOK_CAN_RESURRECT, PLAYERHOOK_ON_CAN_GIVE_LEVEL, PLAYERHOOK_ON_SEND_LIST_INVENTORY, + PLAYERHOOK_ON_GIVE_REPUTATION, PLAYERHOOK_END }; @@ -285,6 +286,9 @@ public: // Called when a player's reputation rank changes (before it is actually changed) virtual void OnPlayerReputationRankChange(Player* /*player*/, uint32 /*factionID*/, ReputationRank /*newRank*/, ReputationRank /*olRank*/, bool /*increased*/) { } + // Called when a player gains Reputation (before anything is given) + virtual void OnPlayerGiveReputation(Player* /*player*/, int32 /*factionID*/, float& /*amount*/, ReputationSource /*repSource*/) { } + // Called when a player learned new spell virtual void OnPlayerLearnSpell(Player* /*player*/, uint32 /*spellID*/) {} diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index c0a3cf95c..9633bb29a 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -334,6 +334,7 @@ public: /* PlayerScript */ void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource); bool OnPlayerReputationChange(Player* player, uint32 factionID, int32& standing, bool incremental); void OnPlayerReputationRankChange(Player* player, uint32 factionID, ReputationRank newRank, ReputationRank oldRank, bool increased); + void OnPlayerGiveReputation(Player* player, int32 factionID, float& amount, ReputationSource repSource); void OnPlayerLearnSpell(Player* player, uint32 spellID); void OnPlayerForgotSpell(Player* player, uint32 spellID); void OnPlayerDuelRequest(Player* target, Player* challenger); @@ -592,7 +593,7 @@ public: /* AllCreatureScript */ //listener function (OnAllCreatureUpdate) is called by OnCreatureUpdate //void OnAllCreatureUpdate(Creature* creature, uint32 diff); void OnBeforeCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level); - void Creature_SelectLevel(const CreatureTemplate* cinfo, Creature* creature); + void OnCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature); void OnCreatureSaveToDB(Creature* creature); public: /* AllGameobjectScript */ diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 1e3457e1d..498dafb90 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -24,9 +24,11 @@ #include "CombatLogPackets.h" #include "CombatPackets.h" #include "GuildPackets.h" +#include "ItemPackets.h" #include "LFGPackets.h" #include "MiscPackets.h" #include "PetPackets.h" +#include "QueryPackets.h" #include "TotemPackets.h" #include "WorldStatePackets.h" diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp new file mode 100644 index 000000000..e86ae3879 --- /dev/null +++ b/src/server/game/Server/Packets/ItemPackets.cpp @@ -0,0 +1,163 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "ItemPackets.h" + +void WorldPackets::Item::SplitItem::Read() +{ + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; + _worldPacket >> DestinationBag; + _worldPacket >> DestinationSlot; + _worldPacket >> Count; +} + +void WorldPackets::Item::SwapInventoryItem::Read() +{ + _worldPacket >> DestinationSlot; + _worldPacket >> SourceSlot; +} + +void WorldPackets::Item::AutoEquipItemSlot::Read() +{ + _worldPacket >> ItemGuid; + _worldPacket >> DestinationSlot; +} + +void WorldPackets::Item::SwapItem::Read() +{ + _worldPacket >> DestinationBag; + _worldPacket >> DestinationSlot; + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; +} + +void WorldPackets::Item::AutoEquipItem::Read() +{ + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; +} + +void WorldPackets::Item::DestroyItem::Read() +{ + _worldPacket >> Bag; + _worldPacket >> Slot; + _worldPacket >> Count; + _worldPacket >> Data1; + _worldPacket >> Data2; + _worldPacket >> Data3; +} + +void WorldPackets::Item::ReadItem::Read() +{ + _worldPacket >> Bag; + _worldPacket >> Slot; +} + +void WorldPackets::Item::SellItem::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> ItemGuid; + _worldPacket >> Count; +} + +void WorldPackets::Item::BuybackItem::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> Slot; +} + +void WorldPackets::Item::BuyItemInSlot::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> Item; + _worldPacket >> Slot; + _worldPacket >> BagGuid; + _worldPacket >> BagSlot; + _worldPacket >> Count; +} + +void WorldPackets::Item::BuyItem::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> Item; + _worldPacket >> Slot; + _worldPacket >> Count; + _worldPacket >> Unk; +} + +void WorldPackets::Item::ListInventory::Read() +{ + _worldPacket >> VendorGuid; +} + +void WorldPackets::Item::AutoStoreBagItem::Read() +{ + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; + _worldPacket >> DestinationBag; +} + +WorldPacket const* WorldPackets::Item::EnchantmentLog::Write() +{ + _worldPacket << Target; + _worldPacket << Caster; + _worldPacket << ItemId; + _worldPacket << EnchantId; + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Item::ItemEnchantTimeUpdate::Write() +{ + _worldPacket << ItemGuid; + _worldPacket << Slot; + _worldPacket << Duration; + _worldPacket << PlayerGuid; + + return &_worldPacket; +} + +void WorldPackets::Item::WrapItem::Read() +{ + _worldPacket >> GiftBag; + _worldPacket >> GiftSlot; + _worldPacket >> ItemBag; + _worldPacket >> ItemSlot; +} + +void WorldPackets::Item::SocketGems::Read() +{ + _worldPacket >> ItemGuid; + for (int i = 0; i < MAX_GEM_SOCKETS; ++i) + _worldPacket >> GemGuids[i]; +} + +void WorldPackets::Item::CancelTempEnchantment::Read() +{ + _worldPacket >> EquipmentSlot; +} + +void WorldPackets::Item::ItemRefundInfo::Read() +{ + _worldPacket >> ItemGuid; +} + +void WorldPackets::Item::ItemRefund::Read() +{ + _worldPacket >> ItemGuid; +} diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h new file mode 100644 index 000000000..04d884188 --- /dev/null +++ b/src/server/game/Server/Packets/ItemPackets.h @@ -0,0 +1,272 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ItemPackets_h__ +#define ItemPackets_h__ + +#include "Item.h" +#include "ObjectGuid.h" +#include "Packet.h" + +namespace WorldPackets +{ + namespace Item + { + class SplitItem final : public ClientPacket + { + public: + SplitItem(WorldPacket&& packet) : ClientPacket(CMSG_SPLIT_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + uint8 DestinationBag = 0; + uint8 DestinationSlot = 0; + uint32 Count = 0; + }; + + class SwapInventoryItem final : public ClientPacket + { + public: + SwapInventoryItem(WorldPacket&& packet) : ClientPacket(CMSG_SWAP_INV_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 DestinationSlot = 0; + uint8 SourceSlot = 0; + }; + + class AutoEquipItemSlot final : public ClientPacket + { + public: + AutoEquipItemSlot(WorldPacket&& packet) : ClientPacket(CMSG_AUTOEQUIP_ITEM_SLOT, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + uint8 DestinationSlot = 0; + }; + + class SwapItem final : public ClientPacket + { + public: + SwapItem(WorldPacket&& packet) : ClientPacket(CMSG_SWAP_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 DestinationBag = 0; + uint8 DestinationSlot = 0; + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + }; + + class AutoEquipItem final : public ClientPacket + { + public: + AutoEquipItem(WorldPacket&& packet) : ClientPacket(CMSG_AUTOEQUIP_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + }; + + class DestroyItem final : public ClientPacket + { + public: + DestroyItem(WorldPacket&& packet) : ClientPacket(CMSG_DESTROYITEM, std::move(packet)) {} + + void Read() override; + + uint8 Bag = 0; + uint8 Slot = 0; + uint8 Count = 0; + uint8 Data1 = 0; + uint8 Data2 = 0; + uint8 Data3 = 0; + }; + + class ReadItem final : public ClientPacket + { + public: + ReadItem(WorldPacket&& packet) : ClientPacket(CMSG_READ_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 Bag = 0; + uint8 Slot = 0; + }; + + class SellItem final : public ClientPacket + { + public: + SellItem(WorldPacket&& packet) : ClientPacket(CMSG_SELL_ITEM, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + ObjectGuid ItemGuid; + uint32 Count = 0; + }; + + class BuybackItem final : public ClientPacket + { + public: + BuybackItem(WorldPacket&& packet) : ClientPacket(CMSG_BUYBACK_ITEM, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + uint32 Slot = 0; + }; + + class BuyItemInSlot final : public ClientPacket + { + public: + BuyItemInSlot(WorldPacket&& packet) : ClientPacket(CMSG_BUY_ITEM_IN_SLOT, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + uint32 Item = 0; + uint32 Slot = 0; + ObjectGuid BagGuid; + uint8 BagSlot = 0; + uint32 Count = 0; + }; + + class BuyItem final : public ClientPacket + { + public: + BuyItem(WorldPacket&& packet) : ClientPacket(CMSG_BUY_ITEM, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + uint32 Item = 0; + uint32 Slot = 0; + uint32 Count = 0; + uint8 Unk = 0; + }; + + class ListInventory final : public ClientPacket + { + public: + ListInventory(WorldPacket&& packet) : ClientPacket(CMSG_LIST_INVENTORY, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + }; + + class AutoStoreBagItem final : public ClientPacket + { + public: + AutoStoreBagItem(WorldPacket&& packet) : ClientPacket(CMSG_AUTOSTORE_BAG_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + uint8 DestinationBag = 0; + }; + + class EnchantmentLog final : public ServerPacket + { + public: + EnchantmentLog() : ServerPacket(SMSG_ENCHANTMENTLOG, 8 + 8 + 4 + 4) {} + + WorldPacket const* Write() override; + + PackedGuid Target; + PackedGuid Caster; + uint32 ItemId = 0; + uint32 EnchantId = 0; + }; + + class ItemEnchantTimeUpdate final : public ServerPacket + { + public: + ItemEnchantTimeUpdate() : ServerPacket(SMSG_ITEM_ENCHANT_TIME_UPDATE, 8 + 4 + 4 + 8) {} + + WorldPacket const* Write() override; + + // last check 2.0.10 + ObjectGuid ItemGuid; + uint32 Slot = 0; + uint32 Duration = 0; + ObjectGuid PlayerGuid; + }; + + class WrapItem final : public ClientPacket + { + public: + WrapItem(WorldPacket&& packet) : ClientPacket(CMSG_WRAP_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 GiftBag = 0; + uint8 GiftSlot = 0; + uint8 ItemBag = 0; + uint8 ItemSlot = 0; + }; + + class SocketGems final : public ClientPacket + { + public: + SocketGems(WorldPacket&& packet) : ClientPacket(CMSG_SOCKET_GEMS, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + ObjectGuid GemGuids[MAX_GEM_SOCKETS]; + }; + + class CancelTempEnchantment final : public ClientPacket + { + public: + CancelTempEnchantment(WorldPacket&& packet) : ClientPacket(CMSG_CANCEL_TEMP_ENCHANTMENT, std::move(packet)) {} + + void Read() override; + + uint32 EquipmentSlot = 0; + }; + + class ItemRefundInfo final : public ClientPacket + { + public: + ItemRefundInfo(WorldPacket&& packet) : ClientPacket(CMSG_ITEM_REFUND_INFO, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + }; + + class ItemRefund final : public ClientPacket + { + public: + ItemRefund(WorldPacket&& packet) : ClientPacket(CMSG_ITEM_REFUND, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + }; + } +} + +#endif // ItemPackets_h__ diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index a418a517e..109845381 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -67,6 +67,21 @@ WorldPacket const* WorldPackets::Misc::Playsound::Write() return &_worldPacket; } +void WorldPackets::Misc::MinimapPingClient::Read() +{ + _worldPacket >> MapX; + _worldPacket >> MapY; +} + +WorldPacket const* WorldPackets::Misc::MinimapPing::Write() +{ + _worldPacket << SourceGuid; + _worldPacket << float(MapX); + _worldPacket << float(MapY); + + return &_worldPacket; +} + void WorldPackets::Misc::RandomRollClient::Read() { _worldPacket >> Min; diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index 6f991293f..7453ad7fc 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -93,6 +93,29 @@ namespace WorldPackets uint32 SoundKitID = 0; }; + class MinimapPingClient final : public ClientPacket + { + public: + MinimapPingClient(WorldPacket&& packet) : ClientPacket(MSG_MINIMAP_PING, std::move(packet)) {} + + void Read() override; + + float MapX = 0.0f; // Raw position coordinates + float MapY = 0.0f; + }; + + class MinimapPing final : public ServerPacket + { + public: + MinimapPing() : ServerPacket(MSG_MINIMAP_PING, 8 + 4 + 4) { } + + WorldPacket const* Write() override; + + ObjectGuid SourceGuid; + float MapX = 0.0f; + float MapY = 0.0f; + }; + class RandomRollClient final : public ClientPacket { public: diff --git a/src/server/game/Server/Packets/QueryPackets.cpp b/src/server/game/Server/Packets/QueryPackets.cpp new file mode 100644 index 000000000..6efa96275 --- /dev/null +++ b/src/server/game/Server/Packets/QueryPackets.cpp @@ -0,0 +1,58 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "QueryPackets.h" + +void WorldPackets::Query::NameQuery::Read() +{ + _worldPacket >> Guid; +} + +WorldPacket const* WorldPackets::Query::NameQueryResponse::Write() +{ + _worldPacket << Guid; + _worldPacket << NameUnknown; + if (NameUnknown) + return &_worldPacket; + + _worldPacket << Name; + _worldPacket << RealmName; + _worldPacket << Race; + _worldPacket << Sex; + _worldPacket << Class; + _worldPacket << Declined; + if (Declined) + { + for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + _worldPacket << DeclinedNames.name[i]; + } + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Query::TimeQueryResponse::Write() +{ + _worldPacket << ServerTime; + _worldPacket << TimeResponse; + + return &_worldPacket; +} + +void WorldPackets::Query::CorpseMapPositionQuery::Read() +{ + _worldPacket >> unk; +} diff --git a/src/server/game/Server/Packets/QueryPackets.h b/src/server/game/Server/Packets/QueryPackets.h new file mode 100644 index 000000000..85d7a76ce --- /dev/null +++ b/src/server/game/Server/Packets/QueryPackets.h @@ -0,0 +1,87 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef QueryPackets_h__ +#define QueryPackets_h__ + +#include "Packet.h" +#include "Unit.h" + +namespace WorldPackets +{ + namespace Query + { + class NameQuery final : public ClientPacket + { + public: + NameQuery(WorldPacket&& packet) : ClientPacket(CMSG_NAME_QUERY, std::move(packet)) {} + + void Read() override; + + ObjectGuid Guid; + }; + + class NameQueryResponse final : public ServerPacket + { + public: + NameQueryResponse() : ServerPacket(SMSG_NAME_QUERY_RESPONSE, 8 + 1 + 1 + 1 + 1 + 1 + 10) {} + + WorldPacket const* Write() override; + + PackedGuid Guid; + uint8 NameUnknown = false; + std::string_view Name; + std::string_view RealmName = ""; // Only set for cross realm interaction (such as Battlegrounds) + uint8 Race = RACE_NONE; + uint8 Sex = GENDER_MALE; + uint8 Class = CLASS_NONE; + uint8 Declined = false; + DeclinedName DeclinedNames; + }; + + class TimeQuery final : public ClientPacket + { + public: + TimeQuery(WorldPacket&& packet) : ClientPacket(CMSG_QUERY_TIME, std::move(packet)) {} + + void Read() override {}; + }; + + class TimeQueryResponse final : public ServerPacket + { + public: + TimeQueryResponse() : ServerPacket(SMSG_QUERY_TIME_RESPONSE, 4 + 4) {} + + WorldPacket const* Write() override; + + uint32 ServerTime; + uint32 TimeResponse; + }; + + class CorpseMapPositionQuery final : public ClientPacket + { + public: + CorpseMapPositionQuery(WorldPacket&& packet) : ClientPacket(CMSG_CORPSE_MAP_POSITION_QUERY, std::move(packet)) {} + + void Read() override; + + uint32 unk; + }; + } +} + +#endif // QueryPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 85bcb4186..bc12f7764 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -590,7 +590,7 @@ void OpcodeTable::Initialize() /*0x1CB*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFICATION, STATUS_NEVER); /*0x1CC*/ DEFINE_HANDLER(CMSG_PLAYED_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePlayedTime ); /*0x1CD*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAYED_TIME, STATUS_NEVER); - /*0x1CE*/ DEFINE_HANDLER(CMSG_QUERY_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleQueryTimeOpcode ); + /*0x1CE*/ DEFINE_HANDLER(CMSG_QUERY_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeQueryOpcode ); /*0x1CF*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_TIME_RESPONSE, STATUS_NEVER); /*0x1D0*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOG_XPGAIN, STATUS_NEVER); /*0x1D1*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AURACASTLOG, STATUS_NEVER); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 55919f7ea..2b7a08047 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -148,6 +148,7 @@ namespace WorldPackets namespace Misc { + class MinimapPingClient; class RandomRollClient; } @@ -159,6 +160,35 @@ namespace WorldPackets class PetSpellAutocast; class RequestPetInfo; } + + namespace Query + { + class NameQuery; + class TimeQuery; + class CorpseMapPositionQuery; + } + + namespace Item + { + class SplitItem; + class SwapInventoryItem; + class AutoEquipItemSlot; + class SwapItem; + class AutoEquipItem; + class DestroyItem; + class ReadItem; + class SellItem; + class BuybackItem; + class BuyItemInSlot; + class BuyItem; + class ListInventory; + class AutoStoreBagItem; + class WrapItem; + class SocketGems; + class CancelTempEnchantment; + class ItemRefundInfo; + class ItemRefund; + } } enum AccountDataType @@ -389,7 +419,7 @@ public: } void SendSetPhaseShift(uint32 phaseShift); - void SendQueryTimeResponse(); + void SendTimeQueryResponse(); void SendAuthResponse(uint8 code, bool shortForm, uint32 queuePos = 0); void SendClientCacheVersion(uint32 version); @@ -680,9 +710,9 @@ public: // opcodes handlers void HandleGameObjectUseOpcode(WorldPacket& recPacket); void HandleGameobjectReportUse(WorldPacket& recvPacket); - void HandleNameQueryOpcode(WorldPacket& recvPacket); + void HandleNameQueryOpcode(WorldPackets::Query::NameQuery& packet); - void HandleQueryTimeOpcode(WorldPacket& recvPacket); + void HandleTimeQueryOpcode(WorldPackets::Query::TimeQuery& packet); void HandleCreatureQueryOpcode(WorldPacket& recvPacket); @@ -822,21 +852,21 @@ public: // opcodes handlers void HandleQueryNextMailTime(WorldPacket& recvData); void HandleCancelChanneling(WorldPacket& recvData); - void HandleSplitItemOpcode(WorldPacket& recvPacket); - void HandleSwapInvItemOpcode(WorldPacket& recvPacket); - void HandleDestroyItemOpcode(WorldPacket& recvPacket); - void HandleAutoEquipItemOpcode(WorldPacket& recvPacket); + void HandleSplitItemOpcode(WorldPackets::Item::SplitItem& packet); + void HandleSwapInvItemOpcode(WorldPackets::Item::SwapInventoryItem& packet); + void HandleDestroyItemOpcode(WorldPackets::Item::DestroyItem& packet); + void HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& packet); void HandleItemQuerySingleOpcode(WorldPacket& recvPacket); - void HandleSellItemOpcode(WorldPacket& recvPacket); - void HandleBuyItemInSlotOpcode(WorldPacket& recvPacket); - void HandleBuyItemOpcode(WorldPacket& recvPacket); - void HandleListInventoryOpcode(WorldPacket& recvPacket); - void HandleAutoStoreBagItemOpcode(WorldPacket& recvPacket); - void HandleReadItem(WorldPacket& recvPacket); - void HandleAutoEquipItemSlotOpcode(WorldPacket& recvPacket); - void HandleSwapItem(WorldPacket& recvPacket); - void HandleBuybackItem(WorldPacket& recvPacket); - void HandleWrapItemOpcode(WorldPacket& recvPacket); + void HandleSellItemOpcode(WorldPackets::Item::SellItem& packet); + void HandleBuyItemInSlotOpcode(WorldPackets::Item::BuyItemInSlot& packet); + void HandleBuyItemOpcode(WorldPackets::Item::BuyItem& packet); + void HandleListInventoryOpcode(WorldPackets::Item::ListInventory& packet); + void HandleAutoStoreBagItemOpcode(WorldPackets::Item::AutoStoreBagItem& packet); + void HandleReadItem(WorldPackets::Item::ReadItem& packet); + void HandleAutoEquipItemSlotOpcode(WorldPackets::Item::AutoEquipItemSlot& packet); + void HandleSwapItem(WorldPackets::Item::SwapItem& packet); + void HandleBuybackItem(WorldPackets::Item::BuybackItem& packet); + void HandleWrapItemOpcode(WorldPackets::Item::WrapItem& packet); void HandleAttackSwingOpcode(WorldPacket& recvPacket); void HandleAttackStopOpcode(WorldPacket& recvPacket); @@ -882,7 +912,7 @@ public: // opcodes handlers void HandleReclaimCorpseOpcode(WorldPacket& recvPacket); void HandleCorpseQueryOpcode(WorldPacket& recvPacket); - void HandleCorpseMapPositionQuery(WorldPacket& recvPacket); + void HandleCorpseMapPositionQuery(WorldPackets::Query::CorpseMapPositionQuery& packet); void HandleResurrectResponseOpcode(WorldPacket& recvPacket); void HandleSummonResponseOpcode(WorldPacket& recvData); @@ -953,7 +983,7 @@ public: // opcodes handlers void HandleWardenDataOpcode(WorldPacket& recvData); void HandleWorldTeleportOpcode(WorldPacket& recvData); - void HandleMinimapPingOpcode(WorldPacket& recvData); + void HandleMinimapPingOpcode(WorldPackets::Misc::MinimapPingClient& packet); void HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& packet); void HandleFarSightOpcode(WorldPacket& recvData); void HandleSetDungeonDifficultyOpcode(WorldPacket& recvData); @@ -1026,12 +1056,12 @@ public: // opcodes handlers void HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo& packet); // Socket gem - void HandleSocketOpcode(WorldPacket& recvData); + void HandleSocketOpcode(WorldPackets::Item::SocketGems& packet); - void HandleCancelTempEnchantmentOpcode(WorldPacket& recvData); + void HandleCancelTempEnchantmentOpcode(WorldPackets::Item::CancelTempEnchantment& packet); - void HandleItemRefundInfoRequest(WorldPacket& recvData); - void HandleItemRefund(WorldPacket& recvData); + void HandleItemRefundInfoRequest(WorldPackets::Item::ItemRefundInfo& packet); + void HandleItemRefund(WorldPackets::Item::ItemRefund& packet); void HandleChannelVoiceOnOpcode(WorldPacket& recvData); void HandleVoiceSessionEnableOpcode(WorldPacket& recvData); diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 4ba8f62c6..e5360809f 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -5143,6 +5143,14 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(21); // -1 }); + ApplySpellFix({ + 28032, // Zap Crystal + 28056, // Zap Crystal Corpse + }, [](SpellInfo* spellInfo) + { + spellInfo->AttributesEx3 |= SPELL_ATTR3_ALWAYS_HIT; + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 3ef1bfdb1..025d23e39 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -114,7 +114,6 @@ public: virtual void ResetEventSeasonalQuests(uint16 event_id) = 0; [[nodiscard]] virtual std::string const& GetRealmName() const = 0; virtual void SetRealmName(std::string name) = 0; - virtual void RemoveOldCorpses() = 0; virtual SQLQueryHolderCallback& AddQueryHolderCallback(SQLQueryHolderCallback&& callback) = 0; }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index b98e4d32b..9cb8cef47 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -889,8 +889,6 @@ void World::SetInitialWorldSettings() _timers[WUPDATE_UPTIME].SetInterval(getIntConfig(CONFIG_UPTIME_UPDATE)*MINUTE * IN_MILLISECONDS); //Update "uptime" table based on configuration entry in minutes. - _timers[WUPDATE_CORPSES].SetInterval(20 * MINUTE * IN_MILLISECONDS); - //erase corpses every 20 minutes _timers[WUPDATE_CLEANDB].SetInterval(getIntConfig(CONFIG_LOGDB_CLEARINTERVAL)*MINUTE * IN_MILLISECONDS); // clean logs table every 14 days by default _timers[WUPDATE_AUTOBROADCAST].SetInterval(getIntConfig(CONFIG_AUTOBROADCAST_INTERVAL)); @@ -1281,18 +1279,6 @@ void World::Update(uint32 diff) LoginDatabase.Execute(stmt); } - ///- Erase corpses once every 20 minutes - if (_timers[WUPDATE_CORPSES].Passed()) - { - METRIC_TIMER("world_update_time", METRIC_TAG("type", "Remove old corpses")); - _timers[WUPDATE_CORPSES].Reset(); - - sMapMgr->DoForAllMaps([](Map* map) - { - map->RemoveOldCorpses(); - }); - } - ///- Process Game events when necessary if (_timers[WUPDATE_EVENTS].Passed()) { @@ -1835,11 +1821,6 @@ SQLQueryHolderCallback& World::AddQueryHolderCallback(SQLQueryHolderCallback&& c return _queryHolderProcessor.AddCallback(std::move(callback)); } -void World::RemoveOldCorpses() -{ - _timers[WUPDATE_CORPSES].SetCurrent(_timers[WUPDATE_CORPSES].GetInterval()); -} - bool World::IsPvPRealm() const { return getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 228a97fe1..94351f825 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -60,7 +60,6 @@ enum WorldTimers { WUPDATE_WEATHERS, WUPDATE_UPTIME, - WUPDATE_CORPSES, WUPDATE_EVENTS, WUPDATE_CLEANDB, WUPDATE_AUTOBROADCAST, @@ -244,8 +243,6 @@ public: [[nodiscard]] std::string const& GetRealmName() const override { return _realmName; } // pussywizard void SetRealmName(std::string name) override { _realmName = name; } // pussywizard - void RemoveOldCorpses() override; - protected: void _UpdateGameTime(); // callback for UpdateRealmCharacters diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 7a0b1db40..c38b36ff0 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -492,6 +492,8 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_LEAVE_GROUP_ON_LOGOUT, "LeaveGroupOnLogout.Enabled", false); + SetConfigValue(CONFIG_RANDOM_ROLL_MAXIMUM, "Group.RandomRollMaximum", 1000000); + SetConfigValue(CONFIG_QUEST_POI_ENABLED, "QuestPOI.Enabled", true); SetConfigValue(CONFIG_CHANGE_FACTION_MAX_MONEY, "ChangeFaction.MaxMoney", 0); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 3776fcac9..475bed337 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -129,6 +129,7 @@ enum ServerConfigs CONFIG_ALLOW_JOIN_BG_AND_LFG, CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS, CONFIG_LEAVE_GROUP_ON_LOGOUT, + CONFIG_RANDOM_ROLL_MAXIMUM, CONFIG_QUEST_POI_ENABLED, CONFIG_VMAP_BLIZZLIKE_PVP_LOS, CONFIG_VMAP_BLIZZLIKE_LOS_OPEN_WORLD, diff --git a/src/server/game/World/WorldState.cpp b/src/server/game/World/WorldState.cpp index 0ca7ea0c0..106e8fd99 100644 --- a/src/server/game/World/WorldState.cpp +++ b/src/server/game/World/WorldState.cpp @@ -1072,8 +1072,8 @@ std::string WorldState::GetScourgeInvasionPrintout() { TimePoint tp = m_siData.m_timers[timerId]; std::string timerStr; - if (tp.time_since_epoch().count() == 0) - timerStr = "0 (not set)"; + if (tp == TimePoint()) + timerStr = "Not set"; else if (tp <= now) timerStr = "Elapsed"; else @@ -1312,6 +1312,8 @@ void WorldState::StartScourgeInvasion(bool sendMail) Acore::Containers::RandomShuffle(randomIds); for (uint32 id : randomIds) OnEnable(m_siData.m_activeInvasions[id]); + + sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_BOSSES); } } @@ -1418,10 +1420,15 @@ void ScourgeInvasionData::Reset() std::string ScourgeInvasionData::GetData() { std::string output = std::to_string(m_state) + " "; - for (auto& timer : m_timers) - output += std::to_string(timer.time_since_epoch().count()) + " "; + for (TimePoint& timer : m_timers) + { + if (timer == TimePoint()) + output += "0 "; + else + output += std::to_string(std::chrono::duration_cast(timer.time_since_epoch()).count()) + " "; + } output += std::to_string(m_battlesWon) + " " + std::to_string(m_lastAttackZone) + " "; - for (auto& remaining : m_remaining) + for (uint32& remaining : m_remaining) output += std::to_string(remaining) + " "; return output; } diff --git a/src/server/game/World/WorldState.h b/src/server/game/World/WorldState.h index be5379246..b850010ac 100644 --- a/src/server/game/World/WorldState.h +++ b/src/server/game/World/WorldState.h @@ -65,6 +65,7 @@ enum WorldStateGameEvents { // Scourge Invasion GAME_EVENT_SCOURGE_INVASION = 17, + GAME_EVENT_SCOURGE_INVASION_BOSSES = 120, GAME_EVENT_SCOURGE_INVASION_WINTERSPRING = 121, GAME_EVENT_SCOURGE_INVASION_TANARIS = 122, GAME_EVENT_SCOURGE_INVASION_AZSHARA = 123, diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 3198936fa..40bb89b93 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -73,7 +73,7 @@ public: { "hostile", HandleDebugHostileRefListCommand, SEC_ADMINISTRATOR, Console::No }, { "anim", HandleDebugAnimCommand, SEC_ADMINISTRATOR, Console::No }, { "arena", HandleDebugArenaCommand, SEC_ADMINISTRATOR, Console::No }, - { "bg", HandleDebugBattlegroundCommand, SEC_ADMINISTRATOR, Console::No }, + { "bg", HandleDebugBattlegroundCommand, SEC_ADMINISTRATOR, Console::Yes}, { "cooldown", HandleDebugCooldownCommand, SEC_ADMINISTRATOR, Console::No }, { "getitemstate", HandleDebugGetItemStateCommand, SEC_ADMINISTRATOR, Console::No }, { "lootrecipient", HandleDebugGetLootRecipientCommand, SEC_ADMINISTRATOR, Console::No }, @@ -92,14 +92,15 @@ public: { "update", HandleDebugUpdateCommand, SEC_ADMINISTRATOR, Console::No }, { "itemexpire", HandleDebugItemExpireCommand, SEC_ADMINISTRATOR, Console::No }, { "areatriggers", HandleDebugAreaTriggersCommand, SEC_ADMINISTRATOR, Console::No }, - { "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::No }, + { "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::Yes}, { "los", HandleDebugLoSCommand, SEC_ADMINISTRATOR, Console::No }, { "moveflags", HandleDebugMoveflagsCommand, SEC_ADMINISTRATOR, Console::No }, { "unitstate", HandleDebugUnitStateCommand, SEC_ADMINISTRATOR, Console::No }, { "objectcount", HandleDebugObjectCountCommand, SEC_ADMINISTRATOR, Console::Yes}, { "dummy", HandleDebugDummyCommand, SEC_ADMINISTRATOR, Console::No }, { "mapdata", HandleDebugMapDataCommand, SEC_ADMINISTRATOR, Console::No }, - { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No } + { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No }, + { "visibilitydata", HandleDebugVisibilityDataCommand, SEC_ADMINISTRATOR, Console::No } }; static ChatCommandTable commandTable = { @@ -1351,7 +1352,7 @@ public: map->GetId(), map->GetMapName(), map->GetInstanceId(), uint64(map->GetObjectsStore().Size()), uint64(map->GetObjectsStore().Size()), - uint64(map->GetUpdateObjectsCount())); + uint64(map->GetUpdatableObjectsCount())); CreatureCountWorker worker; TypeContainerVisitor visitor(worker); @@ -1403,6 +1404,36 @@ public: return true; } + + static bool HandleDebugVisibilityDataCommand(ChatHandler* handler) + { + Player* player = handler->GetPlayer(); + if (!player) + return false; + + std::array objectByTypeCount = {}; + + ObjectVisibilityContainer const& objectVisibilityContainer = player->GetObjectVisibilityContainer(); + for (auto const& kvPair : *objectVisibilityContainer.GetVisibleWorldObjectsMap()) + { + WorldObject const* obj = kvPair.second; + ++objectByTypeCount[obj->GetTypeId()]; + } + + uint32 zoneWideVisibleObjectsInZone = 0; + if (ZoneWideVisibleWorldObjectsSet const* farVisibleSet = player->GetMap()->GetZoneWideVisibleWorldObjectsForZone(player->GetZoneId())) + zoneWideVisibleObjectsInZone = farVisibleSet->size(); + + handler->PSendSysMessage("Visibility Range: {}", player->GetVisibilityRange()); + handler->PSendSysMessage("Visible Creatures: {}", objectByTypeCount[TYPEID_UNIT]); + handler->PSendSysMessage("Visible Players: {}", objectByTypeCount[TYPEID_PLAYER]); + handler->PSendSysMessage("Visible GameObjects: {}", objectByTypeCount[TYPEID_GAMEOBJECT]); + handler->PSendSysMessage("Visible DynamicObjects: {}", objectByTypeCount[TYPEID_DYNAMICOBJECT]); + handler->PSendSysMessage("Visible Corpses: {}", objectByTypeCount[TYPEID_CORPSE]); + handler->PSendSysMessage("Players we are visible to: {}", objectVisibilityContainer.GetVisiblePlayersMap().size()); + handler->PSendSysMessage("Zone wide visible objects in zone: {}", zoneWideVisibleObjectsInZone); + return true; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_event.cpp b/src/server/scripts/Commands/cs_event.cpp index 742bc6789..0f5264e8c 100644 --- a/src/server/scripts/Commands/cs_event.cpp +++ b/src/server/scripts/Commands/cs_event.cpp @@ -71,8 +71,6 @@ public: if (counter == 0) handler->SendSysMessage(LANG_NOEVENTFOUND); - handler->SetSentErrorMessage(true); - return true; } diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index 72bb9a2a3..737d35561 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -21,6 +21,7 @@ #include "GameTime.h" #include "GitRevision.h" #include "Log.h" +#include "MapMgr.h" #include "ModuleMgr.h" #include "MotdMgr.h" #include "MySQLThreading.h" @@ -101,7 +102,10 @@ public: // Triggering corpses expire check in world static bool HandleServerCorpsesCommand(ChatHandler* /*handler*/) { - sWorld->RemoveOldCorpses(); + sMapMgr->DoForAllMaps([](Map* map) + { + map->RemoveOldCorpses(); + }); return true; } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp new file mode 100644 index 000000000..bed336447 --- /dev/null +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp @@ -0,0 +1,380 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "karazhan.h" +#include "AreaTriggerScript.h" +#include "CreatureScript.h" +#include "GameObjectAI.h" +#include "GameObjectScript.h" +#include "Player.h" +#include "ScriptedCreature.h" +#include "SpellInfo.h" +#include "SpellScript.h" +#include "SpellScriptLoader.h" +#include "UnitAI.h" + +enum Text +{ + SAY_APPROACH = 0, + SAY_AGGRO = 1, + SAY_SUMMON = 2 +}; + +enum Spells +{ + SPELL_BLOOD_MIRROR0 = 50844, + SPELL_BLOOD_MIRROR1 = 50845, + SPELL_BLOOD_MIRROR_TARGET_PICKER = 50883, + SPELL_BLOOD_MIRROR_TRANSITION_VISUAL = 50910, + SPELL_BLOOD_MIRROR_DAMAGE = 50846, + + SPELL_BLOOD_TAP = 51135, + + SPELL_BLOOD_SWOOP = 50922, + SPELL_DASH_GASH_PRE_SPELL = 50923, + SPELL_DASH_GASH_RETURN_TO_TANK = 50924, + // SPELL_DASH_GASH_RETURN_TO_TANK_PRE_SPELL = 50925, + // SPELL_DASH_GASH_RETURN_TO_TANK_PRE_SPELL_ROOT = 50932, + + SPELL_DESPAWN_SANGUINE_SPIRIT_VISUAL = 51214, + SPELL_DESPAWN_SANGUINE_SPIRITS = 51212, + SPELL_SANGUINE_SPIRIT_AURA = 50993, + SPELL_SANGUINE_SPIRIT_PRE_AURA = 51282, + SPELL_SANGUINE_SPIRIT_PRE_AURA2 = 51283, + SPELL_SUMMON_SANGUINE_SPIRIT0 = 50996, + SPELL_SUMMON_SANGUINE_SPIRIT1 = 50998, + // SPELL_SUMMON_SANGUINE_SPIRIT2 = 51204, + SPELL_SUMMON_SANGUINE_SPIRIT_MISSILE_BURST = 51208, + SPELL_SUMMON_SANGUINE_SPIRIT_SHORT_MISSILE_BURST = 51280, + SPELL_SUMMON_SANGUINE_SPIRIT_ON_KILL = 51205, + SPELL_EXSANGUINATE = 51013, + SPELL_DUMMY_NUKE_RANGE_SELF = 51106, +}; + +enum Events +{ + EVENT_SAY = 1, + EVENT_FLAG = 2 +}; + +struct boss_tenris_mirkblood : public BossAI +{ + boss_tenris_mirkblood(Creature* creature) : BossAI(creature, DATA_MIRKBLOOD) + { + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } + + void Reset() override + { + _Reset(); + + me->SetImmuneToPC(true); + + ScheduleHealthCheckEvent(50, [&] { + Talk(SAY_SUMMON); + DoCast(SPELL_SUMMON_SANGUINE_SPIRIT_MISSILE_BURST); + }); + + ScheduleHealthCheckEvent(45, [&] { + ScheduleTimedEvent(10s, 15s, [&] { + DoCast(SPELL_BLOOD_TAP); + }, 15s, 40s); + }); + } + + void JustEngagedWith(Unit* /*who*/) override + { + DoZoneInCombat(); + + ScheduleTimedEvent(1s, 5s, [&] { + // Blood Mirror + DoCast(SPELL_BLOOD_MIRROR_TARGET_PICKER); + }, 20s, 50s); + ScheduleTimedEvent(30s, [&] { + // Blood Swoop + DoCast(SPELL_DASH_GASH_PRE_SPELL); + }, 15s, 40s); + ScheduleTimedEvent(6s, 15s, [&] { + // Sanguine Spirit + DoCast(SPELL_SUMMON_SANGUINE_SPIRIT_SHORT_MISSILE_BURST); + }, 6s, 15s); + } + + void KilledUnit(Unit* victim) override + { + if (!victim) + return; + + DoCast(victim, SPELL_SUMMON_SANGUINE_SPIRIT_ON_KILL); + } + + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageType, SpellSchoolMask damageSchoolMask) override + { + BossAI::DamageTaken(attacker, damage, damageType, damageSchoolMask); + + if (!me->HasAura(SPELL_BLOOD_MIRROR0)) + return; + + if (!_mirrorTarget) + return; + + int32 damageTaken = damage; + + me->CastCustomSpell(_mirrorTarget, SPELL_BLOOD_MIRROR_DAMAGE, &damageTaken, &damageTaken, &damageTaken, true, nullptr, nullptr, me->GetGUID()); + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_BLOOD_MIRROR0 && caster != me) + _mirrorTarget = caster; + } + + void EnterEvadeMode(EvadeReason why) override + { + _EnterEvadeMode(why); + me->SetImmuneToPC(false); + } + +private: + Unit* _mirrorTarget = nullptr; +}; + +struct npc_sanguine_spirit : public ScriptedAI +{ + npc_sanguine_spirit(Creature* creature) : ScriptedAI(creature) {} + + void Reset() override + { + scheduler.CancelAll(); + me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ALL, true); + + me->SetReactState(REACT_PASSIVE); + + DoCastSelf(SPELL_SANGUINE_SPIRIT_PRE_AURA); + + scheduler.Schedule(5s, [this](TaskContext /*context*/) + { + DoCastSelf(SPELL_SANGUINE_SPIRIT_PRE_AURA2); + }).Schedule(3s, [this](TaskContext /*context*/) + { + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + DoCastSelf(SPELL_SANGUINE_SPIRIT_AURA); + }).Schedule(30s, [this](TaskContext /*context*/) + { + me->DespawnOrUnsummon(); + }); + } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + UpdateVictim(); + } +}; + +class spell_mirkblood_blood_mirror_target_picker : public SpellScript +{ + PrepareSpellScript(spell_mirkblood_blood_mirror_target_picker) + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_BLOOD_MIRROR0, SPELL_BLOOD_MIRROR1, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL }); + } + + void HandleHit() + { + Unit* caster = GetCaster(); + + if (!caster->ToCreature()) + return; + + Unit* target = caster->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false); + + if (!target) // Only Blood Mirror the tank if they're the only one around + target = caster->GetVictim(); + + if (!target) + return; + + caster->CastSpell(caster, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL, TRIGGERED_FULL_MASK); + caster->CastSpell(target, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL, TRIGGERED_FULL_MASK); + + caster->AddAura(SPELL_BLOOD_MIRROR1, caster); // Should be a cast, but channeled spell results in either Mirkblood or player being unactionable + caster->AddAura(SPELL_BLOOD_MIRROR1, target); // Adding aura manually causes visual to not appear properly, but better than breaking gameplay + + target->CastSpell(caster, SPELL_BLOOD_MIRROR0); // Clone player + } + + void Register() override + { + OnCast += SpellCastFn(spell_mirkblood_blood_mirror_target_picker::HandleHit); + } +}; + +class spell_mirkblood_dash_gash_return_to_tank_pre_spell : public SpellScript +{ + PrepareSpellScript(spell_mirkblood_dash_gash_return_to_tank_pre_spell) + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_DASH_GASH_RETURN_TO_TANK }); + } + + void HandleCast() + { + if (!GetCaster() || !GetCaster()->GetThreatMgr().GetCurrentVictim()) + return; + // Probably wrong, maybe don't charge if would charge the same target? + if (GetCaster()->GetDistance2d(GetCaster()->GetThreatMgr().GetCurrentVictim()) < 5.0f) + return; + + GetCaster()->CastSpell(GetCaster()->GetVictim(), SPELL_DASH_GASH_RETURN_TO_TANK); + } + + void Register() override + { + OnCast += SpellCastFn(spell_mirkblood_dash_gash_return_to_tank_pre_spell::HandleCast); + } +}; + +class spell_mirkblood_exsanguinate : public SpellScript +{ + PrepareSpellScript(spell_mirkblood_exsanguinate) + + void CalculateDamage() + { + if (!GetHitUnit()) + return; + + SetHitDamage(std::max((GetHitUnit()->GetHealth() * 0.66f), 2000.0f)); + } + + void Register() override + { + OnHit += SpellHitFn(spell_mirkblood_exsanguinate::CalculateDamage); + } +}; + +class at_karazhan_mirkblood_approach : public AreaTriggerScript +{ +public: + at_karazhan_mirkblood_approach() : AreaTriggerScript("at_karazhan_mirkblood_approach") {} + + 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)) + mirkblood->AI()->Talk(SAY_APPROACH, player); + + return false; + } +}; + +class at_karazhan_mirkblood_entrance : public AreaTriggerScript +{ +public: + at_karazhan_mirkblood_entrance() : AreaTriggerScript("at_karazhan_mirkblood_entrance") {} + + 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)) + mirkblood->SetImmuneToPC(false); + + return false; + } +}; + +class go_blood_drenched_door : public GameObjectScript +{ +public: + go_blood_drenched_door() : GameObjectScript("go_blood_drenched_door") {} + + struct go_blood_drenched_doorAI : public GameObjectAI + { + go_blood_drenched_doorAI(GameObject* go) : GameObjectAI(go) {} + + EventMap events; + Creature* mirkblood; + Player* opener; + + bool GossipHello(Player* player, bool /*reportUse*/) override + { + events.Reset(); + + if (InstanceScript* instance = player->GetInstanceScript()) + { + if (instance->GetBossState(DATA_MIRKBLOOD) != DONE) + { + opener = player; + mirkblood = instance->GetCreature(DATA_MIRKBLOOD); + + events.ScheduleEvent(EVENT_SAY, 1s); + events.ScheduleEvent(EVENT_FLAG, 5s); + } + } + + me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + + return true; + } + + void UpdateAI(uint32 diff) override + { + if (events.Empty()) + return; + + events.Update(diff); + switch (events.ExecuteEvent()) + { + case EVENT_SAY: + if (!mirkblood || !mirkblood->IsAlive()) + return; + mirkblood->AI()->Talk(SAY_AGGRO, opener); + break; + case EVENT_FLAG: + if (mirkblood) + mirkblood->SetImmuneToPC(false); + me->Delete(); + break; + } + } + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_blood_drenched_doorAI(go); + } +}; + +void AddSC_boss_tenris_mirkblood() +{ + RegisterKarazhanCreatureAI(boss_tenris_mirkblood); + RegisterKarazhanCreatureAI(npc_sanguine_spirit); + RegisterSpellScript(spell_mirkblood_blood_mirror_target_picker); + RegisterSpellScript(spell_mirkblood_dash_gash_return_to_tank_pre_spell); + RegisterSpellScript(spell_mirkblood_exsanguinate); + new at_karazhan_mirkblood_approach(); + new at_karazhan_mirkblood_entrance(); + new go_blood_drenched_door(); +} diff --git a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp index 2f4f27dae..a283e65cc 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp @@ -47,6 +47,7 @@ ObjectData const creatureData[] = { NPC_JULIANNE, DATA_JULIANNE }, { NPC_NIGHTBANE, DATA_NIGHTBANE }, { NPC_TERESTIAN_ILLHOOF, DATA_TERESTIAN }, + { NPC_TENRIS_MIRKBLOOD, DATA_MIRKBLOOD }, { 0, 0 } }; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h index 4e0d57f4b..890fdcf2d 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h @@ -73,7 +73,9 @@ enum KZDataTypes DATA_ROAR = 41, DATA_STRAWMAN = 42, DATA_TINHEAD = 43, - DATA_TITO = 44 + DATA_TITO = 44, + + DATA_MIRKBLOOD = 45 }; enum KZOperaEvents @@ -135,7 +137,9 @@ enum KZCreatures // Malchezaar Helpers NPC_INFERNAL_TARGET = 17644, - NPC_INFERNAL_RELAY = 17645 + NPC_INFERNAL_RELAY = 17645, + + NPC_TENRIS_MIRKBLOOD = 28194 }; diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp index a3325c58d..9c71aad73 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp @@ -1001,20 +1001,25 @@ class spell_chapter2_persuasive_strike : public SpellScript creature->AI()->Talk(SAY_PERSUADED3, 24s); creature->AI()->Talk(SAY_PERSUADED4, 32s); - creature->m_Events.AddEventAtOffset([creature, player] + ObjectGuid playerGuid = player->GetGUID(); + + creature->m_Events.AddEventAtOffset([creature, playerGuid] { - if (player) - sCreatureTextMgr->SendChat(creature, SAY_PERSUADED5, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, player); + if (Player* caster = ObjectAccessor::GetPlayer(*creature, playerGuid)) + sCreatureTextMgr->SendChat(creature, SAY_PERSUADED5, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, caster); }, 40s); - creature->m_Events.AddEventAtOffset([creature, player] + creature->m_Events.AddEventAtOffset([creature, playerGuid] { creature->AI()->Talk(SAY_PERSUADED6); - if (player) + + if (Player* caster = ObjectAccessor::GetPlayer(*creature, playerGuid)) { - Unit::Kill(player, creature); - player->GroupEventHappens(QUEST_HOW_TO_WIN_FRIENDS, creature); + Unit::Kill(caster, creature); + caster->GroupEventHappens(QUEST_HOW_TO_WIN_FRIENDS, creature); } + else + creature->KillSelf(); }, 48s); } else diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp index 2858a5cf6..ddd75f48a 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp @@ -1181,6 +1181,26 @@ class spell_kiljaeden_dragon_breath : public SpellScript } }; +// 45848 - Shield of the Blue +class spell_kiljaeden_shield_of_the_blue : public AuraScript +{ + PrepareAuraScript(spell_kiljaeden_shield_of_the_blue); + + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* target = GetTarget()) + { + target->RemoveAurasDueToSpell(SPELL_FIRE_BLOOM); + target->RemoveMovementImpairingAuras(false); + } + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_kiljaeden_shield_of_the_blue::HandleEffectApply, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + } +}; + void AddSC_boss_kiljaeden() { RegisterSunwellPlateauCreatureAI(npc_kiljaeden_controller); @@ -1196,4 +1216,5 @@ void AddSC_boss_kiljaeden() RegisterSpellScript(spell_kiljaeden_armageddon_periodic_aura); RegisterSpellScript(spell_kiljaeden_armageddon_missile); RegisterSpellScript(spell_kiljaeden_dragon_breath); + RegisterSpellScript(spell_kiljaeden_shield_of_the_blue); } diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index 67360281a..b92b35215 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -78,6 +78,7 @@ void AddSC_bosses_opera(); void AddSC_boss_netherspite(); void AddSC_karazhan(); void AddSC_boss_nightbane(); +void AddSC_boss_tenris_mirkblood(); void AddSC_boss_felblood_kaelthas(); // Magister's Terrace void AddSC_boss_selin_fireheart(); void AddSC_boss_vexallus(); @@ -229,6 +230,7 @@ void AddEasternKingdomsScripts() AddSC_boss_netherspite(); AddSC_karazhan(); AddSC_boss_nightbane(); + AddSC_boss_tenris_mirkblood(); AddSC_boss_felblood_kaelthas(); // Magister's Terrace AddSC_boss_selin_fireheart(); AddSC_boss_vexallus(); diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h index c124b4457..39f15f6ab 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h @@ -26,14 +26,17 @@ enum ANData { - DATA_KRIKTHIR_THE_GATEWATCHER_EVENT = 0, - DATA_HADRONOX_EVENT = 1, + DATA_KRIKTHIR = 0, + DATA_HADRONOX = 1, DATA_ANUBARAK_EVENT = 2, MAX_ENCOUNTERS = 3 }; enum ANIds { + NPC_WATCHER_NARJIL = 28729, + NPC_WATCHER_GASHRA = 28730, + NPC_WATCHER_SILTHIK = 28731, NPC_SKITTERING_SWARMER = 28735, NPC_SKITTERING_INFECTIOR = 28736, NPC_KRIKTHIR_THE_GATEWATCHER = 28684, diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index daa36c306..f3f9396ba 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -84,7 +84,7 @@ public: struct boss_hadronoxAI : public BossAI { - boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX_EVENT) + boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX) { } @@ -99,7 +99,7 @@ public: { if (param == ACTION_START_EVENT) { - instance->SetBossState(DATA_HADRONOX_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_HADRONOX, IN_PROGRESS); me->setActive(true); events.ScheduleEvent(EVENT_HADRONOX_MOVE1, 20s); events.ScheduleEvent(EVENT_HADRONOX_MOVE2, 40s); @@ -342,7 +342,7 @@ public: PreventDefaultAction(); Unit* owner = GetUnitOwner(); if (InstanceScript* instance = owner->GetInstanceScript()) - if (instance->GetBossState(DATA_HADRONOX_EVENT) != DONE) + if (!instance->IsBossDone(DATA_HADRONOX)) { if (!owner->HasAura(SPELL_WEB_FRONT_DOORS)) owner->CastSpell(owner, _spellEntry, true); diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp index c32f6490c..36fd31ff1 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp @@ -29,24 +29,8 @@ enum Spells SPELL_FRENZY = 28747 }; -enum Events -{ - EVENT_KRIK_START_WAVE = 1, - EVENT_KRIK_SUMMON = 2, - EVENT_KRIK_MIND_FLAY = 3, - EVENT_KRIK_CURSE = 4, - EVENT_KRIK_HEALTH_CHECK = 5, - EVENT_KRIK_ENTER_COMBAT = 6, - EVENT_KILL_TALK = 7, - EVENT_CALL_ADDS = 8, - EVENT_KRIK_CHECK_EVADE = 9 -}; - enum Npcs { - NPC_WATCHER_NARJIL = 28729, - NPC_WATCHER_GASHRA = 28730, - NPC_WATCHER_SILTHIK = 28731, NPC_WARRIOR = 28732, NPC_SKIRMISHER = 28734, NPC_SHADOWCASTER = 28733 @@ -62,6 +46,12 @@ enum Yells SAY_SEND_GROUP = 5 }; +enum MiscActions +{ + ACTION_MINION_ENGAGED = 1, + GROUP_SWARM = 1 +}; + class boss_krik_thir : public CreatureScript { public: @@ -69,18 +59,21 @@ public: struct boss_krik_thirAI : public BossAI { - boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR_THE_GATEWATCHER_EVENT) + boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR) { _initTalk = false; - } + _canTalk = true; + _minionInCombat = false; - EventMap events2; - bool _initTalk; + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } void Reset() override { BossAI::Reset(); - events2.Reset(); me->SummonCreature(NPC_WATCHER_NARJIL, 511.8f, 666.493f, 776.278f, 0.977f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); me->SummonCreature(NPC_SHADOWCASTER, 511.63f, 672.44f, 775.71f, 0.90f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); @@ -91,6 +84,22 @@ public: me->SummonCreature(NPC_WATCHER_SILTHIK, 543.826f, 665.123f, 776.245f, 1.55f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); me->SummonCreature(NPC_SKIRMISHER, 547.5f, 669.96f, 776.1f, 2.3f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); me->SummonCreature(NPC_SHADOWCASTER, 548.64f, 664.27f, 776.74f, 1.77f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + + ScheduleHealthCheckEvent(25, [&] { + DoCastSelf(SPELL_FRENZY, true); + + scheduler.CancelGroup(GROUP_SWARM); + + scheduler.Schedule(7s, 17s, GROUP_SWARM, [&](TaskContext context) + { + Talk(SAY_SWARM); + DoCastAOE(SPELL_SWARM); + context.Repeat(20s); + }); + }); + + _canTalk = true; + _minionInCombat = false; } void MoveInLineOfSight(Unit* who) override @@ -103,21 +112,25 @@ public: _initTalk = true; Talk(SAY_PREFIGHT); } + } - if (events.Empty() && who->GetPositionZ() < 785.0f) + void DoAction(int32 actionId) override + { + if (actionId == ACTION_MINION_ENGAGED && !_minionInCombat) { - events2.ScheduleEvent(EVENT_KRIK_START_WAVE, 10s); - events2.ScheduleEvent(EVENT_KRIK_START_WAVE, 40s); - events2.ScheduleEvent(EVENT_KRIK_START_WAVE, 70s); - events2.ScheduleEvent(EVENT_KRIK_ENTER_COMBAT, 100s); - events2.ScheduleEvent(EVENT_KRIK_CHECK_EVADE, 5s); + _minionInCombat = true; - events.ScheduleEvent(EVENT_KRIK_HEALTH_CHECK, 1s); - events.ScheduleEvent(EVENT_KRIK_MIND_FLAY, 13s); - events.ScheduleEvent(EVENT_KRIK_SUMMON, 17s); - events.ScheduleEvent(EVENT_KRIK_CURSE, 8s); - events.ScheduleEvent(EVENT_CALL_ADDS, 1s); - me->setActive(true); + for (Seconds const& timer : { 10s, 40s, 70s }) + { + me->m_Events.AddEventAtOffset([this] { + me->CastCustomSpell(SPELL_SUBBOSS_AGGRO_TRIGGER, SPELLVALUE_MAX_TARGETS, 1, me, true); + Talk(SAY_SEND_GROUP); + }, timer); + } + + me->m_Events.AddEventAtOffset([this] { + me->SetInCombatWithZone(); + }, 100s); } } @@ -132,7 +145,28 @@ public: { BossAI::JustEngagedWith(who); Talk(SAY_AGGRO); - events2.Reset(); + + me->m_Events.KillAllEvents(false); + + scheduler.Schedule(8s, 14s, [&](TaskContext context) + { + DoCastVictim(SPELL_MIND_FLAY); + if (!IsInFrenzy()) + context.Repeat(8s, 14s); + else + context.Repeat(5s, 9s); + }).Schedule(10s, 13s, GROUP_SWARM, [&](TaskContext context) + { + Talk(SAY_SWARM); + DoCastAOE(SPELL_SWARM); + context.Repeat(26s, 30s); + }); + + ScheduleTimedEvent(27s, 35s, [&] { + DoCastRandomTarget(SPELL_CURSE_OF_FATIGUE); + }, 27s, 35s); + + summons.DoZoneInCombat(); } void JustDied(Unit* killer) override @@ -141,12 +175,15 @@ public: Talk(SAY_DEATH); } - void KilledUnit(Unit* ) override + void KilledUnit(Unit* /*victim*/) override { - if (events.GetNextEventTime(EVENT_KILL_TALK) == 0) + if (_canTalk) { + _canTalk = false; Talk(SAY_SLAY); - events.ScheduleEvent(EVENT_KILL_TALK, 6s); + me->m_Events.AddEventAtOffset([&] { + _canTalk = true; + }, 6s); } } @@ -161,66 +198,12 @@ public: summons.Despawn(summon); } - void UpdateAI(uint32 diff) override - { - events2.Update(diff); - switch (events2.ExecuteEvent()) - { - case EVENT_KRIK_START_WAVE: - me->CastCustomSpell(SPELL_SUBBOSS_AGGRO_TRIGGER, SPELLVALUE_MAX_TARGETS, 1, me, true); - Talk(SAY_SEND_GROUP); - break; - case EVENT_KRIK_ENTER_COMBAT: - me->SetInCombatWithZone(); - break; - case EVENT_KRIK_CHECK_EVADE: - if (!SelectTargetFromPlayerList(100.0f)) - { - EnterEvadeMode(); - return; - } - events2.ScheduleEvent(EVENT_KRIK_CHECK_EVADE, 5s); - break; - } + private: + bool _initTalk; + bool _canTalk; + bool _minionInCombat; - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_KRIK_HEALTH_CHECK: - if (HealthBelowPct(10)) - { - me->CastSpell(me, SPELL_FRENZY, true); - break; - } - events.ScheduleEvent(EVENT_KRIK_HEALTH_CHECK, 1s); - break; - case EVENT_KRIK_SUMMON: - Talk(SAY_SWARM); - me->CastSpell(me, SPELL_SWARM, false); - events.ScheduleEvent(EVENT_KRIK_SUMMON, 20s); - break; - case EVENT_KRIK_MIND_FLAY: - me->CastSpell(me->GetVictim(), SPELL_MIND_FLAY, false); - events.ScheduleEvent(EVENT_KRIK_MIND_FLAY, 15s); - break; - case EVENT_KRIK_CURSE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - me->CastSpell(target, SPELL_CURSE_OF_FATIGUE, true); - events.ScheduleEvent(EVENT_KRIK_CURSE, 10s); - break; - case EVENT_CALL_ADDS: - summons.DoZoneInCombat(); - break; - } - - DoMeleeAttackIfReady(); - } + [[nodiscard]] bool IsInFrenzy() const { return me->HasAura(SPELL_FRENZY); } }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp index 9de3f63be..3ee2f91f9 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp @@ -25,7 +25,7 @@ DoorData const doorData[] = { - { GO_KRIKTHIR_DOORS, DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, DOOR_TYPE_PASSAGE }, + { GO_KRIKTHIR_DOORS, DATA_KRIKTHIR, DOOR_TYPE_PASSAGE }, { GO_ANUBARAK_DOORS1, DATA_ANUBARAK_EVENT, DOOR_TYPE_ROOM }, { GO_ANUBARAK_DOORS2, DATA_ANUBARAK_EVENT, DOOR_TYPE_ROOM }, { GO_ANUBARAK_DOORS3, DATA_ANUBARAK_EVENT, DOOR_TYPE_ROOM }, @@ -34,15 +34,25 @@ DoorData const doorData[] = ObjectData const creatureData[] = { - { NPC_KRIKTHIR_THE_GATEWATCHER, DATA_KRIKTHIR_THE_GATEWATCHER_EVENT }, - { NPC_HADRONOX, DATA_HADRONOX_EVENT }, - { 0, 0 } + { NPC_KRIKTHIR_THE_GATEWATCHER, DATA_KRIKTHIR }, + { NPC_HADRONOX, DATA_HADRONOX }, + { 0, 0 } +}; + +ObjectData const summonData[] = +{ + { NPC_SKITTERING_SWARMER, DATA_KRIKTHIR }, + { NPC_SKITTERING_INFECTIOR, DATA_KRIKTHIR }, + { NPC_ANUB_AR_CHAMPION, DATA_HADRONOX }, + { NPC_ANUB_AR_NECROMANCER, DATA_HADRONOX }, + { NPC_ANUB_AR_CRYPTFIEND, DATA_HADRONOX }, + { 0, 0 } }; BossBoundaryData const boundaries = { - { DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) }, - { DATA_HADRONOX_EVENT, new ZRangeBoundary(666.0f, 776.0f) }, + { DATA_KRIKTHIR, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) }, + { DATA_HADRONOX, new ZRangeBoundary(666.0f, 776.0f) }, { DATA_ANUBARAK_EVENT, new CircleBoundary(Position(550.6178f, 253.5917f), 26.0f) } }; @@ -60,52 +70,14 @@ public: LoadBossBoundaries(boundaries); LoadDoorData(doorData); LoadObjectData(creatureData, nullptr); + LoadSummonData(summonData); }; - void OnCreatureCreate(Creature* creature) override + void OnCreatureEvade(Creature* creature) override { - switch (creature->GetEntry()) - { - case NPC_SKITTERING_SWARMER: - case NPC_SKITTERING_INFECTIOR: - if (Creature* krikthir = GetCreature((DATA_KRIKTHIR_THE_GATEWATCHER_EVENT))) - krikthir->AI()->JustSummoned(creature); - break; - case NPC_ANUB_AR_CHAMPION: - case NPC_ANUB_AR_NECROMANCER: - case NPC_ANUB_AR_CRYPTFIEND: - if (Creature* hadronox = GetCreature(DATA_HADRONOX_EVENT)) - hadronox->AI()->JustSummoned(creature); - break; - } - - InstanceScript::OnCreatureCreate(creature); - } - - void OnGameObjectCreate(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_KRIKTHIR_DOORS: - case GO_ANUBARAK_DOORS1: - case GO_ANUBARAK_DOORS2: - case GO_ANUBARAK_DOORS3: - AddDoor(go); - break; - } - } - - void OnGameObjectRemove(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_KRIKTHIR_DOORS: - case GO_ANUBARAK_DOORS1: - case GO_ANUBARAK_DOORS2: - case GO_ANUBARAK_DOORS3: - RemoveDoor(go); - break; - } + if (creature->EntryEquals(NPC_WATCHER_NARJIL, NPC_WATCHER_GASHRA, NPC_WATCHER_SILTHIK)) + if (Creature* krikthir = GetCreature(DATA_KRIKTHIR)) + krikthir->AI()->EnterEvadeMode(); } }; diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 17ac54f3b..01cdb73fb 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -75,8 +75,6 @@ enum DrakeGiverTexts class npc_oculus_drakegiver : public CreatureScript { public: - std::unordered_mapopenedMenu; - npc_oculus_drakegiver() : CreatureScript("npc_oculus_drakegiver") { } CreatureAI* GetAI(Creature* creature) const override @@ -193,20 +191,8 @@ public: SendGossipMenuFor(player, GOSSIP_TEXTID_VERDISA1, creature->GetGUID()); break; case NPC_BELGARISTRASZ: - if (HAS_ESSENCE(player)) - { - openedMenu[player->GetGUID()] = true; - } - - if (!openedMenu[player->GetGUID()]) - { - AddGossipItemFor(player, 9708, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - SendGossipMenuFor(player, GOSSIP_TEXTID_DRAKES, creature->GetGUID()); - } - else - { - OnGossipSelect(player, creature, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - } + AddGossipItemFor(player, 9708, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + SendGossipMenuFor(player, GOSSIP_TEXTID_DRAKES, creature->GetGUID()); break; case NPC_ETERNOS: AddGossipItemFor(player, 9574, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); @@ -277,7 +263,6 @@ public: switch (uiAction) { case GOSSIP_ACTION_INFO_DEF: - openedMenu[player->GetGUID()] = true; if (player->HasItemCount(ITEM_AMBER_ESSENCE)) { AddGossipItemFor(player, 9575, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 448f1aaca..88013c47c 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -2124,6 +2124,25 @@ public: } }; +// 45612 - Necropolis Beam +class spell_necropolis_beam: public SpellScript +{ + PrepareSpellScript(spell_necropolis_beam); + + void SetDest(SpellDestination& dest) + { + Unit* caster = GetCaster(); + float floorZ = caster->GetMapHeight(caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ()); + if (floorZ > INVALID_HEIGHT) + dest._position.m_positionZ = floorZ; + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_necropolis_beam::SetDest, EFFECT_0, TARGET_DEST_CASTER); + } +}; + void AddSC_borean_tundra() { RegisterSpellScript(spell_q11919_q11940_drake_hunt_aura); @@ -2148,4 +2167,5 @@ void AddSC_borean_tundra() RegisterSpellScript(spell_q11719_bloodspore_ruination_45997); new npc_bloodmage_laurith(); RegisterCreatureAI(npc_jenny); + RegisterSpellScript(spell_necropolis_beam); } diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index 2faac7047..9cf0cb7c0 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -2240,6 +2240,27 @@ class spell_dragonblight_corrosive_spit : public AuraScript } }; +// 48297 - Hand Over Reins +enum HandOverReins +{ + SPELL_ONSLAUGHT_RIDING_CROP = 48290 +}; + +class spell_handover_reins : public SpellScript +{ + PrepareSpellScript(spell_handover_reins); + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetCaster()->RemoveAura(SPELL_ONSLAUGHT_RIDING_CROP); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_handover_reins::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_dragonblight() { new npc_conversing_with_the_depths_trigger(); @@ -2267,4 +2288,5 @@ void AddSC_dragonblight() RegisterSpellScript(spell_q12096_q12092_bark); new npc_torturer_lecraft(); RegisterSpellScript(spell_dragonblight_corrosive_spit); + RegisterSpellScript(spell_handover_reins); } diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index 2b0efc76f..c6405cc18 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -22,6 +22,7 @@ #include "ScriptedEscortAI.h" #include "ScriptedGossip.h" #include "SpellInfo.h" +#include "SpellScript.h" class npc_attracted_reef_bull : public CreatureScript { @@ -393,6 +394,36 @@ public: } }; +enum HawkHunting +{ + SPELL_HAWK_HUNTING_ITEM = 44408 +}; + +// 44407 - Spell hawk Hunting +class spell_hawk_hunting : public SpellScript +{ + PrepareSpellScript(spell_hawk_hunting); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HAWK_HUNTING_ITEM }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (!GetCaster()) + return; + + GetCaster()->CastSpell(GetCaster(), SPELL_HAWK_HUNTING_ITEM, true); + GetHitUnit()->ToCreature()->DespawnOrUnsummon(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hawk_hunting::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_howling_fjord() { new npc_attracted_reef_bull(); @@ -400,4 +431,5 @@ void AddSC_howling_fjord() new npc_apothecary_hanes(); new npc_plaguehound_tracker(); new npc_razael_and_lyana(); + RegisterSpellScript(spell_hawk_hunting); } diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index ec2da77ea..35fd65e31 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -1167,6 +1167,31 @@ public: return new npc_vehicle_d16_propelled_deliveryAI(creature); } }; + +enum StormcrestEagle +{ + NPC_STORMCREST_EAGLE = 29854 +}; + +// 56393 - Feed Stormcrest Eagle +class spell_feed_stormcrest_eagle : public SpellScript +{ + PrepareSpellScript(spell_feed_stormcrest_eagle); + + SpellCastResult CheckCast() + { + if (GetCaster()->FindNearestCreature(NPC_STORMCREST_EAGLE, 15.0f, true)) + return SPELL_CAST_OK; + + return SPELL_FAILED_BAD_TARGETS; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_feed_stormcrest_eagle::CheckCast); + } +}; + void AddSC_storm_peaks() { new npc_frosthound(); @@ -1183,4 +1208,5 @@ void AddSC_storm_peaks() RegisterSpellScript(spell_close_rift_aura); new npc_vehicle_d16_propelled_delivery(); RegisterSpellScript(spell_q12823_remove_collapsing_cave_aura); + RegisterSpellScript(spell_feed_stormcrest_eagle); } diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp index c2eedba77..1a8a81540 100644 --- a/src/server/scripts/Pet/pet_dk.cpp +++ b/src/server/scripts/Pet/pet_dk.cpp @@ -245,6 +245,20 @@ struct npc_pet_dk_ghoul : public CombatAI { npc_pet_dk_ghoul(Creature* c) : CombatAI(c) { } + void IsSummonedBy(WorldObject* summoner) override + { + if (!summoner || !summoner->IsPlayer()) + return; + + Player* player = summoner->ToPlayer(); + + if (Unit* victim = player->GetVictim()) + { + me->Attack(victim, true); + me->GetMotionMaster()->MoveChase(victim); + } + } + void JustDied(Unit* /*who*/) override { if (me->IsGuardian() || me->IsSummon()) @@ -278,6 +292,28 @@ struct npc_pet_dk_army_of_the_dead : public CombatAI CombatAI::InitializeAI(); ((Minion*)me)->SetFollowAngle(rand_norm() * 2 * M_PI); } + + void IsSummonedBy(WorldObject* summoner) override + { + if (Unit* owner = summoner->ToUnit()) + { + Unit* victim = owner->GetVictim(); + + if (victim && me->IsValidAttackTarget(victim)) + { + AttackStart(victim); + } + else + { + // If there is no valid target, attack the nearest enemy within 30m + if (Unit* nearest = me->SelectNearbyTarget(nullptr, 30.0f)) + { + if (me->IsValidAttackTarget(nearest)) + AttackStart(nearest); + } + } + } + } }; struct npc_pet_dk_dancing_rune_weapon : public NullCreatureAI diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index d1d371710..b2d312a5a 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -345,7 +345,7 @@ class spell_dk_death_and_decay_aura : public AuraScript if (GetCaster() && GetTarget()) { int32 basePoints0 = aurEff->GetAmount(); - GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, false, 0, aurEff); + GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, true, 0, aurEff); } } diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index b6dae13a2..37d438e50 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -1527,12 +1527,27 @@ class spell_q12805_lifeblood_dummy : public SpellScript void HandleScript(SpellEffIndex /*effIndex*/) { Player* caster = GetCaster()->ToPlayer(); - if (Creature* target = GetHitCreature()) + Creature* target = GetHitCreature(); + + if (!target) + return; + + if (Group* group = caster->GetGroup()) { - caster->KilledMonsterCredit(NPC_SHARD_KILL_CREDIT); - target->CastSpell(target, uint32(GetEffectValue()), true); - target->DespawnOrUnsummon(2000); + ObjectGuid targetGUID = target->GetGUID(); + group->DoForAllMembers([targetGUID](Player* player) + { + if (Creature* shard = ObjectAccessor::GetCreature(*player, targetGUID)) + if (player->IsAtGroupRewardDistance(shard)) + player->KilledMonsterCredit(NPC_SHARD_KILL_CREDIT); + + }); } + else + caster->KilledMonsterCredit(NPC_SHARD_KILL_CREDIT); + + target->CastSpell(target, uint32(GetEffectValue()), true); + target->DespawnOrUnsummon(2000); } void Register() override diff --git a/src/server/scripts/World/scourge_invasion.cpp b/src/server/scripts/World/scourge_invasion.cpp index eac52f705..dc61a79ce 100644 --- a/src/server/scripts/World/scourge_invasion.cpp +++ b/src/server/scripts/World/scourge_invasion.cpp @@ -92,6 +92,7 @@ struct npc_herald_of_the_lich_king : public ScriptedAI Talk(HERALD_OF_THE_LICH_KING_SAY_ATTACK_END); ChangeZoneEventStatus(false); UpdateWeather(false); + me->DespawnOrUnsummon(); } } @@ -361,7 +362,6 @@ struct npc_necrotic_shard : public ScriptedAI { scheduler.Schedule(5s, [this](TaskContext context) // Spawn Cultists every 60 minutes. { - me->SetFullHealth(); DespawnShadowsOfDoom(); // Despawn all remaining Shadows before respawning the Cultists? SummonCultists(); context.Repeat(1h); @@ -499,7 +499,7 @@ struct npc_necrotic_shard : public ScriptedAI // Only Minions and the shard itself can deal damage. void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override { - if (attacker->GetFactionTemplateEntry() != me->GetFactionTemplateEntry()) + if (attacker && attacker->GetFactionTemplateEntry() != me->GetFactionTemplateEntry()) damage = 0; } @@ -519,7 +519,8 @@ struct npc_necrotic_shard : public ScriptedAI // Buff Players. DoCastSelf(SPELL_SOUL_REVIVAL, true); // Sending the Death Bolt. - DoCastAOE(SPELL_COMMUNIQUE_CAMP_TO_RELAY_DEATH, true); + if (Creature* relay = GetClosestCreatureWithEntry(me, NPC_NECROPOLIS_RELAY, 200.0f)) + me->CastSpell(relay, SPELL_COMMUNIQUE_CAMP_TO_RELAY_DEATH, true); DespawnCultists(); // Despawn remaining Cultists (should never happen). DespawnEventDoodads(); sWorldState->Save(SAVE_ID_SCOURGE_INVASION); @@ -802,69 +803,6 @@ struct npc_cultist_engineer : public ScriptedAI } }; -struct npc_shadow_of_doom : public CombatAI -{ - npc_shadow_of_doom(Creature* creature) : CombatAI(creature) { } - - void JustEngagedWith(Unit* /*who*/) override - { - scheduler.Schedule(2s, [&](TaskContext context) - { - DoCastVictim(SPELL_MINDFLAY); - context.Repeat(6500ms, 13s); - }).Schedule(2s, [&](TaskContext context) - { - DoCastVictim(SPELL_FEAR); - context.Repeat(14500ms, 14500ms); - }); - } - - void Reset() override - { - scheduler.CancelAll(); - me->SetImmuneToPC(false); - } - - void IsSummonedBy(WorldObject* summoner) override - { - if (!summoner) - return; - - if (Player* player = summoner->ToPlayer()) - { - me->SetImmuneToPC(true); - me->SetFacingToObject(player); - - Talk(SHADOW_OF_DOOM_SAY_AGGRO, player); - DoCastSelf(SPELL_SPAWN_SMOKE, true); - - scheduler.Schedule(5s, [this, player](TaskContext const& /*context*/) - { - me->SetImmuneToPC(false); - if (me->CanStartAttack(player)) - AttackStart(player); - }); - } - } - - void JustDied(Unit* /*pKiller*/) override - { - DoCastSelf(SPELL_ZAP_CRYSTAL_CORPSE, true); - } - - void SpellHit(Unit* /* caster */, SpellInfo const* spell) override - { - if (spell->Id == SPELL_SPIRIT_SPAWN_OUT) - me->DespawnOrUnsummon(3000); - } - - void UpdateAI(uint32 const diff) override - { - scheduler.Update(diff); - DoMeleeAttackIfReady(); - } -}; - struct npc_flameshocker : public CombatAI { npc_flameshocker(Creature* creature) : CombatAI(creature) { } @@ -1121,7 +1059,6 @@ void AddSC_scourge_invasion() RegisterCreatureAI(npc_minion_spawner); RegisterCreatureAI(npc_pallid_horror); RegisterCreatureAI(npc_cultist_engineer); - RegisterCreatureAI(npc_shadow_of_doom); RegisterCreatureAI(npc_flameshocker); RegisterSpellScript(spell_communique_trigger); RegisterSpellScript(spell_despawner_self); diff --git a/src/server/scripts/World/scourge_invasion.h b/src/server/scripts/World/scourge_invasion.h index fa927340a..0bc511ace 100644 --- a/src/server/scripts/World/scourge_invasion.h +++ b/src/server/scripts/World/scourge_invasion.h @@ -137,10 +137,7 @@ enum ScourgeInvasionSpells */ // Shadow of Doom - SPELL_SPAWN_SMOKE = 10389, // Spawning Visual. SPELL_ZAP_CRYSTAL_CORPSE = 28056, // Casted on Shard if Shadow of Doom dies. - SPELL_MINDFLAY = 16568, - SPELL_FEAR = 12542, // Pallid Horror - Patchwerk Terror (also uses: 28315) SPELL_SUMMON_CRACKED_NECROTIC_CRYSTAL = 28424, // Alliance. @@ -336,7 +333,6 @@ enum ScourgeInvasionTalk HERALD_OF_THE_LICH_KING_SAY_ATTACK_END = 1, HERALD_OF_THE_LICH_KING_SAY_ATTACK_RANDOM = 2, PALLID_HORROR_SAY_RANDOM_YELL = 0, - SHADOW_OF_DOOM_SAY_AGGRO = 0, SYLVANAS_SAY_ATTACK_END = 3, VARIAN_SAY_ATTACK_END = 3 }; diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 8b75e97dc..c7bded8be 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -187,6 +187,17 @@ enum ReputationRank : uint8 REP_EXALTED = 7 }; +enum ReputationSource +{ + REPUTATION_SOURCE_KILL, + REPUTATION_SOURCE_QUEST, + REPUTATION_SOURCE_DAILY_QUEST, + REPUTATION_SOURCE_WEEKLY_QUEST, + REPUTATION_SOURCE_MONTHLY_QUEST, + REPUTATION_SOURCE_REPEATABLE_QUEST, + REPUTATION_SOURCE_SPELL +}; + enum FactionTemplates { FACTION_NONE = 0,