Merge branch 'master' into Playerbot-updated

This commit is contained in:
Yunfan Li
2025-09-11 12:20:54 +08:00
102 changed files with 5252 additions and 949 deletions
+53 -2
View File
@@ -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
+267
View File
@@ -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[@]}"
}
+49 -60
View File
@@ -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
}
# Menu command handler - called by menu system for each command
function handle_compiler_command() {
local key="$1"
shift
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[@]}"
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[@]}" -- "$@"
+12 -10
View File
@@ -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" {
+18 -132
View File
@@ -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 ..."
@@ -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
```
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd )
source "$CURRENT_PATH/modules.sh"
inst_module "$@"
File diff suppressed because it is too large Load Diff
+70 -68
View File
@@ -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 <command> [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")
# 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")
"install-deps")
inst_configureOS
;;
""|"u"|"pull"|"3")
"pull")
inst_updateRepo
;;
""|"r"|"reset"|"4")
"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"
;;
""|"mu"|"module-update"|"8")
inst_module_update "$_opt"
;;
""|"mr"|"module-remove"|"9")
inst_module_remove "$_opt"
;;
""|"gd"|"client-data"|"10")
"client-data")
inst_download_client_data
;;
""|"rw"|"run-worldserver"|"11")
"run-worldserver")
inst_simple_restarter worldserver
;;
""|"ra"|"run-authserver"|"12")
"run-authserver")
inst_simple_restarter authserver
;;
""|"dr"|"docker"|"13")
DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "${@:2}"
"docker")
DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "$@"
exit
;;
""|"v"|"version"|"14")
# denoRunFile "$AC_PATH_APPS/installer/main.ts" "version"
"version")
printf "AzerothCore Rev. %s\n" "$ACORE_VERSION"
exit
;;
""|"sm"|"service-manager"|"15")
bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "${@:2}"
"service-manager")
bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@"
exit
;;
""|"quit"|"16")
"quit")
echo "Goodbye!"
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[@]}" -- "$@"
+14
View File
@@ -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
+755
View File
@@ -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" ]]
}
+25 -1
View File
@@ -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
@@ -624,4 +649,3 @@ sudo npm install -g pm2
```
+300 -2
View File
@@ -279,6 +279,11 @@ function print_help() {
echo " $base_name start|stop|restart|status <service-name>"
echo " $base_name logs <service-name> [--follow]"
echo " $base_name attach <service-name>"
echo " $base_name is-running <service-name> # exit 0 if running, 1 otherwise"
echo " $base_name uptime-seconds <service-name> # print uptime in seconds (fails if not running)"
echo " $base_name wait-uptime <service> <sec> [t] # wait until uptime >= seconds (timeout t, default 120)"
echo " $base_name send <service-name> <command...> # send console command to service"
echo " $base_name show-config <service-name> # print current service + run-engine config"
echo " $base_name edit-config <service-name>"
echo ""
echo "Providers:"
@@ -735,7 +740,7 @@ 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"
@@ -1473,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 <service-name> and <command>${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:-<none>}"
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 <min_seconds> 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"
@@ -1629,7 +1881,7 @@ case "${1:-help}" in
delete_service "$2"
;;
list)
list_services "$2"
list_services "${2:-}"
;;
restore)
restore_missing_services
@@ -1670,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 <service-name> <min-seconds> [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
;;
@@ -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 <service-name>" ]]
[[ "$output" =~ "uptime-seconds <service-name>" ]]
[[ "$output" =~ "wait-uptime <service> <sec>" ]]
[[ "$output" =~ "send <service-name>" ]]
[[ "$output" =~ "show-config <service-name>" ]]
}
@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" {
+23
View File
@@ -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:
# <module-name> <branch> <commit>
# 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:-}
@@ -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` <> '';
@@ -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\'');
@@ -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);
@@ -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');
+151
View File
@@ -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));
@@ -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');
@@ -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\'');
@@ -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');
@@ -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);
@@ -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))
@@ -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\'');
@@ -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)');
@@ -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);
@@ -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);
@@ -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));
@@ -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\'');
@@ -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);
@@ -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);
@@ -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);
@@ -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');
@@ -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');
@@ -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);
@@ -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);
@@ -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)');
@@ -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);
@@ -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);
@@ -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');
@@ -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))
@@ -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');
+19 -17
View File
@@ -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 ===="
+2 -1
View File
@@ -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)
+46
View File
@@ -26,6 +26,7 @@
#include "Dynamic/TypeList.h"
#include "GridRefMgr.h"
#include <unordered_map>
#include <vector>
/*
* @class ContainerMapList is a mulit-type container for map elements
@@ -50,6 +51,24 @@ struct ContainerMapList<TypeList<H, T>>
ContainerMapList<T> _TailElements;
};
template<class OBJECT>
struct ContainerVector
{
std::vector<OBJECT*> _element;
};
template<>
struct ContainerVector<TypeNull>
{
};
template<class H, class T>
struct ContainerVector<TypeList<H, T>>
{
ContainerVector<H> _elements;
ContainerVector<T> _TailElements;
};
template<class OBJECT, class KEY_TYPE>
struct ContainerUnorderedMap
{
@@ -123,6 +142,33 @@ private:
ContainerMapList<OBJECT_TYPES> i_elements;
};
template<class OBJECT_TYPES>
class TypeVectorContainer
{
public:
template<class SPECIFIC_TYPE> [[nodiscard]] std::size_t Count() const { return Acore::Count(i_elements, (SPECIFIC_TYPE*)nullptr); }
template<class SPECIFIC_TYPE>
bool Insert(SPECIFIC_TYPE* obj)
{
SPECIFIC_TYPE* t = Acore::Insert(i_elements, obj);
return (t != nullptr);
}
template<class SPECIFIC_TYPE>
bool Remove(SPECIFIC_TYPE* obj)
{
SPECIFIC_TYPE* t = Acore::Remove(i_elements, obj);
return (t != nullptr);
}
ContainerVector<OBJECT_TYPES>& GetElements() { return i_elements; }
[[nodiscard]] const ContainerVector<OBJECT_TYPES>& GetElements() const { return i_elements; }
private:
ContainerVector<OBJECT_TYPES> i_elements;
};
template<class OBJECT_TYPES, class KEY_TYPE>
class TypeUnorderedMapContainer
{
@@ -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<class SPECIFIC_TYPE>
std::size_t Count(const ContainerVector<SPECIFIC_TYPE>& elements, SPECIFIC_TYPE* /*fake*/)
{
return elements._element.getSize();
}
template<class SPECIFIC_TYPE>
std::size_t Count(const ContainerVector<TypeNull>& /*elements*/, SPECIFIC_TYPE* /*fake*/)
{
return 0;
}
template<class SPECIFIC_TYPE, class T>
std::size_t Count(const ContainerVector<T>& /*elements*/, SPECIFIC_TYPE* /*fake*/)
{
return 0;
}
template<class SPECIFIC_TYPE, class T>
std::size_t Count(const ContainerVector<TypeList<SPECIFIC_TYPE, T>>& elements, SPECIFIC_TYPE* fake)
{
return Count(elements._elements, fake);
}
template<class SPECIFIC_TYPE, class H, class T>
std::size_t Count(const ContainerVector<TypeList<H, T>>& elements, SPECIFIC_TYPE* fake)
{
return Count(elements._TailElements, fake);
}
// non-const insert functions
template<class SPECIFIC_TYPE>
SPECIFIC_TYPE* Insert(ContainerVector<SPECIFIC_TYPE>& elements, SPECIFIC_TYPE* obj)
{
elements._element.push_back(obj);
return obj;
}
template<class SPECIFIC_TYPE>
SPECIFIC_TYPE* Insert(ContainerVector<TypeNull>& /*elements*/, SPECIFIC_TYPE* /*obj*/)
{
return nullptr;
}
// this is a missed
template<class SPECIFIC_TYPE, class T>
SPECIFIC_TYPE* Insert(ContainerVector<T>& /*elements*/, SPECIFIC_TYPE* /*obj*/)
{
return nullptr; // a missed
}
// Recursion
template<class SPECIFIC_TYPE, class H, class T>
SPECIFIC_TYPE* Insert(ContainerVector<TypeList<H, T>>& elements, SPECIFIC_TYPE* obj)
{
SPECIFIC_TYPE* t = Insert(elements._elements, obj);
return (t != nullptr ? t : Insert(elements._TailElements, obj));
}
// non-const remove method
template<class SPECIFIC_TYPE> SPECIFIC_TYPE* Remove(ContainerVector<SPECIFIC_TYPE>& 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<class SPECIFIC_TYPE> SPECIFIC_TYPE* Remove(ContainerVector<TypeNull> &/*elements*/, SPECIFIC_TYPE * /*obj*/)
{
return nullptr;
}
// this is a missed
template<class SPECIFIC_TYPE, class T> SPECIFIC_TYPE* Remove(ContainerVector<T> &/*elements*/, SPECIFIC_TYPE * /*obj*/)
{
return nullptr; // a missed
}
template<class SPECIFIC_TYPE, class T, class H> SPECIFIC_TYPE* Remove(ContainerVector<TypeList<H, T> > &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
+21
View File
@@ -56,6 +56,27 @@ template<class VISITOR, class OBJECT_TYPES> void VisitorHelper(VISITOR& v, TypeM
VisitorHelper(v, c.GetElements());
}
// VectorContainer
template<class VISITOR> void VisitorHelper(VISITOR& /*v*/, ContainerVector<TypeNull>& /*c*/) {}
template<class VISITOR, class T> void VisitorHelper(VISITOR& v, ContainerVector<T>& c)
{
v.Visit(c._element);
}
// recursion container map list
template<class VISITOR, class H, class T> void VisitorHelper(VISITOR& v, ContainerVector<TypeList<H, T>>& c)
{
VisitorHelper(v, c._elements);
VisitorHelper(v, c._TailElements);
}
// for TypeMapContainer
template<class VISITOR, class OBJECT_TYPES> void VisitorHelper(VISITOR& v, TypeVectorContainer<OBJECT_TYPES>& c)
{
VisitorHelper(v, c.GetElements());
}
// TypeUnorderedMapContainer
template<class VISITOR, class KEY_TYPE>
void VisitorHelper(VISITOR& /*v*/, ContainerUnorderedMap<TypeNull, KEY_TYPE>& /*c*/) { }
@@ -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);
@@ -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,
@@ -660,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;
}
@@ -3834,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);
}
}
}
@@ -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>(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
@@ -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;
}
@@ -629,6 +629,8 @@ struct GameObjectTemplate
return true;
case GAMEOBJECT_TYPE_TRAPDOOR:
return true;
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
return true;
default:
return false;
}
+55 -44
View File
@@ -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;
}
@@ -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;
}
+20 -7
View File
@@ -358,9 +358,20 @@ template<class T>
class GridObject
{
public:
[[nodiscard]] bool IsInGrid() const { return _gridRef.isValid(); }
void AddToGrid(GridRefMgr<T>& m) { ASSERT(!IsInGrid()); _gridRef.link(&m, (T*)this); }
void RemoveFromGrid() { ASSERT(IsInGrid()); _gridRef.unlink(); }
bool IsInGrid() const
{
return _gridRef.isValid();
}
void AddToGrid(GridRefMgr<T>& m)
{
ASSERT(!IsInGrid());
_gridRef.link(&m, (T*)this);
}
void RemoveFromGrid()
{
ASSERT(IsInGrid());
_gridRef.unlink();
}
private:
GridReference<T> _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<float> m_visibilityDistanceOverride;
VisibilityDistanceType _visibilityDistanceOverrideType;
ZoneScript* m_zoneScript;
virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data);
@@ -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
+22 -2
View File
@@ -7078,6 +7078,14 @@ void Player::ApplyItemObtainSpells(Item* item, bool apply)
}
}
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))
@@ -16314,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();
+2
View File
@@ -2199,6 +2199,7 @@ public:
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;
@@ -2620,6 +2621,7 @@ public:
[[nodiscard]] Optional<float> GetFarSightDistance() const;
float GetSightRange(WorldObject const* target = nullptr) const override;
bool IsWorldObjectOutOfSightRange(WorldObject const* target) const;
std::string GetPlayerName();
@@ -2654,9 +2654,7 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
AddEnchantmentDurations(pItem);
AddItemDurations(pItem);
if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
ApplyItemObtainSpells(pItem, true);
UpdateItemObtainSpells(pItem, bag, slot);
return pItem;
}
@@ -2694,8 +2692,7 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool
pItem2->SetState(ITEM_CHANGED, this);
if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END))
ApplyItemObtainSpells(pItem2, true);
UpdateItemObtainSpells(pItem2, bag, slot);
return pItem2;
}
@@ -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);
+9 -17
View File
@@ -20315,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())
@@ -20354,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);
}
@@ -20383,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);
}
+2
View File
@@ -106,6 +106,8 @@ struct Cell
template<class T> static void VisitObjects(WorldObject const* obj, T& visitor, float radius);
template<class T> static void VisitObjects(float x, float y, Map* map, T& visitor, float radius);
template<class T> static void VisitFarVisibleObjects(WorldObject const* obj, T& visitor, float radius);
private:
template<class T, class CONTAINER> void VisitCircle(TypeContainerVisitor<T, CONTAINER>&, Map&, CellCoord const&, CellCoord const&) const;
};
+10
View File
@@ -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<class T>
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<T, FarVisibleGridContainer> gnotifier(visitor);
cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius);
}
#endif
+27 -3
View File
@@ -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<class SPECIFIC_OBJECT> void AddGridObject(SPECIFIC_OBJECT* obj)
template<class SPECIFIC_OBJECT>
void AddGridObject(SPECIFIC_OBJECT* obj)
{
_gridObjects.template insert<SPECIFIC_OBJECT>(obj);
ASSERT(obj->IsInGrid());
@@ -50,12 +54,32 @@ public:
// Visit grid objects
template<class T>
void Visit(TypeContainerVisitor<T, TypeMapContainer<GRID_OBJECT_TYPES> >& visitor)
void Visit(TypeContainerVisitor<T, TypeMapContainer<GRID_OBJECT_TYPES>>& visitor)
{
visitor.Visit(_gridObjects);
}
template<class SPECIFIC_OBJECT>
void AddFarVisibleObject(SPECIFIC_OBJECT* obj)
{
_farVisibleObjects.template Insert<SPECIFIC_OBJECT>(obj);
}
template<class SPECIFIC_OBJECT>
void RemoveFarVisibleObject(SPECIFIC_OBJECT* obj)
{
_farVisibleObjects.template Remove<SPECIFIC_OBJECT>(obj);
}
// Visit far objects
template<class T>
void Visit(TypeContainerVisitor<T, TypeVectorContainer<FAR_VISIBLE_OBJECT_TYPES>>& visitor)
{
visitor.Visit(_farVisibleObjects);
}
private:
TypeMapContainer<GRID_OBJECT_TYPES> _gridObjects;
TypeVectorContainer<FAR_VISIBLE_OBJECT_TYPES> _farVisibleObjects;
};
#endif
+6 -2
View File
@@ -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<Corpse> CorpseMapType;
typedef GridRefMgr<Creature> CreatureMapType;
typedef GridRefMgr<DynamicObject> DynamicObjectMapType;
@@ -73,10 +76,11 @@ enum GridMapTypeMask
GRID_MAP_TYPE_MASK_ALL = 0x1F
};
typedef GridCell<AllMapGridStoredObjectTypes> GridCellType;
typedef MapGrid<AllMapGridStoredObjectTypes> MapGridType;
typedef GridCell<AllMapGridStoredObjectTypes, AllFarVisibleObjectTypes> GridCellType;
typedef MapGrid<AllMapGridStoredObjectTypes, AllFarVisibleObjectTypes> MapGridType;
typedef TypeMapContainer<AllMapGridStoredObjectTypes> GridTypeMapContainer;
typedef TypeVectorContainer<AllFarVisibleObjectTypes> FarVisibleGridContainer;
typedef TypeUnorderedMapContainer<AllMapStoredObjectTypes, ObjectGuid> MapStoredObjectTypesContainer;
template<uint32 LIMIT>
+17 -6
View File
@@ -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<GRID_OBJECT_TYPES> GridCellType;
typedef GridCell<GRID_OBJECT_TYPES, FAR_VISIBLE_OBJECT_TYPES> 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<class SPECIFIC_OBJECT> void AddFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj)
{
GetOrCreateCell(x, y).AddFarVisibleObject(obj);
}
template<class SPECIFIC_OBJECT> void RemoveFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj)
{
GetOrCreateCell(x, y).RemoveFarVisibleObject(obj);
}
// Visit all cells
template<class T, class TT>
void VisitAllCells(TypeContainerVisitor<T, TypeMapContainer<TT> >& visitor)
void VisitAllCells(TypeContainerVisitor<T, TT>& visitor)
{
for (auto& cellX : _cells)
{
@@ -72,7 +83,7 @@ public:
// Visit single cell
template<class T, class TT>
void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor<T, TypeMapContainer<TT> >& visitor)
void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor<T, TT>& visitor)
{
GridCellType* gridCell = GetCell(x, y);
if (!gridCell)
@@ -81,7 +92,7 @@ public:
gridCell->Visit(visitor);
}
void link(GridRefMgr<MapGrid<GRID_OBJECT_TYPES>>* pTo)
void link(GridRefMgr<MapGrid<GRID_OBJECT_TYPES, FAR_VISIBLE_OBJECT_TYPES>>* pTo)
{
_gridReference.link(pTo, this);
}
@@ -134,7 +145,7 @@ private:
bool _objectDataLoaded;
std::array<std::array<std::unique_ptr<GridCellType>, MAX_NUMBER_OF_CELLS>, MAX_NUMBER_OF_CELLS> _cells; // N * N array
GridReference<MapGrid<GRID_OBJECT_TYPES>> _gridReference;
GridReference<MapGrid<GRID_OBJECT_TYPES, FAR_VISIBLE_OBJECT_TYPES>> _gridReference;
// Instances will share a copy of the parent maps terrainData
std::shared_ptr<GridTerrainData> _terrainData;
@@ -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<Unit*>::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)
@@ -45,16 +45,16 @@ namespace Acore
Player& i_player;
std::vector<Unit*>& 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<class T> void Visit(std::vector<T>& m);
template<class T> void Visit(GridRefMgr<T>& 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<class T> void Visit(std::vector<T>& m) { VisibleNotifier::Visit(m); }
template<class T> void Visit(GridRefMgr<T>& m) { VisibleNotifier::Visit(m); }
void Visit(PlayerMapType&);
};
@@ -25,6 +25,13 @@
#include "WorldPacket.h"
#include "WorldSession.h"
template<class T>
inline void Acore::VisibleNotifier::Visit(std::vector<T>& m)
{
for (typename std::vector<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
i_player.UpdateVisibilityOf((*iter), i_data, i_visibleNow);
}
template<class T>
inline void Acore::VisibleNotifier::Visit(GridRefMgr<T>& m)
{
@@ -33,12 +40,7 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr<T>& m)
return;
for (typename GridRefMgr<T>::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
+133 -214
View File
@@ -29,91 +29,82 @@
#include "WorldSession.h"
#include <cmath>
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);
+20 -16
View File
@@ -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);
+95
View File
@@ -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);
}
@@ -494,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<Creature>()),
@@ -600,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())
@@ -652,6 +723,8 @@ void Map::RemoveFromMap(T* obj, bool remove)
obj->RemoveFromWorld();
obj->RemoveFromGrid();
if (obj->IsFarVisible())
RemoveWorldObjectFromFarVisibleMap(obj);
obj->ResetMap();
@@ -848,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))
@@ -878,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);
@@ -1580,6 +1664,17 @@ 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())
+13
View File
@@ -147,6 +147,8 @@ struct ZoneDynamicInfo
typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;
typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap;
typedef std::unordered_set<Transport*> TransportsContainer;
typedef std::unordered_set<WorldObject*> ZoneWideVisibleWorldObjectsSet;
typedef std::unordered_map<uint32 /*ZoneId*/, ZoneWideVisibleWorldObjectsSet> 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);
@@ -494,6 +498,12 @@ public:
typedef std::vector<WorldObject*> UpdatableObjectList;
typedef std::unordered_set<WorldObject*> 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<class T> void InitializeObject(T* obj);
@@ -576,6 +586,8 @@ private:
ZoneDynamicInfoMap _zoneDynamicInfo;
uint32 _defaultLight;
IntervalTimer _corpseUpdateTimer;
template<HighGuid high>
inline ObjectGuidGeneratorBase& GetGuidSequenceGenerator()
{
@@ -599,6 +611,7 @@ private:
UpdatableObjectList _updatableObjectList;
PendingAddUpdatableObjectList _pendingAddUpdatableObjectList;
IntervalTimer _updatableObjectListRecheckTimer;
ZoneWideVisibleWorldObjectsMap _zoneWideVisibleWorldObjectsMap;
};
enum InstanceResetMethod
+71 -6
View File
@@ -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<HighGuid::Item>(fields[1].Get<uint32>()), ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()), fields[2].Get<std::string>(), fields[3].Get<uint8>());
uint32 itemLow = fields[1].Get<uint32>();
uint32 petitionId = fields[4].Get<uint32>();
ObjectGuid itemGuid = ObjectGuid::Create<HighGuid::Item>(itemLow);
ObjectGuid ownerGuid = ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>());
AddPetition(itemGuid, ownerGuid, fields[2].Get<std::string>(), fields[3].Get<uint8>(), 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<uint32>(std::max<uint32>(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<HighGuid::Item>(fields[0].Get<uint32>()), fields[2].Get<uint32>(), ObjectGuid::Create<HighGuid::Player>(fields[1].Get<uint32>()));
uint32 petitionId = fields[0].Get<uint32>();
auto it = PetitionIdToItemGuid.find(petitionId);
if (it == PetitionIdToItemGuid.end())
continue; // orphan signature, skip
AddSignature(it->second, fields[2].Get<uint32>(), ObjectGuid::Create<HighGuid::Player>(fields[1].Get<uint32>()));
++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;
}
+18 -1
View File
@@ -20,6 +20,7 @@
#include "ObjectGuid.h"
#include <unordered_map>
#include <map>
#define CHARTER_DISPLAY_ID 16161
@@ -37,14 +38,21 @@ typedef std::map<ObjectGuid, uint32> 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<uint32, ObjectGuid> PetitionIdToItemGuid;
// Next petition id (kept < 2^31)
uint32 _nextPetitionId = 1;
};
#define sPetitionMgr PetitionMgr::instance()
@@ -24,6 +24,7 @@
#include "CombatLogPackets.h"
#include "CombatPackets.h"
#include "GuildPackets.h"
#include "ItemPackets.h"
#include "LFGPackets.h"
#include "MiscPackets.h"
#include "PetPackets.h"
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}
@@ -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 <http://www.gnu.org/licenses/>.
*/
#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__
+40 -18
View File
@@ -167,6 +167,28 @@ namespace WorldPackets
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
@@ -830,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);
@@ -1034,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);
@@ -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];
-1
View File
@@ -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;
};
-19
View File
@@ -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;
-3
View File
@@ -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
+10 -5
View File
@@ -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
@@ -1420,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<std::chrono::milliseconds>(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;
}
+32 -1
View File
@@ -99,7 +99,8 @@ public:
{ "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 =
{
@@ -1403,6 +1404,36 @@ public:
return true;
}
static bool HandleDebugVisibilityDataCommand(ChatHandler* handler)
{
Player* player = handler->GetPlayer();
if (!player)
return false;
std::array<uint32, NUM_CLIENT_OBJECT_TYPES> 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()
-2
View File
@@ -71,8 +71,6 @@ public:
if (counter == 0)
handler->SendSysMessage(LANG_NOEVENTFOUND);
handler->SetSentErrorMessage(true);
return true;
}
+5 -1
View File
@@ -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;
}
@@ -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
@@ -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,
@@ -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);
@@ -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
@@ -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();
}
};
@@ -75,8 +75,6 @@ enum DrakeGiverTexts
class npc_oculus_drakegiver : public CreatureScript
{
public:
std::unordered_map<ObjectGuid, bool>openedMenu;
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);
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}

Some files were not shown because too many files have changed in this diff Show More