Merge remote-tracking branch 'upstream/master' into test-staging
# Conflicts: # .github/workflows/windows_build.yml # src/server/game/Handlers/CharacterHandler.cpp
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# .agents
|
||||
|
||||
Source of truth for this repo's agent skills and rules:
|
||||
|
||||
- `skills/<name>/SKILL.md`
|
||||
- `rules/<name>.md`
|
||||
|
||||
Agent-specific directories expose each via a relative symlink — for Claude Code:
|
||||
|
||||
.claude/skills/<name> -> ../../.agents/skills/<name>
|
||||
.claude/rules/<name>.md -> ../../.agents/rules/<name>.md
|
||||
|
||||
To add a skill or rule: create it here, then symlink it.
|
||||
|
||||
The skills in this repository often build on those from
|
||||
[agent-toolkit](https://github.com/eai-org/agent-toolkit/), invoking them when available.
|
||||
Installing the toolkit at user level is therefore recommended.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: generate-pr-description
|
||||
description: Generate the PR title and description from the current branch changes, following the repo's PR template.
|
||||
metadata:
|
||||
version: "1.0"
|
||||
---
|
||||
|
||||
# Generate PR description
|
||||
|
||||
Produce a copy-pasteable PR title and body for the current changes, based on the repo's
|
||||
`pull_request_template.md`. Output text only — never create the PR, push, or run any
|
||||
state-changing git command.
|
||||
|
||||
If a `use-conversational-language` skill is available, invoke it and follow it when writing the
|
||||
prose. If not available, fetch it from its [remote source](https://github.com/eai-org/agent-toolkit/blob/main/skills/use-conversational-language/SKILL.md).
|
||||
|
||||
## Gather the changes
|
||||
|
||||
"The changes" is the union of:
|
||||
|
||||
- committed branch work: `git diff <master-ref>...HEAD`, where `<master-ref>` is `origin/master` —
|
||||
or `upstream/master` when the checkout is a fork with an `upstream` remote;
|
||||
- staged and unstaged changes;
|
||||
- the branch's commit messages (context only).
|
||||
|
||||
If all three are empty, say so and stop.
|
||||
|
||||
## Facts, never assumptions
|
||||
|
||||
Only two sources may fill the description: the diff and facts stated in the conversation. Never
|
||||
present anything else as fact — no assumed testing, sources, issue links, or TODOs. A section
|
||||
without backing facts stays empty/unchecked, silently: an empty section is correct, a guessed one
|
||||
is wrong.
|
||||
|
||||
Ask the user only when unsure about substance — what the change does, which scope the title should
|
||||
carry, contradictory information. Missing fill-in facts are never a reason to ask.
|
||||
|
||||
## Title
|
||||
|
||||
`type(Scope/Subscope): short description` — imperative, max 50 chars. Types: `feat`, `fix`,
|
||||
`refactor`, `style`, `docs`, `test`, `chore`; scopes: `Core`, `Scripts`, `DB`. Copy subscope naming
|
||||
from recent commits touching the same areas (`git log --oneline -- <paths>`).
|
||||
|
||||
## Body
|
||||
|
||||
Fill in `pull_request_template.md` (repo root):
|
||||
|
||||
- Strip the instructional `<!-- -->` comments; keep the "How to Test AzerothCore PRs" footer
|
||||
intact.
|
||||
- Describe what the changes do and why.
|
||||
- "Changes Proposed" checkboxes, from diff paths: `src/server/scripts/` → Scripts; `data/sql/` →
|
||||
Database; other `src/` → Core.
|
||||
- AI disclosure checkbox: always tick it; name the agent/model running this skill and state
|
||||
whether AI produced the changes themselves or only this description, per the conversation.
|
||||
- Issues Addressed, SOURCE, Tests Performed, How to Test, Known Issues/TODO: stated facts only
|
||||
(see above).
|
||||
|
||||
## Output
|
||||
|
||||
Print exactly:
|
||||
|
||||
~~~
|
||||
TITLE: <title>
|
||||
|
||||
PR DESCRIPTION:
|
||||
```markdown
|
||||
<body>
|
||||
```
|
||||
~~~
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../.agents/skills/generate-pr-description
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
||||
# CodeRabbit review config. Mirrors the hard rules in CLAUDE.md so the AI
|
||||
# CodeRabbit review config. Mirrors the hard rules in AGENTS.md so the AI
|
||||
# reviewer enforces project conventions on every PR.
|
||||
language: en-US
|
||||
|
||||
@@ -74,10 +74,10 @@ reviews:
|
||||
knowledge_base:
|
||||
code_guidelines:
|
||||
enabled: true
|
||||
# CLAUDE.md is in CodeRabbit's defaults, but list it explicitly so the project's
|
||||
# AGENTS.md is in CodeRabbit's defaults, but list it explicitly so the project's
|
||||
# full guideline doc is always pulled into review context.
|
||||
filePatterns:
|
||||
- "CLAUDE.md"
|
||||
- "AGENTS.md"
|
||||
|
||||
chat:
|
||||
auto_reply: true
|
||||
|
||||
+4
-4
@@ -23,10 +23,10 @@ We support the following versions of dependencies.
|
||||
### Supported Operating Systems
|
||||
|
||||
| Linux (Ubuntu) | Status | Recommended |
|
||||
| :------------- | :----------------: | :------------------: |
|
||||
| 24.04 | :white_check_mark: | :large_blue_diamond: |
|
||||
| 22.04 | :white_check_mark: | |
|
||||
| 20.04 ≤ | :red_circle: | |
|
||||
|:---------------| :----------------: | :------------------: |
|
||||
| 26.04 | :white_check_mark: | :large_blue_diamond: |
|
||||
| 24.04 | :white_check_mark: | |
|
||||
| 22.04 ≤ | :red_circle: | |
|
||||
|
||||
| macOS | Status | Recommended |
|
||||
| :---- | :----------------: | :------------------: |
|
||||
|
||||
@@ -64,23 +64,14 @@ runs:
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt remove needrestart #refer: https://github.com/actions/runner-images/issues/9937
|
||||
sudo apt-get -y install ccache clang cmake curl google-perftools \
|
||||
libmysqlclient-dev make unzip build-essential cmake-data \
|
||||
libboost-all-dev libbz2-dev libncurses5-dev libmysql++-dev \
|
||||
sudo apt-get -y install ccache clang cmake curl \
|
||||
make unzip build-essential cmake-data \
|
||||
libboost-all-dev libbz2-dev libmysql++-dev \
|
||||
libreadline6-dev libssl-dev libtool openssl zlib1g-dev
|
||||
|
||||
# Account for https://github.com/actions/runner-images/issues/8659
|
||||
# based off of https://github.com/actions/runner-images/issues/8659#issuecomment-1852353116
|
||||
UBUNTU_VERSION="$(grep VERSION_ID /etc/os-release | cut -f2 -d\")"
|
||||
source /etc/os-release
|
||||
if [[ "$VERSION_CODENAME" == "jammy" ]]; then
|
||||
if [[ "${{ inputs.CC }}" =~ "clang-" ]]; then
|
||||
CLANG_VERSION="$(echo '${{ inputs.CC }}' | cut -f2 -d\-)"
|
||||
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/$VERSION_CODENAME/ llvm-toolchain-$VERSION_CODENAME-$CLANG_VERSION main"
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get -qq install '${{ inputs.CC }}'
|
||||
fi
|
||||
if [[ "$(lsb_release -sr)" == "26.04" ]]; then
|
||||
sudo apt-get -y install libgoogle-perftools-dev default-libmysqlclient-dev
|
||||
else
|
||||
sudo apt-get -y install google-perftools libmysqlclient-dev libncurses5-dev
|
||||
fi
|
||||
|
||||
- name: setup ccache
|
||||
|
||||
@@ -19,14 +19,14 @@ The PR Reviewer agent is configured to review pull requests with deep understand
|
||||
- Testing and validation requirements
|
||||
- Security and quality standards
|
||||
|
||||
**Key Feature**: This agent always references `/CLAUDE.md`, the C++ Code Standards wiki, and the SQL Standards wiki before reviewing any PR.
|
||||
**Key Feature**: This agent always references `/AGENTS.md`, the C++ Code Standards wiki, and the SQL Standards wiki before reviewing any PR.
|
||||
|
||||
## How It Works
|
||||
|
||||
When an AI tool reviews a PR in this repository:
|
||||
|
||||
1. The tool reads the appropriate agent configuration from this directory
|
||||
2. The agent configuration instructs it to read `/CLAUDE.md` for full project context
|
||||
2. The agent configuration instructs it to read `/AGENTS.md` for full project context
|
||||
3. For C++ changes, the agent also references the [C++ Code Standards wiki](https://github.com/azerothcore/wiki/blob/master/docs/cpp-code-standards.md)
|
||||
4. For SQL changes, the agent also references the [SQL Standards wiki](https://github.com/azerothcore/wiki/blob/master/docs/sql-standards.md)
|
||||
5. The agent applies project-specific rules during review
|
||||
@@ -36,7 +36,7 @@ When an AI tool reviews a PR in this repository:
|
||||
|
||||
If you're using AI tools to assist with contributions:
|
||||
|
||||
1. Familiarize yourself with `/CLAUDE.md` - it contains essential project knowledge
|
||||
1. Familiarize yourself with `/AGENTS.md` - it contains essential project knowledge
|
||||
2. Review the [C++ Code Standards](https://github.com/azerothcore/wiki/blob/master/docs/cpp-code-standards.md) for C++ contributions
|
||||
3. Review the [SQL Standards](https://github.com/azerothcore/wiki/blob/master/docs/sql-standards.md) for database contributions
|
||||
4. The AI agents will help ensure your changes follow project standards
|
||||
@@ -47,13 +47,13 @@ If you're using AI tools to assist with contributions:
|
||||
To update agent configurations:
|
||||
|
||||
1. Edit the relevant `.md` file in this directory
|
||||
2. Ensure any changes align with documentation in `/CLAUDE.md` and the wiki standards
|
||||
2. Ensure any changes align with documentation in `/AGENTS.md` and the wiki standards
|
||||
3. Test the updated configuration with a sample PR review
|
||||
4. Commit changes following the project's commit message format
|
||||
|
||||
## References
|
||||
|
||||
- Project guidelines: `/CLAUDE.md`
|
||||
- Project guidelines: `/AGENTS.md`
|
||||
- C++ standards: [C++ Code Standards](https://github.com/azerothcore/wiki/blob/master/docs/cpp-code-standards.md)
|
||||
- SQL standards: [SQL Standards](https://github.com/azerothcore/wiki/blob/master/docs/sql-standards.md)
|
||||
- PR template: `/pull_request_template.md`
|
||||
|
||||
@@ -5,13 +5,13 @@ You are an expert code reviewer for the AzerothCore project. When reviewing pull
|
||||
## Required Reading
|
||||
|
||||
Before reviewing any PR, you MUST read and follow the instructions in:
|
||||
- `/CLAUDE.md` - Contains comprehensive project architecture, coding standards, build instructions, and PR requirements
|
||||
- `/AGENTS.md` - Contains comprehensive project architecture, coding standards, build instructions, and PR requirements
|
||||
- [C++ Code Standards](https://github.com/azerothcore/wiki/blob/master/docs/cpp-code-standards.md) - Detailed C++ coding conventions and style guide
|
||||
- [SQL Standards](https://github.com/azerothcore/wiki/blob/master/docs/sql-standards.md) - SQL query formatting and database standards
|
||||
|
||||
## Key Review Focus Areas
|
||||
|
||||
Based on CLAUDE.md, always verify:
|
||||
Based on AGENTS.md, always verify:
|
||||
|
||||
### 1. Commit Message Format
|
||||
- Uses Conventional Commits: `Type(Scope/Subscope): Short description`
|
||||
@@ -95,7 +95,7 @@ Based on CLAUDE.md, always verify:
|
||||
|
||||
## Review Process
|
||||
|
||||
1. Read CLAUDE.md to understand project context
|
||||
1. Read AGENTS.md to understand project context
|
||||
2. Review C++ Code Standards wiki for C++ changes
|
||||
3. Review SQL Standards wiki for database changes
|
||||
4. Check commit message format
|
||||
@@ -108,7 +108,7 @@ Based on CLAUDE.md, always verify:
|
||||
## Feedback Style
|
||||
|
||||
- Be constructive and educational
|
||||
- Reference specific sections of CLAUDE.md, C++ Code Standards, or SQL Standards when applicable
|
||||
- Reference specific sections of AGENTS.md, C++ Code Standards, or SQL Standards when applicable
|
||||
- Suggest specific fixes with code examples if necessary
|
||||
- Highlight both issues and good practices
|
||||
- For C++ issues, cite specific rule from C++ Code Standards wiki
|
||||
|
||||
@@ -25,10 +25,6 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
compiler:
|
||||
CC: clang-15
|
||||
CXX: clang++-15
|
||||
- os: ubuntu-24.04
|
||||
compiler:
|
||||
CC: clang-18
|
||||
@@ -37,6 +33,10 @@ jobs:
|
||||
compiler:
|
||||
CC: gcc-14
|
||||
CXX: g++-14
|
||||
- os: ubuntu-26.04
|
||||
compiler:
|
||||
CC: clang-20
|
||||
CXX: clang++-20
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os }}-${{ matrix.compiler.CC }}-nopch
|
||||
if: github.repository == 'mod-playerbots/azerothcore-wotlk' && !github.event.pull_request.draft
|
||||
|
||||
@@ -25,14 +25,14 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
compiler:
|
||||
CC: clang-15
|
||||
CXX: clang++-15
|
||||
- os: ubuntu-24.04
|
||||
compiler:
|
||||
CC: clang-18
|
||||
CXX: clang++-18
|
||||
- os: ubuntu-26.04
|
||||
compiler:
|
||||
CC: clang-20
|
||||
CXX: clang++-20
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os }}-${{ matrix.compiler.CC }}-pch
|
||||
if: github.repository == 'mod-playerbots/azerothcore-wotlk' && !github.event.pull_request.draft
|
||||
|
||||
@@ -42,8 +42,8 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-24.04
|
||||
- os: ubuntu-26.04
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
|
||||
steps:
|
||||
@@ -74,8 +74,8 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-24.04
|
||||
- os: ubuntu-26.04
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft
|
||||
steps:
|
||||
|
||||
@@ -39,15 +39,73 @@ jobs:
|
||||
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||
|
||||
# Cache the fetched Boost + MySQL client so we don't re-download ~350 MB
|
||||
# and re-run the Boost installer on every run. The Test-Path guards in
|
||||
# "Configure OS" short-circuit the install work when these are restored.
|
||||
- name: Cache Boost & MySQL
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
C:\local\boost_1_87_0
|
||||
C:\tools\mysql\current
|
||||
key: winbuild-deps:boost-1.87.0:mysql-8.4.9
|
||||
- name: Configure OS
|
||||
shell: bash
|
||||
env:
|
||||
CONTINUOUS_INTEGRATION: true
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd C:/ac
|
||||
./acore.sh install-deps
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# The windows-latest runner image already ships CMake, Visual Studio
|
||||
# (with the native desktop workload + MSVC) and OpenSSL, so we only need to
|
||||
# provide Boost and the MySQL client libraries. We fetch these directly
|
||||
# instead of via chocolatey, whose pinned packages keep breaking on CI
|
||||
# (VS detection failures and slproweb removing old OpenSSL installers).
|
||||
|
||||
# --- Boost 1.87.0 (msvc-14.3, 64-bit) prebuilt binaries -> C:\local\boost_1_87_0 (BOOST_ROOT) ---
|
||||
# Use the direct-download host; the plain /download URL serves an HTML interstitial.
|
||||
if (-not (Test-Path 'C:\local\boost_1_87_0\boost'))
|
||||
{
|
||||
$boostExe = Join-Path $env:RUNNER_TEMP 'boost.exe'
|
||||
Invoke-WebRequest -Uri 'https://master.dl.sourceforge.net/project/boost/boost-binaries/1.87.0/boost_1_87_0-msvc-14.3-64.exe?viasf=1' -OutFile $boostExe
|
||||
$boostMB = (Get-Item $boostExe).Length / 1MB
|
||||
Write-Host ("Downloaded Boost installer: {0:N1} MB" -f $boostMB)
|
||||
if ($boostMB -lt 50) { throw "Boost installer download looks wrong ($([int]$boostMB) MB) - likely an HTML page." }
|
||||
$boostProc = Start-Process -FilePath $boostExe -ArgumentList '/VERYSILENT','/SUPPRESSMSGBOXES','/NORESTART','/DIR=C:\local\boost_1_87_0' -Wait -PassThru
|
||||
if ($boostProc.ExitCode -ne 0) { throw "Boost installer failed with exit code $($boostProc.ExitCode)." }
|
||||
# Fail fast if the installer "succeeded" but didn't lay down the expected tree,
|
||||
# instead of surfacing a cryptic error later during CMake configuration.
|
||||
if (-not (Test-Path 'C:\local\boost_1_87_0\boost\version.hpp')) { throw 'Boost install missing headers at C:\local\boost_1_87_0\boost.' }
|
||||
if (-not (Test-Path 'C:\local\boost_1_87_0\lib64-msvc-14.3')) { throw 'Boost install missing prebuilt libs at C:\local\boost_1_87_0\lib64-msvc-14.3.' }
|
||||
}
|
||||
|
||||
# --- MySQL 8.4 client headers + libs -> C:\tools\mysql\current (path the build expects) ---
|
||||
if (-not (Test-Path 'C:\tools\mysql\current\lib\mysqlclient.lib'))
|
||||
{
|
||||
# Use the archives CDN directly (keeps every version, unlike dev.mysql.com/get which only
|
||||
# serves the current GA). A browser user-agent is required or Oracle returns an HTML error page.
|
||||
$mysqlZip = Join-Path $env:RUNNER_TEMP 'mysql.zip'
|
||||
Invoke-WebRequest -Uri 'https://cdn.mysql.com/archives/mysql-8.4/mysql-8.4.9-winx64.zip' -OutFile $mysqlZip -UserAgent 'Mozilla/5.0'
|
||||
$mysqlMB = (Get-Item $mysqlZip).Length / 1MB
|
||||
Write-Host ("Downloaded MySQL zip: {0:N1} MB" -f $mysqlMB)
|
||||
if ($mysqlMB -lt 50) { throw "MySQL zip download looks wrong ($([int]$mysqlMB) MB)." }
|
||||
New-Item -ItemType Directory -Force -Path 'C:\tools\mysql' | Out-Null
|
||||
Expand-Archive -Path $mysqlZip -DestinationPath 'C:\tools\mysql' -Force
|
||||
if (Test-Path 'C:\tools\mysql\current') { Remove-Item -Recurse -Force 'C:\tools\mysql\current' }
|
||||
Rename-Item 'C:\tools\mysql\mysql-8.4.9-winx64' 'C:\tools\mysql\current'
|
||||
# Fail fast if the archive layout changed and the client import library isn't
|
||||
# where the build expects it, rather than failing later during CMake/link.
|
||||
if (-not (Test-Path 'C:\tools\mysql\current\lib\mysqlclient.lib')) { throw 'MySQL client import library missing at C:\tools\mysql\current\lib\mysqlclient.lib.' }
|
||||
}
|
||||
|
||||
# --- OpenSSL: use the copy already installed on the runner image ---
|
||||
$opensslRoot = @($env:OPENSSL_ROOT_DIR, 'C:\Program Files\OpenSSL', 'C:\Program Files\OpenSSL-Win64') |
|
||||
Where-Object { $_ -and (Test-Path (Join-Path $_ 'include\openssl\opensslv.h')) } |
|
||||
Select-Object -First 1
|
||||
if (-not $opensslRoot)
|
||||
{
|
||||
throw 'No OpenSSL development install found on the runner image.'
|
||||
}
|
||||
"OPENSSL_ROOT_DIR=$opensslRoot" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
||||
Write-Host "Using OpenSSL at $opensslRoot"
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# AGENTS.md
|
||||
|
||||
AzerothCore is a C++ MMORPG server emulator for World of Warcraft 3.3.5a (WotLK), built with CMake, backed by MySQL.
|
||||
|
||||
## Agent rules
|
||||
|
||||
- **Do not configure or build unless explicitly asked.** Builds are slow and rarely needed for code changes.
|
||||
- **Never edit SQL files outside `data/sql/updates/pending_db_*/` unless explicitly requested. ** `data/sql/base/`, `data/sql/archive/`, and `data/sql/updates/db_*/` are immutable.
|
||||
|
||||
## Build
|
||||
|
||||
Out-of-source build is required (in-source is blocked).
|
||||
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/azeroth-server -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DSCRIPTS=static -DMODULES=static
|
||||
make -j$(nproc) && make install
|
||||
```
|
||||
|
||||
C++20 required (`CMAKE_CXX_STANDARD 20`). Useful flags: `BUILD_TESTING=ON` (Google Test), `NOPCH=1` (disable precompiled headers). Full set in `conf/dist/config.cmake`. `compile_commands.json` is exported automatically.
|
||||
|
||||
Tests (Google Test, in `src/test/`): configure `-DBUILD_TESTING=ON`, then `ctest` or `./src/test/unit_tests` from the build dir.
|
||||
|
||||
## Repository layout
|
||||
|
||||
- `src/common/` — networking (Asio), crypto, config, logging, shared utilities.
|
||||
- `src/server/game/` — core gameplay; compiled into worldserver.
|
||||
- `src/server/scripts/` — content scripts grouped by region (`EasternKingdoms/`, `Northrend/`, …), class (`Spells/spell_mage.cpp`, …), and domain (`Commands/`, `Pet/`, `OutdoorPvP/`, `World/`).
|
||||
- `src/server/database/` — DB abstraction and schema updater.
|
||||
- `src/server/shared/` — code shared by auth and world servers.
|
||||
- `src/server/apps/{authserver,worldserver}/` — entry points (ports 3724 and 8085).
|
||||
- `src/test/` — Google Test unit tests + mocks.
|
||||
- `data/sql/` — `base/` (historical schema), `updates/db_*/` (merged), `updates/pending_db_*/` (in-flight, **edit here**), `custom/` (gitignored).
|
||||
- `modules/` — external modules (each a subdir with its own `CMakeLists.txt`). Disable with `-DDISABLED_AC_MODULES="mod1;mod2"`. See `modules/how_to_make_a_module.md`.
|
||||
- `apps/` — helper scripts; `apps/codestyle/` holds the lint scripts.
|
||||
- `conf/dist/` — distributed config templates; `conf/*.conf` is gitignored.
|
||||
- `deps/` — vendored third-party dependencies.
|
||||
|
||||
## Adding SQL updates
|
||||
|
||||
1. `cd data/sql/updates/pending_db_world/` (or `pending_db_auth` / `pending_db_characters`).
|
||||
2. `./create_sql.sh` generates an empty `rev_<timestamp>.sql` to write into.
|
||||
3. Conventions enforced by `apps/codestyle/codestyle-sql.py`: every `INSERT` preceded by a matching `DELETE` (idempotency); no double semicolons; no multiple blank lines; InnoDB engine.
|
||||
|
||||
The three databases:
|
||||
|
||||
- `acore_auth` — accounts, realm list, IP/account bans, session keys. Shared across all realms.
|
||||
- `acore_characters` — per-character state: characters, inventory, in-progress quests, mail, guilds, arena teams, achievements. One per realm.
|
||||
- `acore_world` — static game content: creature/gameobject/item/quest templates, spawn lists, loot tables, SmartAI scripts, gossip, conditions. Read-mostly; rebuilt from SQL.
|
||||
|
||||
## Code style
|
||||
|
||||
Formatting (charset, indent width, line length, final newline, trailing whitespace) follows `.editorconfig`.
|
||||
|
||||
Run the linters before claiming a change is done:
|
||||
|
||||
```bash
|
||||
python apps/codestyle/codestyle-cpp.py # C++
|
||||
python apps/codestyle/codestyle-sql.py # SQL (compares to origin/master)
|
||||
```
|
||||
|
||||
Hard rules (also enforced by CI with `-Werror`, plus `cppcheck`):
|
||||
|
||||
- 4-space indent for C++ (tabs forbidden); 2-space for JSON/YAML/sh/ts/js. UTF-8, LF, max 120 cols, trailing newline.
|
||||
- Allman braces. No braces around single-line statements. `if (x)` — never `if(x)` or `if ( x )`.
|
||||
- `auto const&` (not `const auto&`); `Type const*` (not `const Type*`).
|
||||
- Use `{}` format specifiers (`fmt`-style), not `%u`/`%s`.
|
||||
- Use the typed helpers, not raw flag access:
|
||||
- `IsPlayer()`, `IsCreature()`, `IsItem()`, … instead of `GetTypeId() == TYPEID_*`.
|
||||
- `GetNpcFlags()`, `HasNpcFlag()`, `SetNpcFlag()`, `RemoveNpcFlag()`, `ReplaceAllNpcFlags()` instead of `*Flag(UNIT_NPC_FLAGS, …)`.
|
||||
- `IsRefundable()`, `IsBOPTradable()`, `IsWrapped()` instead of `HasFlag(ITEM_FIELD_FLAGS, …)`.
|
||||
- `HasFlag(ItemFlag)` / `HasFlag2(ItemFlag2)` / `HasFlagCu(ItemFlagsCustom)` instead of bitwise `Flags & ITEM_FLAG…`.
|
||||
- `ObjectGuid::ToString().c_str()` instead of `ObjectGuid::GetCounter()`.
|
||||
|
||||
## Project conventions
|
||||
|
||||
- **Logging**: `LOG_INFO("category.sub", "msg with {}", arg)` (also `LOG_WARN`/`ERROR`/`DEBUG`/`TRACE`). Categories are hierarchical, dot-separated (`server.loading`, `entities.player`, `sql.dev`). No `printf`-style, no `sLog->`, no `TC_LOG_*`. Macro in `src/common/Logging/Log.h`.
|
||||
- **Random**: use helpers in `src/common/Utilities/Random.h` — `urand`, `irand`, `frand`, `rand32`, `rand_chance`, `roll_chance_f`, `roll_chance_i`. Not `std::rand` or `<random>`.
|
||||
- **Strings**: `Acore::StringFormat(fmt, args...)` (`{}` placeholders) — `src/common/Utilities/StringFormat.h`.
|
||||
- **Config**: `sConfigMgr->GetOption<T>("Name", default)`.
|
||||
- **Namespace**: project-wide `Acore::` (no `Trinity::` remnants — rename when porting from upstream forks).
|
||||
- **Long-lived references**: don't store a raw `Player*` / `Creature*` / `Unit*` past the current call/tick — the object can be removed (logout, despawn, instance unload) and the pointer dangles. Store the `ObjectGuid` and resolve at use time via `ObjectAccessor::FindPlayer(guid)`, `Map::GetCreature(guid)`, etc.
|
||||
- **DB queries**: use `PreparedStatement` (via `WorldDatabase` / `CharacterDatabase` / `LoginDatabase` and the prepared-statement enums), not raw query strings. Non-blocking reads go async: `_queryProcessor.AddCallback(db.AsyncQuery(stmt).WithPreparedCallback(...))` (or `WithCallback`). Multi-statement writes wrap in `SQLTransaction` + `Execute` / `AppendPreparedStatement`.
|
||||
- **Timed actions in AI**: use `EventMap` (event id → delay; simple) or `TaskScheduler` (lambdas, repeats, cancellation), both members of `CreatureAI` — don't roll your own tick counters. See any boss script under `src/server/scripts/`.
|
||||
|
||||
## Scripting registration
|
||||
|
||||
Scripts inherit from a `ScriptObject` subclass (`SpellScript`, `AuraScript`, `CreatureScript`, `InstanceMapScript`, `GameObjectScript`, `CommandScript`, …). Two registration styles coexist:
|
||||
|
||||
- **Spell / aura scripts**: `RegisterSpellScript(ClassName)` (or `RegisterSpellAndAuraScriptPair(...)`) inside `AddSC_<name>()`.
|
||||
- **Creature scripts**: prefer `RegisterCreatureAI(ClassName)` for new code; legacy zones still use `new ClassName();`. Match the surrounding pattern.
|
||||
|
||||
Then declare and call `AddSC_<name>()` from the regional loader (`Spells/spells_script_loader.cpp`, `EasternKingdoms/eastern_kingdoms_script_loader.cpp`, …).
|
||||
|
||||
**SmartAI** (data-driven creature behaviour) lives in the world DB's `smart_scripts` table, not C++ (engine: `src/server/game/AI/SmartScripts/`). For new creature behaviour prefer SmartAI (via the SQL update workflow); reach for `CreatureScript` only when SmartAI's event/action vocabulary isn't enough.
|
||||
|
||||
**Module hooks** (e.g. `OnPlayerLogin`, `OnWorldUpdate`, `OnSpellCast`) are declared in `src/server/game/Scripting/ScriptDefines/*.h`. Implement by inheriting the matching base (`PlayerScript`, `WorldScript`, …) and registering with `new MyClass();` (or its `RegisterXxxScript` macro) inside `AddSC_<name>()`. Full list: https://www.azerothcore.org/wiki/hooks-script.
|
||||
|
||||
Custom (non-upstream) scripts go in `src/server/scripts/Custom/` (gitignored).
|
||||
@@ -1,104 +1 @@
|
||||
# CLAUDE.md
|
||||
|
||||
AzerothCore is a C++ MMORPG server emulator for World of Warcraft 3.3.5a (WotLK), built with CMake, backed by MySQL.
|
||||
|
||||
## Agent rules
|
||||
|
||||
- **Do not configure or build unless explicitly asked.** Builds are slow (CMake + compile of a large C++ codebase) and rarely needed to make code changes.
|
||||
- **Never edit SQL files outside `data/sql/updates/pending_db_*/`.** `data/sql/base/`, `data/sql/archive/`, and `data/sql/updates/db_*/` are immutable (do not modify).
|
||||
- **Do not run git commands that modify repo state** (commit, branch, merge, rebase, reset, push, …) unless explicitly requested, and do not include them in plans. Read-only git (status, diff, log) is fine.
|
||||
|
||||
## Build
|
||||
|
||||
Out-of-source build is required (in-source is blocked by CMake).
|
||||
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/azeroth-server -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DSCRIPTS=static -DMODULES=static
|
||||
make -j$(nproc) && make install
|
||||
```
|
||||
|
||||
Compiler: **C++20** required (`CMAKE_CXX_STANDARD 20`). Useful CMake flags: `BUILD_TESTING=ON` (Google Test), `NOPCH=1` (disable precompiled headers). Full flag set in `conf/dist/config.cmake`. `compile_commands.json` is exported automatically.
|
||||
|
||||
Tests (Google Test, in `src/test/`): configure with `-DBUILD_TESTING=ON`, then `ctest` or `./src/test/unit_tests` from the build dir.
|
||||
|
||||
## Repository layout
|
||||
|
||||
- `src/common/` — networking (Asio), crypto, config, logging, shared utilities.
|
||||
- `src/server/game/` — core gameplay; compiled into worldserver.
|
||||
- `src/server/scripts/` — content scripts grouped by region (`EasternKingdoms/`, `Northrend/`, …), class (`Spells/spell_mage.cpp`, …), and domain (`Commands/`, `Pet/`, `OutdoorPvP/`, `World/`).
|
||||
- `src/server/database/` — DB abstraction and schema updater.
|
||||
- `src/server/shared/` — code shared by auth and world servers.
|
||||
- `src/server/apps/{authserver,worldserver}/` — entry points (ports 3724 and 8085).
|
||||
- `src/test/` — Google Test unit tests + mocks.
|
||||
- `data/sql/` — `base/` (historical schema), `updates/db_*/` (merged), `updates/pending_db_*/` (in-flight, **edit here**), `custom/` (gitignored).
|
||||
- `modules/` — external modules (each a subdir with its own `CMakeLists.txt`). Disable with `-DDISABLED_AC_MODULES="mod1;mod2"`. See `modules/how_to_make_a_module.md`.
|
||||
- `apps/` — helper scripts; `apps/codestyle/` holds the lint scripts (see below).
|
||||
- `conf/dist/` — distributed config templates; `conf/*.conf` is gitignored.
|
||||
- `deps/` — vendored third-party dependencies.
|
||||
|
||||
## Adding SQL updates
|
||||
|
||||
1. `cd data/sql/updates/pending_db_world/` (or `pending_db_auth` / `pending_db_characters`).
|
||||
2. `./create_sql.sh` generates an empty `rev_<timestamp>.sql` you write into.
|
||||
3. Required SQL conventions (enforced by `apps/codestyle/codestyle-sql.py`):
|
||||
- Every `INSERT` must be preceded by a matching `DELETE` (idempotency).
|
||||
- 4-space indent (no tabs), trailing newline, no double semicolons, no multiple blank lines.
|
||||
- Tables must use the InnoDB engine.
|
||||
|
||||
The three databases:
|
||||
|
||||
- `acore_auth` — accounts, realm list, IP/account bans, session keys. Shared across all realms.
|
||||
- `acore_characters` — per-character state: characters, inventory, in-progress quests, mail, guilds, arena teams, achievements. One per realm.
|
||||
- `acore_world` — static game content: creature/gameobject/item/quest templates, spawn lists, loot tables, SmartAI scripts, gossip, conditions. Read-mostly; rebuilt from SQL.
|
||||
|
||||
## Code style
|
||||
|
||||
Run the linters before claiming a change is done:
|
||||
|
||||
```bash
|
||||
python apps/codestyle/codestyle-cpp.py # C++
|
||||
python apps/codestyle/codestyle-sql.py # SQL (compares to origin/master)
|
||||
```
|
||||
|
||||
Hard rules (also enforced by CI with `-Werror`):
|
||||
|
||||
- 4-space indent for C++ (tabs forbidden); 2-space for JSON/YAML/sh/ts/js. UTF-8, LF, max 120 cols, trailing newline.
|
||||
- Allman braces. No braces around single-line statements. `if (x)` — never `if(x)` or `if ( x )`.
|
||||
- `auto const&` (not `const auto&`); `Type const*` (not `const Type*`).
|
||||
- Use `{}` format specifiers (`fmt`-style), not `%u`/`%s`.
|
||||
- Use the typed helpers, not raw flag access:
|
||||
- `IsPlayer()`, `IsCreature()`, `IsItem()`, … instead of `GetTypeId() == TYPEID_*`.
|
||||
- `GetNpcFlags()`, `HasNpcFlag()`, `SetNpcFlag()`, `RemoveNpcFlag()`, `ReplaceAllNpcFlags()` instead of `*Flag(UNIT_NPC_FLAGS, …)`.
|
||||
- `IsRefundable()`, `IsBOPTradable()`, `IsWrapped()` instead of `HasFlag(ITEM_FIELD_FLAGS, …)`.
|
||||
- `HasFlag(ItemFlag)` / `HasFlag2(ItemFlag2)` / `HasFlagCu(ItemFlagsCustom)` instead of bitwise `Flags & ITEM_FLAG…`.
|
||||
- `ObjectGuid::ToString().c_str()` instead of `ObjectGuid::GetCounter()`.
|
||||
|
||||
CI also runs `cppcheck`.
|
||||
|
||||
## Project conventions
|
||||
|
||||
- **Logging**: `LOG_INFO("category.sub", "msg with {}", arg)` (also `LOG_WARN`, `LOG_ERROR`, `LOG_DEBUG`, `LOG_TRACE`). Categories are hierarchical, dot-separated (e.g. `server.loading`, `entities.player`, `sql.dev`). No `printf`-style; no `sLog->`; no `TC_LOG_*`. Macro in `src/common/Logging/Log.h`.
|
||||
- **Random**: use project helpers from `src/common/Utilities/Random.h` — `urand`, `irand`, `frand`, `rand32`, `rand_chance`, `roll_chance_f`, `roll_chance_i`. Do not use `std::rand` or `<random>` directly.
|
||||
- **Strings**: `Acore::StringFormat(fmt, args...)` (wraps `fmt::format`, `{}` placeholders) — `src/common/Utilities/StringFormat.h`.
|
||||
- **Config**: read options with `sConfigMgr->GetOption<T>("Name", default)`.
|
||||
- **Namespace**: project-wide is `Acore::` (no `Trinity::` remnants — agents porting from upstream forks must rename).
|
||||
- **Long-lived references**: do not store a raw `Player*` / `Creature*` / `Unit*` past the current call/tick — the object can be removed (logout, despawn, instance unload) and the pointer dangles. Store the `ObjectGuid` and resolve at use time via `ObjectAccessor::FindPlayer(guid)`, `ObjectAccessor::GetCreature(*from, guid)`, `Map::GetCreature(guid)`, etc.
|
||||
- **DB queries**: use `PreparedStatement` (via `WorldDatabase` / `CharacterDatabase` / `LoginDatabase` and the prepared-statement enums) rather than raw query strings. Reads that don't need to block the world tick go through the async path: `_queryProcessor.AddCallback(db.AsyncQuery(stmt).WithPreparedCallback(...))` (or `WithCallback` for non-prepared). Multi-statement writes wrap in `SQLTransaction` + `Execute` / `AppendPreparedStatement`.
|
||||
- **Timed actions in AI**: use `EventMap` (event id → delay; simple) or `TaskScheduler` (lambdas, repeats, cancellation). Both are members of `CreatureAI`; see any boss script under `src/server/scripts/` for examples — don't roll your own tick counters.
|
||||
|
||||
## Scripting registration
|
||||
|
||||
Scripts inherit from a `ScriptObject` subclass (`SpellScript`, `AuraScript`, `CreatureScript`, `InstanceMapScript`, `GameObjectScript`, `CommandScript`, …). Two registration styles coexist:
|
||||
|
||||
- **Spell / aura scripts**: use the `RegisterSpellScript(ClassName)` (or `RegisterSpellAndAuraScriptPair(...)`) macro inside `AddSC_<name>()`.
|
||||
- **Creature scripts**: prefer `RegisterCreatureAI(ClassName)` for new code; legacy zones still use `new ClassName();`. Match the surrounding pattern.
|
||||
|
||||
Then declare and call `AddSC_<name>()` from the regional loader: `Spells/spells_script_loader.cpp`, `EasternKingdoms/eastern_kingdoms_script_loader.cpp`, etc.
|
||||
|
||||
**SmartAI** (data-driven creature behaviour) lives in the world DB's `smart_scripts` table — not in C++. Engine: `src/server/game/AI/SmartScripts/`. For new creature behaviour prefer SmartAI (added via the SQL update workflow); reach for `CreatureScript` only when SmartAI's event/action vocabulary isn't enough.
|
||||
|
||||
**Module hooks** (e.g. `OnPlayerLogin`, `OnWorldUpdate`, `OnSpellCast`) are declared in `src/server/game/Scripting/ScriptDefines/*.h`. Implement by inheriting the matching base (`PlayerScript`, `WorldScript`, …) and registering with `new MyClass();` (or its `RegisterXxxScript` macro where one exists) inside `AddSC_<name>()`. Full hook list: https://www.azerothcore.org/wiki/hooks-script.
|
||||
|
||||
Custom (non-upstream) scripts go in `src/server/scripts/Custom/` (gitignored).
|
||||
@AGENTS.md
|
||||
|
||||
+13
-1
@@ -225,9 +225,21 @@ LABEL description="AzerothCore client-data"
|
||||
ENV DATAPATH=/azerothcore/env/dist/data
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl unzip && \
|
||||
apt-get install -y curl unzip adduser && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG USER_ID=1000
|
||||
ARG GROUP_ID=1000
|
||||
ARG DOCKER_USER=acore
|
||||
|
||||
# To use GID/UID 1000 in ubuntu > 23.04 the existing user must be deleted
|
||||
# See https://bugs.launchpad.net/cloud-images/+bug/2005129
|
||||
RUN userdel --remove ubuntu \
|
||||
&& addgroup --gid "$GROUP_ID" "$DOCKER_USER" \
|
||||
&& adduser --disabled-password --gecos '' --uid "$USER_ID" --gid "$GROUP_ID" "$DOCKER_USER" \
|
||||
&& passwd -d "$DOCKER_USER" \
|
||||
&& chown -R "$DOCKER_USER:$DOCKER_USER" /azerothcore
|
||||
|
||||
COPY --chown=$DOCKER_USER:$DOCKER_USER apps apps
|
||||
|
||||
VOLUME /azerothcore/env/dist/data
|
||||
|
||||
@@ -12,15 +12,15 @@ fi
|
||||
UBUNTU_VERSION=$(lsb_release -sr);
|
||||
|
||||
case $UBUNTU_VERSION in
|
||||
"22.04")
|
||||
;;
|
||||
"24.04")
|
||||
;;
|
||||
"26.04")
|
||||
;;
|
||||
*)
|
||||
echo "########## ########## ##########"
|
||||
echo ""
|
||||
echo " using unsupported Ubuntu version " $UBUNTU_VERSION
|
||||
echo " please update to Ubuntu 22.04 or later"
|
||||
echo " please update to Ubuntu 24.04 or later"
|
||||
echo ""
|
||||
echo "########## ########## ##########"
|
||||
;;
|
||||
@@ -30,21 +30,32 @@ $SUDO apt update
|
||||
|
||||
# shared deps
|
||||
DEBIAN_FRONTEND="noninteractive" $SUDO \
|
||||
apt-get -y install ccache clang cmake curl google-perftools libmysqlclient-dev make unzip jq screen tmux \
|
||||
libreadline-dev libncurses5-dev libncursesw5-dev libbz2-dev git gcc g++ libssl-dev \
|
||||
apt-get -y install ccache clang cmake curl make unzip jq screen tmux \
|
||||
libreadline-dev libbz2-dev git gcc g++ libssl-dev \
|
||||
libncurses-dev libboost-all-dev gdb gdbserver expect
|
||||
|
||||
# version-specific deps
|
||||
if [[ "$UBUNTU_VERSION" == "26.04" ]]; then
|
||||
DEBIAN_FRONTEND="noninteractive" $SUDO \
|
||||
apt-get -y install libgoogle-perftools-dev default-libmysqlclient-dev
|
||||
else
|
||||
DEBIAN_FRONTEND="noninteractive" $SUDO \
|
||||
apt-get -y install google-perftools libmysqlclient-dev libncurses5-dev libncursesw5-dev
|
||||
fi
|
||||
|
||||
VAR_PATH="$CURRENT_PATH/../../../../var"
|
||||
|
||||
|
||||
# Do not install MySQL if we are in docker (It will be used a docker container instead) or we are explicitly skipping it.
|
||||
if [[ $DOCKER != 1 && $SKIP_MYSQL_INSTALL != 1 ]]; then
|
||||
# run noninteractive install for MYSQL 8.4 LTS
|
||||
wget https://dev.mysql.com/get/mysql-apt-config_0.8.35-1_all.deb -P "$VAR_PATH"
|
||||
# resolve expired key issue
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C
|
||||
DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.35-1_all.deb"
|
||||
$SUDO apt-get update
|
||||
if [[ "$UBUNTU_VERSION" == "24.04" ]]; then
|
||||
# run noninteractive install for MYSQL 8.4 LTS
|
||||
wget https://dev.mysql.com/get/mysql-apt-config_0.8.35-1_all.deb -P "$VAR_PATH"
|
||||
# resolve expired key issue
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A8D3785C
|
||||
DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.35-1_all.deb"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
DEBIAN_FRONTEND="noninteractive" $SUDO apt-get install -y mysql-server
|
||||
fi
|
||||
|
||||
|
||||
@@ -45,6 +45,174 @@ if [ ! -f "$BINARY" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function config_value() {
|
||||
local key="$1"
|
||||
|
||||
if [ -z "${CONFIG_ABS:-}" ] || [ ! -f "$CONFIG_ABS" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
awk -F= -v key="$key" '
|
||||
{
|
||||
configKey = $1
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", configKey)
|
||||
}
|
||||
configKey == key {
|
||||
value = $0
|
||||
sub(/^[^=]*=/, "", value)
|
||||
sub(/[[:space:]]*[#;].*$/, "", value)
|
||||
gsub(/"/, "", value)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
' "$CONFIG_ABS"
|
||||
}
|
||||
|
||||
function command_contains_config() {
|
||||
local command_line="$1"
|
||||
|
||||
if [ -z "${CONFIG_ABS:-}" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
case "$command_line" in
|
||||
*" -c $CONFIG_ABS"*|*" -c \"$CONFIG_ABS\""*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function find_existing_instances() {
|
||||
local line pid command_line
|
||||
|
||||
while IFS= read -r line; do
|
||||
pid="${line%% *}"
|
||||
command_line="${line#* }"
|
||||
|
||||
[ -z "$pid" ] && continue
|
||||
[ "$pid" = "$$" ] && continue
|
||||
[ "$pid" = "$PPID" ] && continue
|
||||
|
||||
case "$command_line" in
|
||||
*"/starter "*|*"pgrep "*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
if command_contains_config "$command_line"; then
|
||||
printf '%s\n' "$line"
|
||||
fi
|
||||
done < <(pgrep -fa "$EXECPATH" 2>/dev/null || true)
|
||||
}
|
||||
|
||||
function listening_on_port() {
|
||||
local port="$1"
|
||||
|
||||
command -v ss >/dev/null 2>&1 || return 1
|
||||
ss -H -ltn "sport = :$port" 2>/dev/null | grep -q .
|
||||
}
|
||||
|
||||
function guarded_ports() {
|
||||
local port soap_enabled soap_port
|
||||
|
||||
if [ "$BINFILE" != "worldserver" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
port="$(config_value "WorldServerPort" || true)"
|
||||
if [ -n "$port" ]; then
|
||||
printf '%s\n' "$port"
|
||||
fi
|
||||
|
||||
soap_enabled="$(config_value "SOAP.Enabled" || true)"
|
||||
soap_port="$(config_value "SOAP.Port" || true)"
|
||||
if [ "$soap_enabled" = "1" ] && [ -n "$soap_port" ]; then
|
||||
printf '%s\n' "$soap_port"
|
||||
fi
|
||||
}
|
||||
|
||||
function find_listening_guard_ports() {
|
||||
local port
|
||||
|
||||
while IFS= read -r port; do
|
||||
[ -z "$port" ] && continue
|
||||
if listening_on_port "$port"; then
|
||||
printf '%s\n' "$port"
|
||||
fi
|
||||
done < <(guarded_ports)
|
||||
}
|
||||
|
||||
function wait_for_startup_guard() {
|
||||
local timeout="${AC_STARTUP_GUARD_TIMEOUT:-90}"
|
||||
local interval="${AC_STARTUP_GUARD_INTERVAL:-2}"
|
||||
local elapsed=0
|
||||
local instances ports
|
||||
local sleep_for
|
||||
local warned=0
|
||||
|
||||
if [ "${AC_STARTUP_SKIP_GUARD:-0}" = "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
case "$timeout" in
|
||||
''|*[!0-9]*)
|
||||
echo "Startup guard: invalid AC_STARTUP_GUARD_TIMEOUT '$timeout', using 90."
|
||||
timeout=90
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$interval" in
|
||||
''|*[!0-9]*)
|
||||
echo "Startup guard: invalid AC_STARTUP_GUARD_INTERVAL '$interval', using 2."
|
||||
interval=2
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$interval" -lt 1 ]; then
|
||||
echo "Startup guard: AC_STARTUP_GUARD_INTERVAL must be at least 1, using 1."
|
||||
interval=1
|
||||
fi
|
||||
|
||||
while true; do
|
||||
instances="$(find_existing_instances)"
|
||||
ports="$(find_listening_guard_ports)"
|
||||
|
||||
if [ -z "$instances" ] && [ -z "$ports" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$elapsed" -ge "$timeout" ]; then
|
||||
echo "Startup guard: refusing to start duplicate $BINFILE after waiting ${timeout}s."
|
||||
if [ -n "$instances" ]; then
|
||||
echo "Startup guard: existing matching process(es):"
|
||||
echo "$instances"
|
||||
fi
|
||||
if [ -n "$ports" ]; then
|
||||
echo "Startup guard: configured port(s) still listening: $(echo "$ports" | paste -sd ',' -)"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$warned" -eq 0 ]; then
|
||||
echo "Startup guard: waiting for existing $BINFILE instance or configured port(s) to clear..."
|
||||
warned=1
|
||||
fi
|
||||
|
||||
sleep_for="$interval"
|
||||
if [ $((elapsed + sleep_for)) -gt "$timeout" ]; then
|
||||
sleep_for=$((timeout - elapsed))
|
||||
fi
|
||||
|
||||
[ "$sleep_for" -le 0 ] && continue
|
||||
|
||||
sleep "$sleep_for"
|
||||
elapsed=$((elapsed + sleep_for))
|
||||
done
|
||||
}
|
||||
|
||||
# Create crashes directory if it doesn't exist
|
||||
mkdir -p "$CRASHES_PATH"
|
||||
|
||||
@@ -55,6 +223,8 @@ cd "$BINPATH" || {
|
||||
|
||||
EXECPATH=$(realpath "$BINFILE")
|
||||
|
||||
wait_for_startup_guard
|
||||
|
||||
if [ "$GDB_ENABLED" -eq 1 ]; then
|
||||
echo "Starting $EXECPATH with GDB enabled"
|
||||
|
||||
|
||||
@@ -50,6 +50,86 @@ teardown() {
|
||||
[[ "$output" =~ "Test server starting" ]]
|
||||
}
|
||||
|
||||
@test "starter: should refuse duplicate binary/config start" {
|
||||
cat > "$TEST_DIR/bin/duplicate-server" << 'EOF'
|
||||
#!/usr/bin/env bash
|
||||
sleep 30
|
||||
EOF
|
||||
chmod +x "$TEST_DIR/bin/duplicate-server"
|
||||
|
||||
"$TEST_DIR/bin/duplicate-server" -c "$TEST_DIR/test-server.conf" &
|
||||
local duplicate_pid=$!
|
||||
|
||||
AC_STARTUP_GUARD_TIMEOUT=1 AC_STARTUP_GUARD_INTERVAL=1 run timeout 5s "$SCRIPT_DIR/starter" "$TEST_DIR/bin" "duplicate-server" "" "$TEST_DIR/test-server.conf" "" "" 0
|
||||
kill "$duplicate_pid" 2>/dev/null || true
|
||||
wait "$duplicate_pid" 2>/dev/null || true
|
||||
|
||||
debug_on_failure
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Startup guard: refusing to start duplicate duplicate-server" ]]
|
||||
[[ "$output" =~ "existing matching process" ]]
|
||||
}
|
||||
|
||||
@test "starter: should refuse occupied configured worldserver ports" {
|
||||
if ! command -v nc >/dev/null 2>&1; then
|
||||
skip "nc is required for this test"
|
||||
fi
|
||||
|
||||
cat > "$TEST_DIR/bin/worldserver" << 'EOF'
|
||||
#!/usr/bin/env bash
|
||||
echo "worldserver should not start"
|
||||
EOF
|
||||
chmod +x "$TEST_DIR/bin/worldserver"
|
||||
|
||||
local listen_port=40123
|
||||
cat > "$TEST_DIR/worldserver.conf" << EOF
|
||||
WorldServerPort = $listen_port
|
||||
SOAP.Enabled = 0
|
||||
EOF
|
||||
nc -l 127.0.0.1 "$listen_port" >/dev/null &
|
||||
local listener_pid=$!
|
||||
|
||||
AC_STARTUP_GUARD_TIMEOUT=1 AC_STARTUP_GUARD_INTERVAL=1 run timeout 5s "$SCRIPT_DIR/starter" "$TEST_DIR/bin" "worldserver" "" "$TEST_DIR/worldserver.conf" "" "" 0
|
||||
kill "$listener_pid" 2>/dev/null || true
|
||||
wait "$listener_pid" 2>/dev/null || true
|
||||
|
||||
debug_on_failure
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Startup guard: refusing to start duplicate worldserver" ]]
|
||||
[[ "$output" =~ "configured port(s) still listening: $listen_port" ]]
|
||||
}
|
||||
|
||||
@test "starter: should parse config keys literally for startup guard" {
|
||||
if ! command -v nc >/dev/null 2>&1; then
|
||||
skip "nc is required for this test"
|
||||
fi
|
||||
|
||||
cat > "$TEST_DIR/bin/worldserver" << 'EOF'
|
||||
#!/usr/bin/env bash
|
||||
echo "worldserver started"
|
||||
EOF
|
||||
chmod +x "$TEST_DIR/bin/worldserver"
|
||||
|
||||
local listen_port=40124
|
||||
cat > "$TEST_DIR/worldserver.conf" << EOF
|
||||
WorldServerPort = 40125
|
||||
SOAPXEnabled = 1
|
||||
SOAPXPort = $listen_port
|
||||
SOAP.Enabled = 0
|
||||
EOF
|
||||
nc -l 127.0.0.1 "$listen_port" >/dev/null &
|
||||
local listener_pid=$!
|
||||
|
||||
AC_STARTUP_GUARD_TIMEOUT=1 AC_STARTUP_GUARD_INTERVAL=1 run timeout 5s "$SCRIPT_DIR/starter" "$TEST_DIR/bin" "worldserver" "" "$TEST_DIR/worldserver.conf" "" "" 0
|
||||
kill "$listener_pid" 2>/dev/null || true
|
||||
wait "$listener_pid" 2>/dev/null || true
|
||||
|
||||
debug_on_failure
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "worldserver started" ]]
|
||||
[[ ! "$output" =~ "Startup guard: refusing" ]]
|
||||
}
|
||||
|
||||
# ===== SIMPLE RESTARTER TESTS =====
|
||||
|
||||
@test "simple-restarter: should fail with missing parameters" {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
-- DB update 2026_06_01_00 -> 2026_07_15_00
|
||||
-- RBAC permissions for core commands migrated off deprecated GM security levels.
|
||||
DELETE FROM `rbac_permissions` WHERE `id` BETWEEN 926 AND 938;
|
||||
INSERT INTO `rbac_permissions` (`id`, `name`) VALUES
|
||||
(926, 'Command: autobroadcast list'),
|
||||
(927, 'Command: autobroadcast add'),
|
||||
(928, 'Command: autobroadcast locale'),
|
||||
(929, 'Command: autobroadcast remove'),
|
||||
(930, 'Command: mail list'),
|
||||
(931, 'Command: mail return'),
|
||||
(932, 'Command: npc load'),
|
||||
(933, 'Command: pool info'),
|
||||
(934, 'Command: pool lookup'),
|
||||
(935, 'Command: spellinfo attributes'),
|
||||
(936, 'Command: spellinfo effects'),
|
||||
(937, 'Command: spellinfo targets'),
|
||||
(938, 'Command: spellinfo all');
|
||||
|
||||
-- Link each command permission to its role: 196 = Administrator role, 197 = Gamemaster role.
|
||||
DELETE FROM `rbac_linked_permissions` WHERE `linkedId` BETWEEN 926 AND 938;
|
||||
INSERT INTO `rbac_linked_permissions` (`id`, `linkedId`) VALUES
|
||||
(197, 926),
|
||||
(196, 927),
|
||||
(196, 928),
|
||||
(196, 929),
|
||||
(197, 930),
|
||||
(197, 931),
|
||||
(196, 932),
|
||||
(197, 933),
|
||||
(197, 934),
|
||||
(197, 935),
|
||||
(197, 936),
|
||||
(197, 937),
|
||||
(197, 938);
|
||||
@@ -0,0 +1,4 @@
|
||||
-- DB update 2026_07_11_00 -> 2026_07_12_00
|
||||
DELETE FROM `gameobject_template` WHERE `entry` = 301337;
|
||||
DELETE FROM `gameobject_template_addon` WHERE `entry` = 301337;
|
||||
DELETE FROM `gameobject_template_locale` WHERE `entry` = 301337;
|
||||
@@ -0,0 +1,14 @@
|
||||
-- DB update 2026_07_12_00 -> 2026_07_12_01
|
||||
DELETE FROM `areatrigger_teleport` WHERE `ID` = 5206;
|
||||
INSERT INTO `areatrigger_teleport` (`ID`, `Name`, `target_map`, `target_position_x`, `target_position_y`, `target_position_z`, `target_orientation`) VALUES
|
||||
(5206, 'Gundrak (North Entrance)', 604, 1882.32, 631.027, 176.696, 3.1415927);
|
||||
|
||||
-- For below, Wrath Classic (51943) has the player arrive at 1880.74, 853.76, 176.696, 3.14159.
|
||||
-- However, retail Wrath seems to put the player on the opposing side.
|
||||
-- The above areatrigger_teleport position was also sniffed from Wrath Classic, so that is being used for the LFG teleport,
|
||||
-- as it is on the correct side of the instance.
|
||||
|
||||
DELETE FROM `lfg_dungeon_template` WHERE `dungeonId` IN (216, 217);
|
||||
INSERT INTO `lfg_dungeon_template` (`dungeonId`, `name`, `position_x`, `position_y`, `position_z`, `orientation`, `VerifiedBuild`) VALUES
|
||||
(216, 'Gundrak', 1882.32, 631.027, 176.696, 3.1415927, 0), -- Normal
|
||||
(217, 'Gundrak', 1882.32, 631.027, 176.696, 3.1415927, 0); -- Heroic
|
||||
@@ -0,0 +1,4 @@
|
||||
-- DB update 2026_07_12_01 -> 2026_07_12_02
|
||||
DELETE FROM `spell_proc` WHERE `SpellId` = -13960;
|
||||
INSERT INTO `spell_proc` (`SpellId`, `SchoolMask`, `SpellFamilyName`, `SpellFamilyMask0`, `SpellFamilyMask1`, `SpellFamilyMask2`, `ProcFlags`, `SpellTypeMask`, `SpellPhaseMask`, `HitMask`, `AttributesMask`, `DisableEffectsMask`, `ProcsPerMinute`, `Chance`, `Cooldown`, `Charges`) VALUES
|
||||
(-13960, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- DB update 2026_07_12_02 -> 2026_07_13_00
|
||||
-- Hodir helpers (#26330): swap PACIFIED (131072) for IMMUNE_TO_NPC (512) per sniffs, so Freya's
|
||||
-- instance-wide Ground Tremor can't tag them and block her reset. SWIMMING kept where set;
|
||||
-- immunity is cleared on Hodir engage (boss_hodir.cpp).
|
||||
UPDATE `creature_template` SET `unit_flags` = (`unit_flags` & ~131072) | 512 WHERE `entry` IN (32893, 32897, 32900, 32901, 32941, 32946, 32948, 32950, 33325, 33326, 33327, 33328, 33330, 33331, 33332, 33333);
|
||||
@@ -0,0 +1,247 @@
|
||||
-- DB update 2026_07_13_00 -> 2026_07_14_00
|
||||
-- -------------------------------------------
|
||||
-- Acherus: The Ebon Hold (Eastern Plaguelands, map 0)
|
||||
-- Ebon Hold Re-haul Part 1: Training Room (Upper Floor)
|
||||
-- NPC Updates, waypoints, etc
|
||||
-- -------------------------------------------
|
||||
-- Corpulous (Entry 29205, GUID 125759)
|
||||
-- Spawn, home anchor, waypoints
|
||||
UPDATE `creature` SET `position_x`= 2438.5864, `position_y`= -5554.151, `position_z`= 420.79996, `orientation`= 3.9704, `wander_distance`= 0, `MovementType`= 2 WHERE `guid`= 125759;
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid`= 125759;
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125759, 1257590, 0, 0, 0, 0, 0, NULL);
|
||||
|
||||
DELETE FROM `waypoint_data` WHERE `id`= 1257590;
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
(1257590, 1, 2438.5864, -5554.151, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 2, 2417.4072, -5577.2603, 420.76572, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 3, 2418.5613, -5602.91, 420.8, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 4, 2430.862, -5626.988, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 5, 2462.9768, -5636.701, 420.6738, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 6, 2484.44, -5626.886, 420.42795, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 7, 2500.5364, -5595.214, 420.70547, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 8, 2492.1143, -5571.9556, 420.5543, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257590, 9, 2468.1182, -5553.14, 420.66876, NULL, 0, 0, 0, 0, 0, 100, 0);
|
||||
-- Knight of the Ebon Blade (Entry 29202, GUIDs 125750-751)
|
||||
-- Patrol pair
|
||||
UPDATE `creature` SET `wander_distance`= 0, `MovementType`= 2 WHERE `guid`= 125750;
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid`= 125750;
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125750, 1257500, 25280, 0, 1, 0, 0, NULL);
|
||||
|
||||
DELETE FROM `waypoint_data` WHERE `id`= 1257500;
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
(1257500, 1, 2433.57, -5553.8066, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 2, 2420.6594, -5566.9326, 420.63525, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 3, 2413.44, -5584.155, 420.79105, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 4, 2413.6936, -5602.8584, 420.8, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 5, 2420.8923, -5620.4087, 420.9104, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 6, 2433.7537, -5633.345, 420.73993, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 7, 2450.98, -5640.4243, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 8, 2469.9941, -5640.2007, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 9, 2487.1394, -5633.4414, 420.70834, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 10, 2500.1155, -5620.1206, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 11, 2507.2742, -5602.695, 420.84003, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 12, 2507.081, -5584.0166, 420.79993, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 13, 2500.0461, -5566.8726, 420.79993, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 14, 2486.89, -5553.5205, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 15, 2469.6587, -5546.532, 420.6999, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257500, 16, 2450.987, -5546.5557, 420.70724, NULL, 0, 0, 0, 0, 0, 100, 0);
|
||||
-- Follower 125751 (formation: follows 125750)
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID`= 125750 OR `memberGUID` IN (125750, 125751);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`, `point_1`, `point_2`) VALUES
|
||||
(125750, 125750, 0, 0, 7, 0, 0),
|
||||
(125750, 125751, 3, 90, 519, 0, 0);
|
||||
-- Knight of the Ebon Blade (Entry 29202, GUID 125754)
|
||||
-- Idle, no wander
|
||||
UPDATE `creature` SET `wander_distance`= 0, `MovementType`= 0 WHERE `guid`= 125754;
|
||||
-- Risen Drudge (Entry 29212, dead decorative corpses)
|
||||
-- Make the dead-posed drudges (StandState 7) untargettable: NOT_SELECTABLE (0x2000000) | template flags (32784) = 33587216
|
||||
UPDATE `creature` SET `unit_flags`= 33587216 WHERE `guid` IN (125766, 125768, 125770, 125771, 125773, 125774, 125775, 125776, 125779, 125780, 125781, 125784, 125786, 125788, 125789, 125790, 125792, 125793, 125794, 125795, 125796, 125797, 125799, 125800, 125801, 125803, 125804, 125805, 125807, 125808, 125809, 125810);
|
||||
-- Risen Drudge (Entry 29212) - remove 2 AC-only spawns absent from the retail sniff
|
||||
DELETE FROM `creature_addon` WHERE `guid` IN (125767, 125806);
|
||||
DELETE FROM `creature` WHERE `guid` IN (125767, 125806);
|
||||
-- Knight of the Ebon Blade (Entry 29202)
|
||||
-- Sheath weapons
|
||||
DELETE FROM `creature_addon` WHERE `guid` IN (125651, 125718, 125737, 125742, 125744, 125745, 125748, 125752, 125754, 125755);
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125651, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125718, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125737, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125742, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125744, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125745, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125748, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125752, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125754, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125755, 0, 0, 0, 0, 0, 0, NULL);
|
||||
-- Knight of the Ebon Blade (Entry 29202) - hidden in the rafters (Z~444)
|
||||
DELETE FROM `creature_addon` WHERE `guid` IN (125714, 125716, 125721, 125732, 125734, 125735, 125739, 125740);
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125714, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125716, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125721, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125732, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125734, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125735, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125739, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125740, 0, 0, 0, 0, 0, 0, NULL);
|
||||
-- Vigilant Gargoyle (Entry 29239) - all 8 (Training Room airspace)
|
||||
UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry`= 29239;
|
||||
|
||||
UPDATE `creature` SET `position_x`= 2434.2053, `position_y`= -5662.7285, `position_z`= 445.16962, `orientation`= 1.17073, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125826;
|
||||
UPDATE `creature` SET `position_x`= 2386.5322, `position_y`= -5591.9404, `position_z`= 445.1513, `orientation`= 6.21624, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125827;
|
||||
UPDATE `creature` SET `position_x`= 2391.4082, `position_y`= -5567.293, `position_z`= 444.41574, `orientation`= 5.88485, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125828;
|
||||
UPDATE `creature` SET `position_x`= 2483.3416, `position_y`= -5565.2993, `position_z`= 465.3641, `orientation`= 5.16390, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125829;
|
||||
UPDATE `creature` SET `position_x`= 2451.695, `position_y`= -5646.18, `position_z`= 434.86523, `orientation`= 2.34530, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125830;
|
||||
UPDATE `creature` SET `position_x`= 2430.0688, `position_y`= -5548.5366, `position_z`= 444.9371, `orientation`= 5.23599, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125831;
|
||||
UPDATE `creature` SET `position_x`= 2458.673, `position_y`= -5667.2334, `position_z`= 445.40192, `orientation`= 1.52346, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125832;
|
||||
UPDATE `creature` SET `position_x`= 2488.3079, `position_y`= -5584.386, `position_z`= 481.03842, `orientation`= 2.61030, `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125833;
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid` IN (125826, 125827, 125828, 125829, 125830, 125831, 125832, 125833);
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125826, 1258260, 0, 9, 1, 444, 0, NULL),
|
||||
(125827, 1258270, 0, 9, 1, 444, 0, NULL),
|
||||
(125828, 1258280, 0, 9, 1, 444, 0, NULL),
|
||||
(125829, 1258290, 0, 0, 1, 444, 0, NULL),
|
||||
(125830, 1258300, 0, 0, 1, 444, 0, NULL),
|
||||
(125831, 1258310, 0, 9, 1, 444, 0, NULL),
|
||||
(125832, 1258320, 0, 9, 1, 444, 0, NULL),
|
||||
(125833, 1258330, 0, 0, 1, 444, 0, NULL);
|
||||
-- Flight paths: patrollers
|
||||
DELETE FROM `waypoint_data` WHERE `id` IN (1258260, 1258270, 1258280, 1258290, 1258300, 1258310, 1258320, 1258330);
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
(1258260, 1, 2434.21, -5662.73, 445.17, 1.17073, 5, 65000, 0, 2, 0, 100, 0),
|
||||
(1258260, 2, 2437.7, -5653.26, 447.963, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258260, 3, 2449.12, -5599.83, 455.546, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258260, 4, 2463.19, -5545.83, 455.546, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258260, 5, 2462.16, -5519.81, 445.019, 4.71029, 5, 90000, 0, 2, 0, 100, 0),
|
||||
(1258260, 6, 2461.56, -5528.22, 448.031, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258260, 7, 2456.53, -5572.52, 454.475, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258260, 8, 2441, -5638.43, 454.475, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258270, 1, 2386.53, -5591.94, 445.151, 6.21624, 5, 100000, 0, 2, 0, 100, 0),
|
||||
(1258270, 2, 2396.07, -5594.14, 447.163, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258270, 3, 2436.67, -5610.5, 451.023, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258270, 4, 2473.23, -5633.96, 451.023, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258270, 5, 2490.76, -5638.42, 445.051, 2.17773, 5, 70000, 0, 2, 0, 100, 0),
|
||||
(1258270, 6, 2482.04, -5634.05, 447.43, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258270, 7, 2437.04, -5610.4, 455.235, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258270, 8, 2406.09, -5597.51, 451.596, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258280, 1, 2391.41, -5567.29, 444.416, 5.88485, 5, 82000, 0, 2, 0, 100, 0),
|
||||
(1258280, 2, 2398.94, -5570.2, 446.666, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258280, 3, 2440.29, -5586.84, 449.832, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258280, 4, 2505.82, -5612.69, 449.832, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258280, 5, 2529.11, -5619.67, 445.193, 2.81575, 5, 108000, 0, 2, 0, 100, 0),
|
||||
(1258280, 6, 2523.4, -5615.98, 445.721, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258280, 7, 2457.17, -5592.02, 452.471, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258280, 8, 2417.3, -5576.37, 452.471, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258290, 1, 2483.34, -5565.3, 465.364, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258290, 2, 2496.26, -5592, 465.364, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258290, 3, 2487.79, -5622.76, 465.364, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258290, 4, 2454.34, -5633.35, 465.364, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258290, 5, 2425.15, -5610.25, 465.364, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258290, 6, 2428.72, -5575.9, 465.364, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258290, 7, 2455.77, -5556.59, 465.364, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 1, 2451.7, -5646.18, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 2, 2439.38, -5633.58, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 3, 2435.8, -5612.65, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 4, 2415.5, -5592.08, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 5, 2420.02, -5564.28, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 6, 2442.14, -5561.77, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 7, 2455.83, -5546.95, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 8, 2473.27, -5540.89, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 9, 2485.81, -5558.36, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 10, 2486.56, -5585.89, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 11, 2512.01, -5598.65, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 12, 2498.43, -5621.79, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 13, 2473.17, -5621.09, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258300, 14, 2464.31, -5642.48, 434.865, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258310, 1, 2430.07, -5548.54, 444.937, 5.23599, 5, 95000, 0, 3, 0, 100, 0),
|
||||
(1258310, 2, 2440.2, -5553.77, 447.152, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258310, 3, 2479.28, -5579.33, 458.901, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258310, 4, 2508.33, -5596.56, 458.901, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258310, 5, 2534.25, -5595.29, 445.207, 3.49066, 5, 60000, 0, 3, 0, 100, 0),
|
||||
(1258310, 6, 2531.36, -5596.21, 447.298, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258310, 7, 2486.9, -5597.17, 448.131, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258310, 8, 2441.01, -5584.44, 448.52, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258320, 1, 2458.67, -5667.23, 445.402, 1.52346, 5, 75000, 0, 2, 0, 100, 0),
|
||||
(1258320, 2, 2461.16, -5656.92, 448.499, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258320, 3, 2478.7, -5619.14, 450.054, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258320, 4, 2495.67, -5585.25, 450.054, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258320, 5, 2504.88, -5563.37, 444.887, 3.80665, 5, 105000, 0, 2, 0, 100, 0),
|
||||
(1258320, 6, 2499.79, -5572.24, 447.68, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258320, 7, 2471.41, -5619.1, 454.346, NULL, 5, 0, 0, 3, 0, 100, 0),
|
||||
(1258330, 1, 2488.31, -5584.39, 481.038, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258330, 2, 2454.49, -5564.5, 481.038, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258330, 3, 2435.28, -5578, 481.038, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258330, 4, 2431.28, -5602.2, 481.038, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258330, 5, 2446.33, -5619.11, 481.038, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258330, 6, 2468.88, -5620.94, 481.038, NULL, 5, 0, 1, 0, 0, 100, 0),
|
||||
(1258330, 7, 2486.97, -5604.07, 481.038, NULL, 5, 0, 1, 0, 0, 100, 0);
|
||||
-- SmartAI (5 patrollers)
|
||||
DELETE FROM `smart_scripts` WHERE `entryorguid` IN (-125826, -125827, -125828, -125831, -125832) 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
|
||||
(-125826, 0, 0, 0, 108, 0, 100, 0, 1, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch A: SUBMERGED (idle)'),
|
||||
(-125826, 0, 1, 0, 108, 0, 100, 0, 2, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff A: STAND (flap)'),
|
||||
(-125826, 0, 2, 0, 108, 0, 100, 0, 5, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch B: SUBMERGED (idle)'),
|
||||
(-125826, 0, 3, 0, 108, 0, 100, 0, 6, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff B: STAND (flap)'),
|
||||
(-125827, 0, 0, 0, 108, 0, 100, 0, 1, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch A: SUBMERGED (idle)'),
|
||||
(-125827, 0, 1, 0, 108, 0, 100, 0, 2, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff A: STAND (flap)'),
|
||||
(-125827, 0, 2, 0, 108, 0, 100, 0, 5, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch B: SUBMERGED (idle)'),
|
||||
(-125827, 0, 3, 0, 108, 0, 100, 0, 6, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff B: STAND (flap)'),
|
||||
(-125828, 0, 0, 0, 108, 0, 100, 0, 1, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch A: SUBMERGED (idle)'),
|
||||
(-125828, 0, 1, 0, 108, 0, 100, 0, 2, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff A: STAND (flap)'),
|
||||
(-125828, 0, 2, 0, 108, 0, 100, 0, 5, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch B: SUBMERGED (idle)'),
|
||||
(-125828, 0, 3, 0, 108, 0, 100, 0, 6, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff B: STAND (flap)'),
|
||||
(-125831, 0, 0, 0, 108, 0, 100, 0, 1, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch A: SUBMERGED (idle)'),
|
||||
(-125831, 0, 1, 0, 108, 0, 100, 0, 2, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff A: STAND (flap)'),
|
||||
(-125831, 0, 2, 0, 108, 0, 100, 0, 5, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch B: SUBMERGED (idle)'),
|
||||
(-125831, 0, 3, 0, 108, 0, 100, 0, 6, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff B: STAND (flap)'),
|
||||
(-125832, 0, 0, 0, 108, 0, 100, 0, 1, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch A: SUBMERGED (idle)'),
|
||||
(-125832, 0, 1, 0, 108, 0, 100, 0, 2, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff A: STAND (flap)'),
|
||||
(-125832, 0, 2, 0, 108, 0, 100, 0, 5, 0, 0, 0, 0, 0, 90, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - perch B: SUBMERGED (idle)'),
|
||||
(-125832, 0, 3, 0, 108, 0, 100, 0, 6, 0, 0, 0, 0, 0, 91, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Gargoyle - takeoff B: STAND (flap)');
|
||||
-- Faction correction for vendors
|
||||
UPDATE `creature_template` SET `faction`= 2050 WHERE `entry` IN (28500, 29203, 29205, 29207, 29208);
|
||||
-- Master Siegesmith Corvus (Entry 28500, GUID 125869)
|
||||
UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry`= 28500;
|
||||
UPDATE `creature` SET `MovementType`= 2, `wander_distance`= 0 WHERE `guid`= 125869;
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid`= 125869;
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125869, 1258690, 0, 0, 1, 233, 0, NULL);
|
||||
|
||||
DELETE FROM `waypoint_data` WHERE `id`= 1258690;
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
-- 16-point weave loop (close-range sniff); ~22-32s craft pause at the 4 anvils (pt 5,7,10,16)
|
||||
(1258690, 1, 2438.5435, -5634.903, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 2, 2449.8833, -5641.199, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 3, 2460.378, -5650.1074, 420.8016, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 4, 2467.7244, -5654.838, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 5, 2466.9749, -5656.5464, 420.79996, NULL, 0, 22000, 0, 0, 0, 100, 0),
|
||||
(1258690, 6, 2440.863, -5647.832, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 7, 2433.588, -5649.348, 420.79996, NULL, 0, 32000, 0, 0, 0, 100, 0),
|
||||
(1258690, 8, 2449.2322, -5647.929, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 9, 2456.4922, -5647.385, 420.77432, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 10, 2466.7793, -5648.771, 420.79996, NULL, 0, 28000, 0, 0, 0, 100, 0),
|
||||
(1258690, 11, 2455.2024, -5647.0728, 420.78763, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 12, 2447.792, -5646.166, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 13, 2439.7908, -5641.177, 420.79996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 14, 2435.9255, -5634.6216, 420.72903, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 15, 2432.0063, -5633.902, 420.7254, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1258690, 16, 2431.6282, -5635.6772, 420.65244, NULL, 0, 25000, 0, 0, 0, 100, 0);
|
||||
|
||||
DELETE FROM `creature_text` WHERE `CreatureID`= 28500;
|
||||
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
|
||||
(28500, 0, 0, 'First, he gives away all of my precious Saronite, then he demands I begin crafting Runeblades of even greater power! Pah!', 12, 0, 100, 11, 0, 0, 0, 0, 'Master Siegesmith Corvus'),
|
||||
(28500, 0, 1, 'Fix my armor, Corvus! My blade is rusty, Corvus! ... when will these damned mortals ever learn to care for themselves?', 12, 0, 100, 11, 0, 0, 0, 0, 'Master Siegesmith Corvus'),
|
||||
(28500, 0, 2, 'I may not have any mindless servants, but at least I can see a quality blade made from time to time.', 12, 0, 100, 11, 0, 0, 0, 0, 'Master Siegesmith Corvus'),
|
||||
(28500, 0, 3, 'There is work to be done here, but do you see anyone jumping to help me? No! Wretches, all of them!', 12, 0, 100, 11, 0, 0, 0, 0, 'Master Siegesmith Corvus'),
|
||||
(28500, 0, 4, 'These Death Knights truly know no honor. First, they take my greatest arms and armor, crafted with care, forged with the Lich King''s blessing. Then they wander off to who knows where and come back dressed like Goblins, mumbling something about pawning off their armor for a few coppers! Pathetic!', 12, 0, 100, 11, 0, 0, 0, 0, 'Master Siegesmith Corvus'),
|
||||
(28500, 0, 5, 'What is this nonsense about the Light? I have weapons and armor to make!', 12, 0, 100, 11, 0, 0, 0, 0, 'Master Siegesmith Corvus');
|
||||
|
||||
DELETE FROM `smart_scripts` WHERE `entryorguid`= 28500 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
|
||||
(28500, 0, 0, 0, 1, 0, 100, 0, 2100000, 2700000, 2100000, 2700000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Corvus - ambient line at random ~40min');
|
||||
@@ -0,0 +1,95 @@
|
||||
-- DB update 2026_07_14_00 -> 2026_07_14_01
|
||||
-- -------------------------------------------
|
||||
-- Acherus: The Ebon Hold (Eastern Plaguelands, map 0)
|
||||
-- Ebon Hold Re-haul Part 2: Command Center (Bottom Floor)
|
||||
-- NPC Updates, waypoints, etc
|
||||
-- -------------------------------------------
|
||||
-- Faction corrections
|
||||
-- Siouxsie the Banshee (Entry 27928)
|
||||
UPDATE `creature_template` SET `faction`= 2050 WHERE `entry`= 27928;
|
||||
-- Squire Edwards (Entry 28486)
|
||||
UPDATE `creature_template` SET `faction`= 2050 WHERE `entry`= 28486;
|
||||
-- Coldwraith (Entry 28488)
|
||||
UPDATE `creature_template` SET `faction`= 2050 WHERE `entry`= 28488;
|
||||
-- Disciple of Frost (Entry 28490)
|
||||
UPDATE `creature_template` SET `faction`= 2082 WHERE `entry`= 28490;
|
||||
-- Lord Thorval (Entry 29196)
|
||||
UPDATE `creature_template` SET `faction`= 2050 WHERE `entry`= 29196;
|
||||
-- Knight of the Ebon Blade (Entry 31094) (part of the DK questline, is phased, but here nonetheless)
|
||||
UPDATE `creature_template` SET `faction`= 2050 WHERE `entry`= 31094;
|
||||
-- Knight of the Ebon Blade (Entry 29202) - Command Center floor - weapon sheathing
|
||||
DELETE FROM `creature_addon` WHERE `guid` IN (125719, 125727, 125730, 125733, 125738, 125743, 125749, 125753);
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125719, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125727, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125730, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125733, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125738, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125743, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125749, 0, 0, 0, 0, 0, 0, NULL),
|
||||
(125753, 0, 0, 0, 0, 0, 0, NULL);
|
||||
-- Knight of the Ebon Blade (Entry 29202, GUIDs 125728-729) - Command Center OUTER ring patrol
|
||||
UPDATE `creature` SET `wander_distance`= 0, `MovementType`= 2 WHERE `guid`= 125728;
|
||||
UPDATE `creature` SET `wander_distance`= 0, `MovementType`= 0 WHERE `guid`= 125729;
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid`= 125728;
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125728, 1257280, 25280, 0, 1, 0, 0, NULL);
|
||||
|
||||
DELETE FROM `waypoint_data` WHERE `id`= 1257280;
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
(1257280, 1, 2453.019, -5659.009, 376.963, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 2, 2436.503, -5654.292, 377.010, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 3, 2421.870, -5646.072, 377.085, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 4, 2409.462, -5633.826, 377.061, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 5, 2401.033, -5619.471, 377.059, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 6, 2396.028, -5602.503, 377.042, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 7, 2395.134, -5584.744, 377.032, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 8, 2400.252, -5568.880, 377.175, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 9, 2408.467, -5554.637, 377.087, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 10, 2422.047, -5542.582, 377.077, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 11, 2436.357, -5533.727, 377.048, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 12, 2453.023, -5528.724, 377.021, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 13, 2470.619, -5528.724, 376.969, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 14, 2486.896, -5534.060, 377.074, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 15, 2500.929, -5542.755, 377.070, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 16, 2511.579, -5556.004, 377.029, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 17, 2520.007, -5569.621, 377.088, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 18, 2525.179, -5586.072, 377.027, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 19, 2525.346, -5603.292, 376.955, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 20, 2521.025, -5618.274, 377.021, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 21, 2512.638, -5633.113, 377.022, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 22, 2500.469, -5644.090, 377.130, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 23, 2485.997, -5652.299, 377.080, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257280, 24, 2470.307, -5658.005, 377.048, NULL, 0, 0, 0, 0, 0, 100, 0);
|
||||
-- Follower 125729
|
||||
DELETE FROM `creature_formations` WHERE `leaderGUID`= 125728 OR `memberGUID` IN (125728, 125729);
|
||||
INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`, `point_1`, `point_2`) VALUES
|
||||
(125728, 125728, 0, 0, 7, 0, 0),
|
||||
(125728, 125729, 3, 270, 519, 0, 0);
|
||||
-- Knight of the Ebon Blade (Entry 29202, GUID 125746) - Command Center INNER ring patrol
|
||||
UPDATE `creature` SET `wander_distance`= 0, `MovementType`= 2 WHERE `guid`= 125746;
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid`= 125746;
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125746, 1257460, 25280, 0, 1, 0, 0, NULL);
|
||||
|
||||
DELETE FROM `waypoint_data` WHERE `id`= 1257460;
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
(1257460, 1, 2476.547, -5569.105, 366.987, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 2, 2465.888, -5564.635, 366.987, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 3, 2454.430, -5564.898, 366.996, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 4, 2439.936, -5573.309, 366.987, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 5, 2431.440, -5587.982, 366.987, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 6, 2431.727, -5599.489, 367.319, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 7, 2436.017, -5610.170, 367.033, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 8, 2442.707, -5616.534, 367.034, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 9, 2446.977, -5619.203, 366.987, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 10, 2456.497, -5622.578, 366.988, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 11, 2465.452, -5622.340, 367.009, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 12, 2468.838, -5621.344, 367.016, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 13, 2476.673, -5618.066, 366.987, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 14, 2482.256, -5612.358, 366.987, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 15, 2489.440, -5599.080, 367.042, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 16, 2488.702, -5586.393, 367.230, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257460, 17, 2485.971, -5579.744, 367.045, NULL, 0, 0, 0, 0, 0, 100, 0);
|
||||
@@ -0,0 +1,49 @@
|
||||
-- DB update 2026_07_14_01 -> 2026_07_14_02
|
||||
-- -------------------------------------------
|
||||
-- Acherus: The Ebon Hold (Eastern Plaguelands, map 0)
|
||||
-- Ebon Hold Re-haul Part 3: Lord Thorval (Walking Lecture)
|
||||
-- NPC Updates, waypoints, etc
|
||||
-- -------------------------------------------
|
||||
-- Lord Thorval (Entry 29196, GUID 125712)
|
||||
UPDATE `creature` SET `position_x`= 2527.7344, `position_y`= -5549.2075, `position_z`= 377.18475, `orientation`= 3.735, `wander_distance`= 0, `MovementType`= 2 WHERE `guid`= 125712;
|
||||
|
||||
DELETE FROM `creature_addon` WHERE `guid`= 125712;
|
||||
INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES
|
||||
(125712, 1257120, 0, 0, 1, 0, 0, NULL);
|
||||
-- Waypoints (22 min between lectures/walking cycle)
|
||||
DELETE FROM `waypoint_data` WHERE `id`= 1257120;
|
||||
INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `velocity`, `delay`, `smoothTransition`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES
|
||||
(1257120, 1, 2527.7344, -5549.2075, 377.18475, 3.735, 0, 1320000, 0, 0, 0, 100, 0),
|
||||
(1257120, 2, 2523.0664, -5544.4194, 377.22162, 4.433136, 0, 8000, 0, 0, 0, 100, 0),
|
||||
(1257120, 3, 2530.72, -5555.91, 377.13217, 3.263766, 0, 6500, 0, 0, 0, 100, 0),
|
||||
(1257120, 4, 2530.4983, -5555.5771, 377.135, 3.263766, 0, 8500, 0, 0, 0, 100, 0),
|
||||
(1257120, 5, 2523.0664, -5544.4194, 377.22162, 4.433136, 0, 6400, 0, 0, 0, 100, 0),
|
||||
(1257120, 6, 2523.2881, -5544.7523, 377.219, 4.433136, 0, 8500, 0, 0, 0, 100, 0),
|
||||
(1257120, 7, 2530.72, -5555.91, 377.13217, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257120, 8, 2523.0664, -5544.4194, 377.22162, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257120, 9, 2530.72, -5555.91, 377.13217, NULL, 0, 0, 0, 0, 0, 100, 0),
|
||||
(1257120, 10, 2523.0664, -5544.4194, 377.22162, 4.433136, 0, 10000, 0, 0, 0, 100, 0);
|
||||
-- Speech
|
||||
DELETE FROM `creature_text` WHERE (`CreatureID` = 29196) AND (`GroupID` IN (0, 1, 2, 3, 4, 5, 6, 7, 8));
|
||||
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
|
||||
(29196, 0, 0, 'As disciples of blood, you strive to master the very lifeforce of your enemies.', 12, 0, 100, 1, 0, 0, 29867, 0, 'Lord Thorval Lecture 1'),
|
||||
(29196, 1, 0, 'Be it by blade or incantation, blood feeds our attacks and weakens our foes.', 12, 0, 100, 1, 0, 0, 29868, 0, 'Lord Thorval Lecture 2'),
|
||||
(29196, 2, 0, 'True masters learn to make blood serve more than just their strength in battle.', 12, 0, 100, 1, 0, 0, 29869, 0, 'Lord Thorval Lecture 3'),
|
||||
(29196, 3, 0, 'Stripping energy from our foes, both fighting and fallen, allows us to persevere where lesser beings fall exhausted.', 12, 0, 100, 1, 0, 0, 29870, 0, 'Lord Thorval Lecture 4'),
|
||||
(29196, 4, 0, 'And every foe that falls, energy sapped and stolen, only further fuels our assault.', 12, 0, 100, 1, 0, 0, 29871, 0, 'Lord Thorval Lecture 5'),
|
||||
(29196, 5, 0, 'As masters of blood, we know battle without end...', 12, 0, 100, 1, 0, 0, 29872, 0, 'Lord Thorval Lecture 6'),
|
||||
(29196, 6, 0, 'We know hunger never to be quenched...', 12, 0, 100, 1, 0, 0, 29873, 0, 'Lord Thorval Lecture 7'),
|
||||
(29196, 7, 0, 'We know power never to be overcome...', 12, 0, 100, 1, 0, 0, 29874, 0, 'Lord Thorval Lecture 8'),
|
||||
(29196, 8, 0, 'As masters of blood, we are masters of life and death itself. Against us, even hope falls drained and lifeless.', 12, 0, 100, 1, 0, 0, 29875, 0, 'Lord Thorval Lecture 9');
|
||||
-- SAI to talk on waypoint reached
|
||||
DELETE FROM `smart_scripts` WHERE (`entryorguid`= 29196 AND `source_type`= 0) OR (`entryorguid`= 2919600 AND `source_type`= 9);
|
||||
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
|
||||
(29196, 0, 0, 0, 108, 0, 100, 0, 2, 1257120, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 2 Reached - Say Line 0'),
|
||||
(29196, 0, 1, 0, 108, 0, 100, 0, 3, 1257120, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 3 Reached - Say Line 1'),
|
||||
(29196, 0, 2, 0, 108, 0, 100, 0, 4, 1257120, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 4 Reached - Say Line 2'),
|
||||
(29196, 0, 3, 0, 108, 0, 100, 0, 5, 1257120, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 5 Reached - Say Line 3'),
|
||||
(29196, 0, 4, 0, 108, 0, 100, 0, 6, 1257120, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 6 Reached - Say Line 4'),
|
||||
(29196, 0, 5, 0, 108, 0, 100, 0, 7, 1257120, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 7 Reached - Say Line 5'),
|
||||
(29196, 0, 6, 0, 108, 0, 100, 0, 8, 1257120, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 8 Reached - Say Line 6'),
|
||||
(29196, 0, 7, 0, 108, 0, 100, 0, 9, 1257120, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 9 Reached - Say Line 7'),
|
||||
(29196, 0, 8, 0, 108, 0, 100, 0, 10, 1257120, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Thorval - On Waypoint 10 Reached - Say Line 8');
|
||||
@@ -0,0 +1,115 @@
|
||||
-- DB update 2026_07_14_02 -> 2026_07_14_03
|
||||
-- Reusable option sets for creature text groups
|
||||
CREATE TABLE IF NOT EXISTS `creature_text_option_sets` (
|
||||
`SetID` TINYINT UNSIGNED NOT NULL,
|
||||
`Cooldown` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Group cooldown in ms before it can fire again',
|
||||
`TriggerChance` TINYINT UNSIGNED NOT NULL DEFAULT 100 COMMENT '0-100 pct chance to fire at all',
|
||||
`PlayerOnly` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Only fire if target is a player',
|
||||
`comment` VARCHAR(255) DEFAULT '',
|
||||
PRIMARY KEY (`SetID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
DELETE FROM `creature_text_option_sets`;
|
||||
INSERT INTO `creature_text_option_sets` (`SetID`, `Cooldown`, `TriggerChance`, `PlayerOnly`, `comment`) VALUES
|
||||
(1, 6000, 100, 0, 'Standard kill yell: 6s cooldown'),
|
||||
(2, 6000, 100, 1, 'Player-only kill yell: 6s cooldown'),
|
||||
(3, 5000, 100, 1, 'Short cooldown player-only kill yell: 5s cooldown'),
|
||||
(4, 0, 30, 0, 'Chance-based: 30% trigger, no cooldown'),
|
||||
(5, 5000, 100, 0, 'Standard kill yell: 5s cooldown'),
|
||||
(6, 0, 25, 1, 'Chance-based: 25% trigger, player-only'),
|
||||
(7, 0, 50, 1, 'Chance-based: 50% trigger, player-only');
|
||||
|
||||
-- Assign option sets to creature text groups
|
||||
CREATE TABLE IF NOT EXISTS `creature_text_options` (
|
||||
`CreatureID` INT UNSIGNED NOT NULL,
|
||||
`GroupID` TINYINT UNSIGNED NOT NULL,
|
||||
`OptionSetID` TINYINT UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`CreatureID`, `GroupID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
DELETE FROM `creature_text_options`;
|
||||
INSERT INTO `creature_text_options` (`CreatureID`, `GroupID`, `OptionSetID`) VALUES
|
||||
-- Black Temple
|
||||
(22887, 2, 3), -- High Warlord Najentus SAY_SLAY
|
||||
(22871, 2, 2), -- Teron Gorefiend SAY_SLAY
|
||||
(22917, 1, 1), -- Illidan Stormrage SAY_ILLIDAN_KILL
|
||||
(22947, 3, 1), -- Mother Shahraz SAY_SLAY
|
||||
(22949, 3, 1), -- Gathios the Shatterer SAY_COUNCIL_SLAY
|
||||
(22950, 3, 1), -- High Nethermancer Zerevor SAY_COUNCIL_SLAY
|
||||
(22951, 3, 1), -- Lady Malande SAY_COUNCIL_SLAY
|
||||
(22952, 3, 1), -- Veras Darkshadow SAY_COUNCIL_SLAY
|
||||
|
||||
-- Azjol-Nerub
|
||||
(28684, 1, 1), -- Krik'thir the Gatewatcher SAY_SLAY
|
||||
(29120, 1, 1), -- Anub'arak SAY_SLAY
|
||||
|
||||
-- Drak'Tharon Keep
|
||||
(26631, 1, 1), -- Novos the Summoner SAY_KILL
|
||||
(26632, 1, 1), -- The Prophet Tharon'ja SAY_KILL
|
||||
(26630, 1, 1), -- Trollgore SAY_KILL
|
||||
|
||||
-- Gundrak
|
||||
(29306, 1, 1), -- Gal'darah SAY_SLAY
|
||||
(29305, 1, 1), -- Moorabi SAY_SLAY
|
||||
(29304, 1, 1), -- Slad'ran SAY_SLAY
|
||||
|
||||
-- The Nexus
|
||||
(26723, 1, 1), -- Keristrasza SAY_SLAY
|
||||
(26731, 1, 1), -- Grand Magus Telestra SAY_KILL
|
||||
(26794, 5, 1), -- Ormorok the Tree-Shaper SAY_KILL
|
||||
|
||||
-- Ruby Sanctum
|
||||
(39863, 6, 2), -- Halion SAY_KILL
|
||||
(39751, 2, 1), -- Baltharus the Warborn SAY_KILL
|
||||
(39746, 1, 1), -- General Zarithrian SAY_KILL
|
||||
(39747, 3, 1), -- Saviana Ragefire SAY_KILL
|
||||
|
||||
-- Icecrown Citadel
|
||||
(36597, 10, 3), -- The Lich King SAY_LK_KILL
|
||||
|
||||
-- Naxxramas
|
||||
(16061, 1, 4), -- Instructor Razuvious SAY_SLAY
|
||||
|
||||
-- Karazhan
|
||||
(15687, 2, 3), -- Moroes SAY_KILL
|
||||
|
||||
-- Battle for Mount Hyjal
|
||||
(17888, 0, 2), -- Kazrogal SAY_ONSLAY
|
||||
(17842, 1, 2), -- Azgalor SAY_ONSLAY
|
||||
(17767, 1, 2), -- Rage Winterchill SAY_ONSLAY
|
||||
(17808, 1, 2), -- Anetheron SAY_ONSLAY
|
||||
|
||||
-- Serpentshrine Cavern
|
||||
(21212, 6, 1), -- Lady Vashj SAY_SLAY
|
||||
(21216, 2, 1), -- Hydross the Unstable SAY_CLEAN_SLAY
|
||||
(21216, 5, 1), -- Hydross the Unstable SAY_CORRUPT_SLAY
|
||||
(21214, 5, 1), -- Fathom-Lord Karathress SAY_SLAY
|
||||
(21213, 3, 1), -- Morogrim Tidewalker SAY_SLAY
|
||||
|
||||
-- Gruul's Lair
|
||||
(19044, 3, 5), -- Gruul the Dragonkiller SAY_SLAY
|
||||
(18831, 3, 5), -- High King Maulgar SAY_SLAY
|
||||
|
||||
-- Magtheridon's Lair
|
||||
(17257, 2, 5), -- Magtheridon SAY_SLAY
|
||||
|
||||
-- Tempest Keep: The Eye
|
||||
(19516, 1, 5), -- Void Reaver SAY_SLAY
|
||||
|
||||
-- Black Temple (additional)
|
||||
(22948, 1, 1), -- Gurtogg Bloodboil SAY_SLAY
|
||||
(23418, 2, 1), -- Essence of Suffering SUFF_SAY_SLAY
|
||||
(23419, 1, 1), -- Essence of Desire DESI_SAY_SLAY
|
||||
(23420, 1, 1), -- Essence of Anger ANGER_SAY_SLAY
|
||||
|
||||
-- Molten Core
|
||||
(12018, 2, 6), -- Majordomo Executus SAY_SLAY
|
||||
(11502, 9, 6), -- Ragnaros SAY_KILL
|
||||
|
||||
-- Sunwell Plateau
|
||||
(24882, 6, 7), -- Brutallus YELL_KILL
|
||||
(25038, 1, 7), -- Felmyst YELL_KILL
|
||||
(24850, 1, 7), -- Kalecgos SAY_EVIL_SLAY
|
||||
|
||||
-- Tempest Keep: The Eye (additional)
|
||||
(18805, 2, 7); -- High Astromancer Solarian SAY_KILL
|
||||
@@ -0,0 +1,4 @@
|
||||
-- DB update 2026_07_14_03 -> 2026_07_15_00
|
||||
-- Missile Barrage: consume on FINISH instead of CAST; its mods (DURATION,
|
||||
-- ACTIVATION_TIME, COST) are applied inside handle_immediate, after CAST fires.
|
||||
UPDATE `spell_proc` SET `SpellPhaseMask` = 4 WHERE `SpellId` = 44401;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- DB update 2026_07_15_00 -> 2026_07_15_01
|
||||
-- Watcher Gashra's Enrage (52470) is a self-buff, but its -50% physical damage
|
||||
-- done effect (EFFECT_2) makes the core classify the whole aura as negative,
|
||||
-- so it shows as a debuff on the target frame.
|
||||
-- Mark all three effects positive (SPELL_ATTR0_CU_POSITIVE_EFF0 | EFF1 | EFF2).
|
||||
DELETE FROM `spell_custom_attr` WHERE `spell_id` = 52470;
|
||||
INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES (52470, 0x0E000000);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- DB update 2026_07_15_01 -> 2026_07_15_02
|
||||
--
|
||||
DELETE FROM `gossip_menu_option` WHERE `MenuID` = 6626 AND `OptionID` = 0;
|
||||
INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`) VALUES
|
||||
(6626, 0, 0, 'Please tell me the Phrase..', 0, 0, 0, 0, 0, 0, 0, '', 0);
|
||||
@@ -0,0 +1,6 @@
|
||||
-- DB update 2026_07_15_02 -> 2026_07_15_03
|
||||
-- Yogg Portal 'Descend Into Madness'
|
||||
-- Movement flags: DisableGravity + Root
|
||||
DELETE FROM `creature_template_movement` WHERE (`CreatureId` = 34072);
|
||||
INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES
|
||||
(34072, 0, 0, 1, 1, 0, 0, 0);
|
||||
@@ -0,0 +1,8 @@
|
||||
-- DB update 2026_07_15_03 -> 2026_07_15_04
|
||||
-- Sara should start friendly, becoming hostile when Yogg-Saron reveals himself (phase 2)
|
||||
UPDATE `creature_template` SET `faction` = 35 WHERE `entry` IN (33134, 34332);
|
||||
|
||||
-- Sara (1)
|
||||
DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 65719) AND (`SourceId` = 0) AND (`ElseGroup` = 1) AND (`ConditionTypeOrReference` = 31) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 3) AND (`ConditionValue2` = 34332) AND (`ConditionValue3` = 0);
|
||||
INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
|
||||
(13, 1, 65719, 0, 1, 31, 0, 3, 34332, 0, 0, 0, 0, '', 'Shadow Nova');
|
||||
@@ -0,0 +1,2 @@
|
||||
-- DB update 2026_07_15_04 -> 2026_07_16_00
|
||||
UPDATE `quest_offer_reward` SET `RewardText` = 'You''ve been a tremendous help here today; we now have a solid chance at unlocking the mystery of why these creatures choose to beach themselves on the coast of Darkshore. The idea that they might be fleeing the area around Teldrassil is disturbing. Should you come across others in your travels, please let me know. Our research here is but in its infancy.$B$BOn behalf of the Temple of the Moon, please accept this as thanks for the effort you have given. Thank you, $N!' WHERE `ID` = 4681;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- DB update 2026_07_16_00 -> 2026_07_16_01
|
||||
-- Aerial Command Unit: hover height and flags to match TDB (hover-based chase)
|
||||
-- 256 = UNIT_FLAG_IMMUNE_TO_PC (cleared by script at phase start), 512 = CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE
|
||||
UPDATE `creature_template` SET `HoverHeight` = 15, `unit_flags` = `unit_flags`|256, `flags_extra` = `flags_extra`|512 WHERE `entry` IN (33670, 34109);
|
||||
|
||||
-- Values ported from TrinityCore (TrinityCore/TrinityCore@7c13b383); zeroed combat reach
|
||||
-- made the hovering ACU unreachable for Magnetic Core's 15y check
|
||||
UPDATE `creature_model_info` SET `BoundingRadius` = 0.31, `CombatReach` = 5 WHERE `DisplayID` = 28979;
|
||||
Vendored
+1
-1
@@ -360,7 +360,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {\
|
||||
|
||||
// Bring in shared_ptr and weak_ptr
|
||||
#if (defined(__GNUC__) && defined(__APPLE__)) || defined(__linux__)
|
||||
#include <ciso646> // Defines _LIBCC_VERSION if linking against libc++ or does nothing
|
||||
#include <version> // Defines _LIBCC_VERSION if linking against libc++ or does nothing
|
||||
#endif
|
||||
#if (!defined(_LIBCPP_VERSION) && defined(__APPLE__)) || (!defined(_LIBCPP_VERSION) && defined(__linux__))
|
||||
# include <tr1/memory>
|
||||
|
||||
@@ -13,11 +13,13 @@ This PR proposes changes to:
|
||||
### AI-assisted Pull Requests
|
||||
|
||||
> [!IMPORTANT]
|
||||
> While the use of AI tools when preparing pull requests is not prohibited, contributors must clearly disclose when such tools have been used and specify the model involved.
|
||||
> Using AI tools to prepare pull requests is allowed, but it must be disclosed and it must follow our AC guidelines for AI Agentic Engineering (link below).
|
||||
>
|
||||
> Contributors are also expected to fully understand the changes they are submitting and must be able to explain and justify those changes when requested by maintainers.
|
||||
> You are expected to fully understand the changes you submit and to be able to explain and justify them when maintainers ask.
|
||||
|
||||
- [ ] AI tools (e.g. ChatGPT, Claude, or similar) were used entirely or partially in preparing this pull request. Please specify which tools were used, if any.
|
||||
- [ ] AI tools (e.g. Claude, ChatGPT, or similar) were used entirely or partially to prepare this pull request. If checked, specify which tools and models below.
|
||||
- Tools/models used:
|
||||
- [ ] I have read and understood the [AC guidelines for AI Agentic Engineering](https://www.azerothcore.org/wiki/agentic-engineering)
|
||||
|
||||
## Issues Addressed:
|
||||
<!-- If your fix has a relating issue, link it below -->
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ enum AccountFlag
|
||||
ACCOUNT_FLAG_EXPANSION2_COLLECTOR = 0x4000000, // NYI WotLK Collector's Edition
|
||||
ACCOUNT_FLAG_OVERMIND_LINKED = 0x8000000, // NYI Linked with Battle.net account
|
||||
ACCOUNT_FLAG_DEMOS = 0x10000000, // NYI UNK
|
||||
ACCOUNT_FLAG_DEATH_KNIGHT_OK = 0x20000000, // NYI Has level 55 on account?
|
||||
ACCOUNT_FLAG_DEATH_KNIGHT_OK = 0x20000000, // Allowed to create Death Knight (account met heroic level requirement on WotLK+)
|
||||
// Below might be StarCraft II related
|
||||
ACCOUNT_FLAG_S2_REQUIRE_IGR = 0x40000000, // NYI UNK
|
||||
ACCOUNT_FLAG_S2_TRIAL = 0x80000000, // NYI UNK
|
||||
|
||||
@@ -129,6 +129,10 @@ int main(int argc, char** argv)
|
||||
|
||||
std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
|
||||
|
||||
// Mark every realm offline on startup; each worldserver clears this flag for its own realm once it is ready.
|
||||
// This prevents realms from appearing online in the realm list when no worldserver is actually running.
|
||||
LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag | {}", REALM_FLAG_OFFLINE);
|
||||
|
||||
std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
|
||||
|
||||
// Get the list of realms for the server
|
||||
|
||||
@@ -3978,10 +3978,35 @@ Battleground.SpeedBuffRespawn = 150
|
||||
#
|
||||
# Battleground.Override.LowLevels.MinPlayers
|
||||
# Description: Overrides the minimum number of required players per team for all levels < MaxPlayerLevel
|
||||
# Used as fallback for any battleground without a per-battleground override (see below)
|
||||
# Default: 0 (Disabled)
|
||||
|
||||
Battleground.Override.LowLevels.MinPlayers = 0
|
||||
|
||||
#
|
||||
# Battleground.Override.LowLevels.MinPlayers.AV
|
||||
# Battleground.Override.LowLevels.MinPlayers.WS
|
||||
# Battleground.Override.LowLevels.MinPlayers.AB
|
||||
# Battleground.Override.LowLevels.MinPlayers.EY
|
||||
# Battleground.Override.LowLevels.MinPlayers.SA
|
||||
# Battleground.Override.LowLevels.MinPlayers.IC
|
||||
# Description: Per-battleground overrides of the minimum number of required players per team
|
||||
# for all levels < MaxPlayerLevel.
|
||||
# AV = Alterac Valley, WS = Warsong Gulch, AB = Arathi Basin,
|
||||
# EY = Eye of the Storm, SA = Strand of the Ancients, IC = Isle of Conquest
|
||||
# Resolution order: per-battleground key, then Battleground.Override.LowLevels.MinPlayers,
|
||||
# then the battleground_template DB value.
|
||||
# Matches entered through the Random Battleground queue always use the global
|
||||
# Battleground.Override.LowLevels.MinPlayers, never the per-battleground keys.
|
||||
# Default: 0 (Disabled, use Battleground.Override.LowLevels.MinPlayers)
|
||||
|
||||
Battleground.Override.LowLevels.MinPlayers.AV = 0
|
||||
Battleground.Override.LowLevels.MinPlayers.WS = 0
|
||||
Battleground.Override.LowLevels.MinPlayers.AB = 0
|
||||
Battleground.Override.LowLevels.MinPlayers.EY = 0
|
||||
Battleground.Override.LowLevels.MinPlayers.SA = 0
|
||||
Battleground.Override.LowLevels.MinPlayers.IC = 0
|
||||
|
||||
#
|
||||
# Battleground.Warsong.Flags
|
||||
# Description: Set the number of flags required for a team to win in Warsong battleground
|
||||
|
||||
@@ -92,6 +92,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(LOGIN_UPD_LAST_ATTEMPT_IP, "UPDATE account SET last_attempt_ip = ? WHERE username = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = 1 WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_UPTIME_PLAYERS, "UPDATE uptime SET uptime = ?, maxplayers = ? WHERE realmid = ? AND starttime = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_REALM_ONLINE, "UPDATE realmlist SET flag = flag & ~? WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_DEL_OLD_LOGS, "DELETE FROM logs WHERE (time + ?) < ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT_ACCESS, "DELETE FROM account_access WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM, "DELETE FROM account_access WHERE id = ? AND (RealmID = ? OR RealmID = -1)", CONNECTION_ASYNC);
|
||||
|
||||
@@ -76,6 +76,7 @@ enum LoginDatabaseStatements : uint32
|
||||
LOGIN_UPD_LAST_ATTEMPT_IP,
|
||||
LOGIN_UPD_ACCOUNT_ONLINE,
|
||||
LOGIN_UPD_UPTIME_PLAYERS,
|
||||
LOGIN_UPD_REALM_ONLINE,
|
||||
LOGIN_DEL_OLD_LOGS,
|
||||
LOGIN_DEL_ACCOUNT_ACCESS,
|
||||
LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM,
|
||||
|
||||
@@ -27,6 +27,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(WORLD_DEL_CRELINKED_RESPAWN, "DELETE FROM linked_respawn WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_REP_CREATURE_LINKED_RESPAWN, "REPLACE INTO linked_respawn (guid, linkedGuid) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEXT, "SELECT CreatureID, GroupID, ID, Text, Type, Language, Probability, Emote, Duration, Sound, BroadcastTextId, TextRange FROM creature_text", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEXT_OPTIONS, "SELECT o.CreatureID, o.GroupID, s.Cooldown, s.TriggerChance, s.PlayerOnly FROM creature_text_options o JOIN creature_text_option_sets s ON o.OptionSetID = s.SetID", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_SMART_SCRIPTS, "SELECT 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 FROM smart_scripts ORDER BY entryorguid, source_type, id, link", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_SMARTAI_WP, "SELECT entry, pointid, position_x, position_y, position_z, orientation, delay FROM waypoints ORDER BY entry, pointid", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_DEL_GAMEOBJECT, "DELETE FROM gameobject WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
@@ -32,6 +32,7 @@ enum WorldDatabaseStatements : uint32
|
||||
WORLD_DEL_CRELINKED_RESPAWN,
|
||||
WORLD_REP_CREATURE_LINKED_RESPAWN,
|
||||
WORLD_SEL_CREATURE_TEXT,
|
||||
WORLD_SEL_CREATURE_TEXT_OPTIONS,
|
||||
WORLD_SEL_SMART_SCRIPTS,
|
||||
WORLD_SEL_SMARTAI_WP,
|
||||
WORLD_DEL_GAMEOBJECT,
|
||||
|
||||
@@ -687,6 +687,19 @@ enum RBACPermissions
|
||||
RBAC_PERM_COMMAND_CHATFILTER_LIST = 923,
|
||||
RBAC_PERM_COMMAND_CHATFILTER_ADD = 924,
|
||||
RBAC_PERM_COMMAND_CHATFILTER_REMOVE = 925,
|
||||
RBAC_PERM_COMMAND_AUTOBROADCAST_LIST = 926,
|
||||
RBAC_PERM_COMMAND_AUTOBROADCAST_ADD = 927,
|
||||
RBAC_PERM_COMMAND_AUTOBROADCAST_LOCALE = 928,
|
||||
RBAC_PERM_COMMAND_AUTOBROADCAST_REMOVE = 929,
|
||||
RBAC_PERM_COMMAND_MAIL_LIST = 930,
|
||||
RBAC_PERM_COMMAND_MAIL_RETURN = 931,
|
||||
RBAC_PERM_COMMAND_NPC_LOAD = 932,
|
||||
RBAC_PERM_COMMAND_POOL_INFO = 933,
|
||||
RBAC_PERM_COMMAND_POOL_LOOKUP = 934,
|
||||
RBAC_PERM_COMMAND_SPELLINFO_ATTRIBUTES = 935,
|
||||
RBAC_PERM_COMMAND_SPELLINFO_EFFECTS = 936,
|
||||
RBAC_PERM_COMMAND_SPELLINFO_TARGETS = 937,
|
||||
RBAC_PERM_COMMAND_SPELLINFO_ALL = 938,
|
||||
// custom permissions 1000+
|
||||
RBAC_PERM_MAX
|
||||
};
|
||||
|
||||
@@ -18,12 +18,8 @@
|
||||
#include "Battleground.h"
|
||||
#include "ArenaSpectator.h"
|
||||
#include "ArenaTeam.h"
|
||||
#include "BattlegroundBE.h"
|
||||
#include "BattlegroundDS.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "BattlegroundNA.h"
|
||||
#include "BattlegroundRL.h"
|
||||
#include "BattlegroundRV.h"
|
||||
#include "BattlegroundUtils.h"
|
||||
#include "Chat.h"
|
||||
#include "ChatTextBuilder.h"
|
||||
#include "Creature.h"
|
||||
@@ -247,6 +243,12 @@ Battleground::~Battleground()
|
||||
delete itr.second;
|
||||
}
|
||||
|
||||
uint32 Battleground::GetMinPlayersPerTeam() const
|
||||
{
|
||||
uint32 lowLevelsOverride = GetLowLevelsMinPlayersOverride(GetBgTypeID());
|
||||
return (lowLevelsOverride && !isTemplate() && !isMaxLevel() && !isArena()) ? lowLevelsOverride : m_MinPlayersPerTeam;
|
||||
}
|
||||
|
||||
void Battleground::Update(uint32 diff)
|
||||
{
|
||||
// pussywizard:
|
||||
@@ -533,6 +535,8 @@ inline void Battleground::_ProcessJoin(uint32 diff)
|
||||
|
||||
// Mark setup as completed
|
||||
m_SetupCompleted = true;
|
||||
|
||||
sScriptMgr->OnBattlegroundSetup(this);
|
||||
}
|
||||
|
||||
// First announcement at 120s or 60s (Depending on BG or Arena and configured time)
|
||||
@@ -558,33 +562,6 @@ inline void Battleground::_ProcessJoin(uint32 diff)
|
||||
|
||||
if (StartMessageIds[BG_STARTING_EVENT_THIRD])
|
||||
SendBroadcastText(StartMessageIds[BG_STARTING_EVENT_THIRD], CHAT_MSG_BG_SYSTEM_NEUTRAL);
|
||||
|
||||
if (isArena())
|
||||
switch (GetBgTypeID())
|
||||
{
|
||||
case BATTLEGROUND_NA:
|
||||
DelObject(BG_NA_OBJECT_READY_MARKER_1);
|
||||
DelObject(BG_NA_OBJECT_READY_MARKER_2);
|
||||
break;
|
||||
case BATTLEGROUND_BE:
|
||||
DelObject(BG_BE_OBJECT_READY_MARKER_1);
|
||||
DelObject(BG_BE_OBJECT_READY_MARKER_2);
|
||||
break;
|
||||
case BATTLEGROUND_RL:
|
||||
DelObject(BG_RL_OBJECT_READY_MARKER_1);
|
||||
DelObject(BG_RL_OBJECT_READY_MARKER_2);
|
||||
break;
|
||||
case BATTLEGROUND_DS:
|
||||
DelObject(BG_DS_OBJECT_READY_MARKER_1);
|
||||
DelObject(BG_DS_OBJECT_READY_MARKER_2);
|
||||
break;
|
||||
case BATTLEGROUND_RV:
|
||||
DelObject(BG_RV_OBJECT_READY_MARKER_1);
|
||||
DelObject(BG_RV_OBJECT_READY_MARKER_2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Delay expired (after configured prep time)
|
||||
else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
|
||||
@@ -1380,22 +1357,6 @@ void Battleground::SpectatorsSendPacket(WorldPacket& data)
|
||||
(*itr)->SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
void Battleground::ReadyMarkerClicked(Player* p)
|
||||
{
|
||||
if (!isArena() || GetStatus() >= STATUS_IN_PROGRESS || GetStartDelayTime() <= BG_START_DELAY_15S || (m_Events & BG_STARTING_EVENT_3) || p->IsSpectator())
|
||||
return;
|
||||
readyMarkerClickedSet.insert(p->GetGUID());
|
||||
uint32 count = readyMarkerClickedSet.size();
|
||||
uint32 req = ArenaTeam::GetReqPlayersForType(GetArenaType());
|
||||
ChatHandler(p->GetSession()).SendNotification("You are marked as ready {}/{}", count, req);
|
||||
if (count == req)
|
||||
{
|
||||
m_Events |= BG_STARTING_EVENT_2;
|
||||
m_StartTime += GetStartDelayTime() - BG_START_DELAY_15S;
|
||||
SetStartDelayTime(BG_START_DELAY_15S);
|
||||
}
|
||||
}
|
||||
|
||||
void Battleground::BuildPvPLogDataPacket(WorldPacket& data)
|
||||
{
|
||||
uint8 type = (isArena() ? 1 : 0);
|
||||
|
||||
@@ -284,7 +284,6 @@ enum BGHonorMode
|
||||
};
|
||||
|
||||
#define ARENA_TIMELIMIT_POINTS_LOSS -16
|
||||
#define ARENA_READY_MARKER_ENTRY 301337
|
||||
|
||||
/*
|
||||
This class is used to:
|
||||
@@ -349,11 +348,7 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; }
|
||||
[[nodiscard]] uint32 GetMinPlayersPerTeam() const
|
||||
{
|
||||
auto lowLevelsOverride = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS);
|
||||
return (lowLevelsOverride && !isTemplate() && !isMaxLevel() && !isArena()) ? lowLevelsOverride : m_MinPlayersPerTeam;
|
||||
}
|
||||
[[nodiscard]] uint32 GetMinPlayersPerTeam() const;
|
||||
|
||||
[[nodiscard]] int32 GetStartDelayTime() const { return m_StartDelayTime; }
|
||||
[[nodiscard]] uint8 GetArenaType() const { return m_ArenaType; }
|
||||
@@ -388,6 +383,9 @@ public:
|
||||
void ModifyStartDelayTime(int32 diff) { m_StartDelayTime -= diff; }
|
||||
void SetStartDelayTime(int32 Time) { m_StartDelayTime = Time; }
|
||||
|
||||
[[nodiscard]] uint8 GetStartingEventFlags() const { return m_Events; }
|
||||
void AddStartingEventFlag(uint8 flag) { m_Events |= flag; }
|
||||
|
||||
void SetMaxPlayersPerTeam(uint32 MaxPlayers) { m_MaxPlayersPerTeam = MaxPlayers; }
|
||||
void SetMinPlayersPerTeam(uint32 MinPlayers) { m_MinPlayersPerTeam = MinPlayers; }
|
||||
|
||||
@@ -420,9 +418,6 @@ public:
|
||||
[[nodiscard]] BattlegroundPlayerMap const& GetPlayers() const { return m_Players; }
|
||||
[[nodiscard]] uint32 GetPlayersSize() const { return m_Players.size(); }
|
||||
|
||||
void ReadyMarkerClicked(Player* p); // pussywizard
|
||||
GuidSet readyMarkerClickedSet; // pussywizard
|
||||
|
||||
typedef std::unordered_map<ObjectGuid::LowType, BattlegroundScore*> BattlegroundScoreMap;
|
||||
typedef std::unordered_map<ObjectGuid, ArenaLogEntryData> ArenaLogEntryDataMap; // pussywizard
|
||||
ArenaLogEntryDataMap ArenaLogEntries; // pussywizard
|
||||
|
||||
@@ -948,7 +948,7 @@ BGFreeSlotQueueContainer& BattlegroundMgr::GetBGFreeSlotQueueStore(BattlegroundT
|
||||
|
||||
void BattlegroundMgr::AddToBGFreeSlotQueue(BattlegroundTypeId bgTypeId, Battleground* bg)
|
||||
{
|
||||
bgDataStore[bgTypeId].BGFreeSlotQueue.push_front(bg);
|
||||
bgDataStore[bgTypeId].BGFreeSlotQueue.push_back(bg);
|
||||
}
|
||||
|
||||
void BattlegroundMgr::RemoveFromBGFreeSlotQueue(BattlegroundTypeId bgTypeId, uint32 instanceId)
|
||||
|
||||
@@ -805,8 +805,7 @@ void BattlegroundQueue::BattlegroundQueueUpdate(uint32 diff, BattlegroundTypeId
|
||||
}
|
||||
};
|
||||
|
||||
// battleground with free slot for player should be always in the beggining of the queue
|
||||
// maybe it would be better to create bgfreeslotqueue for each bracket_id
|
||||
// fill existing joinable battlegrounds, oldest first, before a new instance may form below
|
||||
BGFreeSlotQueueContainer& bgQueues = sBattlegroundMgr->GetBGFreeSlotQueueStore(bgTypeId);
|
||||
for (BGFreeSlotQueueContainer::iterator itr = bgQueues.begin(); itr != bgQueues.end();)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,23 @@ uint32 GetMinPlayersPerTeam(Battleground* bg, PvPDifficultyEntry const* bracketE
|
||||
|
||||
auto maxPlayerLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
|
||||
auto isMaxLevel = bracketEntry->minLevel <= maxPlayerLevel && maxPlayerLevel <= bracketEntry->maxLevel;
|
||||
auto lowLevelsOverride = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS);
|
||||
auto lowLevelsOverride = GetLowLevelsMinPlayersOverride(bg->GetBgTypeID());
|
||||
|
||||
return (lowLevelsOverride && !isMaxLevel) ? lowLevelsOverride : bg->GetMinPlayersPerTeam();
|
||||
}
|
||||
|
||||
uint32 GetLowLevelsMinPlayersOverride(BattlegroundTypeId bgTypeId)
|
||||
{
|
||||
uint32 perBg = 0;
|
||||
switch (bgTypeId)
|
||||
{
|
||||
case BATTLEGROUND_AV: perBg = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_AV); break;
|
||||
case BATTLEGROUND_WS: perBg = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_WS); break;
|
||||
case BATTLEGROUND_AB: perBg = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_AB); break;
|
||||
case BATTLEGROUND_EY: perBg = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_EY); break;
|
||||
case BATTLEGROUND_SA: perBg = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_SA); break;
|
||||
case BATTLEGROUND_IC: perBg = sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_IC); break;
|
||||
default: break;
|
||||
}
|
||||
return perBg ? perBg : sWorld->getIntConfig(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS);
|
||||
}
|
||||
|
||||
@@ -5,4 +5,10 @@
|
||||
|
||||
uint32 GetMinPlayersPerTeam(Battleground* bg, PvPDifficultyEntry const* bracketEntry);
|
||||
|
||||
// Effective low-levels MinPlayersPerTeam override for a BG type:
|
||||
// the per-BG config if set, else the global one. 0 = no override.
|
||||
// BGs entered through the Random BG queue have BATTLEGROUND_RB as
|
||||
// their type, so they always resolve through the global key.
|
||||
uint32 GetLowLevelsMinPlayersOverride(BattlegroundTypeId bgTypeId);
|
||||
|
||||
#endif // BATTLEGROUNDUTILS_H
|
||||
|
||||
@@ -104,10 +104,7 @@ bool BattlegroundBE::SetupBattleground()
|
||||
|| !AddObject(BG_BE_OBJECT_DOOR_4, BG_BE_OBJECT_TYPE_DOOR_4, 6177.708f, 227.3481f, 3.604374f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY)
|
||||
// buffs
|
||||
|| !AddObject(BG_BE_OBJECT_BUFF_1, BG_BE_OBJECT_TYPE_BUFF_1, 6249.042f, 275.3239f, 11.22033f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f, 120)
|
||||
|| !AddObject(BG_BE_OBJECT_BUFF_2, BG_BE_OBJECT_TYPE_BUFF_2, 6228.26f, 249.566f, 11.21812f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120)
|
||||
// Arena Ready Marker
|
||||
|| !AddObject(BG_BE_OBJECT_READY_MARKER_1, ARENA_READY_MARKER_ENTRY, 6189.47f, 235.54f, 5.52f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300)
|
||||
|| !AddObject(BG_BE_OBJECT_READY_MARKER_2, ARENA_READY_MARKER_ENTRY, 6287.19f, 288.25f, 5.33f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300))
|
||||
|| !AddObject(BG_BE_OBJECT_BUFF_2, BG_BE_OBJECT_TYPE_BUFF_2, 6228.26f, 249.566f, 11.21812f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "BatteGroundBE: Failed to spawn some object!");
|
||||
return false;
|
||||
|
||||
@@ -28,9 +28,7 @@ enum BattlegroundBEObjectTypes
|
||||
BG_BE_OBJECT_DOOR_4 = 3,
|
||||
BG_BE_OBJECT_BUFF_1 = 4,
|
||||
BG_BE_OBJECT_BUFF_2 = 5,
|
||||
BG_BE_OBJECT_READY_MARKER_1 = 6,
|
||||
BG_BE_OBJECT_READY_MARKER_2 = 7,
|
||||
BG_BE_OBJECT_MAX = 8
|
||||
BG_BE_OBJECT_MAX = 6
|
||||
};
|
||||
|
||||
enum BattlegroundBEGameObjects
|
||||
|
||||
@@ -195,10 +195,7 @@ bool BattlegroundDS::SetupBattleground()
|
||||
// knockback creatures
|
||||
|| !AddCreature(BG_DS_NPC_TYPE_WATER_SPOUT, BG_DS_NPC_WATERFALL_KNOCKBACK, 1291.76f, 791.02f, 7.115f, 3.054326f, RESPAWN_IMMEDIATELY)
|
||||
|| !AddCreature(BG_DS_NPC_TYPE_WATER_SPOUT, BG_DS_NPC_PIPE_KNOCKBACK_1, 1369.977f, 817.2882f, 16.08718f, 3.106686f, RESPAWN_IMMEDIATELY)
|
||||
|| !AddCreature(BG_DS_NPC_TYPE_WATER_SPOUT, BG_DS_NPC_PIPE_KNOCKBACK_2, 1212.833f, 765.3871f, 16.09484f, 0.0f, RESPAWN_IMMEDIATELY)
|
||||
// Arena Ready Marker
|
||||
|| !AddObject(BG_DS_OBJECT_READY_MARKER_1, ARENA_READY_MARKER_ENTRY, 1229.44f, 759.35f, 17.89f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300)
|
||||
|| !AddObject(BG_DS_OBJECT_READY_MARKER_2, ARENA_READY_MARKER_ENTRY, 1352.90f, 822.77f, 17.96f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300))
|
||||
|| !AddCreature(BG_DS_NPC_TYPE_WATER_SPOUT, BG_DS_NPC_PIPE_KNOCKBACK_2, 1212.833f, 765.3871f, 16.09484f, 0.0f, RESPAWN_IMMEDIATELY))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "BatteGroundDS: Failed to spawn some object!");
|
||||
return false;
|
||||
|
||||
@@ -30,9 +30,7 @@ enum BattlegroundDSObjectTypes
|
||||
BG_DS_OBJECT_WATER_2 = 3,
|
||||
BG_DS_OBJECT_BUFF_1 = 4,
|
||||
BG_DS_OBJECT_BUFF_2 = 5,
|
||||
BG_DS_OBJECT_READY_MARKER_1 = 6,
|
||||
BG_DS_OBJECT_READY_MARKER_2 = 7,
|
||||
BG_DS_OBJECT_MAX = 8
|
||||
BG_DS_OBJECT_MAX = 6
|
||||
};
|
||||
|
||||
enum BattlegroundDSGameObjects
|
||||
|
||||
@@ -80,10 +80,7 @@ bool BattlegroundNA::SetupBattleground()
|
||||
|| !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064f, 2858.438f, 10.23631f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f, RESPAWN_IMMEDIATELY)
|
||||
// buffs
|
||||
|| !AddObject(BG_NA_OBJECT_BUFF_1, BG_NA_OBJECT_TYPE_BUFF_1, 4009.189941f, 2895.250000f, 13.052700f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f, 120)
|
||||
|| !AddObject(BG_NA_OBJECT_BUFF_2, BG_NA_OBJECT_TYPE_BUFF_2, 4103.330078f, 2946.350098f, 13.051300f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120)
|
||||
// Arena Ready Marker
|
||||
|| !AddObject(BG_NA_OBJECT_READY_MARKER_1, ARENA_READY_MARKER_ENTRY, 4090.46f, 2875.43f, 12.16f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300)
|
||||
|| !AddObject(BG_NA_OBJECT_READY_MARKER_2, ARENA_READY_MARKER_ENTRY, 4022.82f, 2966.61f, 12.17f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300))
|
||||
|| !AddObject(BG_NA_OBJECT_BUFF_2, BG_NA_OBJECT_TYPE_BUFF_2, 4103.330078f, 2946.350098f, 13.051300f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "BatteGroundNA: Failed to spawn some object!");
|
||||
return false;
|
||||
|
||||
@@ -28,9 +28,7 @@ enum BattlegroundNAObjectTypes
|
||||
BG_NA_OBJECT_DOOR_4 = 3,
|
||||
BG_NA_OBJECT_BUFF_1 = 4,
|
||||
BG_NA_OBJECT_BUFF_2 = 5,
|
||||
BG_NA_OBJECT_READY_MARKER_1 = 6,
|
||||
BG_NA_OBJECT_READY_MARKER_2 = 7,
|
||||
BG_NA_OBJECT_MAX = 8
|
||||
BG_NA_OBJECT_MAX = 6
|
||||
};
|
||||
|
||||
enum BattlegroundNAGameObjects
|
||||
|
||||
@@ -105,10 +105,7 @@ bool BattlegroundRL::SetupBattleground()
|
||||
|| !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648f, 1730.557f, 31.60557f, 1.684245f, 0, 0, 0.7460582f, 0.6658807f, RESPAWN_IMMEDIATELY)
|
||||
// buffs
|
||||
|| !AddObject(BG_RL_OBJECT_BUFF_1, BG_RL_OBJECT_TYPE_BUFF_1, 1328.719971f, 1632.719971f, 36.730400f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f, 120)
|
||||
|| !AddObject(BG_RL_OBJECT_BUFF_2, BG_RL_OBJECT_TYPE_BUFF_2, 1243.300049f, 1699.170044f, 34.872601f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120)
|
||||
// Arena Ready Marker
|
||||
|| !AddObject(BG_RL_OBJECT_READY_MARKER_1, ARENA_READY_MARKER_ENTRY, 1298.61f, 1598.59f, 31.62f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300)
|
||||
|| !AddObject(BG_RL_OBJECT_READY_MARKER_2, ARENA_READY_MARKER_ENTRY, 1273.71f, 1734.05f, 31.61f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300))
|
||||
|| !AddObject(BG_RL_OBJECT_BUFF_2, BG_RL_OBJECT_TYPE_BUFF_2, 1243.300049f, 1699.170044f, 34.872601f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "BatteGroundRL: Failed to spawn some object!");
|
||||
return false;
|
||||
|
||||
@@ -26,9 +26,7 @@ enum BattlegroundRLObjectTypes
|
||||
BG_RL_OBJECT_DOOR_2 = 1,
|
||||
BG_RL_OBJECT_BUFF_1 = 2,
|
||||
BG_RL_OBJECT_BUFF_2 = 3,
|
||||
BG_RL_OBJECT_READY_MARKER_1 = 4,
|
||||
BG_RL_OBJECT_READY_MARKER_2 = 5,
|
||||
BG_RL_OBJECT_MAX = 6
|
||||
BG_RL_OBJECT_MAX = 4
|
||||
};
|
||||
|
||||
enum BattlegroundRLObjects
|
||||
|
||||
@@ -240,12 +240,7 @@ bool BattlegroundRV::SetupBattleground()
|
||||
|| !AddObject(BG_RV_OBJECT_PILAR_1, BG_RV_OBJECT_TYPE_PILAR_1, 763.632385f, -306.162384f, 25.909504f, 3.141593f, 0, 0, 0, RESPAWN_IMMEDIATELY)
|
||||
|| !AddObject(BG_RV_OBJECT_PILAR_2, BG_RV_OBJECT_TYPE_PILAR_2, 723.644287f, -284.493256f, 24.648525f, 3.141593f, 0, 0, 0, RESPAWN_IMMEDIATELY)
|
||||
|| !AddObject(BG_RV_OBJECT_PILAR_3, BG_RV_OBJECT_TYPE_PILAR_3, 763.611145f, -261.856750f, 25.909504f, 0.000000f, 0, 0, 0, RESPAWN_IMMEDIATELY)
|
||||
|| !AddObject(BG_RV_OBJECT_PILAR_4, BG_RV_OBJECT_TYPE_PILAR_4, 802.211609f, -284.493256f, 24.648525f, 0.000000f, 0, 0, 0, RESPAWN_IMMEDIATELY)
|
||||
|
||||
// Arena Ready Marker
|
||||
|| !AddObject(BG_RV_OBJECT_READY_MARKER_1, ARENA_READY_MARKER_ENTRY, 769.93f, -301.04f, 2.80f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300)
|
||||
|| !AddObject(BG_RV_OBJECT_READY_MARKER_2, ARENA_READY_MARKER_ENTRY, 757.02f, -267.30f, 2.80f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 300)
|
||||
)
|
||||
|| !AddObject(BG_RV_OBJECT_PILAR_4, BG_RV_OBJECT_TYPE_PILAR_4, 802.211609f, -284.493256f, 24.648525f, 0.000000f, 0, 0, 0, RESPAWN_IMMEDIATELY))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "BatteGroundRV: Failed to spawn some object!");
|
||||
return false;
|
||||
|
||||
@@ -22,53 +22,50 @@
|
||||
|
||||
enum BattlegroundRVObjectTypes
|
||||
{
|
||||
BG_RV_OBJECT_BUFF_1,
|
||||
BG_RV_OBJECT_BUFF_2,
|
||||
BG_RV_OBJECT_FIRE_1,
|
||||
BG_RV_OBJECT_FIRE_2,
|
||||
BG_RV_OBJECT_FIREDOOR_1,
|
||||
BG_RV_OBJECT_FIREDOOR_2,
|
||||
BG_RV_OBJECT_BUFF_1 = 0,
|
||||
BG_RV_OBJECT_BUFF_2 = 1,
|
||||
BG_RV_OBJECT_FIRE_1 = 2,
|
||||
BG_RV_OBJECT_FIRE_2 = 3,
|
||||
BG_RV_OBJECT_FIREDOOR_1 = 4,
|
||||
BG_RV_OBJECT_FIREDOOR_2 = 5,
|
||||
|
||||
BG_RV_OBJECT_PILAR_1,
|
||||
BG_RV_OBJECT_PILAR_3,
|
||||
BG_RV_OBJECT_GEAR_1,
|
||||
BG_RV_OBJECT_GEAR_2,
|
||||
BG_RV_OBJECT_PILAR_1 = 6,
|
||||
BG_RV_OBJECT_PILAR_3 = 7,
|
||||
BG_RV_OBJECT_GEAR_1 = 8,
|
||||
BG_RV_OBJECT_GEAR_2 = 9,
|
||||
|
||||
BG_RV_OBJECT_PILAR_2,
|
||||
BG_RV_OBJECT_PILAR_4,
|
||||
BG_RV_OBJECT_PULLEY_1,
|
||||
BG_RV_OBJECT_PULLEY_2,
|
||||
BG_RV_OBJECT_PILAR_2 = 10,
|
||||
BG_RV_OBJECT_PILAR_4 = 11,
|
||||
BG_RV_OBJECT_PULLEY_1 = 12,
|
||||
BG_RV_OBJECT_PULLEY_2 = 13,
|
||||
|
||||
BG_RV_OBJECT_ELEVATOR_1,
|
||||
BG_RV_OBJECT_ELEVATOR_2,
|
||||
BG_RV_OBJECT_ELEVATOR_1 = 14,
|
||||
BG_RV_OBJECT_ELEVATOR_2 = 15,
|
||||
|
||||
BG_RV_OBJECT_READY_MARKER_1,
|
||||
BG_RV_OBJECT_READY_MARKER_2,
|
||||
|
||||
BG_RV_OBJECT_MAX,
|
||||
BG_RV_OBJECT_MAX = 16
|
||||
};
|
||||
|
||||
enum BattlegroundRVGameObjects
|
||||
{
|
||||
BG_RV_OBJECT_TYPE_BUFF_1 = 184663,
|
||||
BG_RV_OBJECT_TYPE_BUFF_2 = 184664,
|
||||
BG_RV_OBJECT_TYPE_FIRE_1 = 192704,
|
||||
BG_RV_OBJECT_TYPE_FIRE_2 = 192705,
|
||||
BG_RV_OBJECT_TYPE_BUFF_1 = 184663,
|
||||
BG_RV_OBJECT_TYPE_BUFF_2 = 184664,
|
||||
BG_RV_OBJECT_TYPE_FIRE_1 = 192704,
|
||||
BG_RV_OBJECT_TYPE_FIRE_2 = 192705,
|
||||
|
||||
BG_RV_OBJECT_TYPE_FIREDOOR_2 = 192387,
|
||||
BG_RV_OBJECT_TYPE_FIREDOOR_1 = 192388,
|
||||
BG_RV_OBJECT_TYPE_PULLEY_1 = 192389,
|
||||
BG_RV_OBJECT_TYPE_PULLEY_2 = 192390,
|
||||
BG_RV_OBJECT_TYPE_GEAR_1 = 192393,
|
||||
BG_RV_OBJECT_TYPE_GEAR_2 = 192394,
|
||||
BG_RV_OBJECT_TYPE_FIREDOOR_2 = 192387,
|
||||
BG_RV_OBJECT_TYPE_FIREDOOR_1 = 192388,
|
||||
BG_RV_OBJECT_TYPE_PULLEY_1 = 192389,
|
||||
BG_RV_OBJECT_TYPE_PULLEY_2 = 192390,
|
||||
BG_RV_OBJECT_TYPE_GEAR_1 = 192393,
|
||||
BG_RV_OBJECT_TYPE_GEAR_2 = 192394,
|
||||
|
||||
BG_RV_OBJECT_TYPE_ELEVATOR_1 = 194582,
|
||||
BG_RV_OBJECT_TYPE_ELEVATOR_2 = 194586,
|
||||
BG_RV_OBJECT_TYPE_ELEVATOR_1 = 194582,
|
||||
BG_RV_OBJECT_TYPE_ELEVATOR_2 = 194586,
|
||||
|
||||
BG_RV_OBJECT_TYPE_PILAR_1 = 194583, // axe
|
||||
BG_RV_OBJECT_TYPE_PILAR_2 = 194584, // arena
|
||||
BG_RV_OBJECT_TYPE_PILAR_3 = 194585, // lightning
|
||||
BG_RV_OBJECT_TYPE_PILAR_4 = 194587, // ivory
|
||||
BG_RV_OBJECT_TYPE_PILAR_1 = 194583, // axe
|
||||
BG_RV_OBJECT_TYPE_PILAR_2 = 194584, // arena
|
||||
BG_RV_OBJECT_TYPE_PILAR_3 = 194585, // lightning
|
||||
BG_RV_OBJECT_TYPE_PILAR_4 = 194587, // ivory
|
||||
};
|
||||
|
||||
enum BattlegroundRVData
|
||||
|
||||
@@ -483,7 +483,7 @@ uint32 CalendarMgr::GetPlayerNumPending(ObjectGuid guid)
|
||||
std::string CalendarEvent::BuildCalendarMailSubject(ObjectGuid remover) const
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm << remover.ToString() << ':' << _title;
|
||||
strm << remover.GetRawValue() << ':' << _title;
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -3817,6 +3817,21 @@ void Creature::ClearTextRepeatGroup(uint8 textGroup)
|
||||
groupItr->second.clear();
|
||||
}
|
||||
|
||||
bool Creature::IsTextOnCooldown(uint8 textGroup) const
|
||||
{
|
||||
auto itr = m_textCooldowns.find(textGroup);
|
||||
if (itr == m_textCooldowns.end())
|
||||
return false;
|
||||
|
||||
return GameTime::GetGameTime().count() < itr->second;
|
||||
}
|
||||
|
||||
void Creature::SetTextCooldown(uint8 textGroup, uint32 cooldownMs)
|
||||
{
|
||||
m_textCooldowns[textGroup] =
|
||||
GameTime::GetGameTime().count() + ((cooldownMs + 999) / 1000);
|
||||
}
|
||||
|
||||
void Creature::SetRespawnTime(uint32 respawn)
|
||||
{
|
||||
m_respawnTime = respawn ? GameTime::GetGameTime().count() + respawn : 0;
|
||||
|
||||
@@ -411,6 +411,9 @@ public:
|
||||
void SetTextRepeatId(uint8 textGroup, uint8 id);
|
||||
void ClearTextRepeatGroup(uint8 textGroup);
|
||||
|
||||
bool IsTextOnCooldown(uint8 textGroup) const;
|
||||
void SetTextCooldown(uint8 textGroup, uint32 cooldownMs);
|
||||
|
||||
bool IsFreeToMove();
|
||||
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
|
||||
static constexpr uint32 MOVE_BACKWARDS_CHECK_INTERVAL = 2000;
|
||||
@@ -548,6 +551,7 @@ private:
|
||||
ObjectGuid _spellFocusTarget; ///> Saved target during spell focus for restoration
|
||||
|
||||
CreatureTextRepeatGroup m_textRepeat;
|
||||
std::unordered_map<uint8, uint32> m_textCooldowns; // groupID -> expiry time (s)
|
||||
|
||||
bool _isMissingSwimmingFlagOutOfCombat;
|
||||
|
||||
|
||||
@@ -1846,14 +1846,19 @@ void GameObject::Use(Unit* user)
|
||||
if (GetUniqueUseCount() == info->summoningRitual.reqParticipants)
|
||||
return;
|
||||
|
||||
SpellCastResult castResult;
|
||||
if (info->summoningRitual.animSpell)
|
||||
player->CastSpell(player, info->summoningRitual.animSpell, true);
|
||||
castResult = player->CastSpell(player, info->summoningRitual.animSpell, true);
|
||||
else
|
||||
player->CastSpell(player, GetSpellId(),
|
||||
castResult = player->CastSpell(player, GetSpellId(),
|
||||
TriggerCastFlags(TRIGGERED_IGNORE_EFFECTS
|
||||
| TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD
|
||||
| TRIGGERED_IGNORE_POWER_AND_REAGENT_COST
|
||||
| TRIGGERED_CAST_DIRECTLY));
|
||||
|
||||
if (castResult != SPELL_CAST_OK)
|
||||
return;
|
||||
|
||||
AddUniqueUse(player);
|
||||
|
||||
// full amount unique participants including original summoner
|
||||
|
||||
@@ -11682,6 +11682,12 @@ void Player::SendInitialPacketsAfterAddToMap()
|
||||
auraList.front()->HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true);
|
||||
}
|
||||
|
||||
// Explicitly synchronize CAN_FLY state with client on login to prevent
|
||||
// players from retaining flight ability after disconnecting during a
|
||||
// teleport from a flyable to non-flyable zone (e.g. entering an instance).
|
||||
if (!HasIncreaseMountedFlightSpeedAura() && !HasFlyAura())
|
||||
SetCanFly(false);
|
||||
|
||||
// Fix mount, update block gets messed somewhere
|
||||
{
|
||||
if (!isBeingLoaded() && GetMountBlockId() && !HasMountedAura())
|
||||
|
||||
@@ -1888,6 +1888,10 @@ InventoryResult Player::CanEquipNewItem(uint8 slot, uint16& dest, uint32 item, b
|
||||
if (pItem)
|
||||
{
|
||||
InventoryResult result = CanEquipItem(slot, dest, pItem, swap);
|
||||
// Random-property items queue themselves on creation (SetItemRandomProperties
|
||||
// -> SetState(ITEM_CHANGED, owner)); the probe must leave the update queue
|
||||
// before deletion or the queue keeps a pointer to freed memory.
|
||||
pItem->RemoveFromUpdateQueueOf(const_cast<Player*>(this));
|
||||
delete pItem;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -384,8 +384,6 @@ Unit::Unit() : WorldObject(),
|
||||
for (uint8 i = 0; i < MAX_GAMEOBJECT_SLOT; ++i)
|
||||
m_ObjectSlot[i].Clear();
|
||||
|
||||
m_auraUpdateIterator = m_ownedAuras.end();
|
||||
|
||||
m_interruptMask = 0;
|
||||
m_transform = 0;
|
||||
m_canModifyStats = false;
|
||||
@@ -4034,13 +4032,16 @@ void Unit::_UpdateSpells(uint32 time)
|
||||
}
|
||||
}
|
||||
|
||||
// m_auraUpdateIterator can be updated in indirect called code at aura remove to skip next planned to update but removed auras
|
||||
for (m_auraUpdateIterator = m_ownedAuras.begin(); m_auraUpdateIterator != m_ownedAuras.end();)
|
||||
{
|
||||
Aura* i_aura = m_auraUpdateIterator->second;
|
||||
++m_auraUpdateIterator; // need shift to next for allow update if need into aura update
|
||||
i_aura->UpdateOwner(time, this);
|
||||
}
|
||||
// snapshot - UpdateOwner can mutate the map; pointers stay valid (removed auras
|
||||
// are deleted later in _DeleteRemovedAuras)
|
||||
m_auraUpdateSnapshot.clear();
|
||||
m_auraUpdateSnapshot.reserve(m_ownedAuras.size());
|
||||
for (auto& [id, aura] : m_ownedAuras)
|
||||
m_auraUpdateSnapshot.push_back(aura);
|
||||
|
||||
for (Aura* aura : m_auraUpdateSnapshot)
|
||||
if (!aura->IsRemoved())
|
||||
aura->UpdateOwner(time, this);
|
||||
|
||||
// remove expired auras - do that after updates(used in scripts?)
|
||||
for (AuraMap::iterator i = m_ownedAuras.begin(); i != m_ownedAuras.end();)
|
||||
@@ -4986,10 +4987,6 @@ void Unit::RemoveOwnedAura(AuraMap::iterator& i, AuraRemoveMode removeMode)
|
||||
Aura* aura = i->second;
|
||||
ASSERT(!aura->IsRemoved());
|
||||
|
||||
// if unit currently update aura list then make safe update iterator shift to next
|
||||
if (m_auraUpdateIterator == i && m_auraUpdateIterator != m_ownedAuras.end())
|
||||
++m_auraUpdateIterator;
|
||||
|
||||
m_ownedAuras.erase(i);
|
||||
m_removedAuras.push_back(aura);
|
||||
|
||||
@@ -5063,6 +5060,9 @@ void Unit::RemoveAura(AuraApplicationMap::iterator& i, AuraRemoveMode mode)
|
||||
// Remove aura - for Area and Target auras
|
||||
if (aura->GetOwner() == this)
|
||||
aura->Remove(mode);
|
||||
|
||||
// Aura::Remove can cascade into m_appliedAuras mutations - reset again for the caller
|
||||
i = m_appliedAuras.begin();
|
||||
}
|
||||
|
||||
void Unit::RemoveAura(uint32 spellId, ObjectGuid caster, uint8 reqEffMask, AuraRemoveMode removeMode)
|
||||
@@ -5150,7 +5150,8 @@ void Unit::RemoveAppliedAuras(std::function<bool(AuraApplication const*)> const&
|
||||
{
|
||||
for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
|
||||
{
|
||||
if (check(iter->second))
|
||||
// RemoveAura no-ops on applications already mid-removal
|
||||
if (!iter->second->GetRemoveMode() && check(iter->second))
|
||||
{
|
||||
RemoveAura(iter);
|
||||
continue;
|
||||
@@ -5176,9 +5177,11 @@ void Unit::RemoveAppliedAuras(uint32 spellId, std::function<bool(AuraApplication
|
||||
{
|
||||
for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
|
||||
{
|
||||
if (check(iter->second))
|
||||
// RemoveAura no-ops on applications already mid-removal
|
||||
if (!iter->second->GetRemoveMode() && check(iter->second))
|
||||
{
|
||||
RemoveAura(iter);
|
||||
iter = m_appliedAuras.lower_bound(spellId);
|
||||
continue;
|
||||
}
|
||||
++iter;
|
||||
@@ -7428,24 +7431,27 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
|
||||
else if (victim->IsCreature())
|
||||
victim->ToCreature()->UpdateLeashExtensionTime();
|
||||
|
||||
// set position before any AI calls/assistance
|
||||
//if (IsCreature())
|
||||
// ToCreature()->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());
|
||||
// player-controlled creatures (pets, charms) enter combat on contact instead
|
||||
// (melee swing execution or spell launch/hit, see Unit::AtTargetAttacked)
|
||||
if (creature && !IsControlledByPlayer())
|
||||
// Player-controlled creatures (pets, charms) enter combat on contact instead
|
||||
// (melee swing execution or spell launch/hit, see Unit::AtTargetAttacked).
|
||||
// Non-controllable guardians (e.g. Shaman Elementals, Infernal) have no attack
|
||||
// command and must engage immediately so their AI can chase and attack.
|
||||
|
||||
if (creature && (!IsControlledByPlayer() || (IsGuardian() && !IsControllableGuardian())))
|
||||
{
|
||||
EngageWithTarget(victim);
|
||||
|
||||
creature->SendAIReaction(AI_REACTION_HOSTILE);
|
||||
if (!IsControlledByPlayer())
|
||||
{
|
||||
creature->SendAIReaction(AI_REACTION_HOSTILE);
|
||||
|
||||
/// @todo: Implement aggro range, detection range and assistance range templates
|
||||
if (!(creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE)))
|
||||
creature->CallAssistance();
|
||||
/// @todo: Implement aggro range, detection range and assistance range templates
|
||||
if (!(creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE)))
|
||||
creature->CallAssistance();
|
||||
|
||||
creature->SetAssistanceTimer(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD));
|
||||
creature->SetAssistanceTimer(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD));
|
||||
|
||||
SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
||||
SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// delay offhand weapon attack by 50% of the base attack time
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "ThreatManager.h"
|
||||
#include "UnitDefines.h"
|
||||
#include "UnitUtils.h"
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
@@ -666,15 +667,15 @@ public:
|
||||
typedef std::unordered_set<Unit*> AttackerSet;
|
||||
typedef std::set<Unit*> ControlSet;
|
||||
|
||||
typedef std::multimap<uint32, Aura*> AuraMap;
|
||||
typedef boost::container::flat_multimap<uint32, Aura*> AuraMap;
|
||||
typedef std::pair<AuraMap::const_iterator, AuraMap::const_iterator> AuraMapBounds;
|
||||
typedef std::pair<AuraMap::iterator, AuraMap::iterator> AuraMapBoundsNonConst;
|
||||
|
||||
typedef std::multimap<uint32, AuraApplication*> AuraApplicationMap;
|
||||
typedef boost::container::flat_multimap<uint32, AuraApplication*> AuraApplicationMap;
|
||||
typedef std::pair<AuraApplicationMap::const_iterator, AuraApplicationMap::const_iterator> AuraApplicationMapBounds;
|
||||
typedef std::pair<AuraApplicationMap::iterator, AuraApplicationMap::iterator> AuraApplicationMapBoundsNonConst;
|
||||
|
||||
typedef std::multimap<AuraStateType, AuraApplication*> AuraStateAurasMap;
|
||||
typedef boost::container::flat_multimap<AuraStateType, AuraApplication*> AuraStateAurasMap;
|
||||
typedef std::pair<AuraStateAurasMap::const_iterator, AuraStateAurasMap::const_iterator> AuraStateAurasMapBounds;
|
||||
|
||||
typedef std::vector<AuraEffect*> AuraEffectList;
|
||||
@@ -2166,7 +2167,7 @@ protected:
|
||||
AuraMap m_ownedAuras;
|
||||
AuraApplicationMap m_appliedAuras;
|
||||
AuraList m_removedAuras;
|
||||
AuraMap::iterator m_auraUpdateIterator;
|
||||
std::vector<Aura*> m_auraUpdateSnapshot; // _UpdateSpells scratch buffer
|
||||
uint32 m_removedAurasCount;
|
||||
|
||||
AuraEffectList m_modAuras[TOTAL_AURAS];
|
||||
|
||||
@@ -1960,6 +1960,17 @@ void GameEventMgr::SetHolidayEventTime(GameEventData& event)
|
||||
|
||||
time_t curTime = GameTime::GetGameTime().count();
|
||||
|
||||
if (holiday->Looping)
|
||||
{
|
||||
// Looping events (Battleground Call to Arms) carry a single past anchor in the DBC and
|
||||
// recur via event.Occurence. Anchor to that DBC date so the window keeps the correct phase
|
||||
// instead of inheriting the wrong time of day from a stale game_event.start_time.
|
||||
if (time_t start = HolidayDateCalculator::FindLoopingStartTime(holiday->Date[0], stageOffset, event.Occurence, curTime))
|
||||
event.Start = start;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!singleDate)
|
||||
{
|
||||
time_t start = HolidayDateCalculator::FindStartTimeForStage(
|
||||
|
||||
@@ -671,3 +671,28 @@ time_t HolidayDateCalculator::FindStartTimeForStage(const uint32_t* packedDates,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t HolidayDateCalculator::FindLoopingStartTime(uint32_t packedAnchor, time_t stageOffset,
|
||||
uint32_t occurenceMinutes, time_t curTime)
|
||||
{
|
||||
if (!packedAnchor)
|
||||
return 0;
|
||||
|
||||
std::tm timeInfo = {};
|
||||
timeInfo.tm_year = static_cast<int>((packedAnchor >> 24) & 0x1F) + 100; // years since 1900 (2000-2031)
|
||||
timeInfo.tm_mon = static_cast<int>((packedAnchor >> 20) & 0xF);
|
||||
timeInfo.tm_mday = static_cast<int>((packedAnchor >> 14) & 0x3F) + 1;
|
||||
timeInfo.tm_hour = static_cast<int>((packedAnchor >> 6) & 0x1F);
|
||||
timeInfo.tm_min = static_cast<int>(packedAnchor & 0x3F);
|
||||
timeInfo.tm_sec = 0;
|
||||
timeInfo.tm_isdst = -1;
|
||||
|
||||
time_t anchor = mktime(&timeInfo) + stageOffset;
|
||||
|
||||
// Roll forward by whole periods to the most recent occurrence, preserving phase.
|
||||
time_t const period = static_cast<time_t>(occurenceMinutes) * 60;
|
||||
if (period > 0 && anchor < curTime)
|
||||
anchor += ((curTime - anchor) / period) * period;
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,11 @@ public:
|
||||
static time_t FindStartTimeForStage(const uint32_t* packedDates, uint8_t numDates,
|
||||
time_t stageOffset, uint32_t stageLengthMinutes, time_t curTime);
|
||||
|
||||
// Start time for a looping holiday event (Battleground Call to Arms): rolls the packed anchor
|
||||
// forward by whole occurenceMinutes periods to the most recent occurrence. Returns 0 if anchor is 0.
|
||||
static time_t FindLoopingStartTime(uint32_t packedAnchor, time_t stageOffset,
|
||||
uint32_t occurenceMinutes, time_t curTime);
|
||||
|
||||
private:
|
||||
// Julian Date conversions for lunar calculations
|
||||
static double DateToJulianDay(int year, int month, double day);
|
||||
|
||||
@@ -554,6 +554,8 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData)
|
||||
// leaks forever, permanently skewing team selection and blocking the
|
||||
// empty instance's cleanup.
|
||||
bg->DecreaseInvitedCount(teamId);
|
||||
if (bg->HasFreeSlots())
|
||||
bg->AddToBGFreeSlotQueue();
|
||||
_player->RemoveBattlegroundQueueId(bgQueueTypeId);
|
||||
_player->SetBattlegroundId(0, BATTLEGROUND_TYPE_NONE, PLAYER_MAX_BATTLEGROUND_QUEUES, false, false, TEAM_NEUTRAL);
|
||||
|
||||
|
||||
@@ -368,7 +368,8 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
|
||||
|
||||
// speedup check for heroic class disabled case
|
||||
uint32 req_level_for_heroic = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER);
|
||||
if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_DEATH_KNIGHT) && createInfo->Class == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
|
||||
if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_DEATH_KNIGHT) && createInfo->Class == CLASS_DEATH_KNIGHT
|
||||
&& req_level_for_heroic > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) && !HasAccountFlag(ACCOUNT_FLAG_DEATH_KNIGHT_OK))
|
||||
{
|
||||
SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT);
|
||||
return;
|
||||
@@ -435,7 +436,8 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
|
||||
}
|
||||
bool haveSameRace = false;
|
||||
uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER);
|
||||
bool hasHeroicReqLevel = (heroicReqLevel == 0);
|
||||
bool const deathKnightFlagAlreadySet = HasAccountFlag(ACCOUNT_FLAG_DEATH_KNIGHT_OK); // Account flag is superior to level requirement.
|
||||
bool hasHeroicReqLevel = (heroicReqLevel == 0) || deathKnightFlagAlreadySet;
|
||||
bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || HasPermission(rbac::RBAC_PERM_TWO_SIDE_CHARACTER_CREATION);
|
||||
uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS);
|
||||
bool checkDeathKnightReqs = !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_DEATH_KNIGHT) && createInfo->Class == CLASS_DEATH_KNIGHT;
|
||||
@@ -524,10 +526,16 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
|
||||
}
|
||||
}
|
||||
|
||||
if (checkDeathKnightReqs && !hasHeroicReqLevel)
|
||||
if (checkDeathKnightReqs)
|
||||
{
|
||||
SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT);
|
||||
return;
|
||||
if (!deathKnightFlagAlreadySet && hasHeroicReqLevel)
|
||||
UpdateAccountFlag(ACCOUNT_FLAG_DEATH_KNIGHT_OK);
|
||||
|
||||
if (!hasHeroicReqLevel)
|
||||
{
|
||||
SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check name uniqueness in the same step as saving to database
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "Creature.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "GameGraveyard.h"
|
||||
#include "Language.h"
|
||||
#include "NPCPackets.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
|
||||
@@ -398,6 +398,8 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
|
||||
if (id < bosses.size())
|
||||
{
|
||||
BossInfo* bossInfo = &bosses[id];
|
||||
MinionSet minions = bossInfo->minion;
|
||||
|
||||
sScriptMgr->OnBeforeSetBossState(id, state, bossInfo->state, instance);
|
||||
if (bossInfo->state == TO_BE_DECIDED) // loading
|
||||
{
|
||||
@@ -410,8 +412,8 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
|
||||
return false;
|
||||
|
||||
if (state == DONE)
|
||||
for (MinionSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i)
|
||||
if ((*i)->isWorldBoss() && (*i)->IsAlive())
|
||||
for (Creature* minion : minions)
|
||||
if (minion && minion->isWorldBoss() && minion->IsAlive())
|
||||
return false;
|
||||
|
||||
bossInfo->state = state;
|
||||
@@ -422,8 +424,9 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
|
||||
for (DoorSet::iterator i = bossInfo->door[type].begin(); i != bossInfo->door[type].end(); ++i)
|
||||
UpdateDoorState(*i);
|
||||
|
||||
for (MinionSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i)
|
||||
UpdateMinionState(*i, state);
|
||||
for (Creature* minion : minions)
|
||||
if (minion)
|
||||
UpdateMinionState(minion, state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,11 @@ void ScriptMgr::OnBattlegroundCreate(Battleground* bg)
|
||||
CALL_ENABLED_HOOKS(AllBattlegroundScript, ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_CREATE, script->OnBattlegroundCreate(bg));
|
||||
}
|
||||
|
||||
void ScriptMgr::OnBattlegroundSetup(Battleground* bg)
|
||||
{
|
||||
CALL_ENABLED_HOOKS(AllBattlegroundScript, ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_SETUP, script->OnBattlegroundSetup(bg));
|
||||
}
|
||||
|
||||
bool ScriptMgr::CanAddGroupToMatchingPool(BattlegroundQueue* queue, GroupQueueInfo* group, uint32 poolPlayerCount, Battleground* bg, BattlegroundBracketId bracketId)
|
||||
{
|
||||
CALL_ENABLED_BOOLEAN_HOOKS(AllBattlegroundScript, ALLBATTLEGROUNDHOOK_CAN_ADD_GROUP_TO_MATCHING_POOL, !script->CanAddGroupToMatchingPool(queue, group, poolPlayerCount, bg, bracketId));
|
||||
|
||||
@@ -41,6 +41,7 @@ enum AllBattlegroundHook
|
||||
ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_END,
|
||||
ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_DESTROY,
|
||||
ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_CREATE,
|
||||
ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_SETUP,
|
||||
ALLBATTLEGROUNDHOOK_CAN_ADD_GROUP_TO_MATCHING_POOL,
|
||||
ALLBATTLEGROUNDHOOK_GET_PLAYER_MATCHMAKING_RATING,
|
||||
ALLBATTLEGROUNDHOOK_END
|
||||
@@ -139,6 +140,13 @@ public:
|
||||
*/
|
||||
virtual void OnBattlegroundCreate(Battleground* /*bg*/) { }
|
||||
|
||||
/**
|
||||
* @brief This hook runs after the Battleground setup is completed (map is created and initial objects are spawned)
|
||||
*
|
||||
* @param bg Contains information about the Battleground
|
||||
*/
|
||||
virtual void OnBattlegroundSetup(Battleground* /*bg*/) { }
|
||||
|
||||
/**
|
||||
* @brief This hook runs before adding a group to the battleground matching pool
|
||||
*
|
||||
|
||||
@@ -634,6 +634,7 @@ public: /* BGScript */
|
||||
void OnBattlegroundEnd(Battleground* bg, TeamId winnerTeamId);
|
||||
void OnBattlegroundDestroy(Battleground* bg);
|
||||
void OnBattlegroundCreate(Battleground* bg);
|
||||
void OnBattlegroundSetup(Battleground* bg);
|
||||
bool CanAddGroupToMatchingPool(BattlegroundQueue* queue, GroupQueueInfo* group, uint32 poolPlayerCount, Battleground* bg, BattlegroundBracketId bracketId);
|
||||
bool GetPlayerMatchmakingRating(ObjectGuid playerGuid, BattlegroundTypeId bgTypeId, float& outRating);
|
||||
|
||||
|
||||
@@ -2989,7 +2989,7 @@ void AuraEffect::HandleFeignDeath(AuraApplication const* aurApp, uint8 mode, boo
|
||||
for (auto& pair : target->GetThreatMgr().GetThreatenedByMeList())
|
||||
pair.second->ScaleThreat(0.0f);
|
||||
|
||||
if (target->GetMap()->IsDungeon()) // feign death does not remove combat in dungeons
|
||||
if (target->GetInstanceScript() && target->GetInstanceScript()->IsEncounterInProgress())
|
||||
{
|
||||
target->AttackStop();
|
||||
if (Player* targetPlayer = target->ToPlayer())
|
||||
|
||||
@@ -201,6 +201,11 @@ void AuraApplication::BuildUpdatePacket(ByteBuffer& data, bool remove) const
|
||||
uint32 flags = _flags;
|
||||
if (aura->GetMaxDuration() > 0 && !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DO_NOT_DISPLAY_DURATION))
|
||||
flags |= AFLAG_DURATION;
|
||||
|
||||
// paladin auras are positive for self-cast only
|
||||
if (!IsSelfcasted() && aura->GetSpellInfo()->GetSpellSpecific() == SPELL_SPECIFIC_AURA)
|
||||
flags &= ~AFLAG_POSITIVE;
|
||||
|
||||
data << uint8(flags);
|
||||
data << uint8(aura->GetCasterLevel());
|
||||
// send stack amount for aura which could be stacked (never 0 - causes incorrect display) or charges
|
||||
|
||||
@@ -3951,6 +3951,44 @@ void Spell::_cast(bool skipCheck)
|
||||
}
|
||||
}
|
||||
|
||||
// CAST -> HIT -> FINISH ordering: fire CAST before handle_immediate so an aura
|
||||
// applied during HIT (e.g. Arcane Potency from Clearcasting) isn't consumed by
|
||||
// the same cast. Triggered spells skip this so periodic ticks (Blizzard etc.)
|
||||
// don't burn cast-charge buffs.
|
||||
if (m_originalCaster && !IsTriggered())
|
||||
{
|
||||
uint32 procAttacker = m_procAttacker;
|
||||
if (!procAttacker)
|
||||
{
|
||||
bool IsPositive = m_spellInfo->IsPositive();
|
||||
if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC)
|
||||
{
|
||||
procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG;
|
||||
}
|
||||
else
|
||||
{
|
||||
procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 hitMask = PROC_HIT_NORMAL;
|
||||
|
||||
for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
|
||||
{
|
||||
if (ihit->missCondition != SPELL_MISS_NONE)
|
||||
continue;
|
||||
|
||||
if (!ihit->crit)
|
||||
continue;
|
||||
|
||||
hitMask |= PROC_HIT_CRITICAL;
|
||||
break;
|
||||
}
|
||||
|
||||
Unit::ProcSkillsAndAuras(m_originalCaster, nullptr, procAttacker, PROC_FLAG_NONE, hitMask, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, nullptr, nullptr, PROC_SPELL_PHASE_CAST);
|
||||
}
|
||||
|
||||
// Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
|
||||
if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled())/* xinef: we dont need this || m_spellInfo->Id == 14157*/)
|
||||
{
|
||||
@@ -4008,46 +4046,6 @@ void Spell::_cast(bool skipCheck)
|
||||
if (modOwner)
|
||||
modOwner->SetSpellModTakingSpell(this, false);
|
||||
|
||||
// Handle procs on cast - only for non-triggered spells
|
||||
// Triggered spells (from auras, items, etc.) should not fire CAST phase procs
|
||||
// as they are not player-initiated casts. This prevents issues like Arcane Potency
|
||||
// charges being consumed by periodic damage effects (e.g., Blizzard ticks).
|
||||
// Must be called AFTER handle_immediate() so spell mods (like Missile Barrage's
|
||||
// duration reduction) are applied before the aura is consumed by the proc.
|
||||
if (m_originalCaster && !IsTriggered())
|
||||
{
|
||||
uint32 procAttacker = m_procAttacker;
|
||||
if (!procAttacker)
|
||||
{
|
||||
bool IsPositive = m_spellInfo->IsPositive();
|
||||
if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC)
|
||||
{
|
||||
procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG;
|
||||
}
|
||||
else
|
||||
{
|
||||
procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 hitMask = PROC_HIT_NORMAL;
|
||||
|
||||
for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
|
||||
{
|
||||
if (ihit->missCondition != SPELL_MISS_NONE)
|
||||
continue;
|
||||
|
||||
if (!ihit->crit)
|
||||
continue;
|
||||
|
||||
hitMask |= PROC_HIT_CRITICAL;
|
||||
break;
|
||||
}
|
||||
|
||||
Unit::ProcSkillsAndAuras(m_originalCaster, nullptr, procAttacker, PROC_FLAG_NONE, hitMask, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, nullptr, nullptr, PROC_SPELL_PHASE_CAST);
|
||||
}
|
||||
|
||||
if (std::vector<int32> const* spell_triggered = sSpellMgr->GetSpellLinked(m_spellInfo->Id))
|
||||
{
|
||||
for (int32 id : *spell_triggered)
|
||||
|
||||
@@ -2057,12 +2057,6 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
spellInfo->AttributesEx6 |= SPELL_ATTR6_IGNORE_PHASE_SHIFT;
|
||||
});
|
||||
|
||||
// Shadow Nova
|
||||
ApplySpellFix({ 62714, 65209 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->AttributesEx3 |= SPELL_ATTR3_ALWAYS_HIT;
|
||||
});
|
||||
|
||||
// Cosmic Smash (Algalon the Observer)
|
||||
ApplySpellFix({ 62293 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
@@ -5345,6 +5339,13 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(1520)); // Wyrmrest Vanquisher
|
||||
vse->m_flags |= VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE;
|
||||
|
||||
// Salvaged Siege Engine (Ulduar) extra passenger seats: the only salvaged vehicle seats
|
||||
// without this flag, leaving those passengers targetable by Flame Leviathan's AoE
|
||||
vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(4026));
|
||||
vse->m_flags |= VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE;
|
||||
vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(4027));
|
||||
vse->m_flags |= VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE;
|
||||
|
||||
// pussywizard: fix z offset for some vehicles:
|
||||
vse = const_cast<VehicleSeatEntry*>(sVehicleSeatStore.LookupEntry(6206)); // Marrowgar - Bone Spike
|
||||
vse->m_attachmentOffsetZ = 4.0f;
|
||||
|
||||
@@ -823,12 +823,14 @@ public:
|
||||
#define AuraEffectSplitFn(F, I) EffectSplitFunction(&F, I)
|
||||
|
||||
// executed when aura checks if it can proc
|
||||
// do not use this hook for triggering spellcasts/removing auras etc - runs mid proc-iteration and may be unsafe
|
||||
// example: DoCheckProc += AuraCheckProcFn(class::function);
|
||||
// where function is: bool function (ProcEventInfo& eventInfo);
|
||||
HookList<CheckProcHandler> DoCheckProc;
|
||||
#define AuraCheckProcFn(F) CheckProcHandlerFunction(&F)
|
||||
|
||||
// executed when aura effect checks if it can proc the aura
|
||||
// do not use this hook for triggering spellcasts/removing auras etc - runs mid proc-iteration and may be unsafe
|
||||
// example: DoCheckEffectProc += AuraCheckEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier);
|
||||
// where function is: bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo);
|
||||
HookList<CheckEffectProcHandler> DoCheckEffectProc;
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
#include "Chat.h"
|
||||
#include "Common.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "GameTime.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "MiscPackets.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Random.h"
|
||||
|
||||
class CreatureTextBuilder
|
||||
{
|
||||
@@ -196,6 +198,62 @@ void CreatureTextMgr::LoadCreatureTextLocales()
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void CreatureTextMgr::LoadCreatureTextOptions()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
mTextOptionsMap.clear();
|
||||
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEXT_OPTIONS);
|
||||
PreparedQueryResult result = WorldDatabase.Query(stmt);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 creature text options. DB table `creature_text_options` is empty.");
|
||||
LOG_INFO("server.loading", " ");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 creatureId = fields[0].Get<uint32>();
|
||||
uint8 groupId = fields[1].Get<uint8>();
|
||||
|
||||
CreatureTextOptions options;
|
||||
options.cooldown = fields[2].Get<uint32>();
|
||||
options.triggerChance = fields[3].Get<uint8>();
|
||||
options.playerOnly = fields[4].Get<bool>();
|
||||
|
||||
if (options.triggerChance > 100)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "CreatureTextMgr: Entry {}, Group {} in table `creature_text_options` has TriggerChance {} > 100. Setting to 100.", creatureId, groupId, options.triggerChance);
|
||||
options.triggerChance = 100;
|
||||
}
|
||||
|
||||
mTextOptionsMap[creatureId][groupId] = options;
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} Creature Text Options in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
CreatureTextOptions const* CreatureTextMgr::GetTextOptions(uint32 entry, uint8 group) const
|
||||
{
|
||||
auto entryItr = mTextOptionsMap.find(entry);
|
||||
if (entryItr == mTextOptionsMap.end())
|
||||
return nullptr;
|
||||
|
||||
auto groupItr = entryItr->second.find(group);
|
||||
if (groupItr == entryItr->second.end())
|
||||
return nullptr;
|
||||
|
||||
return &groupItr->second;
|
||||
}
|
||||
|
||||
uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject const* target /*= nullptr*/, ChatMsg msgType /*= CHAT_MSG_ADDON*/, Language language /*= LANG_ADDON*/, CreatureTextRange range /*= TEXT_RANGE_NORMAL*/, uint32 sound /*= 0*/, TeamId teamId /*= TEAM_NEUTRAL*/, bool gmOnly /*= false*/, Player* srcPlr /*= nullptr*/)
|
||||
{
|
||||
if (!source)
|
||||
@@ -218,6 +276,21 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
|
||||
}
|
||||
|
||||
CreatureTextGroup const& textGroupContainer = itr->second; //has all texts in the group
|
||||
|
||||
// Check creature_text_options for this group
|
||||
CreatureTextOptions const* options = GetTextOptions(source->GetEntry(), textGroup);
|
||||
if (options)
|
||||
{
|
||||
if (options->playerOnly && (!target || !target->IsPlayer()))
|
||||
return 0;
|
||||
|
||||
if (options->triggerChance < 100 && !roll_chance_i(options->triggerChance))
|
||||
return 0;
|
||||
|
||||
if (options->cooldown > 0 && source->IsTextOnCooldown(textGroup))
|
||||
return 0;
|
||||
}
|
||||
|
||||
CreatureTextRepeatIds repeatGroup = source->GetTextRepeatGroup(textGroup);//has all textIDs from the group that were already said
|
||||
CreatureTextGroup tempGroup;//will use this to talk after sorting repeatGroup
|
||||
|
||||
@@ -265,6 +338,11 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
|
||||
}
|
||||
|
||||
source->SetTextRepeatId(textGroup, iter->id);
|
||||
|
||||
// Set cooldown if creature_text_options specifies one
|
||||
if (options && options->cooldown > 0)
|
||||
source->SetTextCooldown(textGroup, options->cooldown);
|
||||
|
||||
return iter->duration;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,10 +71,20 @@ struct CreatureTextId
|
||||
uint32 textId;
|
||||
};
|
||||
|
||||
struct CreatureTextOptions
|
||||
{
|
||||
uint32 cooldown; // ms before this group can fire again
|
||||
uint8 triggerChance; // 0-100 chance to fire at all
|
||||
bool playerOnly; // only fire if target is a player
|
||||
};
|
||||
|
||||
typedef std::vector<CreatureTextEntry> CreatureTextGroup; // texts in a group
|
||||
typedef std::unordered_map<uint8, CreatureTextGroup> CreatureTextHolder; // groups for a creature by groupid
|
||||
typedef std::unordered_map<uint32, CreatureTextHolder> CreatureTextMap; // all creatures by entry
|
||||
|
||||
typedef std::unordered_map<uint8, CreatureTextOptions> CreatureTextOptionsMap; // options per group
|
||||
typedef std::unordered_map<uint32, CreatureTextOptionsMap> CreatureTextOptionsContainer; // per creature entry
|
||||
|
||||
typedef std::map<CreatureTextId, CreatureTextLocale> LocaleCreatureTextMap;
|
||||
|
||||
class CreatureTextMgr
|
||||
@@ -87,7 +97,9 @@ public:
|
||||
~CreatureTextMgr() { }
|
||||
void LoadCreatureTexts();
|
||||
void LoadCreatureTextLocales();
|
||||
void LoadCreatureTextOptions();
|
||||
CreatureTextMap const& GetTextMap() const { return mTextMap; }
|
||||
CreatureTextOptions const* GetTextOptions(uint32 entry, uint8 group) const;
|
||||
|
||||
void SendSound(Creature* source, uint32 sound, ChatMsg msgType, WorldObject const* target, CreatureTextRange range, TeamId teamId, bool gmOnly);
|
||||
void SendEmote(Unit* source, uint32 emote);
|
||||
@@ -105,6 +117,7 @@ private:
|
||||
|
||||
CreatureTextMap mTextMap;
|
||||
LocaleCreatureTextMap mLocaleTextMap;
|
||||
CreatureTextOptionsContainer mTextOptionsMap;
|
||||
};
|
||||
|
||||
#define sCreatureTextMgr CreatureTextMgr::instance()
|
||||
|
||||
@@ -870,6 +870,9 @@ void World::SetInitialWorldSettings()
|
||||
LOG_INFO("server.loading", "Loading Creature Texts...");
|
||||
sCreatureTextMgr->LoadCreatureTexts();
|
||||
|
||||
LOG_INFO("server.loading", "Loading Creature Text Options...");
|
||||
sCreatureTextMgr->LoadCreatureTextOptions();
|
||||
|
||||
LOG_INFO("server.loading", "Loading Creature Text Locales...");
|
||||
sCreatureTextMgr->LoadCreatureTextLocales();
|
||||
|
||||
@@ -1286,6 +1289,12 @@ void World::Update(uint32 diff)
|
||||
stmt->SetData(2, realm.Id.Realm);
|
||||
stmt->SetData(3, uint32(GameTime::GetStartTime().count()));
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
// Re-assert this realm as online in case the offline flag was set externally (e.g. an authserver restart).
|
||||
LoginDatabasePreparedStatement* onlineStmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_REALM_ONLINE);
|
||||
onlineStmt->SetData(0, uint8(REALM_FLAG_OFFLINE));
|
||||
onlineStmt->SetData(1, realm.Id.Realm);
|
||||
LoginDatabase.Execute(onlineStmt);
|
||||
}
|
||||
|
||||
///- Process Game events when necessary
|
||||
|
||||
@@ -426,6 +426,12 @@ void WorldConfig::BuildConfigCache()
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_PREP_TIME, "Battleground.PrepTime", 120);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, "Battleground.Override.LowLevels.MinPlayers", 0);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_AV, "Battleground.Override.LowLevels.MinPlayers.AV", 0);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_WS, "Battleground.Override.LowLevels.MinPlayers.WS", 0);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_AB, "Battleground.Override.LowLevels.MinPlayers.AB", 0);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_EY, "Battleground.Override.LowLevels.MinPlayers.EY", 0);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_SA, "Battleground.Override.LowLevels.MinPlayers.SA", 0);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_IC, "Battleground.Override.LowLevels.MinPlayers.IC", 0);
|
||||
SetConfigValue<bool>(CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, "Battleground.DisableQuestShareInBG", false);
|
||||
SetConfigValue<bool>(CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG, "Battleground.DisableReadyCheckInBG", false);
|
||||
SetConfigValue<bool>(CONFIG_BATTLEGROUND_CAST_DESERTER, "Battleground.CastDeserter", true);
|
||||
|
||||
@@ -264,6 +264,12 @@ enum ServerConfigs
|
||||
CONFIG_DISABLE_BREATHING,
|
||||
CONFIG_BATTLEGROUND_PREP_TIME,
|
||||
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS,
|
||||
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_AV,
|
||||
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_WS,
|
||||
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_AB,
|
||||
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_EY,
|
||||
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_SA,
|
||||
CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS_IC,
|
||||
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY,
|
||||
CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER,
|
||||
CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "CommandScript.h"
|
||||
#include "Config.h"
|
||||
#include "Language.h"
|
||||
#include "RBAC.h"
|
||||
|
||||
using namespace Acore::ChatCommands;
|
||||
|
||||
@@ -32,10 +33,10 @@ public:
|
||||
{
|
||||
static ChatCommandTable autobroadcastCommandTable =
|
||||
{
|
||||
{ "list", HandleAutobroadcastListCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "add", HandleAutobroadcastAddCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "locale", HandleAutobroadcastLocaleCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "remove", HandleAutobroadcastRemoveCommand, SEC_ADMINISTRATOR, Console::Yes }
|
||||
{ "list", HandleAutobroadcastListCommand, rbac::RBAC_PERM_COMMAND_AUTOBROADCAST_LIST, Console::Yes },
|
||||
{ "add", HandleAutobroadcastAddCommand, rbac::RBAC_PERM_COMMAND_AUTOBROADCAST_ADD, Console::Yes },
|
||||
{ "locale", HandleAutobroadcastLocaleCommand, rbac::RBAC_PERM_COMMAND_AUTOBROADCAST_LOCALE, Console::Yes },
|
||||
{ "remove", HandleAutobroadcastRemoveCommand, rbac::RBAC_PERM_COMMAND_AUTOBROADCAST_REMOVE, Console::Yes }
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
|
||||
@@ -223,7 +223,7 @@ public:
|
||||
std::string str = name.data(); // Making subtractions to the last character does not with in string_view
|
||||
WorldDatabase.EscapeString(str);
|
||||
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry FROM creature_template WHERE name = \"{}\" LIMIT 1", str);
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry FROM creature_template WHERE name = '{}' LIMIT 1", str);
|
||||
if (!result)
|
||||
{
|
||||
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "RBAC.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Timer.h"
|
||||
|
||||
@@ -40,8 +41,8 @@ public:
|
||||
{
|
||||
static ChatCommandTable mailCommandTable =
|
||||
{
|
||||
{ "list", HandleMailListCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "return", HandleMailReturnCommand, SEC_GAMEMASTER, Console::Yes }
|
||||
{ "list", HandleMailListCommand, rbac::RBAC_PERM_COMMAND_MAIL_LIST, Console::Yes },
|
||||
{ "return", HandleMailReturnCommand, rbac::RBAC_PERM_COMMAND_MAIL_RETURN, Console::Yes }
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
|
||||
@@ -200,11 +200,11 @@ public:
|
||||
{ "add", npcAddCommandTable },
|
||||
{ "delete", npcDeleteCommandTable },
|
||||
{ "follow", npcFollowCommandTable },
|
||||
{ "load", HandleNpcLoadCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "load", HandleNpcLoadCommand, rbac::RBAC_PERM_COMMAND_NPC_LOAD, Console::Yes },
|
||||
{ "set", npcSetCommandTable },
|
||||
{ "showloot", HandleNpcShowLootCommand, rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, Console::No },
|
||||
{ "spawngroup", HandleNpcSpawnGroupCommand, SEC_ADMINISTRATOR, Console::No },
|
||||
{ "despawngroup", HandleNpcDespawnGroupCommand, SEC_ADMINISTRATOR, Console::No }
|
||||
{ "showloot", HandleNpcShowLootCommand, rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, Console::No },
|
||||
{ "spawngroup", HandleNpcSpawnGroupCommand, rbac::RBAC_PERM_COMMAND_NPC_SPAWNGROUP, Console::No },
|
||||
{ "despawngroup", HandleNpcDespawnGroupCommand, rbac::RBAC_PERM_COMMAND_NPC_DESPAWNGROUP, Console::No }
|
||||
};
|
||||
static ChatCommandTable commandTable =
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "PoolMgr.h"
|
||||
#include "RBAC.h"
|
||||
|
||||
using namespace Acore::ChatCommands;
|
||||
|
||||
@@ -37,8 +38,8 @@ public:
|
||||
{
|
||||
static ChatCommandTable poolCommandTable =
|
||||
{
|
||||
{ "info", HandlePoolInfoCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "lookup", HandlePoolLookupCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "info", HandlePoolInfoCommand, rbac::RBAC_PERM_COMMAND_POOL_INFO, Console::Yes },
|
||||
{ "lookup", HandlePoolLookupCommand, rbac::RBAC_PERM_COMMAND_POOL_LOOKUP, Console::No },
|
||||
};
|
||||
static ChatCommandTable commandTable =
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "CommandScript.h"
|
||||
#include "DBCStructure.h"
|
||||
#include "Language.h"
|
||||
#include "RBAC.h"
|
||||
#include "SmartEnum.h"
|
||||
#include "SpellAuraDefines.h"
|
||||
#include "SpellInfo.h"
|
||||
@@ -35,10 +36,10 @@ public:
|
||||
{
|
||||
static ChatCommandTable spellinfoCommandTable =
|
||||
{
|
||||
{ "attributes", HandleSpellInfoAttributesCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "effects", HandleSpellInfoEffectsCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "targets", HandleSpellInfoTargetsCommand, SEC_GAMEMASTER, Console::Yes },
|
||||
{ "all", HandleSpellInfoAllCommand, SEC_GAMEMASTER, Console::Yes }
|
||||
{ "attributes", HandleSpellInfoAttributesCommand, rbac::RBAC_PERM_COMMAND_SPELLINFO_ATTRIBUTES, Console::Yes },
|
||||
{ "effects", HandleSpellInfoEffectsCommand, rbac::RBAC_PERM_COMMAND_SPELLINFO_EFFECTS, Console::Yes },
|
||||
{ "targets", HandleSpellInfoTargetsCommand, rbac::RBAC_PERM_COMMAND_SPELLINFO_TARGETS, Console::Yes },
|
||||
{ "all", HandleSpellInfoAllCommand, rbac::RBAC_PERM_COMMAND_SPELLINFO_ALL, Console::Yes }
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
|
||||
+1
-2
@@ -219,8 +219,7 @@ struct boss_majordomo : public BossAI
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (roll_chance_i(25) && victim->IsPlayer())
|
||||
Talk(SAY_SLAY);
|
||||
Talk(SAY_SLAY, victim);
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit* /*attacker*/) override
|
||||
|
||||
@@ -209,8 +209,7 @@ struct boss_ragnaros : public BossAI
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (roll_chance_i(25) && victim->IsPlayer())
|
||||
Talk(SAY_KILL);
|
||||
Talk(SAY_KILL, victim);
|
||||
}
|
||||
|
||||
void AttackStart(Unit* target) override
|
||||
|
||||
@@ -117,7 +117,6 @@ struct boss_moroes : public BossAI
|
||||
{
|
||||
BossAI::Reset();
|
||||
DoCastSelf(SPELL_DUAL_WIELD, true);
|
||||
_recentlySpoken = false;
|
||||
_vanished = false;
|
||||
me->SetImmuneToAll(false);
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
@@ -170,15 +169,7 @@ struct boss_moroes : public BossAI
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (!_recentlySpoken && victim->IsPlayer())
|
||||
{
|
||||
Talk(SAY_KILL);
|
||||
_recentlySpoken = true;
|
||||
scheduler.Schedule(5s, [this](TaskContext)
|
||||
{
|
||||
_recentlySpoken = false;
|
||||
});
|
||||
}
|
||||
Talk(SAY_KILL, victim);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
@@ -243,7 +234,6 @@ struct boss_moroes : public BossAI
|
||||
private:
|
||||
EventMap _events2;
|
||||
uint8 _activeGuests;
|
||||
bool _recentlySpoken;
|
||||
bool _vanished;
|
||||
};
|
||||
|
||||
|
||||
@@ -104,8 +104,7 @@ struct boss_brutallus : public BossAI
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (victim->IsPlayer() && roll_chance_i(50))
|
||||
Talk(YELL_KILL);
|
||||
Talk(YELL_KILL, victim);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
|
||||
@@ -186,8 +186,7 @@ struct boss_felmyst : public BossAI
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (victim->IsPlayer() && roll_chance_i(50))
|
||||
Talk(YELL_KILL);
|
||||
Talk(YELL_KILL, victim);
|
||||
}
|
||||
|
||||
void SpellHitTarget(Unit* target, const SpellInfo* spell) override
|
||||
|
||||
@@ -260,8 +260,7 @@ struct boss_kalecgos : public BossAI
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (victim->IsPlayer() && roll_chance_i(50))
|
||||
Talk(SAY_EVIL_SLAY);
|
||||
Talk(SAY_EVIL_SLAY, victim);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user